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