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 if (mainIter != gridLayoutInfo_.gridMatrix_.end() && mainIter->second.size() < crossCount_) {
620 auto currentIndex = gridLayoutInfo_.endIndex_ + 1;
621 cellAveLength_ = -1.0f;
622 bool hasNormalItem = false;
623 lastCross_ = 0;
624 for (uint32_t i = mainIter->second.size(); i < crossCount_; i++) {
625 // Step1. Get wrapper of [GridItem]
626 auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
627 if (!itemWrapper) {
628 break;
629 }
630 // Step2. Measure child
631 auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
632 auto childState = MeasureNewChild(frameSize, currentIndex, layoutWrapper, itemWrapper, false);
633 if (childState == -1) {
634 cellAveLength_ = LessNotEqual(cellAveLength_, 0.0)
635 ? gridLayoutInfo_.lineHeightMap_.find(currentMainLineIndex_ - 1)->second
636 : cellAveLength_;
637 --currentIndex;
638 break;
639 }
640 i += static_cast<uint32_t>(childState) - 1;
641 // Step3. Measure [GridItem]
642 LargeItemLineHeight(itemWrapper, hasNormalItem);
643 gridLayoutInfo_.endIndex_ = currentIndex;
644 currentIndex++;
645 }
646 }
647
648 if (GreatNotEqual(mainLength, mainSize)) {
649 if (IsScrollToEndLine()) {
650 TAG_LOGI(AceLogTag::ACE_GRID, "scroll to end line with index:%{public}d", moveToEndLineIndex_);
651 // scrollToIndex(AUTO) on first layout
652 moveToEndLineIndex_ = -1;
653 }
654 return;
655 }
656 // When [mainLength] is still less than [mainSize], do [FillNewLineBackward] repeatedly until filling up the lower
657 // part of the viewport
658 while (LessNotEqual(mainLength, mainSize)) {
659 float lineHeight = FillNewLineBackward(crossSize, mainSize, layoutWrapper, false);
660 if (GreatOrEqual(lineHeight, 0.0)) {
661 mainLength += (lineHeight + mainGap_);
662 continue;
663 }
664 gridLayoutInfo_.reachEnd_ = true;
665 return;
666 };
667 // last line make LessNotEqual(mainLength, mainSize) and continue is reach end too
668 gridLayoutInfo_.reachEnd_ = gridLayoutInfo_.endIndex_ == gridLayoutInfo_.childrenCount_ - 1;
669 }
670
CalculateLargeItemOffset(OffsetF currOffset,int32_t itemIndex,int32_t currLineIndex,int32_t currentCrossIndex)671 OffsetF GridScrollLayoutAlgorithm::CalculateLargeItemOffset(
672 OffsetF currOffset, int32_t itemIndex, int32_t currLineIndex, int32_t currentCrossIndex)
673 {
674 OffsetF offset = currOffset;
675 for (int32_t lastCrossIndex = currLineIndex - 1; lastCrossIndex >= 0; lastCrossIndex--) {
676 auto lastGridMatrixIter = gridLayoutInfo_.gridMatrix_.find(lastCrossIndex);
677 if (lastGridMatrixIter == gridLayoutInfo_.gridMatrix_.end()) {
678 continue;
679 }
680 const auto& lastGridItemRecord = lastGridMatrixIter->second;
681 auto lastLineCrossItem = lastGridItemRecord.find(currentCrossIndex);
682 if (lastLineCrossItem == lastGridItemRecord.end()) {
683 continue;
684 }
685 if (lastLineCrossItem->second == itemIndex) {
686 offset -= axis_ == Axis::VERTICAL ? OffsetF(0, gridLayoutInfo_.lineHeightMap_[lastCrossIndex] + mainGap_)
687 : OffsetF(gridLayoutInfo_.lineHeightMap_[lastCrossIndex] + mainGap_, 0.0);
688 } else {
689 break;
690 }
691 }
692
693 return offset;
694 }
695
NeedAdjust(const RefPtr<GridItemLayoutProperty> & itemLayoutProperty)696 bool GridScrollLayoutAlgorithm::NeedAdjust(const RefPtr<GridItemLayoutProperty>& itemLayoutProperty)
697 {
698 bool needAdjust = false;
699 auto main = axis_ == Axis::VERTICAL ? mainCount_ : crossCount_;
700 auto cross = axis_ == Axis::VERTICAL ? crossCount_ : mainCount_;
701 if (itemLayoutProperty->GetRowStart().has_value()) {
702 currentItemRowStart_ = itemLayoutProperty->GetRowStart().value_or(-1);
703 if ((currentItemRowStart_ < 0) || (currentItemRowStart_ >= static_cast<int32_t>(main))) {
704 needAdjust = true;
705 }
706 }
707 if (itemLayoutProperty->GetRowEnd().has_value()) {
708 currentItemRowEnd_ = itemLayoutProperty->GetRowEnd().value_or(-1);
709 if ((currentItemRowEnd_ < 0) || (currentItemRowEnd_ >= static_cast<int32_t>(main))) {
710 needAdjust = true;
711 }
712 }
713 if (itemLayoutProperty->GetColumnStart().has_value()) {
714 currentItemColStart_ = itemLayoutProperty->GetColumnStart().value_or(-1);
715 if ((currentItemColStart_ < 0) || (currentItemColStart_ >= static_cast<int32_t>(cross))) {
716 needAdjust = true;
717 }
718 }
719 if (itemLayoutProperty->GetColumnEnd().has_value()) {
720 currentItemColEnd_ = itemLayoutProperty->GetColumnEnd().value_or(-1);
721 if ((currentItemColEnd_ < 0) || (currentItemColEnd_ >= static_cast<int32_t>(cross))) {
722 needAdjust = true;
723 }
724 }
725 return needAdjust;
726 }
727
AdjustRowColSpan(const RefPtr<LayoutWrapper> & itemLayoutWrapper,LayoutWrapper *,int32_t)728 void GridScrollLayoutAlgorithm::AdjustRowColSpan(
729 const RefPtr<LayoutWrapper>& itemLayoutWrapper, LayoutWrapper* /* layoutWrapper */, int32_t /* itemIndex */)
730 {
731 auto itemLayoutProperty = DynamicCast<GridItemLayoutProperty>(itemLayoutWrapper->GetLayoutProperty());
732 CHECK_NULL_VOID(itemLayoutProperty);
733 bool needAdjust = false;
734 currentItemRowSpan_ = 1;
735 currentItemColSpan_ = 1;
736 currentItemRowStart_ = -1;
737 currentItemColStart_ = -1;
738 currentItemColEnd_ = -1;
739 currentItemRowEnd_ = -1;
740 needAdjust = NeedAdjust(itemLayoutProperty);
741 if (!needAdjust) {
742 currentItemRowSpan_ = std::max(currentItemRowEnd_ - currentItemRowStart_ + 1, 1);
743 currentItemColSpan_ = std::max(currentItemColEnd_ - currentItemColStart_ + 1, 1);
744 } else {
745 currentItemRowStart_ = -1;
746 currentItemColStart_ = -1;
747 currentItemColEnd_ = -1;
748 currentItemRowEnd_ = -1;
749 }
750 if ((currentItemRowStart_ == -1 && currentItemRowEnd_ != -1) ||
751 (currentItemRowEnd_ == -1 && currentItemRowStart_ != -1) ||
752 (currentItemColStart_ == -1 && currentItemColEnd_ != -1) ||
753 (currentItemColEnd_ == -1 && currentItemColStart_ != -1)) {
754 currentItemRowSpan_ = 1;
755 currentItemColSpan_ = 1;
756 currentItemRowStart_ = -1;
757 currentItemColStart_ = -1;
758 currentItemColEnd_ = -1;
759 currentItemRowEnd_ = -1;
760 }
761 if (currentItemRowSpan_ > 1 || currentItemColSpan_ > 1) {
762 gridLayoutInfo_.hasBigItem_ = true;
763 }
764
765 int32_t mainSpan = axis_ == Axis::VERTICAL ? currentItemRowSpan_ : currentItemColSpan_;
766 if (mainSpan > 1) {
767 gridLayoutInfo_.hasMultiLineItem_ = true;
768 }
769
770 itemLayoutProperty->UpdateRealRowSpan(currentItemRowSpan_);
771 itemLayoutProperty->UpdateRealColumnSpan(currentItemColSpan_);
772 }
773
LargeItemLineHeight(const RefPtr<LayoutWrapper> & itemWrapper,bool & hasNormalItem)774 void GridScrollLayoutAlgorithm::LargeItemLineHeight(const RefPtr<LayoutWrapper>& itemWrapper, bool& hasNormalItem)
775 {
776 AdjustRowColSpan(itemWrapper, nullptr, 0);
777 auto mainSpan = axis_ == Axis::VERTICAL ? currentItemRowSpan_ : currentItemColSpan_;
778 auto itemSize = itemWrapper->GetGeometryNode()->GetMarginFrameSize();
779 if (mainSpan == 1) {
780 cellAveLength_ = std::max(GetMainAxisSize(itemSize, gridLayoutInfo_.axis_), cellAveLength_);
781 hasNormalItem = true;
782 }
783
784 if ((mainSpan > 1) && !hasNormalItem) {
785 cellAveLength_ =
786 std::max((GetMainAxisSize(itemSize, gridLayoutInfo_.axis_) - (mainGap_ * (mainSpan - 1))) / mainSpan,
787 cellAveLength_);
788 }
789 }
790
IsIndexInMatrix(int32_t index,int32_t & startLine)791 bool GridScrollLayoutAlgorithm::IsIndexInMatrix(int32_t index, int32_t& startLine)
792 {
793 auto iter = std::find_if(gridLayoutInfo_.gridMatrix_.begin(), gridLayoutInfo_.gridMatrix_.end(),
794 [index, &startLine](const std::pair<int32_t, std::map<int32_t, int32_t>>& item) {
795 for (auto& subitem : item.second) {
796 if (subitem.second == index) {
797 startLine = item.first;
798 return true;
799 }
800 }
801 return false;
802 });
803 return (iter != gridLayoutInfo_.gridMatrix_.end());
804 }
805
GetTargetIndexInfoWithBenchMark(LayoutWrapper * layoutWrapper,bool isTargetBackward,int32_t targetIndex)806 void GridScrollLayoutAlgorithm::GetTargetIndexInfoWithBenchMark(
807 LayoutWrapper* layoutWrapper, bool isTargetBackward, int32_t targetIndex)
808 {
809 int32_t benchmarkIndex = (isTargetBackward && !gridLayoutInfo_.gridMatrix_.empty())
810 ? gridLayoutInfo_.gridMatrix_.rbegin()->second.rbegin()->second + 1
811 : 0;
812 int32_t mainStartIndex = (isTargetBackward && !gridLayoutInfo_.gridMatrix_.empty())
813 ? gridLayoutInfo_.gridMatrix_.rbegin()->first + 1
814 : 0;
815 int32_t currentIndex = benchmarkIndex;
816 int32_t headOfMainStartLine = currentIndex;
817
818 while (currentIndex < targetIndex) {
819 int32_t crossGridReserve = gridLayoutInfo_.crossCount_;
820 /* go through a new line */
821 while ((crossGridReserve > 0) && (currentIndex <= targetIndex)) {
822 auto currentWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex, false);
823 CHECK_NULL_VOID(currentWrapper);
824 auto layoutProperty = DynamicCast<GridItemLayoutProperty>(currentWrapper->GetLayoutProperty());
825 CHECK_NULL_VOID(layoutProperty);
826 auto gridSpan = layoutProperty->GetCrossSpan(gridLayoutInfo_.axis_);
827 if (crossGridReserve >= gridSpan) {
828 crossGridReserve -= gridSpan;
829 } else if (gridLayoutInfo_.crossCount_ >= gridSpan) {
830 ++mainStartIndex;
831 headOfMainStartLine = currentIndex;
832 crossGridReserve = gridLayoutInfo_.crossCount_ - gridSpan;
833 }
834 ++currentIndex;
835 }
836 if (currentIndex > targetIndex) {
837 break;
838 }
839 ++mainStartIndex;
840 headOfMainStartLine = currentIndex;
841 }
842 gridLayoutInfo_.startMainLineIndex_ = mainStartIndex;
843 gridLayoutInfo_.startIndex_ = headOfMainStartLine;
844 gridLayoutInfo_.endIndex_ = headOfMainStartLine - 1;
845 gridLayoutInfo_.prevOffset_ = 0;
846 gridLayoutInfo_.currentOffset_ = 0;
847 gridLayoutInfo_.ResetPositionFlags();
848 gridLayoutInfo_.gridMatrix_.clear();
849 gridLayoutInfo_.lineHeightMap_.clear();
850 gridLayoutInfo_.irregularItemsPosition_.clear();
851 }
852
UpdateGridLayoutInfo(LayoutWrapper * layoutWrapper,float mainSize)853 void GridScrollLayoutAlgorithm::UpdateGridLayoutInfo(LayoutWrapper* layoutWrapper, float mainSize)
854 {
855 /* 1. Have gotten gridLayoutInfo_.startMainLineIndex_ and directly jump to it */
856 if (gridLayoutInfo_.jumpIndex_ < 0 && gridLayoutInfo_.jumpIndex_ != LAST_ITEM) {
857 return;
858 }
859 if (gridLayoutInfo_.jumpIndex_ == LAST_ITEM) {
860 gridLayoutInfo_.jumpIndex_ = gridLayoutInfo_.childrenCount_ - 1;
861 }
862 /* 2. Need to find out the startMainLineIndex according to startIndex */
863 int32_t targetIndex = gridLayoutInfo_.jumpIndex_;
864 /* 2.1 invalid targetIndex */
865 if (gridLayoutInfo_.childrenCount_ <= targetIndex) {
866 return;
867 }
868
869 switch (gridLayoutInfo_.scrollAlign_) {
870 case ScrollAlign::START:
871 case ScrollAlign::END:
872 case ScrollAlign::CENTER:
873 ScrollToIndexStart(layoutWrapper, targetIndex);
874 return;
875 default:
876 ScrollToIndexAuto(layoutWrapper, mainSize, targetIndex);
877 }
878 }
879
IsScrollToEndLine() const880 bool GridScrollLayoutAlgorithm::IsScrollToEndLine() const
881 {
882 return moveToEndLineIndex_ > 0 && gridLayoutInfo_.endIndex_ >= moveToEndLineIndex_;
883 }
884
IsEndLineInScreenWithGap(int32_t targetLine,float totalViewHeight,float mainSize) const885 bool GridScrollLayoutAlgorithm::IsEndLineInScreenWithGap(
886 int32_t targetLine, float totalViewHeight, float mainSize) const
887 {
888 return targetLine == gridLayoutInfo_.endMainLineIndex_ &&
889 LessOrEqual(totalViewHeight + gridLayoutInfo_.currentOffset_, mainSize);
890 }
891
ScrollToIndexAuto(LayoutWrapper * layoutWrapper,float mainSize,int32_t targetIndex)892 void GridScrollLayoutAlgorithm::ScrollToIndexAuto(LayoutWrapper* layoutWrapper, float mainSize, int32_t targetIndex)
893 {
894 int32_t startLine = 0;
895 if (IsIndexInMatrix(targetIndex, startLine)) {
896 auto& info = gridLayoutInfo_;
897 if (startLine == info.startMainLineIndex_ && info.startMainLineIndex_ == info.endMainLineIndex_) {
898 // startLine occupies the whole viewport
899 return;
900 }
901 if (startLine < gridLayoutInfo_.endMainLineIndex_ && startLine > gridLayoutInfo_.startMainLineIndex_) {
902 return;
903 }
904
905 if (startLine >= gridLayoutInfo_.endMainLineIndex_) {
906 auto totalViewHeight = gridLayoutInfo_.GetTotalHeightOfItemsInView(mainGap_);
907 if (IsEndLineInScreenWithGap(startLine, totalViewHeight, mainSize)) {
908 return;
909 }
910 // When ScrollAlign::AUTO and startLine is greater than endMainLineIndex, the effect of
911 // ScrollToIndex is the same as ScrollAlign::END.
912 gridLayoutInfo_.scrollAlign_ = ScrollAlign::END;
913 }
914
915 // startLine <= gridLayoutInfo_.startMainLineIndex_
916 gridLayoutInfo_.startMainLineIndex_ = startLine;
917 gridLayoutInfo_.UpdateStartIndexByStartLine();
918 gridLayoutInfo_.prevOffset_ = 0;
919 gridLayoutInfo_.currentOffset_ = 0;
920 gridLayoutInfo_.ResetPositionFlags();
921 return;
922 }
923
924 /* 2.3 targetIndex is out of the matrix */
925 bool isTargetBackward = true;
926 if (!gridLayoutInfo_.gridMatrix_.empty()) {
927 if (targetIndex < gridLayoutInfo_.gridMatrix_.begin()->second.begin()->second) {
928 isTargetBackward = false;
929 } else if (targetIndex > gridLayoutInfo_.gridMatrix_.rbegin()->second.rbegin()->second) {
930 isTargetBackward = true;
931 } else {
932 return;
933 }
934 }
935 auto grid = layoutWrapper->GetHostNode();
936 CHECK_NULL_VOID(grid);
937 grid->ChildrenUpdatedFrom(0);
938 GetTargetIndexInfoWithBenchMark(layoutWrapper, isTargetBackward, targetIndex);
939 moveToEndLineIndex_ = isTargetBackward ? targetIndex : moveToEndLineIndex_;
940 }
941
ScrollToIndexStart(LayoutWrapper * layoutWrapper,int32_t targetIndex)942 void GridScrollLayoutAlgorithm::ScrollToIndexStart(LayoutWrapper* layoutWrapper, int32_t targetIndex)
943 {
944 int32_t startLine = 0;
945 /* targetIndex is in the matrix */
946 if (IsIndexInMatrix(targetIndex, startLine)) {
947 if (startLine == gridLayoutInfo_.startMainLineIndex_) {
948 gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
949 gridLayoutInfo_.currentOffset_ = 0;
950 gridLayoutInfo_.ResetPositionFlags();
951 return;
952 }
953
954 gridLayoutInfo_.startMainLineIndex_ = startLine;
955 gridLayoutInfo_.UpdateStartIndexByStartLine();
956 gridLayoutInfo_.prevOffset_ = 0;
957 gridLayoutInfo_.currentOffset_ = 0;
958 gridLayoutInfo_.ResetPositionFlags();
959 return;
960 }
961 /* targetIndex is out of the matrix */
962 bool isTargetBackward = true;
963 if (!gridLayoutInfo_.gridMatrix_.empty()) {
964 if (targetIndex < gridLayoutInfo_.gridMatrix_.begin()->second.begin()->second) {
965 isTargetBackward = false;
966 } else if (targetIndex > gridLayoutInfo_.gridMatrix_.rbegin()->second.rbegin()->second) {
967 isTargetBackward = true;
968 } else {
969 return;
970 }
971 }
972 auto grid = layoutWrapper->GetHostNode();
973 CHECK_NULL_VOID(grid);
974 grid->ChildrenUpdatedFrom(0);
975 GetTargetIndexInfoWithBenchMark(layoutWrapper, isTargetBackward, targetIndex);
976 }
977
UpdateCurrentOffsetForJumpTo(float mainSize)978 void GridScrollLayoutAlgorithm::UpdateCurrentOffsetForJumpTo(float mainSize)
979 {
980 if (gridLayoutInfo_.scrollAlign_ == ScrollAlign::CENTER || gridLayoutInfo_.scrollAlign_ == ScrollAlign::END) {
981 int32_t startLine = 0;
982 /* targetIndex is in the matrix */
983 if (IsIndexInMatrix(gridLayoutInfo_.jumpIndex_, startLine)) {
984 // scroll to end of the screen
985 gridLayoutInfo_.currentOffset_ =
986 mainSize - gridLayoutInfo_.lineHeightMap_[startLine] - gridLayoutInfo_.contentEndPadding_;
987 // scroll to center of the screen
988 if (gridLayoutInfo_.scrollAlign_ == ScrollAlign::CENTER) {
989 gridLayoutInfo_.currentOffset_ /= 2;
990 }
991 gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
992 } else {
993 /* targetIndex is out of the matrix */
994 TAG_LOGW(
995 AceLogTag::ACE_GRID, "can not find jumpIndex in Grid Matrix :%{public}d", gridLayoutInfo_.jumpIndex_);
996 }
997 }
998 if (gridLayoutInfo_.extraOffset_.has_value() && !gridLayoutInfo_.targetIndex_.has_value()) {
999 gridLayoutInfo_.currentOffset_ += gridLayoutInfo_.extraOffset_.value();
1000 }
1001 }
1002
MeasureRecordedItems(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)1003 float GridScrollLayoutAlgorithm::MeasureRecordedItems(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
1004 {
1005 currentMainLineIndex_ = gridLayoutInfo_.startMainLineIndex_ - 1;
1006 float mainLength = gridLayoutInfo_.currentOffset_;
1007 // already at start line, do not use offset for mainLength
1008 if (gridLayoutInfo_.startMainLineIndex_ == 0 && GreatNotEqual(mainLength, 0)) {
1009 mainLength = 0;
1010 }
1011 UseCurrentLines(mainSize, crossSize, layoutWrapper, mainLength);
1012 return mainLength;
1013 }
1014
1015 namespace {
OneLineMovesOffViewportFromAbove(float mainLength,float lineHeight)1016 inline bool OneLineMovesOffViewportFromAbove(float mainLength, float lineHeight)
1017 {
1018 return LessNotEqual(mainLength, 0.0) || (NearZero(mainLength) && GreatNotEqual(lineHeight, 0.0f));
1019 }
1020 } // namespace
1021
MeasureExistingLine(int32_t line,float & mainLength,int32_t & endIdx)1022 bool GridScrollLayoutAlgorithm::MeasureExistingLine(int32_t line, float& mainLength, int32_t& endIdx)
1023 {
1024 auto it = gridLayoutInfo_.gridMatrix_.find(line);
1025 if (it == gridLayoutInfo_.gridMatrix_.end() ||
1026 gridLayoutInfo_.lineHeightMap_.find(line) == gridLayoutInfo_.lineHeightMap_.end()) {
1027 return false;
1028 }
1029 int32_t idx = -1;
1030 bool hasNormalItem = false;
1031 cellAveLength_ = -1.0f;
1032 for (const auto& cell : it->second) {
1033 if (idx == cell.second) {
1034 continue;
1035 }
1036 idx = cell.second;
1037 if (idx == -1) {
1038 // move from another grid
1039 continue;
1040 }
1041 auto item = wrapper_->GetOrCreateChildByIndex(idx);
1042 if (!item) {
1043 break;
1044 }
1045 AdjustRowColSpan(item, wrapper_, idx);
1046 auto crossStart = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1047 if (crossStart == -1) {
1048 MeasureChildPlaced(frameSize_, idx, cell.first, wrapper_, item);
1049 } else {
1050 MeasureChildPlaced(frameSize_, idx, crossStart, wrapper_, item);
1051 }
1052 // Record end index. When fill new line, the [endIndex_] will be the first item index to request
1053 LargeItemLineHeight(item, hasNormalItem);
1054 endIdx = std::max(idx, endIdx);
1055 gridLayoutInfo_.endIndex_ = endIdx;
1056 }
1057
1058 if (NonNegative(cellAveLength_)) { // Means at least one item has been measured
1059 gridLayoutInfo_.lineHeightMap_[line] = cellAveLength_;
1060 mainLength += cellAveLength_ + mainGap_;
1061 }
1062 // If a line moves up out of viewport, update [startIndex_], [currentOffset_] and [startMainLineIndex_]
1063 if (OneLineMovesOffViewportFromAbove(mainLength, cellAveLength_)) {
1064 gridLayoutInfo_.currentOffset_ = mainLength;
1065 gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
1066 gridLayoutInfo_.startMainLineIndex_ = line + 1;
1067 gridLayoutInfo_.UpdateStartIndexByStartLine();
1068 }
1069 return true;
1070 }
1071
UseCurrentLines(float mainSize,float crossSize,LayoutWrapper * layoutWrapper,float & mainLength)1072 bool GridScrollLayoutAlgorithm::UseCurrentLines(
1073 float mainSize, float crossSize, LayoutWrapper* layoutWrapper, float& mainLength)
1074 {
1075 bool runOutOfRecord = false;
1076 // Measure grid items row by row
1077 int32_t tempEndIndex = -1;
1078 while (LessNotEqual(mainLength, mainSize)) {
1079 if (!MeasureExistingLine(++currentMainLineIndex_, mainLength, tempEndIndex)) {
1080 runOutOfRecord = true;
1081 break;
1082 }
1083 }
1084 // Case 1. if this while-loop breaks due to running out of records, the [currentMainLineIndex_] is larger by 1 than
1085 // real main line index, so reduce 1.
1086 // Case 2. if this while-loop stops due to false result of [LessNotEqual(mainLength, mainSize)], the
1087 // [currentMainLineIndex_] is exactly the real main line index. Update [endMainLineIndex_] when the recorded items
1088 // are done measured.
1089 gridLayoutInfo_.endMainLineIndex_ = runOutOfRecord ? --currentMainLineIndex_ : currentMainLineIndex_;
1090 // reset reachEnd_ if any line at bottom is out of viewport
1091 // last line make LessNotEqual(mainLength, mainSize) and continue is reach end too
1092 gridLayoutInfo_.reachEnd_ = gridLayoutInfo_.endIndex_ == gridLayoutInfo_.childrenCount_ - 1;
1093 if (!gridLayoutInfo_.reachEnd_) {
1094 gridLayoutInfo_.offsetEnd_ = false;
1095 }
1096 return runOutOfRecord;
1097 }
1098
SkipLargeOffset(float mainSize,LayoutWrapper * layoutWrapper)1099 void GridScrollLayoutAlgorithm::SkipLargeOffset(float mainSize, LayoutWrapper* layoutWrapper)
1100 {
1101 auto gridLayoutProperty = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
1102 CHECK_NULL_VOID(gridLayoutProperty);
1103 auto cacheCount = gridLayoutProperty->GetCachedCountValue(gridLayoutInfo_.defCachedCount_);
1104 cacheCount = std::max(cacheCount, 1);
1105 SkipForwardLines(cacheCount * mainSize, layoutWrapper);
1106 SkipBackwardLines(cacheCount * mainSize, layoutWrapper);
1107 }
1108
SkipForwardLines(float mainSize,LayoutWrapper * layoutWrapper)1109 void GridScrollLayoutAlgorithm::SkipForwardLines(float mainSize, LayoutWrapper* layoutWrapper)
1110 {
1111 if (!GreatOrEqual(gridLayoutInfo_.currentOffset_ - gridLayoutInfo_.prevOffset_, mainSize)) {
1112 return;
1113 }
1114 // skip lines in matrix
1115 while (GreatOrEqual(gridLayoutInfo_.currentOffset_, mainSize)) {
1116 auto line = gridLayoutInfo_.gridMatrix_.find(gridLayoutInfo_.startMainLineIndex_ - 1);
1117 if (line == gridLayoutInfo_.gridMatrix_.end()) {
1118 break;
1119 }
1120 auto lineHeight = gridLayoutInfo_.lineHeightMap_.find(gridLayoutInfo_.startMainLineIndex_ - 1);
1121 if (lineHeight == gridLayoutInfo_.lineHeightMap_.end()) {
1122 break;
1123 }
1124 gridLayoutInfo_.startMainLineIndex_--;
1125 gridLayoutInfo_.startIndex_ = line->second.begin()->second;
1126 gridLayoutInfo_.currentOffset_ -= lineHeight->second + mainGap_;
1127 }
1128
1129 // skip lines not in matrix
1130 if (GreatOrEqual(gridLayoutInfo_.currentOffset_, mainSize) && gridLayoutInfo_.startIndex_ > 0) {
1131 if (!gridLayoutInfo_.hasBigItem_) {
1132 SkipRegularLines(true);
1133 } else {
1134 SkipIrregularLines(layoutWrapper, true);
1135 }
1136 gridLayoutInfo_.startIndex_ = std::clamp(gridLayoutInfo_.startIndex_, 0, gridLayoutInfo_.childrenCount_ - 1);
1137 TAG_LOGI(AceLogTag::ACE_GRID, "estimatedIndex:%{public}d", gridLayoutInfo_.startIndex_);
1138 auto grid = layoutWrapper->GetHostNode();
1139 CHECK_NULL_VOID(grid);
1140 grid->ChildrenUpdatedFrom(0);
1141 }
1142 }
1143
SkipBackwardLines(float mainSize,LayoutWrapper * layoutWrapper)1144 void GridScrollLayoutAlgorithm::SkipBackwardLines(float mainSize, LayoutWrapper* layoutWrapper)
1145 {
1146 if (!GreatOrEqual(gridLayoutInfo_.prevOffset_ - gridLayoutInfo_.currentOffset_, mainSize)) {
1147 return;
1148 }
1149
1150 if (!SkipLargeLineHeightLines(mainSize)) {
1151 return;
1152 }
1153
1154 // grid size change from big to small
1155 gridLayoutInfo_.UpdateEndLine(mainSize, mainGap_);
1156
1157 // skip lines in matrix
1158 while (GreatOrEqual(-gridLayoutInfo_.currentOffset_, mainSize)) {
1159 auto line = gridLayoutInfo_.gridMatrix_.find(gridLayoutInfo_.endMainLineIndex_ + 1);
1160 if (line == gridLayoutInfo_.gridMatrix_.end()) {
1161 break;
1162 }
1163 auto lineHeight = gridLayoutInfo_.lineHeightMap_.find(gridLayoutInfo_.endMainLineIndex_ + 1);
1164 if (lineHeight == gridLayoutInfo_.lineHeightMap_.end()) {
1165 break;
1166 }
1167 gridLayoutInfo_.startMainLineIndex_++;
1168 gridLayoutInfo_.endMainLineIndex_++;
1169 gridLayoutInfo_.currentOffset_ += lineHeight->second + mainGap_;
1170 }
1171 gridLayoutInfo_.UpdateStartIndexByStartLine();
1172
1173 // skip lines not in matrix
1174 if (GreatOrEqual(-gridLayoutInfo_.currentOffset_, mainSize)) {
1175 if (!gridLayoutInfo_.hasBigItem_) {
1176 SkipRegularLines(false);
1177 } else {
1178 SkipIrregularLines(layoutWrapper, false);
1179 }
1180 gridLayoutInfo_.startIndex_ = std::clamp(gridLayoutInfo_.startIndex_, 0, gridLayoutInfo_.childrenCount_ - 1);
1181 TAG_LOGI(AceLogTag::ACE_GRID, "estimatedIndex:%{public}d, currentOffset:%{public}f",
1182 gridLayoutInfo_.startIndex_, gridLayoutInfo_.currentOffset_);
1183 auto grid = layoutWrapper->GetHostNode();
1184 CHECK_NULL_VOID(grid);
1185 grid->ChildrenUpdatedFrom(0);
1186 }
1187 }
1188
SkipRegularLines(bool forward)1189 void GridScrollLayoutAlgorithm::SkipRegularLines(bool forward)
1190 {
1191 auto lineHeight = gridLayoutInfo_.GetAverageLineHeight() + mainGap_;
1192 if (LessOrEqual(lineHeight, 0.0)) {
1193 return;
1194 }
1195 int32_t estimatedLines = gridLayoutInfo_.currentOffset_ / lineHeight;
1196 if (forward && gridLayoutInfo_.startIndex_ < estimatedLines * static_cast<int32_t>(crossCount_)) {
1197 gridLayoutInfo_.startIndex_ = 0;
1198 gridLayoutInfo_.currentOffset_ = 0;
1199 } else {
1200 gridLayoutInfo_.startIndex_ -= estimatedLines * static_cast<int32_t>(crossCount_);
1201 gridLayoutInfo_.currentOffset_ -= lineHeight * estimatedLines;
1202 }
1203 }
1204
SkipIrregularLines(LayoutWrapper * layoutWrapper,bool forward)1205 void GridScrollLayoutAlgorithm::SkipIrregularLines(LayoutWrapper* layoutWrapper, bool forward)
1206 {
1207 auto grid = layoutWrapper->GetHostNode();
1208 CHECK_NULL_VOID(grid);
1209 auto pattern = grid->GetPattern<GridPattern>();
1210 CHECK_NULL_VOID(pattern);
1211 auto averageHeight = pattern->GetAverageHeight();
1212 if (LessOrEqual(averageHeight, 0.0)) {
1213 return;
1214 }
1215 int32_t estimatedIndex = (gridLayoutInfo_.currentOffset_) / averageHeight;
1216 gridLayoutInfo_.startIndex_ =
1217 std::min(gridLayoutInfo_.startIndex_ - estimatedIndex, gridLayoutInfo_.childrenCount_);
1218 gridLayoutInfo_.currentOffset_ = gridLayoutInfo_.prevOffset_;
1219 }
1220
FillNewLineForward(float crossSize,float mainSize,LayoutWrapper * layoutWrapper)1221 float GridScrollLayoutAlgorithm::FillNewLineForward(float crossSize, float mainSize, LayoutWrapper* layoutWrapper)
1222 {
1223 // To make the code more convenient to read, we name a param in situation of vertical, for example:
1224 // 1. [lineHight] means height of a row when the Grid is vertical;
1225 // 2. [lineHight] means width of a column when the Grid is horizontal;
1226 // Other params are also named according to this principle.
1227 cellAveLength_ = -1.0f;
1228 auto currentIndex = gridLayoutInfo_.startIndex_;
1229 bool hasNormalItem = false;
1230 if (gridLayoutInfo_.startMainLineIndex_ - 1 < 0) {
1231 if (currentIndex == 0) {
1232 return cellAveLength_;
1233 }
1234 // add more than one line
1235 UpdateMatrixForAddedItems();
1236 }
1237 gridLayoutInfo_.startMainLineIndex_--;
1238 bool doneCreateNewLine = false;
1239 auto gridMatrixIter = gridLayoutInfo_.gridMatrix_.find(gridLayoutInfo_.startMainLineIndex_);
1240 if (gridMatrixIter == gridLayoutInfo_.gridMatrix_.end()) {
1241 AddForwardLines(currentIndex, crossSize, mainSize, layoutWrapper);
1242 }
1243 gridMatrixIter = gridLayoutInfo_.gridMatrix_.find(gridLayoutInfo_.startMainLineIndex_);
1244 if (gridMatrixIter == gridLayoutInfo_.gridMatrix_.end()) {
1245 return cellAveLength_;
1246 }
1247
1248 // need to obtain the item node in order and by step one in LazyLayoutWrapperBuilder::OnGetOrCreateWrapperByIndex
1249 for (auto itemIter = gridMatrixIter->second.rbegin(); itemIter != gridMatrixIter->second.rend(); ++itemIter) {
1250 currentIndex = itemIter->second;
1251
1252 // Step1. Get wrapper of [GridItem]
1253 auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
1254 if (!itemWrapper) {
1255 break;
1256 }
1257 // Step2. Measure child
1258 auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
1259 AdjustRowColSpan(itemWrapper, layoutWrapper, currentIndex);
1260 auto crossStart = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1261 if (crossStart == -1) {
1262 MeasureChildPlaced(frameSize, currentIndex, itemIter->first, layoutWrapper, itemWrapper);
1263 } else {
1264 MeasureChildPlaced(frameSize, currentIndex, crossStart, layoutWrapper, itemWrapper);
1265 }
1266 // Step3. Measure [GridItem]
1267 LargeItemLineHeight(itemWrapper, hasNormalItem);
1268 gridLayoutInfo_.startIndex_ = currentIndex;
1269 }
1270
1271 doneCreateNewLine = GreatOrEqual(cellAveLength_, 0.0);
1272 // If it fails to create new line when [FillNewLineForward] is called, it means that it reaches start
1273 gridLayoutInfo_.reachStart_ = !doneCreateNewLine;
1274
1275 return cellAveLength_;
1276 }
1277
UpdateMatrixForAddedItems()1278 void GridScrollLayoutAlgorithm::UpdateMatrixForAddedItems()
1279 {
1280 decltype(gridLayoutInfo_.lineHeightMap_) gridLineHeightMap(std::move(gridLayoutInfo_.lineHeightMap_));
1281 decltype(gridLayoutInfo_.gridMatrix_) gridMatrix(std::move(gridLayoutInfo_.gridMatrix_));
1282 for (const auto& item : gridMatrix) {
1283 gridLayoutInfo_.gridMatrix_[item.first + 1] = item.second;
1284 }
1285 for (const auto& item : gridLineHeightMap) {
1286 gridLayoutInfo_.lineHeightMap_[item.first + 1] = item.second;
1287 }
1288 gridLayoutInfo_.startMainLineIndex_ = gridLayoutInfo_.startMainLineIndex_ + 1;
1289 gridLayoutInfo_.endMainLineIndex_ = gridLayoutInfo_.endMainLineIndex_ + 1;
1290 TAG_LOGI(AceLogTag::ACE_GRID, "add more than one line startMainLineIndex_:%{public}d",
1291 gridLayoutInfo_.startMainLineIndex_);
1292 }
1293
AddForwardLines(int32_t currentIndex,float crossSize,float mainSize,LayoutWrapper * layoutWrapper)1294 void GridScrollLayoutAlgorithm::AddForwardLines(
1295 int32_t currentIndex, float crossSize, float mainSize, LayoutWrapper* layoutWrapper)
1296 {
1297 auto endMainLineIndex = gridLayoutInfo_.endMainLineIndex_;
1298 auto endIndex = gridLayoutInfo_.endIndex_;
1299 auto firstItem = GetStartingItem(layoutWrapper, currentIndex - 1);
1300 auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(firstItem);
1301 CHECK_NULL_VOID(itemWrapper);
1302 AdjustRowColSpan(itemWrapper, layoutWrapper, firstItem);
1303 auto mainSpan = axis_ == Axis::VERTICAL ? currentItemRowSpan_ : currentItemColSpan_;
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 auto crossSpan = axis_ == Axis::VERTICAL ? currentItemColSpan_ : currentItemRowSpan_;
1584 auto crossStart = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1585 if (crossSpan > crossCount) {
1586 TAG_LOGW(AceLogTag::ACE_GRID,
1587 "item %{public}d can not be placed in grid: cross count:%{public}d, cross span:%{public}d", itemIndex,
1588 crossCount, crossSpan);
1589 return crossSpan;
1590 }
1591 int32_t mainIndex = currentMainLineIndex_;
1592
1593 if (crossStart >= 0 && crossStart < crossCount) {
1594 if (crossStart < lastCross_) {
1595 return -1;
1596 } else if (CheckGridPlaced(itemIndex, mainIndex, crossStart, mainSpan, crossSpan)) {
1597 MeasureChild(layoutWrapper, frameSize, childLayoutWrapper, crossStart, crossSpan);
1598 itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(crossStart));
1599 } else {
1600 return -1;
1601 }
1602 } else {
1603 int32_t crossIndex = crossStart >= 0 ? crossStart : lastCross_;
1604
1605 while (!CheckGridPlaced(itemIndex, mainIndex, crossIndex, mainSpan, crossSpan)) {
1606 if (GetNextGrid(mainIndex, crossIndex, reverse) == false) {
1607 return -1;
1608 }
1609 if (mainIndex >= mainCount || crossIndex >= crossCount) {
1610 break;
1611 }
1612 }
1613
1614 MeasureChild(layoutWrapper, frameSize, childLayoutWrapper, crossIndex, crossSpan);
1615 itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(crossIndex));
1616 }
1617 if (mainSpan > 1 || crossSpan > 1) {
1618 for (int i = mainIndex; i < mainSpan; i++) {
1619 gridLayoutInfo_.irregularLines_[i] = true;
1620 }
1621 }
1622 return crossSpan;
1623 }
1624
MeasureChildPlaced(const SizeF & frameSize,int32_t itemIndex,int32_t crossStart,LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & childLayoutWrapper)1625 int32_t GridScrollLayoutAlgorithm::MeasureChildPlaced(const SizeF& frameSize, int32_t itemIndex, int32_t crossStart,
1626 LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& childLayoutWrapper)
1627 {
1628 AdjustRowColSpan(childLayoutWrapper, layoutWrapper, itemIndex);
1629 auto crossSpan = axis_ == Axis::VERTICAL ? currentItemColSpan_ : currentItemRowSpan_;
1630 if (static_cast<uint32_t>(crossStart + crossSpan) > crossCount_) {
1631 TAG_LOGI(AceLogTag::ACE_GRID, "item %{public}d cross not enough, start:%{public}d, span:%{public}d", itemIndex,
1632 crossStart, crossSpan);
1633 return 0;
1634 }
1635 auto mainSpan = axis_ == Axis::HORIZONTAL ? currentItemColSpan_ : currentItemRowSpan_;
1636 if (crossSpan > 1 || mainSpan > 1) {
1637 for (int i = currentMainLineIndex_; i < mainSpan; i++) {
1638 gridLayoutInfo_.irregularLines_[i] = true;
1639 }
1640 }
1641
1642 MeasureChild(layoutWrapper, frameSize, childLayoutWrapper, crossStart, crossSpan);
1643 itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(crossStart));
1644 return crossSpan;
1645 }
1646
CheckNeedMeasure(const RefPtr<LayoutWrapper> & layoutWrapper,const LayoutConstraintF & layoutConstraint) const1647 bool GridScrollLayoutAlgorithm::CheckNeedMeasure(
1648 const RefPtr<LayoutWrapper>& layoutWrapper, const LayoutConstraintF& layoutConstraint) const
1649 {
1650 auto childLayoutProperty = AceType::DynamicCast<GridItemLayoutProperty>(layoutWrapper->GetLayoutProperty());
1651 if (childLayoutProperty && childLayoutProperty->GetNeedStretch() && gridLayoutInfo_.clearStretch_) {
1652 childLayoutProperty->SetNeedStretch(false);
1653 if (axis_ == Axis::VERTICAL) {
1654 childLayoutProperty->ClearUserDefinedIdealSize(false, true);
1655 } else {
1656 childLayoutProperty->ClearUserDefinedIdealSize(true, false);
1657 }
1658 return true;
1659 }
1660
1661 if (expandSafeArea_ || layoutWrapper->CheckNeedForceMeasureAndLayout()) {
1662 return true;
1663 }
1664
1665 auto geometryNode = layoutWrapper->GetGeometryNode();
1666 CHECK_NULL_RETURN(geometryNode, true);
1667 auto constraint = geometryNode->GetParentLayoutConstraint();
1668 CHECK_NULL_RETURN(constraint, true);
1669 return constraint->maxSize != layoutConstraint.maxSize ||
1670 constraint->percentReference != layoutConstraint.percentReference ||
1671 constraint->selfIdealSize.CrossSize(axis_) != layoutConstraint.selfIdealSize.CrossSize(axis_);
1672 }
1673
MeasureChild(LayoutWrapper * layoutWrapper,const SizeF & frameSize,const RefPtr<LayoutWrapper> & childLayoutWrapper,int32_t crossStart,int32_t crossSpan)1674 void GridScrollLayoutAlgorithm::MeasureChild(LayoutWrapper* layoutWrapper, const SizeF& frameSize,
1675 const RefPtr<LayoutWrapper>& childLayoutWrapper, int32_t crossStart, int32_t crossSpan)
1676 {
1677 auto gridLayoutProperty = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
1678 auto mainSize = GetMainAxisSize(frameSize, gridLayoutInfo_.axis_);
1679 auto crossSize = GetCrossAxisSize(frameSize, gridLayoutInfo_.axis_);
1680 auto childConstraint = CreateChildConstraint(mainSize, crossSize, gridLayoutProperty, crossStart, crossSpan);
1681 if (!CheckNeedMeasure(childLayoutWrapper, childConstraint)) {
1682 return;
1683 }
1684 auto childLayoutProperty = childLayoutWrapper->GetLayoutProperty();
1685 if (!childLayoutProperty) {
1686 childLayoutWrapper->Measure(childConstraint);
1687 return;
1688 }
1689
1690 auto oldConstraint = childLayoutProperty->GetLayoutConstraint();
1691 if (oldConstraint.has_value() && !NearEqual(GetCrossAxisSize(oldConstraint.value().maxSize, axis_),
1692 GetCrossAxisSize(childConstraint.maxSize, axis_))) {
1693 auto layoutAlgorithmWrapper = childLayoutWrapper->GetLayoutAlgorithm();
1694 if (layoutAlgorithmWrapper->SkipMeasure()) {
1695 layoutAlgorithmWrapper->SetNeedMeasure();
1696 if (layoutAlgorithmWrapper->GetLayoutAlgorithm() == nullptr) {
1697 layoutAlgorithmWrapper->SetLayoutAlgorithm(
1698 childLayoutWrapper->GetHostNode()->GetPattern()->CreateLayoutAlgorithm());
1699 }
1700 }
1701 }
1702 childLayoutWrapper->Measure(childConstraint);
1703 }
1704
CheckGridPlaced(int32_t index,int32_t main,int32_t cross,int32_t mainSpan,int32_t crossSpan)1705 bool GridScrollLayoutAlgorithm::CheckGridPlaced(
1706 int32_t index, int32_t main, int32_t cross, int32_t mainSpan, int32_t crossSpan)
1707 {
1708 // If start position is already exist in gridMatrix, place grid item fail.
1709 auto mainIter = gridLayoutInfo_.gridMatrix_.find(main);
1710 if (mainIter != gridLayoutInfo_.gridMatrix_.end()) {
1711 auto crossIter = mainIter->second.find(cross);
1712 if (crossIter != mainIter->second.end()) {
1713 return false;
1714 }
1715 }
1716
1717 // If cross length of grid item if out of range, place grid item fail.
1718 if (cross + crossSpan > static_cast<int32_t>(crossCount_)) {
1719 return false;
1720 }
1721
1722 // If any grid item is already exist in gridMatrix, place grid item fail.
1723 for (int32_t i = 0; i < mainSpan; i++) {
1724 mainIter = gridLayoutInfo_.gridMatrix_.find(i + main);
1725 if (mainIter == gridLayoutInfo_.gridMatrix_.end()) {
1726 continue;
1727 }
1728 for (int32_t j = 0; j < crossSpan; j++) {
1729 auto crossIter = mainIter->second.find(j + cross);
1730 if (crossIter != mainIter->second.end()) {
1731 return false;
1732 }
1733 }
1734 }
1735
1736 // Padding grid matrix for grid item's range.
1737 for (int32_t i = main; i < main + mainSpan; ++i) {
1738 std::map<int32_t, int32_t> mainMap;
1739 auto iter = gridLayoutInfo_.gridMatrix_.find(i);
1740 if (iter != gridLayoutInfo_.gridMatrix_.end()) {
1741 mainMap = iter->second;
1742 }
1743 for (int32_t j = cross; j < cross + crossSpan; ++j) {
1744 mainMap.emplace(std::make_pair(j, index));
1745 }
1746 gridLayoutInfo_.gridMatrix_[i] = mainMap;
1747 }
1748 lastCross_ = cross + crossSpan;
1749
1750 return true;
1751 }
1752
ComputeItemCrossPosition(int32_t crossStart) const1753 float GridScrollLayoutAlgorithm::ComputeItemCrossPosition(int32_t crossStart) const
1754 {
1755 float position = 0.0f;
1756 for (int32_t index = 0; index < crossStart; ++index) {
1757 if (index >= 0 && index < static_cast<int32_t>(itemsCrossSize_.size())) {
1758 position += GetOrDefault(itemsCrossSize_, index, 0.0f);
1759 }
1760 }
1761 position += crossStart * crossGap_ + crossPaddingOffset_;
1762 return position;
1763 }
1764
GetStartingItem(LayoutWrapper * layoutWrapper,int32_t currentIndex)1765 int32_t GridScrollLayoutAlgorithm::GetStartingItem(LayoutWrapper* layoutWrapper, int32_t currentIndex)
1766 {
1767 int32_t firstIndex = 0;
1768 currentIndex = currentIndex < gridLayoutInfo_.childrenCount_ ? currentIndex : gridLayoutInfo_.childrenCount_ - 1;
1769 auto index = currentIndex;
1770 if (gridLayoutInfo_.hasBigItem_) {
1771 while (index > 0) {
1772 auto childLayoutWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1773 if (!childLayoutWrapper) {
1774 TAG_LOGW(AceLogTag::ACE_GRID, "item [%{public}d] does not exist, reload to [0]", index);
1775 break;
1776 }
1777
1778 AdjustRowColSpan(childLayoutWrapper, layoutWrapper, index);
1779 auto crossIndex = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1780 if (crossIndex == 0) {
1781 firstIndex = index;
1782 break;
1783 }
1784 --index;
1785 }
1786 } else {
1787 while (index > 0) {
1788 // need to obtain the item node in order and by step one
1789 auto childLayoutWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1790 if (!childLayoutWrapper) {
1791 TAG_LOGW(AceLogTag::ACE_GRID, "item [%{public}d] does not exist, reload to [0]", index);
1792 break;
1793 }
1794 AdjustRowColSpan(childLayoutWrapper, layoutWrapper, index);
1795 auto crossIndex = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1796 // Grid may change from no big item to has big item
1797 if (crossIndex >= 0) {
1798 gridLayoutInfo_.hasBigItem_ = true;
1799 return GetStartingItem(layoutWrapper, currentIndex);
1800 }
1801 if (index % gridLayoutInfo_.crossCount_ == 0) {
1802 firstIndex = index;
1803 break;
1804 }
1805 --index;
1806 }
1807 }
1808 return firstIndex;
1809 }
1810
FillCacheLineAtEnd(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)1811 void GridScrollLayoutAlgorithm::FillCacheLineAtEnd(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
1812 {
1813 auto gridLayoutProperty = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
1814 auto cacheCount = gridLayoutProperty->GetCachedCountValue(gridLayoutInfo_.defCachedCount_);
1815 if (gridLayoutInfo_.reachEnd_ || cacheCount == 0) {
1816 return;
1817 }
1818 auto tempEndIndex = gridLayoutInfo_.endIndex_;
1819 auto tempEndMainLineIndex = gridLayoutInfo_.endMainLineIndex_;
1820 auto tempCurrentMainLineIndex = currentMainLineIndex_;
1821 currentMainLineIndex_ = gridLayoutInfo_.endMainLineIndex_;
1822
1823 for (; currentMainLineIndex_ <= tempEndMainLineIndex + cacheCount; currentMainLineIndex_++) {
1824 float lineHeight = FillNewCacheLineBackward(crossSize, mainSize, layoutWrapper, currentMainLineIndex_);
1825 if (LessNotEqual(lineHeight, 0.0)) {
1826 break;
1827 }
1828 }
1829 gridLayoutInfo_.endIndex_ = tempEndIndex;
1830 gridLayoutInfo_.endMainLineIndex_ = tempEndMainLineIndex;
1831 currentMainLineIndex_ = tempCurrentMainLineIndex;
1832
1833 if (!predictBuildList_.empty()) {
1834 CreateCachedChildConstraint(layoutWrapper, mainSize, crossSize);
1835 }
1836 }
1837
FillNewCacheLineBackward(float crossSize,float mainSize,LayoutWrapper * layoutWrapper,int32_t currentLine)1838 float GridScrollLayoutAlgorithm::FillNewCacheLineBackward(
1839 float crossSize, float mainSize, LayoutWrapper* layoutWrapper, int32_t currentLine)
1840 {
1841 // To make the code more convenient to read, we name a param in situation of vertical, for example:
1842 // 1. [lineHight] means height of a row when the Grid is vertical;
1843 // 2. [lineHight] means width of a column when the Grid is horizontal;
1844 // Other params are also named according to this principle.
1845 cellAveLength_ = -1.0f;
1846 auto currentIndex = gridLayoutInfo_.endIndex_ + 1;
1847
1848 // if it fails to fill a new line backward, do [currentLine--]
1849 auto line = gridLayoutInfo_.gridMatrix_.find(currentLine);
1850 if (gridLayoutInfo_.gridMatrix_.find(currentLine) != gridLayoutInfo_.gridMatrix_.end()) {
1851 if (line->second.size() < crossCount_) {
1852 bool hasNormalItem = false;
1853 lastCross_ = 0;
1854 for (const auto& elem : line->second) {
1855 if (elem.second > gridLayoutInfo_.endIndex_) {
1856 gridLayoutInfo_.endIndex_ = elem.second;
1857 }
1858 }
1859 auto currentIndex = gridLayoutInfo_.endIndex_ + 1;
1860 for (uint32_t i = line->second.size() ; i < crossCount_; i++) {
1861 // Step1. Get wrapper of [GridItem]
1862 auto itemWrapper = layoutWrapper->GetChildByIndex(currentIndex, true);
1863 if (!itemWrapper || itemWrapper->CheckNeedForceMeasureAndLayout()) {
1864 for (uint32_t y = i; y < crossCount_ && currentIndex < gridLayoutInfo_.childrenCount_; y++) {
1865 predictBuildList_.emplace_back(currentIndex++);
1866 }
1867 if (GreatOrEqual(cellAveLength_, 0.0f) &&
1868 gridLayoutInfo_.lineHeightMap_.find(currentLine) == gridLayoutInfo_.lineHeightMap_.end()) {
1869 gridLayoutInfo_.lineHeightMap_[currentLine] = cellAveLength_;
1870 }
1871 return -1.0f;
1872 }
1873 // Step2. Measure child
1874 auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
1875 auto childState = MeasureCachedChild(frameSize, currentIndex, layoutWrapper, itemWrapper);
1876 if (childState == -1) {
1877 cellAveLength_ = LessNotEqual(cellAveLength_, 0.0)
1878 ? gridLayoutInfo_.lineHeightMap_.find(currentLine - 1)->second
1879 : cellAveLength_;
1880 --currentIndex;
1881 break;
1882 }
1883 i += static_cast<uint32_t>(childState) - 1;
1884 // Step3. Measure [GridItem]
1885 LargeItemLineHeight(itemWrapper, hasNormalItem);
1886 gridLayoutInfo_.endIndex_ = currentIndex;
1887 currentIndex++;
1888 }
1889 }
1890 CompleteItemCrossPosition(layoutWrapper, line->second);
1891 for (const auto& elem : line->second) {
1892 if (elem.second > gridLayoutInfo_.endIndex_) {
1893 gridLayoutInfo_.endIndex_ = elem.second;
1894 }
1895 }
1896 if (gridLayoutInfo_.lineHeightMap_.find(currentLine) != gridLayoutInfo_.lineHeightMap_.end()) {
1897 return gridLayoutInfo_.lineHeightMap_.find(currentLine)->second;
1898 } else {
1899 gridLayoutInfo_.lineHeightMap_[currentLine] = cellAveLength_;
1900 return cellAveLength_;
1901 }
1902 }
1903
1904 lastCross_ = 0;
1905 bool hasNormalItem = false;
1906 bool doneFillLine = false;
1907
1908 for (uint32_t i = 0; i < crossCount_; i++) {
1909 if (currentIndex >= gridLayoutInfo_.childrenCount_) {
1910 break;
1911 }
1912 // Step1. Get wrapper of [GridItem]
1913 auto itemWrapper = layoutWrapper->GetChildByIndex(currentIndex, true);
1914 if (!itemWrapper || itemWrapper->CheckNeedForceMeasureAndLayout()) {
1915 for (uint32_t x = i; x < crossCount_ && currentIndex < gridLayoutInfo_.childrenCount_; x++) {
1916 predictBuildList_.emplace_back(currentIndex++);
1917 }
1918 if (GreatOrEqual(cellAveLength_, 0.0f) &&
1919 gridLayoutInfo_.lineHeightMap_.find(currentLine) == gridLayoutInfo_.lineHeightMap_.end()) {
1920 gridLayoutInfo_.lineHeightMap_[currentLine] = cellAveLength_;
1921 }
1922 return -1.0f;
1923 }
1924 // // Step2. Measure child
1925 auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
1926 auto crossSpan = MeasureCachedChild(frameSize, currentIndex, layoutWrapper, itemWrapper);
1927 if (crossSpan < 0) {
1928 cellAveLength_ = LessNotEqual(cellAveLength_, 0.0)
1929 ? gridLayoutInfo_.lineHeightMap_.find(currentLine - 1)->second
1930 : cellAveLength_;
1931 --currentIndex;
1932 break;
1933 }
1934 i = static_cast<uint32_t>(lastCross_ - 1);
1935 // // Step3. Measure [GridItem]
1936 LargeItemLineHeight(itemWrapper, hasNormalItem);
1937
1938 gridLayoutInfo_.endIndex_ = currentIndex;
1939 currentIndex++;
1940 doneFillLine = true;
1941 }
1942
1943 if (doneFillLine || gridLayoutInfo_.gridMatrix_.find(currentLine) != gridLayoutInfo_.gridMatrix_.end()) {
1944 gridLayoutInfo_.lineHeightMap_[currentLine] = cellAveLength_;
1945 gridLayoutInfo_.endMainLineIndex_ = currentLine;
1946 } else {
1947 return -1.0f;
1948 }
1949 return cellAveLength_;
1950 }
1951
MeasureCachedChild(const SizeF & frameSize,int32_t itemIndex,LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & childLayoutWrapper)1952 int32_t GridScrollLayoutAlgorithm::MeasureCachedChild(const SizeF& frameSize, int32_t itemIndex,
1953 LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& childLayoutWrapper)
1954 {
1955 auto mainCount = static_cast<int32_t>(mainCount_);
1956 auto crossCount = static_cast<int32_t>(crossCount_);
1957 AdjustRowColSpan(childLayoutWrapper, layoutWrapper, itemIndex);
1958 auto mainSpan = axis_ == Axis::VERTICAL ? currentItemRowSpan_ : currentItemColSpan_;
1959 auto crossSpan = axis_ == Axis::VERTICAL ? currentItemColSpan_ : currentItemRowSpan_;
1960 auto crossStart = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1961 if (crossSpan > crossCount) {
1962 return crossSpan;
1963 }
1964 int32_t mainIndex = currentMainLineIndex_;
1965
1966 if (crossStart >= 0 && crossStart < crossCount) {
1967 if (crossStart < lastCross_) {
1968 return -1;
1969 } else if (CheckGridPlaced(itemIndex, mainIndex, crossStart, mainSpan, crossSpan)) {
1970 itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(crossStart));
1971 } else {
1972 return -1;
1973 }
1974 } else {
1975 int32_t crossIndex = crossStart >= 0 ? crossStart : lastCross_;
1976
1977 while (!CheckGridPlaced(itemIndex, mainIndex, crossIndex, mainSpan, crossSpan)) {
1978 if (GetNextGrid(mainIndex, crossIndex, false) == false) {
1979 return -1;
1980 }
1981 if (mainIndex >= mainCount || crossIndex >= crossCount) {
1982 break;
1983 }
1984 }
1985
1986 itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(crossIndex));
1987 }
1988 if (crossSpan > 1 || mainSpan > 1) {
1989 for (int i = currentMainLineIndex_; i < mainSpan; i++) {
1990 gridLayoutInfo_.irregularLines_[i] = true;
1991 }
1992 }
1993 return crossSpan;
1994 }
1995
CompleteItemCrossPosition(LayoutWrapper * layoutWrapper,const std::map<int32_t,int32_t> & items)1996 void GridScrollLayoutAlgorithm::CompleteItemCrossPosition(
1997 LayoutWrapper* layoutWrapper, const std::map<int32_t, int32_t>& items)
1998 {
1999 for (auto&& item : items) {
2000 auto currentIndex = item.second;
2001 auto itemWrapper = layoutWrapper->GetChildByIndex(currentIndex, true);
2002 if (!itemWrapper) {
2003 if (predictBuildList_.back().idx < currentIndex) {
2004 predictBuildList_.emplace_front(currentIndex);
2005 } else if (predictBuildList_.front().idx > currentIndex) {
2006 predictBuildList_.emplace_back(currentIndex);
2007 }
2008 }
2009 itemsCrossPosition_.try_emplace(currentIndex, ComputeItemCrossPosition(item.first));
2010 }
2011 }
2012
2013 namespace {
GenerateCacheItemConstraint(const GridItemLayoutProperty & itemProp,Axis axis,const GridPredictLayoutParam & param)2014 LayoutConstraintF GenerateCacheItemConstraint(
2015 const GridItemLayoutProperty& itemProp, Axis axis, const GridPredictLayoutParam& param)
2016 {
2017 auto constraint = param.layoutConstraint;
2018 int32_t crossSpan = itemProp.GetCrossSpan(axis);
2019 int32_t crossStart = itemProp.GetCrossStart(axis);
2020 if (crossSpan > 1) {
2021 float itemCrossSize = param.crossGap * (crossSpan - 1);
2022 for (int32_t index = 0; index < crossSpan; ++index) {
2023 int32_t crossIndex = (crossStart + index) % static_cast<int32_t>(param.itemsCrossSizes.size());
2024 if (crossIndex >= 0 && crossIndex < static_cast<int32_t>(param.itemsCrossSizes.size())) {
2025 itemCrossSize += GetOrDefault(param.itemsCrossSizes, crossIndex, 0.0f);
2026 }
2027 }
2028 constraint.maxSize.SetCrossSize(itemCrossSize, axis);
2029 constraint.selfIdealSize.SetCrossSize(itemCrossSize, axis);
2030 }
2031 return constraint;
2032 }
2033
2034 /* revert layout range in GridLayoutInfo when this object destructs */
2035 class TempLayoutRange {
2036 public:
TempLayoutRange(GridLayoutInfo & info)2037 explicit TempLayoutRange(GridLayoutInfo& info)
2038 : subStart_(info.startIndex_), subStartLine_(info.startMainLineIndex_), subEnd_(info.endIndex_),
2039 subEndLine_(info.endMainLineIndex_), info_(info)
2040 {}
~TempLayoutRange()2041 ~TempLayoutRange()
2042 {
2043 info_.startIndex_ = subStart_;
2044 info_.startMainLineIndex_ = subStartLine_;
2045 info_.endIndex_ = subEnd_;
2046 info_.endMainLineIndex_ = subEndLine_;
2047 }
2048
2049 const int32_t subStart_;
2050 const int32_t subStartLine_;
2051 const int32_t subEnd_;
2052 const int32_t subEndLine_;
2053
2054 private:
2055 GridLayoutInfo& info_;
2056
2057 ACE_DISALLOW_COPY_AND_MOVE(TempLayoutRange);
2058 };
2059 } // namespace
2060
SyncPreload(LayoutWrapper * wrapper,int32_t cacheLineCnt,float crossSize,float mainSize)2061 void GridScrollLayoutAlgorithm::SyncPreload(
2062 LayoutWrapper* wrapper, int32_t cacheLineCnt, float crossSize, float mainSize)
2063 {
2064 TempLayoutRange scope(gridLayoutInfo_);
2065 for (int32_t i = 0; i < cacheLineCnt; ++i) {
2066 FillNewLineForward(crossSize, mainSize, wrapper);
2067
2068 float len = 0.0f;
2069 int32_t endIdx = gridLayoutInfo_.endIndex_;
2070 int32_t line = scope.subEndLine_ + i + 1;
2071 if (!MeasureExistingLine(line, len, endIdx)) {
2072 currentMainLineIndex_ = line - 1;
2073 FillNewLineBackward(crossSize, mainSize, wrapper, false);
2074 }
2075 }
2076 }
2077
PredictBuildItem(FrameNode & host,int32_t itemIdx,const GridPredictLayoutParam & param)2078 bool GridScrollLayoutAlgorithm::PredictBuildItem(FrameNode& host, int32_t itemIdx, const GridPredictLayoutParam& param)
2079 {
2080 // build callback
2081 auto wrapper = host.GetOrCreateChildByIndex(itemIdx, false, true);
2082 CHECK_NULL_RETURN(wrapper, false);
2083 auto itemProperty = DynamicCast<GridItemLayoutProperty>(wrapper->GetLayoutProperty());
2084 CHECK_NULL_RETURN(itemProperty, false);
2085 const Axis axis = host.GetPattern<GridPattern>()->GetAxis();
2086
2087 auto constraint = GenerateCacheItemConstraint(*itemProperty, axis, param);
2088 wrapper->SetActive(false);
2089 auto frameNode = wrapper->GetHostNode();
2090 CHECK_NULL_RETURN(frameNode, false);
2091 frameNode->GetGeometryNode()->SetParentLayoutConstraint(constraint);
2092 FrameNode::ProcessOffscreenNode(frameNode);
2093 return true;
2094 }
2095
CreateCachedChildConstraint(LayoutWrapper * layoutWrapper,float mainSize,float crossSize)2096 void GridScrollLayoutAlgorithm::CreateCachedChildConstraint(
2097 LayoutWrapper* layoutWrapper, float mainSize, float crossSize)
2098 {
2099 auto gridLayoutProperty = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
2100 cachedChildConstraint_ = CreateChildConstraint(mainSize, crossSize, gridLayoutProperty, 0, 1);
2101 }
2102
SupplyAllData2ZeroIndex(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)2103 void GridScrollLayoutAlgorithm::SupplyAllData2ZeroIndex(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
2104 {
2105 // Save the global variable at this moment.
2106 auto tempGridLayoutInfo = gridLayoutInfo_;
2107
2108 // When the data is supplied again, there is an update of the original global variable gridLayoutInfo_. Therefore,
2109 // each time you supply the data, you must re-complete the data based on the current screen data
2110 auto startLineIndex = tempGridLayoutInfo.startMainLineIndex_;
2111 auto startIndex = tempGridLayoutInfo.startIndex_;
2112 auto endLineIndex = tempGridLayoutInfo.endMainLineIndex_;
2113 auto endIndex = tempGridLayoutInfo.endIndex_;
2114 auto targetIndex = tempGridLayoutInfo.targetIndex_;
2115 // Remove redundant data that is visible off-screen. This is the key to completing data accurately
2116 DeleteItemsOutOfScope(gridLayoutInfo_.lineHeightMap_, startLineIndex, endLineIndex);
2117 DeleteItemsOutOfScope(gridLayoutInfo_.gridMatrix_, startLineIndex, endLineIndex);
2118
2119 // The continuous grid information is saved and used in the GridPattern to calculate the scroll distance
2120 // Complete all data with indexes from startIndex to 0
2121 if (startIndex > 0) {
2122 // The start line when completing the data
2123 currentMainLineIndex_ = startLineIndex;
2124 float lineHeight = 0.0f;
2125 do {
2126 lineHeight = FillNewLineForward(crossSize, mainSize, layoutWrapper);
2127 } while (GreatOrEqual(lineHeight, 0.0));
2128 }
2129
2130 // Complete the data from endIndex+1 to targetIndex_
2131 auto lineHeight = 0.f;
2132 if (endIndex < targetIndex) {
2133 // The start line when completing the data
2134 currentMainLineIndex_ = endLineIndex;
2135 int32_t targetLineIndex = 0;
2136 do {
2137 lineHeight = FillNewLineBackward(crossSize, mainSize, layoutWrapper, false);
2138 } while (!(LessNotEqual(lineHeight, 0.0) || IsIndexInMatrix(targetIndex.value(), targetLineIndex)));
2139 }
2140
2141 if (gridLayoutInfo_.extraOffset_.has_value() && Negative(gridLayoutInfo_.extraOffset_.value())) {
2142 auto extraOffset = -gridLayoutInfo_.extraOffset_.value();
2143 gridLayoutInfo_.GetLineIndexByIndex(targetIndex.value(), currentMainLineIndex_);
2144 lineHeight = gridLayoutInfo_.lineHeightMap_[currentMainLineIndex_];
2145 auto heightForExtralOffset = lineHeight + mainGap_;
2146 while (GreatOrEqual(extraOffset, heightForExtralOffset) && !Negative(lineHeight)) {
2147 lineHeight = FillNewLineBackward(crossSize, mainSize, layoutWrapper, false);
2148 heightForExtralOffset += (lineHeight + mainGap_);
2149 }
2150 ACE_SCOPED_TRACE(
2151 "SupplyAllData2ZeroIndex, extraOffset_:%f, heightForExtralOffset:%f, LineIndexForExtralOffset:%d",
2152 extraOffset, heightForExtralOffset, currentMainLineIndex_);
2153 }
2154
2155 // Once the data is completed, the global variables need to be returned
2156 scrollGridLayoutInfo_ = gridLayoutInfo_;
2157 gridLayoutInfo_ = tempGridLayoutInfo;
2158 }
2159
UpdateMainLineOnReload(int32_t startIdx)2160 void GridScrollLayoutAlgorithm::UpdateMainLineOnReload(int32_t startIdx)
2161 {
2162 auto& info = gridLayoutInfo_;
2163 if (!info.hasBigItem_) {
2164 info.startMainLineIndex_ = startIdx / info.crossCount_;
2165 }
2166 }
2167
GetResetMode(LayoutWrapper * layoutWrapper,int32_t updateIdx)2168 std::pair<bool, bool> GridScrollLayoutAlgorithm::GetResetMode(LayoutWrapper* layoutWrapper, int32_t updateIdx)
2169 {
2170 if (updateIdx == -1) {
2171 return { 0, 0 };
2172 }
2173 bool outOfMatrix = false;
2174 if (updateIdx != -1 && updateIdx < gridLayoutInfo_.startIndex_) {
2175 int32_t startLine = 0;
2176 outOfMatrix = !IsIndexInMatrix(updateIdx, startLine);
2177 }
2178 auto gridLayoutProperty = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
2179 bool hasOptions = gridLayoutProperty->GetLayoutOptions().has_value();
2180 return { !gridLayoutInfo_.hasBigItem_ || outOfMatrix || hasOptions,
2181 gridLayoutInfo_.hasBigItem_ && !outOfMatrix && !hasOptions };
2182 }
2183
CheckReset(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)2184 void GridScrollLayoutAlgorithm::CheckReset(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
2185 {
2186 int32_t updateIdx = layoutWrapper->GetHostNode()->GetChildrenUpdated();
2187 // [resetFromStart,resetFromUpdate]
2188 std::pair<bool, bool> resetMode = GetResetMode(layoutWrapper, updateIdx);
2189 if (gridLayoutInfo_.lastCrossCount_ != crossCount_ || resetMode.first || gridLayoutInfo_.IsResetted()) {
2190 gridLayoutInfo_.lastCrossCount_ = crossCount_;
2191 gridLayoutInfo_.lineHeightMap_.clear();
2192 gridLayoutInfo_.gridMatrix_.clear();
2193 gridLayoutInfo_.irregularItemsPosition_.clear();
2194 gridLayoutInfo_.endIndex_ = -1;
2195 gridLayoutInfo_.endMainLineIndex_ = 0;
2196 gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
2197 gridLayoutInfo_.ResetPositionFlags();
2198 gridLayoutInfo_.clearStretch_ = true;
2199 gridLayoutInfo_.hasMultiLineItem_ = false;
2200 isChildrenUpdated_ = true;
2201 if (gridLayoutInfo_.childrenCount_ > 0) {
2202 ReloadToStartIndex(mainSize, crossSize, layoutWrapper);
2203 } else {
2204 gridLayoutInfo_.startIndex_ = 0;
2205 gridLayoutInfo_.startMainLineIndex_ = 0;
2206 }
2207 if (IsScrollToEndLine()) {
2208 gridLayoutInfo_.currentOffset_ =
2209 mainSize - gridLayoutInfo_.lineHeightMap_[gridLayoutInfo_.endMainLineIndex_];
2210 gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
2211 }
2212 } else if (resetMode.second) {
2213 isChildrenUpdated_ = true;
2214 gridLayoutInfo_.irregularItemsPosition_.clear();
2215 gridLayoutInfo_.ResetPositionFlags();
2216 gridLayoutInfo_.clearStretch_ = true;
2217 gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
2218 auto it = gridLayoutInfo_.FindInMatrix(updateIdx);
2219 it = gridLayoutInfo_.FindStartLineInMatrix(it, updateIdx);
2220 if (it != gridLayoutInfo_.gridMatrix_.end()) {
2221 int32_t updateLineIndex = it->first;
2222 gridLayoutInfo_.ClearMatrixToEnd(updateIdx, updateLineIndex);
2223 gridLayoutInfo_.ClearHeightsFromMatrix(updateLineIndex);
2224 if (updateIdx <= gridLayoutInfo_.startIndex_) {
2225 ReloadFromUpdateIdxToStartIndex(mainSize, crossSize, updateLineIndex, layoutWrapper);
2226 }
2227 }
2228 }
2229 }
2230
CheckLastLineItemFullyShowed(LayoutWrapper * layoutWrapper)2231 bool GridScrollLayoutAlgorithm::CheckLastLineItemFullyShowed(LayoutWrapper* layoutWrapper)
2232 {
2233 auto lastLine = gridLayoutInfo_.gridMatrix_.find(gridLayoutInfo_.endMainLineIndex_);
2234 if (lastLine != gridLayoutInfo_.gridMatrix_.end()) {
2235 for (const auto [corssIndex, itemIndex] : lastLine->second) {
2236 auto itemWrapper = layoutWrapper->GetChildByIndex(itemIndex);
2237 if (!itemWrapper) {
2238 continue;
2239 }
2240 auto itemLayoutProperty = DynamicCast<GridItemLayoutProperty>(itemWrapper->GetLayoutProperty());
2241 if (!itemLayoutProperty) {
2242 continue;
2243 }
2244 if (itemLayoutProperty->GetMainSpan(axis_) == 1) {
2245 continue;
2246 }
2247 auto it = gridLayoutInfo_.FindStartLineInMatrix(lastLine, itemIndex);
2248 if (it == gridLayoutInfo_.gridMatrix_.end()) {
2249 continue;
2250 }
2251 int32_t startLine = it->first;
2252 if (startLine + itemLayoutProperty->GetMainSpan(axis_) > gridLayoutInfo_.endMainLineIndex_ + 1) {
2253 return false;
2254 }
2255 }
2256 }
2257 return true;
2258 }
2259
IsIrregularLine(int32_t lineIndex) const2260 bool GridScrollLayoutAlgorithm::IsIrregularLine(int32_t lineIndex) const
2261 {
2262 auto irregular = gridLayoutInfo_.irregularLines_.find(lineIndex);
2263 if (irregular != gridLayoutInfo_.irregularLines_.end() && irregular->second) {
2264 return true;
2265 }
2266 return false;
2267 }
2268
ResetOffsetWhenHeightChanged()2269 void GridScrollLayoutAlgorithm::ResetOffsetWhenHeightChanged()
2270 {
2271 if (scrollSource_ == SCROLL_FROM_NONE) {
2272 gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
2273 }
2274 }
2275
MergeRemainingLines(std::map<int32_t,std::map<int32_t,int32_t>> matrix,int32_t forwardLines)2276 void GridScrollLayoutAlgorithm::MergeRemainingLines(
2277 std::map<int32_t, std::map<int32_t, int32_t>> matrix, int32_t forwardLines)
2278 {
2279 for (const auto& line : matrix) {
2280 if (line.second.empty()) {
2281 continue;
2282 }
2283 for (const auto& [crossIndex, index] : line.second) {
2284 gridLayoutInfo_.gridMatrix_[line.first - forwardLines][crossIndex] = index;
2285 }
2286 }
2287 }
2288
SkipLargeLineHeightLines(float mainSize)2289 bool GridScrollLayoutAlgorithm::SkipLargeLineHeightLines(float mainSize)
2290 {
2291 bool needSkip = false;
2292 for (int32_t line = gridLayoutInfo_.startMainLineIndex_; line <= gridLayoutInfo_.endMainLineIndex_; line++) {
2293 auto iter = gridLayoutInfo_.lineHeightMap_.find(line);
2294 if (iter != gridLayoutInfo_.lineHeightMap_.end() && iter->second >= mainSize) {
2295 needSkip = true;
2296 break;
2297 }
2298 }
2299 if (needSkip) {
2300 auto totalViewHeight = gridLayoutInfo_.GetTotalHeightOfItemsInView(mainGap_, true);
2301 auto needSkipHeight = totalViewHeight + gridLayoutInfo_.prevOffset_ + mainGap_;
2302 if (GreatOrEqual(needSkipHeight, -gridLayoutInfo_.currentOffset_)) {
2303 return false;
2304 }
2305
2306 auto endLine = gridLayoutInfo_.gridMatrix_.find(gridLayoutInfo_.endMainLineIndex_ + 1);
2307 if (endLine != gridLayoutInfo_.gridMatrix_.end() && !endLine->second.empty()) {
2308 gridLayoutInfo_.currentOffset_ += needSkipHeight;
2309 gridLayoutInfo_.endMainLineIndex_++;
2310 gridLayoutInfo_.startMainLineIndex_ = gridLayoutInfo_.endMainLineIndex_;
2311 }
2312 }
2313 return true;
2314 }
2315 } // namespace OHOS::Ace::NG
2316