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