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