• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/grid_scroll/grid_scroll_with_options_layout_algorithm.h"
17 
18 namespace OHOS::Ace::NG {
19 namespace {
UpdateGridItemRowAndColumnInfo(const RefPtr<LayoutWrapper> & itemLayoutWrapper,GridItemIndexInfo irregualItemInfo)20 void UpdateGridItemRowAndColumnInfo(const RefPtr<LayoutWrapper>& itemLayoutWrapper, GridItemIndexInfo irregualItemInfo)
21 {
22     auto gridItemHost = itemLayoutWrapper->GetHostNode();
23     CHECK_NULL_VOID(gridItemHost);
24     auto gridItemPattern = gridItemHost->GetPattern<GridItemPattern>();
25     CHECK_NULL_VOID(gridItemPattern);
26     gridItemPattern->SetIrregularItemInfo(irregualItemInfo);
27 }
28 } // namespace
29 
AdjustRowColSpan(const RefPtr<LayoutWrapper> & itemLayoutWrapper,LayoutWrapper * layoutWrapper,int32_t itemIndex)30 void GridScrollWithOptionsLayoutAlgorithm::AdjustRowColSpan(
31     const RefPtr<LayoutWrapper>& itemLayoutWrapper, LayoutWrapper* layoutWrapper, int32_t itemIndex)
32 {
33     auto result = GetCrossStartAndSpan(layoutWrapper, itemIndex);
34     if (info_.axis_ == Axis::VERTICAL) {
35         currentItemColStart_ = result.first;
36         currentItemColSpan_ = result.second;
37         currentItemColEnd_ = currentItemColStart_ + currentItemColSpan_ - 1;
38         currentItemRowStart_ = -1;
39         currentItemRowEnd_ = -1;
40         currentItemRowSpan_ = 1;
41     } else {
42         currentItemRowStart_ = result.first;
43         currentItemRowSpan_ = result.second;
44         currentItemRowEnd_ = currentItemRowStart_ + currentItemRowSpan_ - 1;
45         currentItemColStart_ = -1;
46         currentItemColEnd_ = -1;
47         currentItemColSpan_ = 1;
48     }
49 
50     if (currentItemRowSpan_ > 1 || currentItemColSpan_ > 1) {
51         info_.hasBigItem_ = true;
52         bool isVertical = info_.axis_ == Axis::VERTICAL;
53         GridItemIndexInfo irregualItemInfo;
54         irregualItemInfo.mainStart = isVertical ? currentItemRowStart_ : currentItemColStart_;
55         irregualItemInfo.mainEnd = isVertical ? currentItemRowEnd_ : currentItemColEnd_;
56         irregualItemInfo.mainSpan = isVertical ? currentItemRowSpan_ : currentItemColSpan_;
57         irregualItemInfo.crossStart = isVertical ? currentItemColStart_ : currentItemRowStart_;
58         irregualItemInfo.crossEnd = isVertical ? currentItemColEnd_ : currentItemRowEnd_;
59         irregualItemInfo.crossSpan = isVertical ? currentItemColSpan_ : currentItemRowSpan_;
60         UpdateGridItemRowAndColumnInfo(itemLayoutWrapper, irregualItemInfo);
61     }
62 }
63 
LargeItemLineHeight(const RefPtr<LayoutWrapper> & itemWrapper)64 void GridScrollWithOptionsLayoutAlgorithm::LargeItemLineHeight(const RefPtr<LayoutWrapper>& itemWrapper)
65 {
66     auto itemSize = itemWrapper->GetGeometryNode()->GetMarginFrameSize();
67     auto itemMainSize = GetMainAxisSize(itemSize, info_.axis_);
68     if (LessNotEqual(itemMainSize, 0.0f)) {
69         TAG_LOGI(
70             AceLogTag::ACE_GRID, "item height of index %{public}d is less than zero", info_.endIndex_ + 1);
71         itemMainSize = 0.0f;
72     }
73     cellAveLength_ = std::max(itemMainSize, cellAveLength_);
74 }
75 
GetTargetIndexInfoWithBenchMark(LayoutWrapper * layoutWrapper,bool isTargetBackward,int32_t targetIndex)76 void GridScrollWithOptionsLayoutAlgorithm::GetTargetIndexInfoWithBenchMark(
77     LayoutWrapper* layoutWrapper, bool isTargetBackward, int32_t targetIndex)
78 {
79     int32_t benchmarkIndex = (isTargetBackward && !info_.gridMatrix_.empty())
80                                  ? info_.gridMatrix_.rbegin()->second.rbegin()->second + 1
81                                  : 0;
82     int32_t mainStartIndex = (isTargetBackward && !info_.gridMatrix_.empty())
83                                  ? info_.gridMatrix_.rbegin()->first + 1
84                                  : 0;
85     int32_t currentIndex = benchmarkIndex;
86     int32_t headOfMainStartLine = currentIndex;
87 
88     while (currentIndex < targetIndex) {
89         int32_t crossGridReserve = info_.crossCount_;
90         /* go through a new line */
91         while ((crossGridReserve > 0) && (currentIndex <= targetIndex)) {
92             auto crossPos = GetCrossStartAndSpan(layoutWrapper, currentIndex);
93             auto gridSpan = crossPos.second;
94             if (crossGridReserve >= gridSpan) {
95                 crossGridReserve -= gridSpan;
96             } else if (info_.crossCount_ >= gridSpan) {
97                 ++mainStartIndex;
98                 headOfMainStartLine = currentIndex;
99                 crossGridReserve = info_.crossCount_ - gridSpan;
100             }
101             ++currentIndex;
102         }
103         if (currentIndex > targetIndex) {
104             break;
105         }
106         ++mainStartIndex;
107         headOfMainStartLine = currentIndex;
108     }
109     info_.startMainLineIndex_ = mainStartIndex;
110     info_.startIndex_ = headOfMainStartLine;
111     info_.endIndex_ = headOfMainStartLine - 1;
112     info_.prevOffset_ = 0;
113     info_.currentOffset_ = 0;
114     info_.ResetPositionFlags();
115     info_.gridMatrix_.clear();
116     info_.lineHeightMap_.clear();
117     info_.irregularItemsPosition_.clear();
118 }
119 
GetCrossStartAndSpan(LayoutWrapper * layoutWrapper,int32_t itemIndex)120 std::pair<int32_t, int32_t> GridScrollWithOptionsLayoutAlgorithm::GetCrossStartAndSpan(
121     LayoutWrapper* layoutWrapper, int32_t itemIndex)
122 {
123     auto layoutProperty = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
124     CHECK_NULL_RETURN(layoutProperty, std::make_pair(-1, 1));
125     const auto& options = *layoutProperty->GetLayoutOptions();
126     if (options.irregularIndexes.empty()) {
127         return std::make_pair(-1, 1);
128     }
129 
130     auto firstIrregularIndex = *(options.irregularIndexes.begin());
131     if (itemIndex < firstIrregularIndex) {
132         return std::make_pair(itemIndex % crossCount_, 1);
133     }
134 
135     // without function
136     if (!options.getSizeByIndex) {
137         if (options.irregularIndexes.find(itemIndex) != options.irregularIndexes.end()) {
138             return std::make_pair(0, crossCount_);
139         }
140         int32_t crossStart = -1;
141         auto iter = options.irregularIndexes.upper_bound(itemIndex);
142         auto crossCount = static_cast<int32_t>(crossCount_);
143         if (iter == options.irregularIndexes.end()) {
144             crossStart = (itemIndex - (*(options.irregularIndexes.rbegin()) + 1)) % crossCount;
145         } else {
146             if (iter != options.irregularIndexes.begin()) {
147                 crossStart = (itemIndex - (*(--iter) + 1)) % crossCount;
148             } else {
149                 crossStart = itemIndex % crossCount;
150             }
151         }
152         return std::make_pair(crossStart, 1);
153     }
154 
155     return GetCrossStartAndSpanWithUserFunction(itemIndex, options, firstIrregularIndex);
156 }
157 
JumpToLastIrregularItem(const std::map<int32_t,int32_t> & irregularItemsPosition,int32_t & sum,int32_t & lastIndex,int32_t targetIndex)158 static void JumpToLastIrregularItem(
159     const std::map<int32_t, int32_t>& irregularItemsPosition, int32_t& sum, int32_t& lastIndex, int32_t targetIndex)
160 {
161     if (irregularItemsPosition.empty()) {
162         return;
163     }
164 
165     auto iter = irregularItemsPosition.lower_bound(targetIndex);
166     if (iter == irregularItemsPosition.begin()) {
167         return;
168     }
169     if (iter != irregularItemsPosition.end()) {
170         --iter;
171         sum = iter->second;
172         lastIndex = iter->first;
173     } else {
174         auto lastIter = irregularItemsPosition.rbegin();
175         sum = lastIter->second;
176         lastIndex = lastIter->first;
177     }
178 }
179 
ResetInvalidCrossSpan(uint32_t crossCount,int32_t & crossSpan)180 static void ResetInvalidCrossSpan(uint32_t crossCount, int32_t& crossSpan)
181 {
182     if (crossSpan > static_cast<int32_t>(crossCount) || crossSpan <= 0) {
183         crossSpan = 1;
184     }
185 }
186 
InitIrregularItemsPosition(std::map<int32_t,int32_t> & irregularItemsPosition,const GridLayoutOptions & options,int32_t firstIrregularIndex,Axis axis,int32_t crossCount)187 static void InitIrregularItemsPosition(std::map<int32_t, int32_t>& irregularItemsPosition,
188     const GridLayoutOptions& options, int32_t firstIrregularIndex, Axis axis, int32_t crossCount)
189 {
190     if (irregularItemsPosition.empty()) {
191         auto sum = firstIrregularIndex;
192         auto crossSpan = options.getSizeByIndex(firstIrregularIndex).GetCrossSize(axis);
193         ResetInvalidCrossSpan(crossCount, crossSpan);
194         // first irregular item in new line
195         if (crossCount != 0) {
196             auto crossStart = sum % crossCount;
197             if (crossStart + crossSpan > crossCount) {
198                 sum += (crossCount - crossStart);
199             }
200         }
201         irregularItemsPosition.emplace(firstIrregularIndex, sum + crossSpan);
202     }
203 }
204 
GetCrossStartAndSpanWithUserFunction(int32_t itemIndex,const GridLayoutOptions & options,int32_t firstIrregularIndex)205 std::pair<int32_t, int32_t> GridScrollWithOptionsLayoutAlgorithm::GetCrossStartAndSpanWithUserFunction(
206     int32_t itemIndex, const GridLayoutOptions& options, int32_t firstIrregularIndex)
207 {
208     auto crossCount = static_cast<int32_t>(crossCount_);
209     InitIrregularItemsPosition(
210         info_.irregularItemsPosition_, options, firstIrregularIndex, info_.axis_, crossCount);
211     auto sum = firstIrregularIndex;
212     auto lastIndex = firstIrregularIndex;
213     JumpToLastIrregularItem(info_.irregularItemsPosition_, sum, lastIndex, itemIndex);
214     auto iter = options.irregularIndexes.find(lastIndex);
215     if (iter == options.irregularIndexes.end()) {
216         iter = options.irregularIndexes.begin();
217     }
218     for (; iter != options.irregularIndexes.end(); ++iter) {
219         auto index = *iter;
220         if (index >= itemIndex) {
221             break;
222         }
223 
224         if (index == lastIndex) {
225             continue;
226         }
227 
228         auto crossSpan = options.getSizeByIndex(index).GetCrossSize(info_.axis_);
229         ResetInvalidCrossSpan(crossCount_, crossSpan);
230         auto irregularStart = (sum + index - lastIndex - 1) % crossCount;
231         // put it into next line
232         if (irregularStart + crossSpan > crossCount) {
233             sum += (crossCount - irregularStart);
234         }
235         sum += (index - lastIndex - 1);
236         sum += crossSpan;
237         lastIndex = index;
238         info_.irregularItemsPosition_.emplace(index, sum);
239     }
240     sum += ((itemIndex > lastIndex) ? (itemIndex - lastIndex - 1) : 0);
241     auto crossStart = sum % crossCount;
242     bool isRegularItem = (options.irregularIndexes.find(itemIndex) == options.irregularIndexes.end());
243     auto crossSpan = isRegularItem ? 1 : options.getSizeByIndex(itemIndex).GetCrossSize(info_.axis_);
244     ResetInvalidCrossSpan(crossCount_, crossSpan);
245     if (crossStart + crossSpan > crossCount) {
246         sum += (crossCount - crossStart);
247         crossStart = 0;
248     }
249     if (!isRegularItem) {
250         sum += crossSpan;
251         info_.irregularItemsPosition_.emplace(itemIndex, sum);
252     }
253     return std::make_pair(crossStart, crossSpan);
254 }
255 
SkipLargeOffset(float mainSize,LayoutWrapper * layoutWrapper)256 void GridScrollWithOptionsLayoutAlgorithm::SkipLargeOffset(float mainSize, LayoutWrapper* layoutWrapper)
257 {
258     SkipForwardLines(mainSize, layoutWrapper);
259     SkipBackwardLines(mainSize, layoutWrapper);
260 }
261 
SkipIrregularLines(LayoutWrapper * layoutWrapper,bool forward)262 void GridScrollWithOptionsLayoutAlgorithm::SkipIrregularLines(LayoutWrapper* layoutWrapper, bool forward)
263 {
264     auto layoutProperty = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
265     CHECK_NULL_VOID(layoutProperty);
266     const auto& options = *layoutProperty->GetLayoutOptions();
267     if (options.irregularIndexes.empty()) {
268         return SkipRegularLines(forward);
269     }
270     if (options.getSizeByIndex) {
271         return GridScrollLayoutAlgorithm::SkipIrregularLines(layoutWrapper, forward);
272     }
273 
274     info_.SkipStartIndexByOffset(options, mainGap_);
275 }
276 
CalculateCachedCount(LayoutWrapper * layoutWrapper,int32_t cachedCount)277 std::pair<int32_t, int32_t> GridScrollWithOptionsLayoutAlgorithm::CalculateCachedCount(
278     LayoutWrapper* layoutWrapper, int32_t cachedCount)
279 {
280     if (cachedCount == 0 || info_.crossCount_ == 1) {
281         return std::make_pair(cachedCount, cachedCount);
282     }
283     int32_t cache = cachedCount * info_.crossCount_;
284 
285     CHECK_NULL_RETURN(layoutWrapper, std::make_pair(cache, cache));
286     auto props = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
287     CHECK_NULL_RETURN(props, std::make_pair(cache, cache));
288 
289     const auto& options = *props->GetLayoutOptions();
290     if (options.irregularIndexes.empty()) {
291         return std::make_pair(cache, cache);
292     }
293     int32_t start = CalculateStartCachedCount(options, cachedCount);
294     int32_t end = CalculateEndCachedCount(options, cachedCount);
295     return std::make_pair(start, end);
296 }
297 
CalculateStartCachedCount(const GridLayoutOptions & options,int32_t cachedCount)298 int32_t GridScrollWithOptionsLayoutAlgorithm::CalculateStartCachedCount(
299     const GridLayoutOptions& options, int32_t cachedCount)
300 {
301     int32_t start = cachedCount * info_.crossCount_;
302 
303     if (info_.startMainLineIndex_ - cachedCount <= 0) {
304         return std::min(info_.startIndex_, start);
305     }
306 
307     auto startLine = info_.gridMatrix_.find(info_.startMainLineIndex_ - cachedCount);
308     if (startLine != info_.gridMatrix_.end()) {
309         auto line = startLine->second;
310         if (!line.empty()) {
311             auto index = line.begin()->second;
312             return info_.startIndex_ - index;
313         }
314     }
315 
316     auto firstIrregularIndex = *(options.irregularIndexes.begin());
317     if (info_.startIndex_ <= firstIrregularIndex) {
318         return start;
319     }
320 
321     if (!options.getSizeByIndex) {
322         auto iter = options.irregularIndexes.lower_bound(info_.startIndex_);
323         auto crossCount = static_cast<int32_t>(crossCount_);
324         if (iter == options.irregularIndexes.end()) {
325             return start;
326         }
327         if (*iter == info_.startIndex_ && iter != options.irregularIndexes.begin()) {
328             iter--;
329         }
330 
331         int lineCount = 0;
332         int sum = 0;
333         int32_t diff = info_.startIndex_ - *(iter)-1;
334         while (lineCount < cachedCount) {
335             if (diff >= (cachedCount - lineCount) * crossCount) {
336                 return (cachedCount - lineCount) * crossCount + sum;
337             }
338 
339             if (diff == 0) {
340                 sum++;
341                 lineCount++;
342             }
343 
344             if (diff > 0 && diff <= (cachedCount - lineCount - 1) * crossCount) {
345                 lineCount += std::ceil(diff / crossCount) + 1;
346                 sum += diff;
347             }
348 
349             if (iter == options.irregularIndexes.begin()) {
350                 return (cachedCount - lineCount) * crossCount + sum;
351             }
352 
353             diff = (*iter) - (*(--iter)) - 1;
354         }
355         return sum;
356     }
357     return start;
358 }
359 
CalculateEndCachedCount(const GridLayoutOptions & options,int32_t cachedCount)360 int32_t GridScrollWithOptionsLayoutAlgorithm::CalculateEndCachedCount(
361     const GridLayoutOptions& options, int32_t cachedCount)
362 {
363     if (info_.startIndex_ + cachedCount >= info_.childrenCount_ - 1) {
364         return info_.startIndex_;
365     }
366 
367     int32_t end = cachedCount * info_.crossCount_;
368 
369     auto endLine = info_.gridMatrix_.find(info_.endMainLineIndex_ + cachedCount);
370     if (endLine != info_.gridMatrix_.end()) {
371         auto line = endLine->second;
372         if (!line.empty()) {
373             auto index = line.rbegin()->second;
374             return index - info_.endIndex_;
375         }
376     }
377 
378     auto lastIrregularIndex = *(options.irregularIndexes.rbegin());
379     if (info_.endIndex_ >= lastIrregularIndex) {
380         return end;
381     }
382 
383     if (!options.getSizeByIndex) {
384         auto iter = options.irregularIndexes.upper_bound(info_.endIndex_);
385         auto crossCount = static_cast<int32_t>(crossCount_);
386         if (iter == options.irregularIndexes.end()) {
387             return end;
388         }
389 
390         int lineCount = 0;
391         int sum = 0;
392         int32_t diff = *(iter)-info_.endIndex_ - 1;
393         while (lineCount < cachedCount) {
394             if (diff >= (cachedCount - lineCount) * crossCount) {
395                 return (cachedCount - lineCount) * crossCount + sum;
396             }
397 
398             if (diff == 0) {
399                 sum++;
400                 lineCount++;
401             }
402 
403             if (diff > 0 && diff <= (cachedCount - lineCount - 1) * crossCount) {
404                 lineCount += std::ceil(diff / crossCount) + 1;
405                 sum += diff;
406             }
407 
408             if (iter == options.irregularIndexes.end()) {
409                 return (cachedCount - lineCount) * crossCount + sum;
410             }
411 
412             diff = -*(iter) + *(++iter) - 1;
413         }
414         return sum;
415     }
416     return end;
417 }
418 } // namespace OHOS::Ace::NG
419