1 /*
2 * Copyright (c) 2023-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/irregular/grid_irregular_layout_algorithm.h"
17
18 #include "core/components_ng/pattern/grid/grid_utils.h"
19 #include "core/components_ng/pattern/grid/irregular/grid_irregular_filler.h"
20 #include "core/components_ng/pattern/grid/irregular/grid_layout_range_solver.h"
21 #include "core/components_ng/pattern/grid/irregular/grid_layout_utils.h"
22 #include "core/components_ng/pattern/scrollable/scrollable_utils.h"
23 #include "core/components_ng/property/templates_parser.h"
24
25 namespace OHOS::Ace::NG {
Measure(LayoutWrapper * layoutWrapper)26 void GridIrregularLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
27 {
28 if (info_.childrenCount_ <= 0) {
29 return;
30 }
31 wrapper_ = layoutWrapper;
32 auto props = DynamicCast<GridLayoutProperty>(wrapper_->GetLayoutProperty());
33
34 float mainSize = MeasureSelf(props);
35 bool matchChildren = GreaterOrEqualToInfinity(mainSize);
36 Init(props);
37
38 if (info_.targetIndex_) {
39 MeasureToTarget();
40 info_.targetIndex_.reset();
41 }
42
43 if (info_.jumpIndex_ != EMPTY_JUMP_INDEX) {
44 MeasureOnJump(mainSize);
45 } else {
46 MeasureOnOffset(mainSize);
47 }
48
49 if (props->GetAlignItems().value_or(GridItemAlignment::DEFAULT) == GridItemAlignment::STRETCH) {
50 GridLayoutBaseAlgorithm::AdjustChildrenHeight(layoutWrapper);
51 }
52
53 UpdateLayoutInfo();
54 if (matchChildren) {
55 AdaptToChildMainSize(props, mainSize, frameSize_);
56 }
57 const int32_t cacheCnt = props->GetCachedCountValue(info_.defCachedCount_) * info_.crossCount_;
58 if (props->GetShowCachedItemsValue(false)) {
59 SyncPreloadItems(cacheCnt);
60 } else {
61 PreloadItems(cacheCnt);
62 }
63 }
64
Layout(LayoutWrapper * layoutWrapper)65 void GridIrregularLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
66 {
67 const auto& info = info_;
68 if (info.childrenCount_ <= 0) {
69 return;
70 }
71 wrapper_ = layoutWrapper;
72 auto props = DynamicCast<GridLayoutProperty>(wrapper_->GetLayoutProperty());
73 CHECK_NULL_VOID(props);
74
75 const int32_t cacheLines = props->GetCachedCountValue(info.defCachedCount_);
76 if (!props->HasCachedCount()) {
77 info_.UpdateDefaultCachedCount();
78 }
79 auto cachedItemCnt = LayoutChildren(info.currentOffset_, cacheLines);
80
81 if (!props->GetShowCachedItemsValue(false)) {
82 // only use accurate counting method when cached items need to be shown
83 cachedItemCnt.first = cachedItemCnt.second = cacheLines * info.crossCount_;
84 }
85 wrapper_->SetActiveChildRange(std::min(info.startIndex_, info.endIndex_), info.endIndex_, cachedItemCnt.first,
86 cachedItemCnt.second, props->GetShowCachedItemsValue(false));
87 wrapper_->SetCacheCount(cachedItemCnt.first);
88 UpdateOverlay(wrapper_);
89 }
90
MeasureSelf(const RefPtr<GridLayoutProperty> & props)91 float GridIrregularLayoutAlgorithm::MeasureSelf(const RefPtr<GridLayoutProperty>& props)
92 {
93 // set self size
94 frameSize_ = CreateIdealSize(props->GetLayoutConstraint().value(), info_.axis_, props->GetMeasureType(), true);
95 wrapper_->GetGeometryNode()->SetFrameSize(frameSize_);
96
97 // set content size
98 const auto& padding = props->CreatePaddingAndBorder();
99 wrapper_->GetGeometryNode()->UpdatePaddingWithBorder(padding);
100 MinusPaddingToSize(padding, frameSize_);
101 info_.contentEndPadding_ = ScrollableUtils::CheckHeightExpansion(props, info_.axis_);
102 frameSize_.AddHeight(info_.contentEndPadding_);
103 wrapper_->GetGeometryNode()->SetContentSize(frameSize_);
104
105 return frameSize_.MainSize(info_.axis_);
106 }
107
Init(const RefPtr<GridLayoutProperty> & props)108 void GridIrregularLayoutAlgorithm::Init(const RefPtr<GridLayoutProperty>& props)
109 {
110 const auto& contentSize = wrapper_->GetGeometryNode()->GetContentSize();
111 crossGap_ = GridUtils::GetCrossGap(props, contentSize, info_.axis_);
112 mainGap_ = GridUtils::GetMainGap(props, contentSize, info_.axis_);
113
114 std::string args;
115 if (props->GetRowsTemplate()) {
116 info_.axis_ = Axis::HORIZONTAL;
117 args = props->GetRowsTemplate().value_or("");
118 } else {
119 info_.axis_ = Axis::VERTICAL;
120 args = props->GetColumnsTemplate().value_or("");
121 }
122
123 const float crossSize = contentSize.CrossSize(info_.axis_);
124 auto res = ParseTemplateArgs(GridUtils::ParseArgs(args), crossSize, crossGap_, info_.childrenCount_);
125
126 crossLens_ = std::vector<float>(res.first.begin(), res.first.end());
127 if (crossLens_.empty()) {
128 crossLens_.push_back(crossSize);
129 }
130
131 crossGap_ = res.second;
132
133 info_.crossCount_ = static_cast<int32_t>(crossLens_.size());
134 CheckForReset();
135 }
136
137 namespace {
PrepareJumpOnReset(GridLayoutInfo & info)138 inline void PrepareJumpOnReset(GridLayoutInfo& info)
139 {
140 info.jumpIndex_ = std::min(info.startIndex_, info.childrenCount_ - 1);
141 info.scrollAlign_ = ScrollAlign::START;
142 }
ResetMaps(GridLayoutInfo & info)143 inline void ResetMaps(GridLayoutInfo& info)
144 {
145 info.gridMatrix_.clear();
146 info.lineHeightMap_.clear();
147 }
ResetLayoutRange(GridLayoutInfo & info)148 inline void ResetLayoutRange(GridLayoutInfo& info)
149 {
150 info.startIndex_ = 0;
151 info.endIndex_ = -1;
152 info.startMainLineIndex_ = 0;
153 info.endMainLineIndex_ = -1;
154 info.currentOffset_ = 0.0f;
155 info.prevOffset_ = 0.0f;
156 }
157 } // namespace
158
CheckForReset()159 void GridIrregularLayoutAlgorithm::CheckForReset()
160 {
161 if (info_.IsResetted()) {
162 // reset layout info_ and perform jump to current startIndex
163 postJumpOffset_ = info_.currentOffset_;
164 PrepareJumpOnReset(info_);
165 ResetMaps(info_);
166 ResetLayoutRange(info_);
167 ResetFocusedIndex(wrapper_);
168 return;
169 }
170
171 int32_t updateIdx = wrapper_->GetHostNode()->GetChildrenUpdated();
172 if (updateIdx != -1) {
173 auto it = info_.FindInMatrix(updateIdx);
174 if (it != info_.gridMatrix_.end()) {
175 info_.ClearHeightsToEnd(it->first);
176 info_.ClearMatrixToEnd(updateIdx, it->first);
177 }
178 if (updateIdx <= info_.endIndex_) {
179 postJumpOffset_ = info_.currentOffset_;
180 PrepareJumpOnReset(info_);
181 ResetLayoutRange(info_);
182 ResetFocusedIndex(wrapper_);
183 }
184 wrapper_->GetHostNode()->ChildrenUpdatedFrom(-1);
185 return;
186 }
187
188 if (wrapper_->GetLayoutProperty()->GetPropertyChangeFlag() & PROPERTY_UPDATE_BY_CHILD_REQUEST) {
189 postJumpOffset_ = info_.currentOffset_;
190 info_.lineHeightMap_.clear();
191 PrepareJumpOnReset(info_);
192 ResetLayoutRange(info_);
193 ResetFocusedIndex(wrapper_);
194 return;
195 }
196
197 if (wrapper_->ConstraintChanged()) {
198 // need to remeasure all items in current view
199 postJumpOffset_ = info_.currentOffset_;
200 PrepareJumpOnReset(info_);
201 }
202 }
203
MeasureOnOffset(float mainSize)204 void GridIrregularLayoutAlgorithm::MeasureOnOffset(float mainSize)
205 {
206 if (TrySkipping(mainSize)) {
207 return;
208 }
209
210 if (GreatNotEqual(info_.currentOffset_, info_.prevOffset_)) {
211 MeasureBackward(mainSize);
212 } else {
213 MeasureForward(mainSize);
214 }
215 }
216
217 namespace {
UpdateStartInfo(GridLayoutInfo & info,const GridLayoutRangeSolver::StartingRowInfo & res)218 inline void UpdateStartInfo(GridLayoutInfo& info, const GridLayoutRangeSolver::StartingRowInfo& res)
219 {
220 info.startMainLineIndex_ = res.row;
221 info.currentOffset_ = res.pos;
222 info.startIndex_ = res.idx;
223 }
224
GetPrevHeight(const GridLayoutInfo & info,float mainGap)225 inline float GetPrevHeight(const GridLayoutInfo& info, float mainGap)
226 {
227 return info.GetHeightInRange(info.startMainLineIndex_, info.endMainLineIndex_, mainGap);
228 }
229 } // namespace
230
MeasureForward(float mainSize)231 void GridIrregularLayoutAlgorithm::MeasureForward(float mainSize)
232 {
233 float heightToFill = mainSize - info_.currentOffset_ - GetPrevHeight(info_, mainGap_);
234 if (Positive(heightToFill)) {
235 GridIrregularFiller filler(&info_, wrapper_);
236 filler.Fill({ crossLens_, crossGap_, mainGap_ }, heightToFill, info_.endMainLineIndex_);
237 }
238
239 GridLayoutRangeSolver solver(&info_, wrapper_);
240 auto res = solver.FindStartingRow(mainGap_);
241 UpdateStartInfo(info_, res);
242 auto [endMainLineIdx, endIdx] = solver.SolveForwardForEndIdx(mainGap_, mainSize - res.pos, res.row);
243 info_.endMainLineIndex_ = endMainLineIdx;
244 info_.endIndex_ = endIdx;
245
246 if (info_.startIndex_ == 0 && NonNegative(info_.currentOffset_)) {
247 return;
248 }
249 // adjust offset
250 if (!canOverScrollEnd_ && info_.endIndex_ == info_.childrenCount_ - 1) {
251 float overDis =
252 -info_.GetDistanceToBottom(mainSize, info_.GetTotalHeightOfItemsInView(mainGap_, true), mainGap_);
253 if (Negative(overDis)) {
254 return;
255 }
256 info_.currentOffset_ += overDis;
257 if (Positive(info_.currentOffset_)) {
258 MeasureBackward(mainSize, true);
259 }
260 }
261 }
262
MeasureBackward(float mainSize,bool toAdjust)263 void GridIrregularLayoutAlgorithm::MeasureBackward(float mainSize, bool toAdjust)
264 {
265 // skip adding starting lines that are outside viewport in LayoutIrregular
266 auto [it, offset] = info_.SkipLinesAboveView(mainGap_);
267 GridIrregularFiller filler(&info_, wrapper_);
268 filler.MeasureBackward({ crossLens_, crossGap_, mainGap_ }, offset + it->second + mainGap_, it->first);
269
270 GridLayoutRangeSolver solver(&info_, wrapper_);
271 auto res = solver.FindStartingRow(mainGap_);
272 if ((toAdjust || !canOverScrollStart_) && res.row == 0) {
273 res.pos = std::min(res.pos, 0.0f);
274 }
275 UpdateStartInfo(info_, res);
276
277 auto [endLine, endIdx] = solver.SolveForwardForEndIdx(mainGap_, mainSize - res.pos, res.row);
278 info_.endMainLineIndex_ = endLine;
279 info_.endIndex_ = endIdx;
280 }
281
282 namespace {
283 constexpr float SKIP_THRESHOLD = 2.0f;
284 }
285
TrySkipping(float mainSize)286 bool GridIrregularLayoutAlgorithm::TrySkipping(float mainSize)
287 {
288 float delta = std::abs(info_.currentOffset_ - info_.prevOffset_);
289 if (enableSkip_ && GreatNotEqual(delta, mainSize)) {
290 // a more costly check, therefore perform after comparing to [mainSize]
291 if (LessOrEqual(delta, SKIP_THRESHOLD * info_.GetTotalHeightOfItemsInView(mainGap_))) {
292 return false;
293 }
294 info_.jumpIndex_ = Negative(info_.currentOffset_) ? SkipLinesForward() : SkipLinesBackward();
295 info_.scrollAlign_ = ScrollAlign::START;
296 info_.currentOffset_ = 0.0f;
297 Jump(mainSize);
298 return true;
299 }
300 return false;
301 }
302
MeasureOnJump(float mainSize)303 void GridIrregularLayoutAlgorithm::MeasureOnJump(float mainSize)
304 {
305 Jump(mainSize);
306
307 if (info_.extraOffset_ && !NearZero(*info_.extraOffset_)) {
308 info_.prevOffset_ = info_.currentOffset_;
309 info_.currentOffset_ += *info_.extraOffset_;
310 MeasureOnOffset(mainSize);
311 }
312 if (!NearZero(postJumpOffset_)) {
313 info_.currentOffset_ = postJumpOffset_;
314 enableSkip_ = false;
315 MeasureOnOffset(mainSize);
316 }
317 }
318
Jump(float mainSize)319 void GridIrregularLayoutAlgorithm::Jump(float mainSize)
320 {
321 if (info_.jumpIndex_ == JUMP_TO_BOTTOM_EDGE) {
322 GridIrregularFiller filler(&info_, wrapper_);
323 filler.FillMatrixOnly(info_.childrenCount_ - 1);
324 info_.PrepareJumpToBottom();
325 }
326
327 if (info_.jumpIndex_ == LAST_ITEM) {
328 info_.jumpIndex_ = info_.childrenCount_ - 1;
329 }
330
331 if (info_.scrollAlign_ == ScrollAlign::AUTO) {
332 int32_t height = GridLayoutUtils::GetItemSize(&info_, wrapper_, info_.jumpIndex_).rows;
333 info_.scrollAlign_ = info_.TransformAutoScrollAlign(info_.jumpIndex_, height, mainSize, mainGap_);
334 }
335 if (info_.scrollAlign_ == ScrollAlign::NONE) {
336 info_.jumpIndex_ = EMPTY_JUMP_INDEX;
337 return;
338 }
339
340 int32_t jumpLineIdx = FindJumpLineIdx(info_.jumpIndex_);
341
342 PrepareLineHeight(mainSize, jumpLineIdx);
343
344 GridLayoutRangeSolver solver(&info_, wrapper_);
345 const auto res = solver.FindRangeOnJump(info_.jumpIndex_, jumpLineIdx, mainGap_);
346
347 info_.currentOffset_ = res.pos;
348 info_.startMainLineIndex_ = res.startRow;
349 info_.startIndex_ = res.startIdx;
350 info_.endMainLineIndex_ = res.endRow;
351 info_.endIndex_ = res.endIdx;
352 info_.jumpIndex_ = EMPTY_JUMP_INDEX;
353 }
354
UpdateLayoutInfo()355 void GridIrregularLayoutAlgorithm::UpdateLayoutInfo()
356 {
357 info_.reachStart_ = info_.startIndex_ == 0 && NonNegative(info_.currentOffset_);
358 // GridLayoutInfo::reachEnd_ has a different meaning
359 info_.reachEnd_ = info_.endIndex_ == info_.childrenCount_ - 1;
360
361 float mainSize = wrapper_->GetGeometryNode()->GetContentSize().MainSize(info_.axis_);
362
363 info_.lastMainSize_ = mainSize;
364 info_.totalHeightOfItemsInView_ = info_.GetTotalHeightOfItemsInView(mainGap_, true);
365 info_.avgLineHeight_ = info_.GetTotalLineHeight(0.0f) / static_cast<float>(info_.lineHeightMap_.size());
366
367 if (info_.reachEnd_) {
368 info_.offsetEnd_ = NonPositive(info_.GetDistanceToBottom(mainSize, info_.totalHeightOfItemsInView_, mainGap_));
369 } else {
370 info_.offsetEnd_ = false;
371 }
372 info_.prevOffset_ = info_.currentOffset_;
373
374 // validity check
375 for (int i = info_.startMainLineIndex_; i <= info_.endMainLineIndex_; ++i) {
376 if (info_.lineHeightMap_.find(i) == info_.lineHeightMap_.end()) {
377 TAG_LOGW(AceLogTag::ACE_GRID,
378 "lineHeight at line %d not ready. Data is corrupted. StartLine = %d, EndLine = %d", i,
379 info_.startMainLineIndex_, info_.endMainLineIndex_);
380 info_.endMainLineIndex_ = i - 1;
381 info_.endIndex_ = info_.startIndex_ - 1;
382 return;
383 }
384 }
385 }
386
387 namespace {
GetAlignment(Axis axis,const RefPtr<GridLayoutProperty> & props)388 Alignment GetAlignment(Axis axis, const RefPtr<GridLayoutProperty>& props)
389 {
390 Alignment align = axis == Axis::VERTICAL ? Alignment::TOP_CENTER : Alignment::CENTER_LEFT;
391 const auto& positionProp = props->GetPositionProperty();
392 if (positionProp) {
393 align = positionProp->GetAlignment().value_or(align);
394 }
395 return align;
396 }
397 /* adjust mainOffset to the first cache line */
AdjustStartOffset(const std::map<int32_t,float> & lineHeights,int32_t startLine,int32_t cacheStartLine,float mainGap,float & mainOffset)398 void AdjustStartOffset(const std::map<int32_t, float>& lineHeights, int32_t startLine, int32_t cacheStartLine,
399 float mainGap, float& mainOffset)
400 {
401 auto startLineIt = lineHeights.lower_bound(startLine);
402 for (auto it = lineHeights.lower_bound(cacheStartLine); it != startLineIt; ++it) {
403 mainOffset -= mainGap + it->second;
404 }
405 }
406 } // namespace
407
LayoutChildren(float mainOffset,int32_t cacheLine)408 std::pair<int32_t, int32_t> GridIrregularLayoutAlgorithm::LayoutChildren(float mainOffset, int32_t cacheLine)
409 {
410 const auto& info = info_;
411 const auto& props = DynamicCast<GridLayoutProperty>(wrapper_->GetLayoutProperty());
412 const Alignment align = GetAlignment(info.axis_, props);
413
414 const auto& padding = *wrapper_->GetGeometryNode()->GetPadding();
415 mainOffset += info.axis_ == Axis::HORIZONTAL ? 0.0f : padding.top.value_or(0.0f);
416 auto crossPos = CalculateCrossPositions(padding);
417
418 auto frameSize = wrapper_->GetGeometryNode()->GetFrameSize();
419 MinusPaddingToSize(padding, frameSize);
420 const bool isRtl = props->GetNonAutoLayoutDirection() == TextDirection::RTL;
421 const int32_t cacheStartLine = info.startMainLineIndex_ - cacheLine;
422 AdjustStartOffset(info.lineHeightMap_, info.startMainLineIndex_, cacheStartLine, mainGap_, mainOffset);
423
424 std::pair<int32_t, int32_t> cacheCnt = { 0, 0 };
425 auto endIt = info.gridMatrix_.upper_bound(std::max(info.endMainLineIndex_ + cacheLine, info.startMainLineIndex_));
426 for (auto it = info.gridMatrix_.lower_bound(cacheStartLine); it != endIt; ++it) {
427 auto lineHeightIt = info.lineHeightMap_.find(it->first);
428 if (lineHeightIt == info.lineHeightMap_.end()) {
429 continue;
430 }
431 const bool isCache = !props->GetShowCachedItemsValue(false) &&
432 (it->first < info.startMainLineIndex_ || it->first > info.endMainLineIndex_);
433 const auto& row = it->second;
434 for (const auto& [c, itemIdx] : row) {
435 if (itemIdx < 0 || (itemIdx == 0 && (it->first > 0 || c > 0))) {
436 // not top-left tile
437 continue;
438 }
439 auto child = wrapper_->GetChildByIndex(itemIdx, isCache);
440 if (!child) {
441 continue;
442 }
443
444 if (it->first < info.startMainLineIndex_) {
445 ++cacheCnt.first;
446 } else if (it->first > info.endMainLineIndex_) {
447 ++cacheCnt.second;
448 }
449
450 SizeF blockSize = SizeF(crossLens_.at(c), lineHeightIt->second, info.axis_);
451 auto childSize = child->GetGeometryNode()->GetMarginFrameSize();
452 auto alignPos = Alignment::GetAlignPosition(blockSize, childSize, align);
453
454 OffsetF offset = OffsetF(crossPos[c], mainOffset, info.axis_);
455
456 if (isRtl) {
457 offset.SetX(frameSize.Width() - offset.GetX() - childSize.Width());
458 }
459 offset += OffsetF { padding.left.value_or(0.0f), 0.0f };
460 child->GetGeometryNode()->SetMarginFrameOffset(offset + alignPos);
461 if (!isCache && child->CheckNeedForceMeasureAndLayout()) {
462 child->Layout();
463 } else {
464 child->GetHostNode()->ForceSyncGeometryNode();
465 }
466 }
467 // add mainGap below the item
468 mainOffset += lineHeightIt->second + mainGap_;
469 }
470 return cacheCnt;
471 }
472
CalculateCrossPositions(const PaddingPropertyF & padding)473 std::vector<float> GridIrregularLayoutAlgorithm::CalculateCrossPositions(const PaddingPropertyF& padding)
474 {
475 std::vector<float> res(info_.crossCount_, 0.0f);
476 res[0] = info_.axis_ == Axis::HORIZONTAL ? padding.top.value_or(0.0f) : 0.0f;
477 for (int32_t i = 1; i < info_.crossCount_; ++i) {
478 res[i] = res[i - 1] + crossLens_[i - 1] + crossGap_;
479 }
480 return res;
481 }
482
FindJumpLineIdx(int32_t jumpIdx)483 int32_t GridIrregularLayoutAlgorithm::FindJumpLineIdx(int32_t jumpIdx)
484 {
485 GridIrregularFiller filler(&info_, wrapper_);
486 int32_t jumpLine = -1;
487 auto it = info_.FindInMatrix(jumpIdx);
488 if (it == info_.gridMatrix_.end()) {
489 // fill matrix up to jumpIndex_
490 jumpLine = filler.FillMatrixOnly(jumpIdx);
491 } else {
492 jumpLine = it->first;
493 }
494
495 if (info_.scrollAlign_ == ScrollAlign::END) {
496 // jump to the last line the item occupies
497 auto lastLine = jumpLine + GridLayoutUtils::GetItemSize(&info_, wrapper_, jumpIdx).rows - 1;
498 filler.FillMatrixByLine(jumpLine, lastLine + 1);
499 jumpLine = lastLine;
500 }
501 return jumpLine;
502 }
503
504 using FillParams = GridIrregularFiller::FillParameters;
PrepareLineHeight(float mainSize,int32_t & jumpLineIdx)505 void GridIrregularLayoutAlgorithm::PrepareLineHeight(float mainSize, int32_t& jumpLineIdx)
506 {
507 /* When mainSize can't be filled, adjust parameters and call function again. The maximum length of
508 * the recursion is 3 iterations ([Start && len not filled] -> [End && len not filled] -> [Start with jumpIdx 0]).
509 */
510 GridIrregularFiller filler(&info_, wrapper_);
511 const FillParams params { crossLens_, crossGap_, mainGap_ };
512 switch (info_.scrollAlign_) {
513 case ScrollAlign::START: {
514 // call this to ensure irregular items on the first line are measured, not skipped
515 filler.MeasureLineWithIrregulars(params, jumpLineIdx);
516
517 float len = filler.Fill(params, mainSize, jumpLineIdx).length;
518 // condition [jumpLineIdx > 0] guarantees a finite call stack
519 if (LessNotEqual(len, mainSize) && jumpLineIdx > 0) {
520 jumpLineIdx = info_.lineHeightMap_.rbegin()->first;
521 info_.scrollAlign_ = ScrollAlign::END;
522 PrepareLineHeight(mainSize, jumpLineIdx);
523 }
524 break;
525 }
526 case ScrollAlign::CENTER: {
527 // because the current line's height is unknown, we can't determine the exact target length to fill.
528 // Using the full [mainSize]
529 const auto pos = info_.GetItemPos(info_.jumpIndex_);
530 const float itemLen = filler.MeasureItem(params, info_.jumpIndex_, pos.first, pos.second, false).first;
531 const float targetLen = mainSize / 2.0f;
532 float backwardLen = filler.MeasureBackward(params, mainSize, jumpLineIdx);
533
534 auto jumpLine = info_.lineHeightMap_.find(jumpLineIdx);
535 if (jumpLine == info_.lineHeightMap_.end()) {
536 return;
537 }
538 backwardLen -= jumpLine->second / 2.0f;
539 if (LessNotEqual(backwardLen, targetLen)) {
540 jumpLineIdx = 0;
541 info_.scrollAlign_ = ScrollAlign::START;
542 PrepareLineHeight(mainSize, jumpLineIdx);
543 return;
544 }
545 float forwardLen = filler.Fill(params, std::max(mainSize, itemLen), jumpLineIdx).length;
546 forwardLen -= jumpLine->second / 2.0f;
547 if (LessNotEqual(forwardLen, targetLen)) {
548 jumpLineIdx = info_.lineHeightMap_.rbegin()->first;
549 info_.scrollAlign_ = ScrollAlign::END;
550 PrepareLineHeight(mainSize, jumpLineIdx);
551 }
552 break;
553 }
554 case ScrollAlign::END: {
555 float len = filler.MeasureBackward(params, mainSize, jumpLineIdx);
556 if (LessNotEqual(len, mainSize)) {
557 jumpLineIdx = 0;
558 info_.scrollAlign_ = ScrollAlign::START;
559 PrepareLineHeight(mainSize, jumpLineIdx);
560 }
561 break;
562 }
563 default:
564 break;
565 }
566 }
567
568 namespace {
AddLineHeight(float & height,int32_t curLine,int32_t startLine,const std::map<int32_t,float> & lineHeights)569 void AddLineHeight(float& height, int32_t curLine, int32_t startLine, const std::map<int32_t, float>& lineHeights)
570 {
571 auto iter = lineHeights.find(curLine);
572 if (iter != lineHeights.end()) {
573 height += iter->second;
574 } else {
575 // estimation
576 height += height / std::abs(curLine - startLine);
577 }
578 }
579 } // namespace
580
SkipLinesForward()581 int32_t GridIrregularLayoutAlgorithm::SkipLinesForward()
582 {
583 int32_t line = info_.startMainLineIndex_;
584 float height = 0.0f;
585 while (LessNotEqual(height, -info_.currentOffset_)) {
586 AddLineHeight(height, line++, info_.startMainLineIndex_, info_.lineHeightMap_);
587 }
588 GridIrregularFiller filler(&info_, wrapper_);
589 return filler.FillMatrixByLine(info_.startMainLineIndex_, line);
590 }
591
SkipLinesBackward() const592 int32_t GridIrregularLayoutAlgorithm::SkipLinesBackward() const
593 {
594 const auto& info = info_;
595 float height = info.GetHeightInRange(info.startMainLineIndex_, info.endMainLineIndex_ + 1, 0.0f);
596
597 float target = info.currentOffset_ + height;
598 int32_t line = info.startMainLineIndex_;
599 while (LessNotEqual(height, target) && line > 0) {
600 AddLineHeight(height, --line, info.endMainLineIndex_, info.lineHeightMap_);
601 }
602 return std::max(0, info.FindEndIdx(line).itemIdx);
603 }
604
MeasureToTarget()605 void GridIrregularLayoutAlgorithm::MeasureToTarget()
606 {
607 GridIrregularFiller filler(&info_, wrapper_);
608 FillParams param { crossLens_, crossGap_, mainGap_ };
609 if (info_.targetIndex_ < info_.startIndex_) {
610 auto it = info_.FindInMatrix(*info_.targetIndex_);
611 filler.MeasureBackwardToTarget(param, it->first, info_.startMainLineIndex_);
612 } else {
613 filler.FillToTarget(param, *info_.targetIndex_, info_.startMainLineIndex_);
614 }
615 }
616
IsIrregularLine(int32_t lineIndex) const617 bool GridIrregularLayoutAlgorithm::IsIrregularLine(int32_t lineIndex) const
618 {
619 const auto& line = info_.gridMatrix_.find(lineIndex);
620 if (line == info_.gridMatrix_.end()) {
621 return true;
622 }
623 auto props = DynamicCast<GridLayoutProperty>(wrapper_->GetLayoutProperty());
624 const auto& opts = *props->GetLayoutOptions();
625 return std::any_of(line->second.begin(), line->second.end(),
626 [opts](const auto& item) { return opts.irregularIndexes.count(std::abs(item.second)); });
627 }
628
SyncPreloadItems(int32_t cacheCnt)629 void GridIrregularLayoutAlgorithm::SyncPreloadItems(int32_t cacheCnt)
630 {
631 const int32_t start = std::max(info_.startIndex_ - cacheCnt, 0);
632 const int32_t end = std::min(info_.endIndex_ + cacheCnt, info_.childrenCount_ - 1);
633 GridIrregularFiller filler(&info_, wrapper_);
634 FillParams param { crossLens_, crossGap_, mainGap_ };
635 auto it = info_.FindInMatrix(start);
636 filler.MeasureBackwardToTarget(param, it->first, info_.startMainLineIndex_ - 1);
637 filler.FillToTarget(param, end, info_.endMainLineIndex_);
638 }
639
PreloadItems(int32_t cacheCnt)640 void GridIrregularLayoutAlgorithm::PreloadItems(int32_t cacheCnt)
641 {
642 std::list<GridPreloadItem> itemsToPreload;
643 for (int32_t i = 1; i <= cacheCnt; ++i) {
644 const int32_t l = info_.startIndex_ - i;
645 if (l >= 0 && !wrapper_->GetChildByIndex(l, true)) {
646 itemsToPreload.emplace_back(l);
647 }
648 const int32_t r = info_.endIndex_ + i;
649 if (r < info_.childrenCount_ && !wrapper_->GetChildByIndex(r, true)) {
650 itemsToPreload.emplace_back(r);
651 }
652 }
653
654 GridIrregularFiller filler(&info_, wrapper_);
655 filler.FillMatrixOnly(std::min(info_.childrenCount_, info_.endIndex_ + cacheCnt));
656
657 GridLayoutUtils::PreloadGridItems(wrapper_->GetHostNode()->GetPattern<GridPattern>(), std::move(itemsToPreload),
658 [crossLens = crossLens_, crossGap = crossGap_, mainGap = mainGap_](
659 const RefPtr<FrameNode>& host, int32_t itemIdx) {
660 CHECK_NULL_RETURN(host, false);
661 auto pattern = host->GetPattern<GridPattern>();
662 CHECK_NULL_RETURN(pattern, false);
663
664 ScopedLayout scope(host->GetContext());
665 auto& info = pattern->GetMutableLayoutInfo();
666 GridIrregularFiller filler(&info, RawPtr(host));
667 const auto pos = info.GetItemPos(itemIdx);
668 auto constraint = filler
669 .MeasureItem(GridIrregularFiller::FillParameters { crossLens, crossGap, mainGap },
670 itemIdx, pos.first, pos.second, true)
671 .second;
672
673 auto item = DynamicCast<FrameNode>(host->GetChildByIndex(itemIdx, true));
674 CHECK_NULL_RETURN(item, false);
675 item->GetGeometryNode()->SetParentLayoutConstraint(constraint);
676 item->Layout();
677 auto pipeline = pattern->GetContext();
678 if (pipeline) {
679 pipeline->FlushSyncGeometryNodeTasks();
680 }
681 item->SetActive(false);
682 return true;
683 });
684 }
685
AdaptToChildMainSize(RefPtr<GridLayoutProperty> & gridLayoutProperty,float mainSize,SizeF idealSize)686 void GridIrregularLayoutAlgorithm::AdaptToChildMainSize(
687 RefPtr<GridLayoutProperty>& gridLayoutProperty, float mainSize, SizeF idealSize)
688 {
689 auto lengthOfItemsInViewport = info_.GetTotalHeightOfItemsInView(mainGap_);
690 auto gridMainSize = std::min(lengthOfItemsInViewport, mainSize);
691 gridMainSize =
692 std::max(gridMainSize, GetMainAxisSize(gridLayoutProperty->GetLayoutConstraint()->minSize, info_.axis_));
693 idealSize.SetMainSize(gridMainSize, info_.axis_);
694 AddPaddingToSize(gridLayoutProperty->CreatePaddingAndBorder(), idealSize);
695 wrapper_->GetGeometryNode()->SetFrameSize(idealSize);
696 info_.lastMainSize_ = gridMainSize;
697 TAG_LOGI(AceLogTag::ACE_GRID, "gridMainSize:%{public}f", gridMainSize);
698 }
699 } // namespace OHOS::Ace::NG
700