1 /*
2 * Copyright (c) 2022-2023 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/utils.h"
22 #include "core/components/common/properties/alignment.h"
23 #include "core/components/scroll/scroll_controller_base.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_item_pattern.h"
27 #include "core/components_ng/pattern/grid/grid_pattern.h"
28 #include "core/components_ng/pattern/grid/grid_utils.h"
29 #include "core/components_ng/pattern/pattern.h"
30 #include "core/components_ng/pattern/text_field/text_field_manager.h"
31 #include "core/components_ng/property/layout_constraint.h"
32 #include "core/components_ng/property/measure_utils.h"
33 #include "core/components_ng/property/templates_parser.h"
34 #include "core/pipeline_ng/pipeline_context.h"
35 namespace OHOS::Ace::NG {
Measure(LayoutWrapper * layoutWrapper)36 void GridScrollLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
37 {
38 auto gridLayoutProperty = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
39 CHECK_NULL_VOID(gridLayoutProperty);
40
41 // Step1: Decide size of Grid
42 Axis axis = gridLayoutInfo_.axis_;
43 auto idealSize = CreateIdealSize(
44 gridLayoutProperty->GetLayoutConstraint().value(), axis, gridLayoutProperty->GetMeasureType(), true);
45 if (GreatOrEqual(GetMainAxisSize(idealSize, axis), Infinity<float>())) {
46 // TODO: use total height of all children as grid's main size when main size of ideal is infinite
47 LOGE("size of main axis value is infinity, please check");
48 return;
49 }
50 layoutWrapper->GetGeometryNode()->SetFrameSize(idealSize);
51 MinusPaddingToSize(gridLayoutProperty->CreatePaddingAndBorder(), idealSize);
52
53 InitialItemsCrossSize(gridLayoutProperty, idealSize, layoutWrapper->GetTotalChildCount());
54
55 // Step2: Measure children that can be displayed in viewport of Grid
56 float mainSize = GetMainAxisSize(idealSize, axis);
57 float crossSize = GetCrossAxisSize(idealSize, axis);
58 if (!NearEqual(mainSize, gridLayoutInfo_.lastMainSize_)) {
59 gridLayoutInfo_.ResetPositionFlags();
60 UpdateOffsetOnVirtualKeyboardHeightChange(layoutWrapper, mainSize);
61 }
62 FillGridViewportAndMeasureChildren(mainSize, crossSize, layoutWrapper);
63
64 // update cache info.
65 layoutWrapper->SetCacheCount(static_cast<int32_t>(gridLayoutProperty->GetCachedCountValue(1) * crossCount_));
66
67 AdaptToChildMainSize(layoutWrapper, gridLayoutProperty, mainSize, idealSize);
68
69 // reset offsetEnd after scroll to moveToEndLineIndex_
70 gridLayoutInfo_.offsetEnd_ = moveToEndLineIndex_ > 0
71 ? (gridLayoutInfo_.endIndex_ + 1 >= layoutWrapper->GetTotalChildCount())
72 : gridLayoutInfo_.offsetEnd_;
73 }
74
UpdateOffsetOnVirtualKeyboardHeightChange(LayoutWrapper * layoutWrapper,float mainSize)75 void GridScrollLayoutAlgorithm::UpdateOffsetOnVirtualKeyboardHeightChange(LayoutWrapper* layoutWrapper, float mainSize)
76 {
77 if (GreatOrEqual(mainSize, gridLayoutInfo_.lastMainSize_)) {
78 return;
79 }
80 // only need to offset vertical grid
81 if (gridLayoutInfo_.axis_ != Axis::VERTICAL) {
82 return;
83 }
84
85 auto grid = layoutWrapper->GetHostNode();
86 CHECK_NULL_VOID_NOLOG(grid);
87 auto focusHub = grid->GetFocusHub();
88 CHECK_NULL_VOID_NOLOG(focusHub);
89 // textField not in Grid
90 if (!focusHub->IsCurrentFocus()) {
91 return;
92 }
93
94 auto context = PipelineContext::GetCurrentContext();
95 CHECK_NULL_VOID_NOLOG(context);
96 auto textFieldManager = AceType::DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
97 CHECK_NULL_VOID_NOLOG(textFieldManager);
98 // only when textField is onFocus
99 auto focused = textFieldManager->GetOnFocusTextField().Upgrade();
100 CHECK_NULL_VOID_NOLOG(focused);
101 auto position = textFieldManager->GetClickPosition().GetY();
102 auto gridOffset = grid->GetTransformRelativeOffset();
103 auto offset = mainSize + gridOffset.GetY() - position;
104 if (LessOrEqual(offset, 0.0)) {
105 // negative offset to scroll down
106 auto lineHeight = gridLayoutInfo_.GetAverageLineHeight();
107 if (GreatNotEqual(lineHeight, 0)) {
108 offset = floor(offset / lineHeight) * lineHeight;
109 }
110 gridLayoutInfo_.currentOffset_ += offset;
111 LOGI("update offset on virtual keyboard height change, %{public}f", offset);
112 }
113 }
114
AdaptToChildMainSize(LayoutWrapper * layoutWrapper,RefPtr<GridLayoutProperty> & gridLayoutProperty,float mainSize,const SizeF & idealSize)115 void GridScrollLayoutAlgorithm::AdaptToChildMainSize(LayoutWrapper* layoutWrapper,
116 RefPtr<GridLayoutProperty>& gridLayoutProperty, float mainSize, const SizeF& idealSize)
117 {
118 gridLayoutInfo_.lastMainSize_ = mainSize;
119 // grid with columnsTemplate/rowsTemplate and maxCount
120 if (!gridLayoutProperty->HasMaxCount()) {
121 return;
122 }
123
124 std::optional<CalcLength> mainAxisIdealSize;
125 const auto& selfLayoutConstraint = gridLayoutProperty->GetCalcLayoutConstraint();
126 if (selfLayoutConstraint && selfLayoutConstraint->selfIdealSize.has_value()) {
127 mainAxisIdealSize = axis_ == Axis::HORIZONTAL ? selfLayoutConstraint->selfIdealSize->Width()
128 : selfLayoutConstraint->selfIdealSize->Height();
129 }
130
131 if (!mainAxisIdealSize.has_value()) {
132 float lengthOfItemsInViewport = 0;
133 for (auto i = gridLayoutInfo_.startMainLineIndex_; i <= gridLayoutInfo_.endMainLineIndex_; i++) {
134 lengthOfItemsInViewport += (gridLayoutInfo_.lineHeightMap_[i] + mainGap_);
135 }
136 lengthOfItemsInViewport -= mainGap_;
137 auto gridMainSize = std::min(lengthOfItemsInViewport, mainSize);
138 // should use minCount * cellLength ?
139 if (axis_ == Axis::HORIZONTAL) {
140 gridMainSize = std::max(gridMainSize, gridLayoutProperty->GetLayoutConstraint()->minSize.Width());
141 layoutWrapper->GetGeometryNode()->SetFrameSize(SizeF(gridMainSize, idealSize.Height()));
142 } else {
143 gridMainSize = std::max(gridMainSize, gridLayoutProperty->GetLayoutConstraint()->minSize.Height());
144 layoutWrapper->GetGeometryNode()->SetFrameSize(SizeF(idealSize.Width(), gridMainSize));
145 }
146 gridLayoutInfo_.lastMainSize_ = gridMainSize;
147 LOGI("gridMainSize:%{public}f", gridMainSize);
148 }
149 }
150
Layout(LayoutWrapper * layoutWrapper)151 void GridScrollLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
152 {
153 auto gridLayoutProperty = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
154 CHECK_NULL_VOID(gridLayoutProperty);
155 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
156 auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
157 MinusPaddingToSize(padding, size);
158 auto childFrameOffset = OffsetF(padding.left.value_or(0.0f), padding.top.value_or(0.0f));
159 childFrameOffset += gridLayoutProperty->IsVertical() ? OffsetF(0.0f, gridLayoutInfo_.currentOffset_)
160 : OffsetF(gridLayoutInfo_.currentOffset_, 0.0f);
161
162 float prevLineHeight = 0.0f;
163 layoutWrapper->RemoveAllChildInRenderTree();
164 LargeItemForwardLineHeight(gridLayoutInfo_.startMainLineIndex_, layoutWrapper);
165 for (auto i = gridLayoutInfo_.startMainLineIndex_; i <= gridLayoutInfo_.endMainLineIndex_; i++) {
166 const auto& line = gridLayoutInfo_.gridMatrix_.find(i);
167 if (line == gridLayoutInfo_.gridMatrix_.end()) {
168 continue;
169 }
170
171 auto prevLineOffset = axis_ == Axis::VERTICAL ? OffsetF(0.0, prevLineHeight) : OffsetF(prevLineHeight, 0.0);
172 if (line->second.empty()) {
173 LOGE("line %{public}d should not be empty, please check.", line->first);
174 break;
175 }
176 int32_t itemIdex = -1;
177 float lineHeight = gridLayoutInfo_.lineHeightMap_[line->first];
178 for (auto iter = line->second.begin(); iter != line->second.end(); iter++) {
179 // If item index is the same, must be the same GridItem, need't layout again.
180 if (itemIdex == iter->second) {
181 continue;
182 }
183 itemIdex = iter->second;
184 auto crossIter = itemsCrossPosition_.find(itemIdex);
185 if (crossIter == itemsCrossPosition_.end()) {
186 LOGI("item %{public}d not in cross position", itemIdex);
187 continue;
188 }
189 auto crossOffset = crossIter->second;
190 auto offset = childFrameOffset + prevLineOffset;
191 offset = CalculateLargeItemOffset(offset, itemIdex, i, iter->first);
192 if (axis_ == Axis::VERTICAL) {
193 offset.SetX(crossOffset);
194 } else {
195 offset.SetY(crossOffset);
196 }
197 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(itemIdex);
198 if (!wrapper) {
199 LOGE("Layout item wrapper of index: %{public}d is null, please check.", itemIdex);
200 continue;
201 }
202 auto frSize = itemsCrossSize_.at(iter->first);
203 SizeF blockSize = gridLayoutProperty->IsVertical() ? SizeF(frSize, lineHeight) : SizeF(lineHeight, frSize);
204 auto translate = OffsetF(0.0f, 0.0f);
205 if (axis_ == Axis::VERTICAL) {
206 translate = Alignment::GetAlignPosition(
207 blockSize, wrapper->GetGeometryNode()->GetMarginFrameSize(), Alignment::TOP_CENTER);
208 } else {
209 translate = Alignment::GetAlignPosition(
210 blockSize, wrapper->GetGeometryNode()->GetMarginFrameSize(), Alignment::CENTER_LEFT);
211 }
212
213 wrapper->GetGeometryNode()->SetMarginFrameOffset(offset + translate);
214 wrapper->Layout();
215 auto layoutProperty = wrapper->GetLayoutProperty();
216 CHECK_NULL_VOID(layoutProperty);
217 auto gridItemLayoutProperty = AceType::DynamicCast<GridItemLayoutProperty>(layoutProperty);
218 CHECK_NULL_VOID(gridItemLayoutProperty);
219 gridItemLayoutProperty->UpdateMainIndex(line->first);
220 gridItemLayoutProperty->UpdateCrossIndex(iter->first);
221 }
222 prevLineHeight += gridLayoutInfo_.lineHeightMap_[line->first] + mainGap_;
223 }
224 gridLayoutInfo_.totalHeightOfItemsInView_ = gridLayoutInfo_.GetTotalHeightOfItemsInView(mainGap_);
225 }
226
InitialItemsCrossSize(const RefPtr<GridLayoutProperty> & layoutProperty,const SizeF & frameSize,int32_t childrenCount)227 void GridScrollLayoutAlgorithm::InitialItemsCrossSize(
228 const RefPtr<GridLayoutProperty>& layoutProperty, const SizeF& frameSize, int32_t childrenCount)
229 {
230 itemsCrossSize_.clear();
231 auto rowsTemplate = layoutProperty->GetRowsTemplate().value_or("");
232 auto columnsTemplate = layoutProperty->GetColumnsTemplate().value_or("");
233 axis_ = columnsTemplate.empty() ? Axis::HORIZONTAL : Axis::VERTICAL;
234 auto scale = layoutProperty->GetLayoutConstraint()->scaleProperty;
235 auto rowsGap = ConvertToPx(layoutProperty->GetRowsGap().value_or(0.0_vp), scale, frameSize.Height()).value_or(0);
236 auto columnsGap =
237 ConvertToPx(layoutProperty->GetColumnsGap().value_or(0.0_vp), scale, frameSize.Width()).value_or(0);
238 mainGap_ = axis_ == Axis::HORIZONTAL ? columnsGap : rowsGap;
239 crossGap_ = axis_ == Axis::VERTICAL ? columnsGap : rowsGap;
240 auto padding = layoutProperty->CreatePaddingAndBorder();
241 crossPaddingOffset_ = axis_ == Axis::HORIZONTAL ? padding.top.value_or(0) : padding.left.value_or(0);
242
243 auto crossSize = frameSize.CrossSize(axis_);
244 std::vector<double> crossLens;
245 std::pair<std::vector<double>, bool> cross;
246 if (!rowsTemplate.empty()) {
247 cross = ParseTemplateArgs(GridUtils::ParseArgs(rowsTemplate), crossSize, crossGap_, childrenCount);
248 } else {
249 cross = ParseTemplateArgs(GridUtils::ParseArgs(columnsTemplate), crossSize, crossGap_, childrenCount);
250 }
251 crossLens = cross.first;
252 if (cross.second) {
253 crossGap_ = 0.0f;
254 }
255
256 if (crossLens.empty()) {
257 crossLens.push_back(crossSize);
258 }
259
260 if (crossCount_ != crossLens.size()) {
261 crossCount_ = crossLens.size();
262 gridLayoutInfo_.crossCount_ = crossCount_;
263 }
264
265 int32_t index = 0;
266 for (const auto& len : crossLens) {
267 itemsCrossSize_.try_emplace(index, len);
268 ++index;
269 }
270 }
271
FillGridViewportAndMeasureChildren(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)272 void GridScrollLayoutAlgorithm::FillGridViewportAndMeasureChildren(
273 float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
274 {
275 itemsCrossPosition_.clear();
276 UpdateGridLayoutInfo(layoutWrapper, mainSize);
277 SkipLargeOffset(mainSize, layoutWrapper);
278
279 if (!gridLayoutInfo_.lastCrossCount_) {
280 gridLayoutInfo_.lastCrossCount_ = crossCount_;
281 }
282
283 if (gridLayoutInfo_.lastCrossCount_ != crossCount_ || layoutWrapper->GetHostNode()->GetChildrenUpdated() != -1 ||
284 gridLayoutInfo_.IsResetted()) {
285 gridLayoutInfo_.lastCrossCount_ = crossCount_;
286 gridLayoutInfo_.lineHeightMap_.clear();
287 gridLayoutInfo_.gridMatrix_.clear();
288 gridLayoutInfo_.irregularItemsPosition_.clear();
289 gridLayoutInfo_.endIndex_ = -1;
290 gridLayoutInfo_.endMainLineIndex_ = 0;
291 gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
292 gridLayoutInfo_.ResetPositionFlags();
293 isChildrenUpdated_ = true;
294
295 int32_t currentItemIndex = gridLayoutInfo_.startIndex_;
296 auto firstItem = GetStartingItem(layoutWrapper, currentItemIndex);
297 gridLayoutInfo_.startIndex_ = firstItem;
298 currentMainLineIndex_ = (firstItem == 0 ? 0 : gridLayoutInfo_.startMainLineIndex_) - 1;
299 gridLayoutInfo_.endIndex_ = firstItem - 1;
300 LOGI("data reload begin, firstItem:%{public}d, currentItemIndex:%{public}d", firstItem, currentItemIndex);
301 while (gridLayoutInfo_.endIndex_ < currentItemIndex) {
302 auto lineHeight = FillNewLineBackward(crossSize, mainSize, layoutWrapper, false);
303 if (LessNotEqual(lineHeight, 0.0)) {
304 gridLayoutInfo_.reachEnd_ = true;
305 break;
306 }
307 }
308 gridLayoutInfo_.startMainLineIndex_ = currentMainLineIndex_;
309 gridLayoutInfo_.UpdateStartIndexByStartLine();
310 // FillNewLineBackward sometimes make startIndex_ > currentItemIndex
311 while (gridLayoutInfo_.startIndex_ > currentItemIndex &&
312 gridLayoutInfo_.gridMatrix_.find(gridLayoutInfo_.startMainLineIndex_) !=
313 gridLayoutInfo_.gridMatrix_.end()) {
314 gridLayoutInfo_.startMainLineIndex_--;
315 gridLayoutInfo_.UpdateStartIndexByStartLine();
316 }
317 LOGI("data reload end, startIndex_:%{public}d, startMainLineIndex_:%{public}d", gridLayoutInfo_.startIndex_,
318 gridLayoutInfo_.startMainLineIndex_);
319 }
320
321 if (gridLayoutInfo_.scrollAlign_ == ScrollAlign::CENTER || gridLayoutInfo_.scrollAlign_ == ScrollAlign::END) {
322 UpdateCurrentOffsetForJumpTo(layoutWrapper, mainSize);
323 }
324 gridLayoutInfo_.jumpIndex_ = -1;
325 gridLayoutInfo_.scrollAlign_ = ScrollAlign::AUTO;
326
327 // Step1: Measure [GridItem] that has been recorded to [gridMatrix_]
328 float mainLength = MeasureRecordedItems(mainSize, crossSize, layoutWrapper);
329
330 // Step2: When done measure items in record, request new items to fill blank at end
331 FillBlankAtEnd(mainSize, crossSize, layoutWrapper, mainLength);
332 if (gridLayoutInfo_.reachEnd_) { // If it reaches end when [FillBlankAtEnd], modify [currentOffset_]
333 ModifyCurrentOffsetWhenReachEnd(mainSize);
334 }
335
336 // Step3: Check if need to fill blank at start (in situation of grid items moving down)
337 auto haveNewLineAtStart = FillBlankAtStart(mainSize, crossSize, layoutWrapper);
338 if (gridLayoutInfo_.reachStart_) {
339 auto offset = gridLayoutInfo_.currentOffset_;
340 if (!canOverScroll_) {
341 gridLayoutInfo_.currentOffset_ = 0.0;
342 gridLayoutInfo_.prevOffset_ = 0.0;
343 }
344 if (!haveNewLineAtStart) {
345 layoutWrapper->GetHostNode()->ChildrenUpdatedFrom(-1);
346 return;
347 }
348 // we need lastline if blank at start is not fully filled when start line is shorter
349 mainLength -= offset;
350 currentMainLineIndex_ = gridLayoutInfo_.endMainLineIndex_;
351 if (UseCurrentLines(mainSize, crossSize, layoutWrapper, mainLength)) {
352 FillBlankAtEnd(mainSize, crossSize, layoutWrapper, mainLength);
353 if (gridLayoutInfo_.reachEnd_) {
354 ModifyCurrentOffsetWhenReachEnd(mainSize);
355 }
356 }
357 }
358
359 layoutWrapper->GetHostNode()->ChildrenUpdatedFrom(-1);
360 }
361
FillBlankAtStart(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)362 bool GridScrollLayoutAlgorithm::FillBlankAtStart(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
363 {
364 bool fillNewLine = false;
365 // If [currentOffset_] is non-positive, it means no blank at start
366 if (LessOrEqual(gridLayoutInfo_.currentOffset_, 0.0)) {
367 return fillNewLine;
368 }
369 auto blankAtStart = gridLayoutInfo_.currentOffset_;
370 while (GreatNotEqual(blankAtStart, 0.0)) {
371 float lineHeight = FillNewLineForward(crossSize, mainSize, layoutWrapper);
372 if (GreatNotEqual(lineHeight, 0.0)) {
373 gridLayoutInfo_.lineHeightMap_[gridLayoutInfo_.startMainLineIndex_] = lineHeight;
374 blankAtStart -= (lineHeight + mainGap_);
375 fillNewLine = true;
376 continue;
377 }
378 gridLayoutInfo_.reachStart_ = true;
379 break;
380 }
381 gridLayoutInfo_.currentOffset_ = blankAtStart;
382 gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
383 return fillNewLine;
384 }
385
386 // When a moving up event comes, the [currentOffset_] may have been reduced too much than the items really need to
387 // be moved up, so we need to modify [currentOffset_] according to previous position.
ModifyCurrentOffsetWhenReachEnd(float mainSize)388 void GridScrollLayoutAlgorithm::ModifyCurrentOffsetWhenReachEnd(float mainSize)
389 {
390 // Step1. Calculate total length of all items with main gap in viewport.
391 // [lengthOfItemsInViewport] must be greater than or equal to viewport height
392 float lengthOfItemsInViewport = gridLayoutInfo_.GetTotalHeightOfItemsInView(mainGap_);
393 // scroll forward
394 if (LessNotEqual(gridLayoutInfo_.prevOffset_, gridLayoutInfo_.currentOffset_)) {
395 if (!canOverScroll_) {
396 gridLayoutInfo_.reachEnd_ = false;
397 return;
398 } else if (!isChildrenUpdated_) {
399 if (LessNotEqual(lengthOfItemsInViewport, mainSize)) {
400 return;
401 }
402 }
403 }
404 // Step2. Calculate real offset that items can only be moved up by.
405 // Hint: [prevOffset_] is a non-positive value
406 if (LessNotEqual(lengthOfItemsInViewport, mainSize) && gridLayoutInfo_.startIndex_ == 0) {
407 if (!canOverScroll_ || isChildrenUpdated_) {
408 gridLayoutInfo_.currentOffset_ = 0;
409 gridLayoutInfo_.prevOffset_ = 0;
410 }
411 gridLayoutInfo_.reachStart_ = true;
412 gridLayoutInfo_.offsetEnd_ = true;
413 return;
414 }
415
416 // last grid item is not fully showed
417 if (GreatNotEqual(gridLayoutInfo_.currentOffset_ + lengthOfItemsInViewport, mainSize)) {
418 return;
419 }
420
421 // Step3. modify [currentOffset_]
422 if (!canOverScroll_) {
423 float realOffsetToMoveUp = lengthOfItemsInViewport - mainSize + gridLayoutInfo_.prevOffset_;
424 gridLayoutInfo_.currentOffset_ = gridLayoutInfo_.prevOffset_ - realOffsetToMoveUp;
425 gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
426 }
427 gridLayoutInfo_.offsetEnd_ = true;
428 }
429
FillBlankAtEnd(float mainSize,float crossSize,LayoutWrapper * layoutWrapper,float & mainLength)430 void GridScrollLayoutAlgorithm::FillBlankAtEnd(
431 float mainSize, float crossSize, LayoutWrapper* layoutWrapper, float& mainLength)
432 {
433 if (GreatNotEqual(mainLength, mainSize)) {
434 return;
435 }
436 // fill current line first
437 auto mainIter = gridLayoutInfo_.gridMatrix_.find(currentMainLineIndex_);
438 auto nextMain = gridLayoutInfo_.gridMatrix_.find(currentMainLineIndex_ + 1);
439 if (mainIter != gridLayoutInfo_.gridMatrix_.end() && mainIter->second.size() < crossCount_ &&
440 nextMain == gridLayoutInfo_.gridMatrix_.end()) {
441 auto currentIndex = gridLayoutInfo_.endIndex_ + 1;
442 cellAveLength_ = -1.0f;
443 bool hasNormalItem = false;
444 lastCross_ = 0;
445 for (uint32_t i = (mainIter->second.empty() ? 0 : mainIter->second.rbegin()->first); i < crossCount_; i++) {
446 // Step1. Get wrapper of [GridItem]
447 auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
448 if (!itemWrapper) {
449 break;
450 }
451 // Step2. Measure child
452 auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
453 auto childState = MeasureNewChild(frameSize, currentIndex, layoutWrapper, itemWrapper, false);
454 if (childState == -1) {
455 cellAveLength_ = LessNotEqual(cellAveLength_, 0.0)
456 ? gridLayoutInfo_.lineHeightMap_.find(currentMainLineIndex_ - 1)->second
457 : cellAveLength_;
458 --currentIndex;
459 break;
460 }
461 i += childState - 1;
462 // Step3. Measure [GridItem]
463 LargeItemLineHeight(itemWrapper, hasNormalItem);
464 gridLayoutInfo_.endIndex_ = currentIndex;
465 currentIndex++;
466 }
467 }
468 // When [mainLength] is still less than [mainSize], do [FillNewLineBackward] repeatedly until filling up the lower
469 // part of the viewport
470 while (LessNotEqual(mainLength, mainSize)) {
471 float lineHeight = FillNewLineBackward(crossSize, mainSize, layoutWrapper, false);
472 if (GreatOrEqual(lineHeight, 0.0)) {
473 mainLength += lineHeight;
474 continue;
475 }
476 gridLayoutInfo_.reachEnd_ = true;
477 return;
478 };
479 // last line make LessNotEqual(mainLength, mainSize) and continue is reach end too
480 gridLayoutInfo_.reachEnd_ = gridLayoutInfo_.endIndex_ == layoutWrapper->GetTotalChildCount() - 1;
481 }
482
CalculateLargeItemOffset(OffsetF currOffset,int32_t itemIndex,int32_t currLineIndex,int32_t currentCrossIndex)483 OffsetF GridScrollLayoutAlgorithm::CalculateLargeItemOffset(
484 OffsetF currOffset, int32_t itemIndex, int32_t currLineIndex, int32_t currentCrossIndex)
485 {
486 OffsetF offset = currOffset;
487 for (int32_t lastCrossIndex = currLineIndex - 1; lastCrossIndex >= 0; lastCrossIndex--) {
488 auto LastGridMatrixIter = gridLayoutInfo_.gridMatrix_.find(lastCrossIndex);
489 if (LastGridMatrixIter == gridLayoutInfo_.gridMatrix_.end()) {
490 continue;
491 }
492 auto lastGridItemRecord = LastGridMatrixIter->second;
493 auto lastLineCrossItem = lastGridItemRecord.find(currentCrossIndex);
494 if (lastLineCrossItem == lastGridItemRecord.end()) {
495 continue;
496 }
497 if (lastLineCrossItem->second == itemIndex) {
498 offset -= axis_ == Axis::VERTICAL ? OffsetF(0, gridLayoutInfo_.lineHeightMap_[lastCrossIndex] + mainGap_)
499 : OffsetF(gridLayoutInfo_.lineHeightMap_[lastCrossIndex] + mainGap_, 0.0);
500 } else {
501 break;
502 }
503 }
504
505 return offset;
506 }
507
NeedAdjust(const RefPtr<GridItemLayoutProperty> & itemLayoutProperty)508 bool GridScrollLayoutAlgorithm::NeedAdjust(const RefPtr<GridItemLayoutProperty>& itemLayoutProperty)
509 {
510 bool needAdjust = false;
511 auto main = axis_ == Axis::VERTICAL ? mainCount_: crossCount_;
512 auto cross = axis_ == Axis::VERTICAL ? crossCount_: mainCount_;
513 if (itemLayoutProperty->GetRowStart().has_value()) {
514 currentItemRowStart_ = itemLayoutProperty->GetRowStart().value_or(-1);
515 if ((currentItemRowStart_ < 0) || (currentItemRowStart_ >= static_cast<int32_t>(main))) {
516 needAdjust = true;
517 }
518 }
519 if (itemLayoutProperty->GetRowEnd().has_value()) {
520 currentItemRowEnd_ = itemLayoutProperty->GetRowEnd().value_or(-1);
521 if ((currentItemRowEnd_ < 0) || (currentItemRowEnd_ >= static_cast<int32_t>(main))) {
522 needAdjust = true;
523 }
524 }
525 if (itemLayoutProperty->GetColumnStart().has_value()) {
526 currentItemColStart_ = itemLayoutProperty->GetColumnStart().value_or(-1);
527 if ((currentItemColStart_ < 0) || (currentItemColStart_ >= static_cast<int32_t>(cross))) {
528 needAdjust = true;
529 }
530 }
531 if (itemLayoutProperty->GetColumnEnd().has_value()) {
532 currentItemColEnd_ = itemLayoutProperty->GetColumnEnd().value_or(-1);
533 if ((currentItemColEnd_ < 0) || (currentItemColEnd_ >= static_cast<int32_t>(cross))) {
534 needAdjust = true;
535 }
536 }
537 return needAdjust;
538 }
539
AdjustRowColSpan(const RefPtr<LayoutWrapper> & itemLayoutWrapper,LayoutWrapper *,int32_t)540 void GridScrollLayoutAlgorithm::AdjustRowColSpan(
541 const RefPtr<LayoutWrapper>& itemLayoutWrapper, LayoutWrapper* /* layoutWrapper */, int32_t /* itemIndex */)
542 {
543 auto itemLayoutProperty = DynamicCast<GridItemLayoutProperty>(itemLayoutWrapper->GetLayoutProperty());
544 CHECK_NULL_VOID(itemLayoutProperty);
545 bool needAdjust = false;
546 currentItemRowSpan_ = 1;
547 currentItemColSpan_ = 1;
548 currentItemRowStart_ = -1;
549 currentItemColStart_ = -1;
550 currentItemColEnd_ = -1;
551 currentItemRowEnd_ = -1;
552 needAdjust = NeedAdjust(itemLayoutProperty);
553 if (!needAdjust) {
554 currentItemRowSpan_ = std::max(currentItemRowEnd_ - currentItemRowStart_ + 1, 1);
555 currentItemColSpan_ = std::max(currentItemColEnd_ - currentItemColStart_ + 1, 1);
556 } else {
557 currentItemRowStart_ = -1;
558 currentItemColStart_ = -1;
559 currentItemColEnd_ = -1;
560 currentItemRowEnd_ = -1;
561 }
562 if ((currentItemRowStart_ == -1 && currentItemRowEnd_ != -1) ||
563 (currentItemRowEnd_ == -1 && currentItemRowStart_ != -1) ||
564 (currentItemColStart_ == -1 && currentItemColEnd_ != -1) ||
565 (currentItemColEnd_ == -1 && currentItemColStart_ != -1)) {
566 currentItemRowSpan_ = 1;
567 currentItemColSpan_ = 1;
568 currentItemRowStart_ = -1;
569 currentItemColStart_ = -1;
570 currentItemColEnd_ = -1;
571 currentItemRowEnd_ = -1;
572 }
573 if (currentItemRowSpan_ > 1 || currentItemColSpan_ > 1) {
574 gridLayoutInfo_.hasBigItem_ = true;
575 }
576 }
577
LargeItemLineHeight(const RefPtr<LayoutWrapper> & itemWrapper,bool & hasNormalItem)578 void GridScrollLayoutAlgorithm::LargeItemLineHeight(const RefPtr<LayoutWrapper>& itemWrapper, bool& hasNormalItem)
579 {
580 AdjustRowColSpan(itemWrapper, nullptr, 0);
581 auto mainSpan = axis_ == Axis::VERTICAL ? currentItemRowSpan_ : currentItemColSpan_;
582 auto itemSize = itemWrapper->GetGeometryNode()->GetMarginFrameSize();
583 if (mainSpan == 1) {
584 cellAveLength_ = std::max(GetMainAxisSize(itemSize, gridLayoutInfo_.axis_), cellAveLength_);
585 hasNormalItem = true;
586 }
587
588 if ((mainSpan > 1) && !hasNormalItem) {
589 cellAveLength_ = std::max(GetMainAxisSize(itemSize, gridLayoutInfo_.axis_) / mainSpan, cellAveLength_);
590 }
591 }
592
IsIndexInMatrix(int32_t index,int32_t & startLine)593 bool GridScrollLayoutAlgorithm::IsIndexInMatrix(int32_t index, int32_t& startLine)
594 {
595 auto iter = std::find_if(gridLayoutInfo_.gridMatrix_.begin(), gridLayoutInfo_.gridMatrix_.end(),
596 [index, &startLine](const std::pair<int32_t, std::map<int32_t, int32_t>>& item) {
597 for (auto& subitem : item.second) {
598 if (subitem.second == index) {
599 startLine = item.first;
600 return true;
601 }
602 }
603 return false;
604 });
605 return (iter != gridLayoutInfo_.gridMatrix_.end());
606 }
607
GetTargetIndexInfoWithBenchMark(LayoutWrapper * layoutWrapper,bool isTargetBackward,int32_t targetIndex)608 void GridScrollLayoutAlgorithm::GetTargetIndexInfoWithBenchMark(
609 LayoutWrapper* layoutWrapper, bool isTargetBackward, int32_t targetIndex)
610 {
611 int32_t benchmarkIndex = isTargetBackward ? gridLayoutInfo_.gridMatrix_.rbegin()->second.rbegin()->second + 1 : 0;
612 int32_t mainStartIndex = isTargetBackward ? gridLayoutInfo_.gridMatrix_.rbegin()->first + 1 : 0;
613 int32_t currentIndex = benchmarkIndex;
614 int32_t headOfMainStartLine = currentIndex;
615
616 while (currentIndex < targetIndex) {
617 int32_t crossGridReserve = gridLayoutInfo_.crossCount_;
618 /* go through a new line */
619 while ((crossGridReserve > 0) && (currentIndex <= targetIndex)) {
620 auto currentWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex, false);
621 CHECK_NULL_VOID(currentWrapper);
622 auto layoutProperty = DynamicCast<GridItemLayoutProperty>(currentWrapper->GetLayoutProperty());
623 CHECK_NULL_VOID(layoutProperty);
624 auto gridSpan = layoutProperty->GetCrossSpan(gridLayoutInfo_.axis_);
625 if (crossGridReserve >= gridSpan) {
626 crossGridReserve -= gridSpan;
627 } else if (gridLayoutInfo_.crossCount_ >= gridSpan) {
628 ++mainStartIndex;
629 headOfMainStartLine = currentIndex;
630 crossGridReserve = gridLayoutInfo_.crossCount_ - gridSpan;
631 }
632 ++currentIndex;
633 }
634 if (currentIndex > targetIndex) {
635 break;
636 }
637 ++mainStartIndex;
638 headOfMainStartLine = currentIndex;
639 }
640 gridLayoutInfo_.startMainLineIndex_ = mainStartIndex;
641 gridLayoutInfo_.startIndex_ = headOfMainStartLine;
642 gridLayoutInfo_.endIndex_ = headOfMainStartLine - 1;
643 gridLayoutInfo_.prevOffset_ = 0;
644 gridLayoutInfo_.currentOffset_ = 0;
645 gridLayoutInfo_.ResetPositionFlags();
646 gridLayoutInfo_.gridMatrix_.clear();
647 gridLayoutInfo_.lineHeightMap_.clear();
648 gridLayoutInfo_.irregularItemsPosition_.clear();
649 }
650
UpdateGridLayoutInfo(LayoutWrapper * layoutWrapper,float mainSize)651 void GridScrollLayoutAlgorithm::UpdateGridLayoutInfo(LayoutWrapper* layoutWrapper, float mainSize)
652 {
653 /* 1. Have gotten gridLayoutInfo_.startMainLineIndex_ and directly jump to it */
654 if (gridLayoutInfo_.jumpIndex_ < 0) {
655 return;
656 }
657 /* 2. Need to find out the startMainLineIndex according to startIndex */
658 int32_t targetIndex = gridLayoutInfo_.jumpIndex_;
659 /* 2.1 invalid targetIndex */
660 if (layoutWrapper->GetTotalChildCount() <= targetIndex) {
661 return;
662 }
663
664 switch (gridLayoutInfo_.scrollAlign_) {
665 case ScrollAlign::START:
666 case ScrollAlign::END:
667 case ScrollAlign::CENTER:
668 ScrollToIndexStart(layoutWrapper, targetIndex);
669 return;
670 default:
671 ScrollToIndexAuto(layoutWrapper, mainSize, targetIndex);
672 }
673 }
674
ScrollToIndexAuto(LayoutWrapper * layoutWrapper,float mainSize,int32_t targetIndex)675 void GridScrollLayoutAlgorithm::ScrollToIndexAuto(LayoutWrapper* layoutWrapper, float mainSize, int32_t targetIndex)
676 {
677 int32_t startLine = 0;
678 if (IsIndexInMatrix(targetIndex, startLine)) {
679 if (startLine < gridLayoutInfo_.endMainLineIndex_ && startLine > gridLayoutInfo_.startMainLineIndex_) {
680 return;
681 }
682
683 if (startLine >= gridLayoutInfo_.endMainLineIndex_) {
684 auto totalViewHeight = gridLayoutInfo_.GetTotalHeightOfItemsInView(mainGap_);
685 gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
686 gridLayoutInfo_.currentOffset_ -= (totalViewHeight - mainSize + gridLayoutInfo_.currentOffset_);
687 for (int32_t i = gridLayoutInfo_.endMainLineIndex_ + 1; i <= startLine; ++i) {
688 gridLayoutInfo_.currentOffset_ -= (mainGap_ + gridLayoutInfo_.lineHeightMap_[i]);
689 }
690 gridLayoutInfo_.ResetPositionFlags();
691 return;
692 }
693
694 // startLine <= gridLayoutInfo_.startMainLineIndex_
695 gridLayoutInfo_.startMainLineIndex_ = startLine;
696 gridLayoutInfo_.UpdateStartIndexByStartLine();
697 gridLayoutInfo_.prevOffset_ = 0;
698 gridLayoutInfo_.currentOffset_ = 0;
699 gridLayoutInfo_.ResetPositionFlags();
700 return;
701 }
702
703 /* 2.3 targetIndex is out of the matrix */
704 if (gridLayoutInfo_.gridMatrix_.empty()) {
705 LOGW("no grid for jump to index:%{public}d", targetIndex);
706 return;
707 }
708 bool isTargetBackward = true;
709 if (targetIndex < gridLayoutInfo_.gridMatrix_.begin()->second.begin()->second) {
710 isTargetBackward = false;
711 } else if (targetIndex > gridLayoutInfo_.gridMatrix_.rbegin()->second.rbegin()->second) {
712 isTargetBackward = true;
713 } else {
714 return;
715 }
716 auto grid = layoutWrapper->GetHostNode();
717 CHECK_NULL_VOID(grid);
718 grid->ChildrenUpdatedFrom(0);
719 GetTargetIndexInfoWithBenchMark(layoutWrapper, isTargetBackward, targetIndex);
720 moveToEndLineIndex_ = isTargetBackward ? targetIndex : moveToEndLineIndex_;
721 }
722
ScrollToIndexStart(LayoutWrapper * layoutWrapper,int32_t targetIndex)723 void GridScrollLayoutAlgorithm::ScrollToIndexStart(LayoutWrapper* layoutWrapper, int32_t targetIndex)
724 {
725 int32_t startLine = 0;
726 /* targetIndex is in the matrix */
727 if (IsIndexInMatrix(targetIndex, startLine)) {
728 if (startLine == gridLayoutInfo_.startMainLineIndex_) {
729 gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
730 gridLayoutInfo_.currentOffset_ = 0;
731 gridLayoutInfo_.ResetPositionFlags();
732 return;
733 }
734
735 gridLayoutInfo_.startMainLineIndex_ = startLine;
736 gridLayoutInfo_.UpdateStartIndexByStartLine();
737 gridLayoutInfo_.prevOffset_ = 0;
738 gridLayoutInfo_.currentOffset_ = 0;
739 gridLayoutInfo_.ResetPositionFlags();
740 return;
741 }
742 /* targetIndex is out of the matrix */
743 if (gridLayoutInfo_.gridMatrix_.empty()) {
744 LOGW("no grid for jump to index:%{public}d", targetIndex);
745 return;
746 }
747 bool isTargetBackward = true;
748 if (targetIndex < gridLayoutInfo_.gridMatrix_.begin()->second.begin()->second) {
749 isTargetBackward = false;
750 } else if (targetIndex > gridLayoutInfo_.gridMatrix_.rbegin()->second.rbegin()->second) {
751 isTargetBackward = true;
752 } else {
753 return;
754 }
755 auto grid = layoutWrapper->GetHostNode();
756 CHECK_NULL_VOID(grid);
757 grid->ChildrenUpdatedFrom(0);
758 GetTargetIndexInfoWithBenchMark(layoutWrapper, isTargetBackward, targetIndex);
759 }
760
UpdateCurrentOffsetForJumpTo(LayoutWrapper * layoutWrapper,float mainSize)761 void GridScrollLayoutAlgorithm::UpdateCurrentOffsetForJumpTo(LayoutWrapper* layoutWrapper, float mainSize)
762 {
763 int32_t startLine = 0;
764 /* targetIndex is in the matrix */
765 if (IsIndexInMatrix(gridLayoutInfo_.jumpIndex_, startLine)) {
766 // scroll to end of the screen
767 gridLayoutInfo_.currentOffset_ = mainSize - gridLayoutInfo_.lineHeightMap_[startLine];
768 // scroll to center of the screen
769 if (gridLayoutInfo_.scrollAlign_ == ScrollAlign::CENTER) {
770 gridLayoutInfo_.currentOffset_ /= 2;
771 }
772 gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
773 return;
774 }
775 /* targetIndex is out of the matrix */
776 LOGW("can not find jumpIndex in Grid Matrix :%{public}d", gridLayoutInfo_.jumpIndex_);
777 }
778
MeasureRecordedItems(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)779 float GridScrollLayoutAlgorithm::MeasureRecordedItems(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
780 {
781 currentMainLineIndex_ = gridLayoutInfo_.startMainLineIndex_ - 1;
782 float mainLength = gridLayoutInfo_.currentOffset_;
783 // already at start line, do not use offset for mainLength
784 if (gridLayoutInfo_.startMainLineIndex_ == 0 && GreatNotEqual(mainLength, 0)) {
785 mainLength = 0;
786 }
787 UseCurrentLines(mainSize, crossSize, layoutWrapper, mainLength);
788 return mainLength;
789 }
790
UseCurrentLines(float mainSize,float crossSize,LayoutWrapper * layoutWrapper,float & mainLength)791 bool GridScrollLayoutAlgorithm::UseCurrentLines(
792 float mainSize, float crossSize, LayoutWrapper* layoutWrapper, float& mainLength)
793 {
794 bool runOutOfRecord = false;
795 // Measure grid items row by row
796 int32_t tempEndIndex = -1;
797 while (LessNotEqual(mainLength, mainSize)) {
798 // If [gridMatrix_] does not contain record of line [currentMainLineIndex_], do [FillNewLineBackward]
799 auto gridMatrixIter = gridLayoutInfo_.gridMatrix_.find(++currentMainLineIndex_);
800 if ((gridMatrixIter == gridLayoutInfo_.gridMatrix_.end()) ||
801 (gridLayoutInfo_.lineHeightMap_.find(currentMainLineIndex_) == gridLayoutInfo_.lineHeightMap_.end())) {
802 runOutOfRecord = true;
803 break;
804 }
805 int32_t currentIndex = -1;
806 bool hasNormalItem = false;
807 cellAveLength_ = -1.0f;
808 for (const auto& gridItemRecord : gridMatrixIter->second) {
809 if (currentIndex == gridItemRecord.second) {
810 continue;
811 }
812 currentIndex = gridItemRecord.second;
813 if (currentIndex == -1) {
814 // move from another grid
815 continue;
816 }
817 auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
818 if (!itemWrapper) {
819 LOGE("GridItem wrapper of index %{public}u null", currentIndex);
820 break;
821 }
822 auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
823 AdjustRowColSpan(itemWrapper, layoutWrapper, currentIndex);
824 auto crossStart = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
825 if (crossStart == -1) {
826 MeasureChildPlaced(frameSize, currentIndex, gridItemRecord.first, layoutWrapper, itemWrapper);
827 } else {
828 MeasureChildPlaced(frameSize, currentIndex, crossStart, layoutWrapper, itemWrapper);
829 }
830 // Record end index. When fill new line, the [endIndex_] will be the first item index to request
831 LargeItemLineHeight(itemWrapper, hasNormalItem);
832 tempEndIndex = std::max(currentIndex, tempEndIndex);
833 gridLayoutInfo_.endIndex_ = tempEndIndex;
834 }
835
836 if (GreatOrEqual(cellAveLength_, 0.0)) { // Means at least one item has been measured
837 gridLayoutInfo_.lineHeightMap_[currentMainLineIndex_] = cellAveLength_;
838 mainLength += (cellAveLength_ + mainGap_);
839 }
840 // If a line moves up out of viewport, update [startIndex_], [currentOffset_] and [startMainLineIndex_]
841 if (LessNotEqual(mainLength, 0.0)) {
842 gridLayoutInfo_.currentOffset_ = mainLength;
843 gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
844 gridLayoutInfo_.startMainLineIndex_ = currentMainLineIndex_ + 1;
845 gridLayoutInfo_.UpdateStartIndexByStartLine();
846 }
847 }
848 // Case 1. if this while-loop breaks due to running out of records, the [currentMainLineIndex_] is larger by 1 than
849 // real main line index, so reduce 1.
850 // Case 2. if this while-loop stops due to false result of [LessNotEqual(mainLength, mainSize)], the
851 // [currentMainLineIndex_] is exactly the real main line index. Update [endMainLineIndex_] when the recorded items
852 // are done measured.
853 gridLayoutInfo_.endMainLineIndex_ = runOutOfRecord ? --currentMainLineIndex_ : currentMainLineIndex_;
854 // reset reachEnd_ if any line at bottom is out of viewport
855 // last line make LessNotEqual(mainLength, mainSize) and continue is reach end too
856 gridLayoutInfo_.reachEnd_ = gridLayoutInfo_.endIndex_ == layoutWrapper->GetTotalChildCount() - 1;
857 return runOutOfRecord;
858 }
859
SkipLargeOffset(float mainSize,LayoutWrapper * layoutWrapper)860 void GridScrollLayoutAlgorithm::SkipLargeOffset(float mainSize, LayoutWrapper* layoutWrapper)
861 {
862 auto gridLayoutProperty = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
863 CHECK_NULL_VOID(gridLayoutProperty);
864 auto cacheCount = gridLayoutProperty->GetCachedCountValue(1);
865 cacheCount = std::max(cacheCount, 1);
866 SkipForwardLines(cacheCount * mainSize, layoutWrapper);
867 SkipBackwardLines(cacheCount * mainSize, layoutWrapper);
868 }
869
SkipForwardLines(float mainSize,LayoutWrapper * layoutWrapper)870 void GridScrollLayoutAlgorithm::SkipForwardLines(float mainSize, LayoutWrapper* layoutWrapper)
871 {
872 if (!GreatOrEqual(gridLayoutInfo_.currentOffset_ - gridLayoutInfo_.prevOffset_, mainSize)) {
873 return;
874 }
875
876 // skip lines in matrix
877 while (GreatOrEqual(gridLayoutInfo_.currentOffset_, mainSize)) {
878 auto line = gridLayoutInfo_.gridMatrix_.find(gridLayoutInfo_.startMainLineIndex_ - 1);
879 if (line == gridLayoutInfo_.gridMatrix_.end()) {
880 break;
881 }
882 auto lineHeight = gridLayoutInfo_.lineHeightMap_.find(gridLayoutInfo_.startMainLineIndex_ - 1);
883 if (lineHeight == gridLayoutInfo_.lineHeightMap_.end()) {
884 break;
885 }
886 gridLayoutInfo_.startMainLineIndex_--;
887 gridLayoutInfo_.startIndex_ = line->second.begin()->second;
888 gridLayoutInfo_.currentOffset_ -= lineHeight->second;
889 }
890
891 // skip lines not in matrix
892 if (GreatOrEqual(gridLayoutInfo_.currentOffset_, mainSize) && gridLayoutInfo_.startIndex_ > 0) {
893 auto grid = layoutWrapper->GetHostNode();
894 CHECK_NULL_VOID(grid);
895 auto pattern = grid->GetPattern<GridPattern>();
896 CHECK_NULL_VOID(pattern);
897 auto estimatedHeight = pattern->GetScrollableDistance();
898 // no scroller
899 if (LessOrEqual(estimatedHeight, 0)) {
900 return;
901 }
902 auto averageHeight = pattern->GetAverageHeight();
903 if (LessOrEqual(averageHeight, 0.0)) {
904 return;
905 }
906 int32_t estimatedIndex = (gridLayoutInfo_.currentOffset_) / averageHeight;
907 gridLayoutInfo_.startIndex_ = gridLayoutInfo_.startIndex_ > estimatedIndex
908 ? std::max(gridLayoutInfo_.startIndex_ - estimatedIndex, 0)
909 : 0;
910 gridLayoutInfo_.currentOffset_ =
911 gridLayoutInfo_.startIndex_ > estimatedIndex ? gridLayoutInfo_.prevOffset_ : 0.0f;
912 LOGI("estimatedIndex:%{public}d", gridLayoutInfo_.startIndex_);
913 grid->ChildrenUpdatedFrom(0);
914 }
915 }
916
SkipBackwardLines(float mainSize,LayoutWrapper * layoutWrapper)917 void GridScrollLayoutAlgorithm::SkipBackwardLines(float mainSize, LayoutWrapper* layoutWrapper)
918 {
919 if (!GreatOrEqual(gridLayoutInfo_.prevOffset_ - gridLayoutInfo_.currentOffset_, mainSize)) {
920 return;
921 }
922
923 // grid size change from big to small
924 gridLayoutInfo_.UpdateEndLine(mainSize, mainGap_);
925
926 // skip lines in matrix
927 while (GreatOrEqual(-gridLayoutInfo_.currentOffset_, mainSize)) {
928 auto line = gridLayoutInfo_.gridMatrix_.find(gridLayoutInfo_.endMainLineIndex_ + 1);
929 if (line == gridLayoutInfo_.gridMatrix_.end()) {
930 break;
931 }
932 auto lineHeight = gridLayoutInfo_.lineHeightMap_.find(gridLayoutInfo_.endMainLineIndex_ + 1);
933 if (lineHeight == gridLayoutInfo_.lineHeightMap_.end()) {
934 break;
935 }
936 gridLayoutInfo_.startMainLineIndex_++;
937 gridLayoutInfo_.endMainLineIndex_++;
938 gridLayoutInfo_.currentOffset_ += lineHeight->second;
939 }
940 gridLayoutInfo_.UpdateStartIndexByStartLine();
941
942 // skip lines not in matrix
943 if (GreatOrEqual(-gridLayoutInfo_.currentOffset_, mainSize)) {
944 auto grid = layoutWrapper->GetHostNode();
945 CHECK_NULL_VOID(grid);
946 auto pattern = grid->GetPattern<GridPattern>();
947 CHECK_NULL_VOID(pattern);
948 auto estimatedHeight = pattern->GetScrollableDistance();
949 // no scroller
950 if (LessOrEqual(estimatedHeight, 0)) {
951 return;
952 }
953 auto averageHeight = pattern->GetAverageHeight();
954 if (LessOrEqual(averageHeight, 0.0)) {
955 return;
956 }
957 int32_t estimatedIndex = (gridLayoutInfo_.currentOffset_) / averageHeight;
958 gridLayoutInfo_.startIndex_ =
959 std::min(gridLayoutInfo_.startIndex_ - estimatedIndex, gridLayoutInfo_.childrenCount_);
960 gridLayoutInfo_.currentOffset_ = gridLayoutInfo_.prevOffset_;
961 LOGI("estimatedIndex:%{public}d, currentOffset_:%{public}f", gridLayoutInfo_.startIndex_,
962 gridLayoutInfo_.currentOffset_);
963 grid->ChildrenUpdatedFrom(0);
964 }
965 }
966
FillNewLineForward(float crossSize,float mainSize,LayoutWrapper * layoutWrapper)967 float GridScrollLayoutAlgorithm::FillNewLineForward(float crossSize, float mainSize, LayoutWrapper* layoutWrapper)
968 {
969 // To make the code more convenient to read, we name a param in situation of vertical, for example:
970 // 1. [lineHight] means height of a row when the Grid is vertical;
971 // 2. [lineHight] means width of a column when the Grid is horizontal;
972 // Other params are also named according to this principle.
973 cellAveLength_ = -1.0f;
974 auto currentIndex = gridLayoutInfo_.startIndex_;
975 bool hasNormalItem = false;
976 // TODO: shoule we use policy of adaptive layout according to size of [GridItem] ?
977 if (gridLayoutInfo_.startMainLineIndex_ - 1 < 0) {
978 if (currentIndex == 0) {
979 return cellAveLength_;
980 }
981 // add more than one line
982 UpdateMatrixForAddedItems();
983 }
984 gridLayoutInfo_.startMainLineIndex_--;
985 bool doneCreateNewLine = false;
986 auto gridMatrixIter = gridLayoutInfo_.gridMatrix_.find(gridLayoutInfo_.startMainLineIndex_);
987 if (gridMatrixIter == gridLayoutInfo_.gridMatrix_.end()) {
988 AddForwardLines(currentIndex, crossSize, mainSize, layoutWrapper);
989 }
990 gridMatrixIter = gridLayoutInfo_.gridMatrix_.find(gridLayoutInfo_.startMainLineIndex_);
991 if (gridMatrixIter == gridLayoutInfo_.gridMatrix_.end()) {
992 return cellAveLength_;
993 }
994
995 // need to obtain the item node in order and by step one in LazyLayoutWrapperBuilder::OnGetOrCreateWrapperByIndex
996 for (auto itemIter = gridMatrixIter->second.rbegin(); itemIter != gridMatrixIter->second.rend(); ++itemIter) {
997 currentIndex = itemIter->second;
998
999 // Step1. Get wrapper of [GridItem]
1000 auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
1001 if (!itemWrapper) {
1002 LOGE("GridItem wrapper of index %{public}u null", currentIndex);
1003 break;
1004 }
1005 // Step2. Measure child
1006 // TODO: need to use [isScrollable_]
1007 auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
1008 AdjustRowColSpan(itemWrapper, layoutWrapper, currentIndex);
1009 auto crossStart = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1010 if (crossStart == -1) {
1011 MeasureChildPlaced(frameSize, currentIndex, itemIter->first, layoutWrapper, itemWrapper);
1012 } else {
1013 MeasureChildPlaced(frameSize, currentIndex, crossStart, layoutWrapper, itemWrapper);
1014 }
1015 // Step3. Measure [GridItem]
1016 LargeItemLineHeight(itemWrapper, hasNormalItem);
1017 gridLayoutInfo_.startIndex_ = currentIndex;
1018 }
1019
1020 doneCreateNewLine = GreatOrEqual(cellAveLength_, 0.0);
1021 // If it fails to create new line when [FillNewLineForward] is called, it means that it reaches start
1022 gridLayoutInfo_.reachStart_ = !doneCreateNewLine;
1023
1024 return cellAveLength_;
1025 }
1026
UpdateMatrixForAddedItems()1027 void GridScrollLayoutAlgorithm::UpdateMatrixForAddedItems()
1028 {
1029 decltype(gridLayoutInfo_.lineHeightMap_) gridLineHeightMap(std::move(gridLayoutInfo_.lineHeightMap_));
1030 decltype(gridLayoutInfo_.gridMatrix_) gridMatrix(std::move(gridLayoutInfo_.gridMatrix_));
1031 for (const auto& item : gridMatrix) {
1032 gridLayoutInfo_.gridMatrix_[item.first + 1] = item.second;
1033 }
1034 for (const auto& item : gridLineHeightMap) {
1035 gridLayoutInfo_.lineHeightMap_[item.first + 1] = item.second;
1036 }
1037 gridLayoutInfo_.startMainLineIndex_ = gridLayoutInfo_.startMainLineIndex_ + 1;
1038 gridLayoutInfo_.endMainLineIndex_ = gridLayoutInfo_.endMainLineIndex_ + 1;
1039 LOGI("add more than one line startMainLineIndex_:%{public}d", gridLayoutInfo_.startMainLineIndex_);
1040 }
1041
AddForwardLines(int32_t currentIndex,float crossSize,float mainSize,LayoutWrapper * layoutWrapper)1042 void GridScrollLayoutAlgorithm::AddForwardLines(
1043 int32_t currentIndex, float crossSize, float mainSize, LayoutWrapper* layoutWrapper)
1044 {
1045 auto endMainLineIndex = gridLayoutInfo_.endMainLineIndex_;
1046 auto endIndex = gridLayoutInfo_.endIndex_;
1047 auto firstItem = GetStartingItem(layoutWrapper, currentIndex - 1);
1048 auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(firstItem);
1049 AdjustRowColSpan(itemWrapper, layoutWrapper, firstItem);
1050 auto mainSpan = axis_ == Axis::VERTICAL ? currentItemRowSpan_ : currentItemColSpan_;
1051 auto measureNumber = 0;
1052 currentMainLineIndex_ = (firstItem == 0 ? 0 : gridLayoutInfo_.startMainLineIndex_) - 1;
1053 gridLayoutInfo_.endIndex_ = firstItem - 1;
1054 // firstItem may be more than one line ahead, use new matrix to save and merge to old matrix
1055 decltype(gridLayoutInfo_.lineHeightMap_) gridLineHeightMap(std::move(gridLayoutInfo_.lineHeightMap_));
1056 decltype(gridLayoutInfo_.gridMatrix_) gridMatrix(std::move(gridLayoutInfo_.gridMatrix_));
1057 bool addLine = false;
1058 while (gridLayoutInfo_.endIndex_ < currentIndex - 1 || mainSpan > measureNumber) {
1059 auto newLineHeight = FillNewLineBackward(crossSize, mainSize, layoutWrapper, true);
1060 measureNumber++;
1061 if (LessNotEqual(newLineHeight, 0.0)) {
1062 gridLayoutInfo_.reachEnd_ = true;
1063 break;
1064 }
1065 addLine = true;
1066 }
1067 if (!addLine) {
1068 return;
1069 }
1070 // merge matrix
1071 auto forwardLines = gridLayoutInfo_.endMainLineIndex_ - gridLayoutInfo_.startMainLineIndex_;
1072 if (forwardLines >= 0) {
1073 auto begin = gridLayoutInfo_.gridMatrix_.begin()->first;
1074 if (gridLayoutInfo_.endMainLineIndex_ - begin <= begin) {
1075 for (auto i = begin; i <= gridLayoutInfo_.endMainLineIndex_; i++) {
1076 gridMatrix.emplace(i - forwardLines, std::move(gridLayoutInfo_.gridMatrix_[i]));
1077 gridLineHeightMap.emplace(i - forwardLines, gridLayoutInfo_.lineHeightMap_[i]);
1078 }
1079 gridMatrix.swap(gridLayoutInfo_.gridMatrix_);
1080 gridLineHeightMap.swap(gridLayoutInfo_.lineHeightMap_);
1081 } else {
1082 for (auto i = gridLayoutInfo_.startMainLineIndex_ + 1; i <= gridMatrix.rbegin()->first; i++) {
1083 gridLayoutInfo_.gridMatrix_.emplace(forwardLines + i, std::move(gridMatrix[i]));
1084 gridLayoutInfo_.lineHeightMap_.emplace(forwardLines + i, gridLineHeightMap[i]);
1085 }
1086 }
1087 } else {
1088 // delete more than one line items
1089 for (auto i = gridLayoutInfo_.startMainLineIndex_ + 1; i <= gridMatrix.rbegin()->first; i++) {
1090 gridLayoutInfo_.gridMatrix_.emplace(forwardLines + i, std::move(gridMatrix[i]));
1091 gridLayoutInfo_.lineHeightMap_.emplace(forwardLines + i, gridLineHeightMap[i]);
1092 }
1093 }
1094
1095 gridLayoutInfo_.startMainLineIndex_ = gridLayoutInfo_.endMainLineIndex_ - (forwardLines > 0 ? forwardLines : 0);
1096 gridLayoutInfo_.endMainLineIndex_ = endMainLineIndex + (forwardLines < 0 ? forwardLines : 0);
1097 gridLayoutInfo_.endIndex_ = endIndex;
1098 LOGI("after load forward:start main line %{public}d end main line %{public}d", gridLayoutInfo_.startMainLineIndex_,
1099 gridLayoutInfo_.endMainLineIndex_);
1100 }
1101
FillNewLineBackward(float crossSize,float mainSize,LayoutWrapper * layoutWrapper,bool reverse)1102 float GridScrollLayoutAlgorithm::FillNewLineBackward(
1103 float crossSize, float mainSize, LayoutWrapper* layoutWrapper, bool reverse)
1104 {
1105 // To make the code more convenient to read, we name a param in situation of vertical, for example:
1106 // 1. [lineHight] means height of a row when the Grid is vertical;
1107 // 2. [lineHight] means width of a column when the Grid is horizontal;
1108 // Other params are also named according to this principle.
1109 cellAveLength_ = -1.0f;
1110 if (moveToEndLineIndex_ > 0 && gridLayoutInfo_.endIndex_ >= moveToEndLineIndex_) {
1111 LOGI("scroll to end line with index:%{public}d", moveToEndLineIndex_);
1112 return cellAveLength_;
1113 }
1114 auto currentIndex = gridLayoutInfo_.endIndex_ + 1;
1115 currentMainLineIndex_++; // if it fails to fill a new line backward, do [currentMainLineIndex_--]
1116 lastCross_ = 0;
1117 bool hasNormalItem = false;
1118 bool doneFillLine = false;
1119 // TODO: shoule we use policy of adaptive layout according to size of [GridItem] ?
1120
1121 for (uint32_t i = 0; i < crossCount_; i++) {
1122 // already finish first line forward
1123 if (reverse && currentIndex >= gridLayoutInfo_.startIndex_) {
1124 break;
1125 }
1126 // Step1. Get wrapper of [GridItem]
1127 auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
1128 if (!itemWrapper) {
1129 LOGE("GridItem wrapper of index %{public}u null", currentIndex);
1130 LargeItemNextLineHeight(currentMainLineIndex_, layoutWrapper);
1131 break;
1132 }
1133 // Step2. Measure child
1134 auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
1135 auto crossSpan = MeasureNewChild(frameSize, currentIndex, layoutWrapper, itemWrapper, false);
1136 if (crossSpan < 0) {
1137 cellAveLength_ = LessNotEqual(cellAveLength_, 0.0)
1138 ? gridLayoutInfo_.lineHeightMap_.find(currentMainLineIndex_ - 1)->second
1139 : cellAveLength_;
1140 --currentIndex;
1141 break;
1142 }
1143 i = lastCross_ - 1;
1144 // Step3. Measure [GridItem]
1145 LargeItemLineHeight(itemWrapper, hasNormalItem);
1146
1147 gridLayoutInfo_.endIndex_ = currentIndex;
1148 currentIndex++;
1149 doneFillLine = true;
1150 }
1151
1152 if (doneFillLine) {
1153 gridLayoutInfo_.lineHeightMap_[currentMainLineIndex_] = cellAveLength_;
1154 gridLayoutInfo_.endMainLineIndex_ = currentMainLineIndex_;
1155 } else {
1156 currentMainLineIndex_--;
1157 }
1158 return cellAveLength_;
1159 }
1160
LargeItemNextLineHeight(int32_t currentLineIndex,LayoutWrapper * layoutWrapper)1161 void GridScrollLayoutAlgorithm::LargeItemNextLineHeight(int32_t currentLineIndex, LayoutWrapper* layoutWrapper)
1162 {
1163 auto gridMatrixIter = gridLayoutInfo_.gridMatrix_.find(currentLineIndex);
1164 bool hasNormalItem = false;
1165 auto currentIndex = 0;
1166 if (gridMatrixIter != gridLayoutInfo_.gridMatrix_.end()) {
1167 for (auto itemIter = gridMatrixIter->second.rbegin(); itemIter != gridMatrixIter->second.rend(); ++itemIter) {
1168 currentIndex = itemIter->second;
1169 auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
1170 if (!itemWrapper) {
1171 break;
1172 }
1173 LargeItemLineHeight(itemWrapper, hasNormalItem);
1174 }
1175 }
1176 }
1177
LargeItemForwardLineHeight(int32_t currentLineIndex,LayoutWrapper * layoutWrapper)1178 void GridScrollLayoutAlgorithm::LargeItemForwardLineHeight(int32_t currentLineIndex, LayoutWrapper* layoutWrapper)
1179 {
1180 auto lineIndex = currentLineIndex;
1181 auto gridMatrixIter = gridLayoutInfo_.gridMatrix_.find(lineIndex);
1182 if (gridMatrixIter == gridLayoutInfo_.gridMatrix_.end()) {
1183 return;
1184 }
1185 auto currentIndex = -1;
1186
1187 lineIndex = CalculateLineIndexForLargeItem(gridMatrixIter, currentIndex, lineIndex, layoutWrapper);
1188 CalculateLineHeightForLargeItem(lineIndex, currentLineIndex, gridMatrixIter, layoutWrapper);
1189 }
1190
CalculateLineIndexForLargeItem(std::map<int32_t,std::map<int32_t,int32_t>>::iterator gridMatrixIter,int32_t currentIndex,int32_t lineIndex,LayoutWrapper * layoutWrapper)1191 int32_t GridScrollLayoutAlgorithm::CalculateLineIndexForLargeItem(
1192 std::map<int32_t, std::map<int32_t, int32_t>>::iterator gridMatrixIter, int32_t currentIndex, int32_t lineIndex,
1193 LayoutWrapper* layoutWrapper)
1194 {
1195 for (const auto& gridItemRecord : gridMatrixIter->second) {
1196 if (currentIndex == gridItemRecord.second || gridItemRecord.second == -1) {
1197 continue;
1198 }
1199 currentIndex = gridItemRecord.second;
1200 auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
1201 if (!itemWrapper) {
1202 break;
1203 }
1204 AdjustRowColSpan(itemWrapper, layoutWrapper, currentIndex);
1205 for (int32_t lastCrossIndex = lineIndex - 1; lastCrossIndex >= 0; lastCrossIndex--) {
1206 auto lastGridMatrixIter = gridLayoutInfo_.gridMatrix_.find(lastCrossIndex);
1207 if (lastGridMatrixIter == gridLayoutInfo_.gridMatrix_.end()) {
1208 continue;
1209 }
1210 auto lastGridItemRecord = lastGridMatrixIter->second;
1211 auto lastLineCrossItem = lastGridItemRecord.find(gridItemRecord.first);
1212 if (lastLineCrossItem == lastGridItemRecord.end()) {
1213 continue;
1214 }
1215 if (lastLineCrossItem->second == currentIndex) {
1216 lineIndex--;
1217 } else {
1218 break;
1219 }
1220 }
1221 }
1222 return lineIndex;
1223 }
1224
CalculateLineHeightForLargeItem(int32_t lineIndex,int32_t currentLineIndex,std::map<int32_t,std::map<int32_t,int32_t>>::iterator gridMatrixIter,LayoutWrapper * layoutWrapper)1225 void GridScrollLayoutAlgorithm::CalculateLineHeightForLargeItem(int32_t lineIndex, int32_t currentLineIndex,
1226 std::map<int32_t, std::map<int32_t, int32_t>>::iterator gridMatrixIter, LayoutWrapper* layoutWrapper)
1227 {
1228 for (int32_t i = lineIndex; i <= currentLineIndex; i++) {
1229 auto currentGridMatrixIter = gridLayoutInfo_.gridMatrix_.find(i);
1230 if (currentGridMatrixIter == gridLayoutInfo_.gridMatrix_.end()) {
1231 continue;
1232 }
1233 bool hasNormalItem = false;
1234 auto currentIndex = 0;
1235 cellAveLength_ = -1.0f;
1236 for (auto itemIter = gridMatrixIter->second.rbegin(); itemIter != gridMatrixIter->second.rend(); ++itemIter) {
1237 if (currentIndex == itemIter->second) {
1238 continue;
1239 }
1240 currentIndex = itemIter->second;
1241 auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
1242 if (!itemWrapper) {
1243 break;
1244 }
1245 LargeItemLineHeight(itemWrapper, hasNormalItem);
1246 gridLayoutInfo_.lineHeightMap_[i] = cellAveLength_;
1247 }
1248 }
1249 }
1250
CreateChildConstraint(float mainSize,float crossSize,const RefPtr<GridLayoutProperty> & gridLayoutProperty,int32_t crossStart,int32_t crossSpan) const1251 LayoutConstraintF GridScrollLayoutAlgorithm::CreateChildConstraint(float mainSize, float crossSize,
1252 const RefPtr<GridLayoutProperty>& gridLayoutProperty, int32_t crossStart, int32_t crossSpan) const
1253 {
1254 float itemMainSize =
1255 gridLayoutProperty->IsConfiguredScrollable() ? Infinity<float>() : mainSize / static_cast<float>(mainCount_);
1256
1257 auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
1258 float itemCrossSize = GridUtils::GetCrossGap(gridLayoutProperty, frameSize, axis_) * (crossSpan - 1);
1259 for (int32_t index = 0; index < crossSpan; ++index) {
1260 int32_t crossIndex = (crossStart + index) % static_cast<int32_t>(crossCount_);
1261 if (crossIndex >= 0 && crossIndex < static_cast<int32_t>(itemsCrossSize_.size())) {
1262 itemCrossSize += itemsCrossSize_.at(crossIndex);
1263 }
1264 }
1265
1266 SizeF itemIdealSize =
1267 gridLayoutProperty->IsVertical() ? SizeF(itemCrossSize, itemMainSize) : SizeF(itemMainSize, itemCrossSize);
1268 auto itemConstraint = gridLayoutProperty->CreateChildConstraint();
1269
1270 // The percent size of GridItem is based on the fraction size, for e.g., if a GridItem has width of "50%" in Grid
1271 // configured with columnsTemplate = "1fr 1fr", rowsTemplate = "1fr 1fr",
1272 // then the GridItem width = [width of 1fr] * 50%,
1273 // [itemFractionCount] is now only in direction of cross axis
1274 float widthPercentBase =
1275 GreatOrEqual(crossCount_, Infinity<uint32_t>()) ? itemConstraint.percentReference.Width() : itemCrossSize;
1276 float heightPercentBase = GreatOrEqual(mainCount_, Infinity<uint32_t>())
1277 ? itemConstraint.percentReference.Height()
1278 : itemConstraint.percentReference.Height() / static_cast<float>(mainCount_);
1279 if (axis_ == Axis::VERTICAL) {
1280 itemConstraint.percentReference = SizeF(widthPercentBase, itemConstraint.percentReference.Height());
1281 } else {
1282 itemConstraint.percentReference = SizeF(itemConstraint.percentReference.Width(), heightPercentBase);
1283 }
1284 itemConstraint.maxSize = itemIdealSize;
1285 itemConstraint.UpdateIllegalSelfMarginSizeWithCheck(axis_ == Axis::VERTICAL
1286 ? OptionalSizeF(itemCrossSize, std::nullopt)
1287 : OptionalSizeF(std::nullopt, itemCrossSize));
1288 return itemConstraint;
1289 }
1290
GetNextGrid(int32_t & curMain,int32_t & curCross,bool reverse) const1291 bool GridScrollLayoutAlgorithm::GetNextGrid(int32_t& curMain, int32_t& curCross, bool reverse) const
1292 {
1293 if (!reverse) {
1294 ++curCross;
1295 if (curCross >= static_cast<int32_t>(crossCount_)) {
1296 return false;
1297 }
1298 return true;
1299 }
1300
1301 --curCross;
1302 if (curCross < 0) {
1303 return false;
1304 }
1305 return true;
1306 }
1307
MeasureNewChild(const SizeF & frameSize,int32_t itemIndex,LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & childLayoutWrapper,bool reverse)1308 int32_t GridScrollLayoutAlgorithm::MeasureNewChild(const SizeF& frameSize, int32_t itemIndex,
1309 LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& childLayoutWrapper, bool reverse)
1310 {
1311 auto mainCount = static_cast<int32_t>(mainCount_);
1312 auto crossCount = static_cast<int32_t>(crossCount_);
1313 AdjustRowColSpan(childLayoutWrapper, layoutWrapper, itemIndex);
1314 auto mainSpan = axis_ == Axis::VERTICAL ? currentItemRowSpan_ : currentItemColSpan_;
1315 auto crossSpan = axis_ == Axis::VERTICAL ? currentItemColSpan_ : currentItemRowSpan_;
1316 auto crossStart = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1317 if (crossSpan > crossCount) {
1318 LOGW("item %{public}d can not be placed in grid: cross count:%{public}d, cross span:%{public}d", itemIndex,
1319 crossCount, crossSpan);
1320 return crossSpan;
1321 }
1322 int32_t mainIndex = currentMainLineIndex_;
1323
1324 if (crossStart >= 0 && crossStart < crossCount) {
1325 if (crossStart < lastCross_) {
1326 return -1;
1327 } else if (CheckGridPlaced(itemIndex, mainIndex, crossStart, mainSpan, crossSpan)) {
1328 MeasureChild(layoutWrapper, frameSize, childLayoutWrapper, crossStart, crossSpan);
1329 itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(layoutWrapper, crossStart));
1330 } else {
1331 return -1;
1332 }
1333 } else {
1334 int32_t crossIndex = crossStart >= 0 ? crossStart : lastCross_;
1335
1336 while (!CheckGridPlaced(itemIndex, mainIndex, crossIndex, mainSpan, crossSpan)) {
1337 if (GetNextGrid(mainIndex, crossIndex, reverse) == false) {
1338 return -1;
1339 }
1340 if (mainIndex >= mainCount || crossIndex >= crossCount) {
1341 break;
1342 }
1343 }
1344
1345 MeasureChild(layoutWrapper, frameSize, childLayoutWrapper, crossIndex, crossSpan);
1346 itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(layoutWrapper, crossIndex));
1347 }
1348 return crossSpan;
1349 }
1350
MeasureChildPlaced(const SizeF & frameSize,int32_t itemIndex,int32_t crossStart,LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & childLayoutWrapper)1351 int32_t GridScrollLayoutAlgorithm::MeasureChildPlaced(const SizeF& frameSize, int32_t itemIndex, int32_t crossStart,
1352 LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& childLayoutWrapper)
1353 {
1354 AdjustRowColSpan(childLayoutWrapper, layoutWrapper, itemIndex);
1355 auto crossSpan = axis_ == Axis::VERTICAL ? currentItemColSpan_ : currentItemRowSpan_;
1356 if (static_cast<uint32_t>(crossStart + crossSpan) > crossCount_) {
1357 LOGI("item %{public}d cross not enough, start:%{public}d, span:%{public}d", itemIndex, crossStart, crossSpan);
1358 return 0;
1359 }
1360
1361 MeasureChild(layoutWrapper, frameSize, childLayoutWrapper, crossStart, crossSpan);
1362 itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(layoutWrapper, crossStart));
1363 return crossSpan;
1364 }
1365
MeasureChild(LayoutWrapper * layoutWrapper,const SizeF & frameSize,const RefPtr<LayoutWrapper> & childLayoutWrapper,int32_t crossStart,int32_t crossSpan)1366 void GridScrollLayoutAlgorithm::MeasureChild(LayoutWrapper* layoutWrapper, const SizeF& frameSize,
1367 const RefPtr<LayoutWrapper>& childLayoutWrapper, int32_t crossStart, int32_t crossSpan)
1368 {
1369 auto gridLayoutProperty = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
1370 auto mainSize = GetMainAxisSize(frameSize, gridLayoutInfo_.axis_);
1371 auto crossSize = GetCrossAxisSize(frameSize, gridLayoutInfo_.axis_);
1372 auto childConstraint = CreateChildConstraint(mainSize, crossSize, gridLayoutProperty, crossStart, crossSpan);
1373 auto childLayoutProperty = childLayoutWrapper->GetLayoutProperty();
1374 if (!childLayoutProperty) {
1375 LOGW("childLayoutProperty is nullptr");
1376 childLayoutWrapper->Measure(childConstraint);
1377 return;
1378 }
1379 auto oldConstraint = childLayoutProperty->GetLayoutConstraint();
1380 if (oldConstraint.has_value() && !NearEqual(GetCrossAxisSize(oldConstraint.value().maxSize, axis_),
1381 GetCrossAxisSize(childConstraint.maxSize, axis_))) {
1382 auto layoutAlgorithmWrapper = childLayoutWrapper->GetLayoutAlgorithm();
1383 if (layoutAlgorithmWrapper->SkipMeasure()) {
1384 layoutAlgorithmWrapper->SetNeedMeasure();
1385 if (layoutAlgorithmWrapper->GetLayoutAlgorithm() == nullptr) {
1386 layoutAlgorithmWrapper->SetLayoutAlgorithm(
1387 childLayoutWrapper->GetHostNode()->GetPattern()->CreateLayoutAlgorithm());
1388 }
1389 }
1390 }
1391 childLayoutWrapper->Measure(childConstraint);
1392 }
1393
CheckGridPlaced(int32_t index,int32_t main,int32_t cross,int32_t mainSpan,int32_t crossSpan)1394 bool GridScrollLayoutAlgorithm::CheckGridPlaced(
1395 int32_t index, int32_t main, int32_t cross, int32_t mainSpan, int32_t crossSpan)
1396 {
1397 // If start position is already exist in gridMatrix, place grid item fail.
1398 auto mainIter = gridLayoutInfo_.gridMatrix_.find(main);
1399 if (mainIter != gridLayoutInfo_.gridMatrix_.end()) {
1400 auto crossIter = mainIter->second.find(cross);
1401 if (crossIter != mainIter->second.end()) {
1402 return false;
1403 }
1404 }
1405
1406 // If cross length of grid item if out of range, place grid item fail.
1407 if (cross + crossSpan > static_cast<int32_t>(crossCount_)) {
1408 return false;
1409 }
1410
1411 // If any grid item is already exist in gridMatrix, place grid item fail.
1412 for (int32_t i = 0; i < mainSpan; i++) {
1413 mainIter = gridLayoutInfo_.gridMatrix_.find(i + main);
1414 if (mainIter == gridLayoutInfo_.gridMatrix_.end()) {
1415 continue;
1416 }
1417 for (int32_t j = 0; j < crossSpan; j++) {
1418 auto crossIter = mainIter->second.find(j + cross);
1419 if (crossIter != mainIter->second.end()) {
1420 return false;
1421 }
1422 }
1423 }
1424
1425 // Padding grid matrix for grid item's range.
1426 for (int32_t i = main; i < main + mainSpan; ++i) {
1427 std::map<int32_t, int32_t> mainMap;
1428 auto iter = gridLayoutInfo_.gridMatrix_.find(i);
1429 if (iter != gridLayoutInfo_.gridMatrix_.end()) {
1430 mainMap = iter->second;
1431 }
1432 for (int32_t j = cross; j < cross + crossSpan; ++j) {
1433 mainMap.emplace(std::make_pair(j, index));
1434 }
1435 gridLayoutInfo_.gridMatrix_[i] = mainMap;
1436 }
1437 lastCross_ = cross + crossSpan;
1438
1439 return true;
1440 }
1441
ComputeItemCrossPosition(LayoutWrapper * layoutWrapper,int32_t crossStart) const1442 float GridScrollLayoutAlgorithm::ComputeItemCrossPosition(LayoutWrapper* layoutWrapper, int32_t crossStart) const
1443 {
1444 float position = 0.0f;
1445 for (int32_t index = 0; index < crossStart; ++index) {
1446 if (index >= 0 && index < static_cast<int32_t>(itemsCrossSize_.size())) {
1447 position += itemsCrossSize_.at(index);
1448 }
1449 }
1450 position += crossStart * crossGap_ + crossPaddingOffset_;
1451 return position;
1452 }
1453
GetStartingItem(LayoutWrapper * layoutWrapper,int32_t currentIndex)1454 int32_t GridScrollLayoutAlgorithm::GetStartingItem(LayoutWrapper* layoutWrapper, int32_t currentIndex)
1455 {
1456 int32_t firstIndex = 0;
1457 currentIndex =
1458 currentIndex < layoutWrapper->GetTotalChildCount() ? currentIndex : layoutWrapper->GetTotalChildCount() - 1;
1459 auto index = currentIndex;
1460 if (gridLayoutInfo_.hasBigItem_) {
1461 while (index > 0) {
1462 auto childLayoutWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1463 if (!childLayoutWrapper) {
1464 LOGE("GridItem wrapper of index %{public}u null", index);
1465 break;
1466 }
1467
1468 AdjustRowColSpan(childLayoutWrapper, layoutWrapper, index);
1469 auto crossIndex = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1470 if (crossIndex == 0) {
1471 firstIndex = index;
1472 break;
1473 }
1474 --index;
1475 }
1476 } else {
1477 while (index > 0) {
1478 // need to obtain the item node in order and by step one
1479 auto childLayoutWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1480 if (!childLayoutWrapper) {
1481 LOGE("GridItem wrapper of index %{public}u null", index);
1482 break;
1483 }
1484 AdjustRowColSpan(childLayoutWrapper, layoutWrapper, index);
1485 auto crossIndex = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1486 // Grid may change from no big item to has big item
1487 if (crossIndex >= 0) {
1488 gridLayoutInfo_.hasBigItem_ = true;
1489 return GetStartingItem(layoutWrapper, currentIndex);
1490 }
1491 if (index % gridLayoutInfo_.crossCount_ == 0) {
1492 firstIndex = index;
1493 break;
1494 }
1495 --index;
1496 }
1497 }
1498 return firstIndex;
1499 }
1500
1501 // only for debug use
PrintGridMatrix(const std::map<int32_t,std::map<int32_t,int32_t>> & gridMatrix,const std::map<int32_t,float> & positions)1502 void GridScrollLayoutAlgorithm::PrintGridMatrix(
1503 const std::map<int32_t, std::map<int32_t, int32_t>>& gridMatrix, const std::map<int32_t, float>& positions)
1504 {
1505 for (const auto& record : gridMatrix) {
1506 for (const auto& item : record.second) {
1507 float position = -1;
1508 auto iter = positions.find(item.second);
1509 if (iter != positions.end()) {
1510 position = iter->second;
1511 }
1512 LOGI("grid matrix -- line: %{public}d, item: %{public}d, fr: %{public}d, position: %{public}f",
1513 record.first, item.first, item.second, position);
1514 }
1515 }
1516 }
1517
1518 // only for debug use
PrintLineHeightMap(const std::map<int32_t,float> & lineHeightMap)1519 void GridScrollLayoutAlgorithm::PrintLineHeightMap(const std::map<int32_t, float>& lineHeightMap)
1520 {
1521 for (const auto& record : lineHeightMap) {
1522 LOGI("line height -- line: %{public}d, lineHeight: %{public}f", record.first, record.second);
1523 }
1524 }
1525
1526 } // namespace OHOS::Ace::NG
1527