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 #include "core/components_ng/pattern/grid/grid_item_pattern.h"
19
20 namespace OHOS::Ace::NG {
21 namespace {
UpdateGridItemRowAndColumnInfo(const RefPtr<LayoutWrapper> & itemLayoutWrapper,GridItemIndexInfo irregualItemInfo)22 void UpdateGridItemRowAndColumnInfo(const RefPtr<LayoutWrapper>& itemLayoutWrapper, GridItemIndexInfo irregualItemInfo)
23 {
24 auto gridItemHost = itemLayoutWrapper->GetHostNode();
25 CHECK_NULL_VOID(gridItemHost);
26 auto gridItemPattern = gridItemHost->GetPattern<GridItemPattern>();
27 CHECK_NULL_VOID(gridItemPattern);
28 gridItemPattern->SetIrregularItemInfo(irregualItemInfo);
29 }
30 } // namespace
31
AdjustRowColSpan(const RefPtr<LayoutWrapper> & itemLayoutWrapper,LayoutWrapper * layoutWrapper,int32_t itemIndex)32 void GridScrollWithOptionsLayoutAlgorithm::AdjustRowColSpan(
33 const RefPtr<LayoutWrapper>& itemLayoutWrapper, LayoutWrapper* layoutWrapper, int32_t itemIndex)
34 {
35 auto result = GetCrossStartAndSpan(layoutWrapper, itemIndex);
36 if (gridLayoutInfo_.axis_ == Axis::VERTICAL) {
37 currentItemColStart_ = result.first;
38 currentItemColSpan_ = result.second;
39 currentItemColEnd_ = currentItemColStart_ + currentItemColSpan_ - 1;
40 currentItemRowStart_ = -1;
41 currentItemRowEnd_ = -1;
42 currentItemRowSpan_ = 1;
43 } else {
44 currentItemRowStart_ = result.first;
45 currentItemRowSpan_ = result.second;
46 currentItemRowEnd_ = currentItemRowStart_ + currentItemRowSpan_ - 1;
47 currentItemColStart_ = -1;
48 currentItemColEnd_ = -1;
49 currentItemColSpan_ = 1;
50 }
51
52 if (currentItemRowSpan_ > 1 || currentItemColSpan_ > 1) {
53 gridLayoutInfo_.hasBigItem_ = true;
54 bool isVertical = gridLayoutInfo_.axis_ == Axis::VERTICAL;
55 GridItemIndexInfo irregualItemInfo;
56 irregualItemInfo.mainStart = isVertical ? currentItemRowStart_ : currentItemColStart_;
57 irregualItemInfo.mainEnd = isVertical ? currentItemRowEnd_ : currentItemColEnd_;
58 irregualItemInfo.mainSpan = isVertical ? currentItemRowSpan_ : currentItemColSpan_;
59 irregualItemInfo.crossStart = isVertical ? currentItemColStart_ : currentItemRowStart_;
60 irregualItemInfo.crossEnd = isVertical ? currentItemColEnd_ : currentItemRowEnd_;
61 irregualItemInfo.crossSpan = isVertical ? currentItemColSpan_ : currentItemRowSpan_;
62 UpdateGridItemRowAndColumnInfo(itemLayoutWrapper, irregualItemInfo);
63 }
64 }
65
LargeItemLineHeight(const RefPtr<LayoutWrapper> & itemWrapper,bool &)66 void GridScrollWithOptionsLayoutAlgorithm::LargeItemLineHeight(
67 const RefPtr<LayoutWrapper>& itemWrapper, bool& /* hasNormalItem */)
68 {
69 auto itemSize = itemWrapper->GetGeometryNode()->GetMarginFrameSize();
70 auto itemMainSize = GetMainAxisSize(itemSize, gridLayoutInfo_.axis_);
71 if (LessNotEqual(itemMainSize, 0.0f)) {
72 TAG_LOGI(
73 AceLogTag::ACE_GRID, "item height of index %{public}d is less than zero", gridLayoutInfo_.endIndex_ + 1);
74 itemMainSize = 0.0f;
75 }
76 cellAveLength_ = std::max(itemMainSize, cellAveLength_);
77 }
78
GetTargetIndexInfoWithBenchMark(LayoutWrapper * layoutWrapper,bool isTargetBackward,int32_t targetIndex)79 void GridScrollWithOptionsLayoutAlgorithm::GetTargetIndexInfoWithBenchMark(
80 LayoutWrapper* layoutWrapper, bool isTargetBackward, int32_t targetIndex)
81 {
82 int32_t benchmarkIndex = (isTargetBackward && !gridLayoutInfo_.gridMatrix_.empty())
83 ? gridLayoutInfo_.gridMatrix_.rbegin()->second.rbegin()->second + 1
84 : 0;
85 int32_t mainStartIndex = (isTargetBackward && !gridLayoutInfo_.gridMatrix_.empty())
86 ? gridLayoutInfo_.gridMatrix_.rbegin()->first + 1
87 : 0;
88 int32_t currentIndex = benchmarkIndex;
89 int32_t headOfMainStartLine = currentIndex;
90
91 while (currentIndex < targetIndex) {
92 int32_t crossGridReserve = gridLayoutInfo_.crossCount_;
93 /* go through a new line */
94 while ((crossGridReserve > 0) && (currentIndex <= targetIndex)) {
95 auto crossPos = GetCrossStartAndSpan(layoutWrapper, currentIndex);
96 auto gridSpan = crossPos.second;
97 if (crossGridReserve >= gridSpan) {
98 crossGridReserve -= gridSpan;
99 } else if (gridLayoutInfo_.crossCount_ >= gridSpan) {
100 ++mainStartIndex;
101 headOfMainStartLine = currentIndex;
102 crossGridReserve = gridLayoutInfo_.crossCount_ - gridSpan;
103 }
104 ++currentIndex;
105 }
106 if (currentIndex > targetIndex) {
107 break;
108 }
109 ++mainStartIndex;
110 headOfMainStartLine = currentIndex;
111 }
112 gridLayoutInfo_.startMainLineIndex_ = mainStartIndex;
113 gridLayoutInfo_.startIndex_ = headOfMainStartLine;
114 gridLayoutInfo_.endIndex_ = headOfMainStartLine - 1;
115 gridLayoutInfo_.prevOffset_ = 0;
116 gridLayoutInfo_.currentOffset_ = 0;
117 gridLayoutInfo_.ResetPositionFlags();
118 gridLayoutInfo_.gridMatrix_.clear();
119 gridLayoutInfo_.lineHeightMap_.clear();
120 gridLayoutInfo_.irregularItemsPosition_.clear();
121 }
122
GetCrossStartAndSpan(LayoutWrapper * layoutWrapper,int32_t itemIndex)123 std::pair<int32_t, int32_t> GridScrollWithOptionsLayoutAlgorithm::GetCrossStartAndSpan(
124 LayoutWrapper* layoutWrapper, int32_t itemIndex)
125 {
126 auto layoutProperty = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
127 CHECK_NULL_RETURN(layoutProperty, std::make_pair(-1, 1));
128 const auto& options = *layoutProperty->GetLayoutOptions();
129 if (options.irregularIndexes.empty()) {
130 return std::make_pair(itemIndex % crossCount_, 1);
131 }
132
133 auto firstIrregularIndex = *(options.irregularIndexes.begin());
134 if (itemIndex < firstIrregularIndex) {
135 return std::make_pair(itemIndex % crossCount_, 1);
136 }
137
138 // without function
139 if (!options.getSizeByIndex) {
140 if (options.irregularIndexes.find(itemIndex) != options.irregularIndexes.end()) {
141 return std::make_pair(0, crossCount_);
142 }
143 int32_t crossStart = -1;
144 auto iter = options.irregularIndexes.upper_bound(itemIndex);
145 auto crossCount = static_cast<int32_t>(crossCount_);
146 if (iter == options.irregularIndexes.end()) {
147 crossStart = (itemIndex - (*(options.irregularIndexes.rbegin()) + 1)) % crossCount;
148 } else {
149 if (iter != options.irregularIndexes.begin()) {
150 crossStart = (itemIndex - (*(--iter) + 1)) % crossCount;
151 } else {
152 crossStart = itemIndex % crossCount;
153 }
154 }
155 return std::make_pair(crossStart, 1);
156 }
157
158 return GetCrossStartAndSpanWithUserFunction(itemIndex, options, firstIrregularIndex);
159 }
160
JumpToLastIrregularItem(const std::map<int32_t,int32_t> & irregularItemsPosition,int32_t & sum,int32_t & lastIndex,int32_t targetIndex)161 static void JumpToLastIrregularItem(
162 const std::map<int32_t, int32_t>& irregularItemsPosition, int32_t& sum, int32_t& lastIndex, int32_t targetIndex)
163 {
164 if (irregularItemsPosition.empty()) {
165 return;
166 }
167
168 auto iter = irregularItemsPosition.lower_bound(targetIndex);
169 if (iter == irregularItemsPosition.begin()) {
170 return;
171 }
172 if (iter != irregularItemsPosition.end()) {
173 --iter;
174 sum = iter->second;
175 lastIndex = iter->first;
176 } else {
177 auto lastIter = irregularItemsPosition.rbegin();
178 sum = lastIter->second;
179 lastIndex = lastIter->first;
180 }
181 }
182
ResetInvalidCrossSpan(uint32_t crossCount,int32_t & crossSpan)183 static void ResetInvalidCrossSpan(uint32_t crossCount, int32_t& crossSpan)
184 {
185 if (crossSpan > static_cast<int32_t>(crossCount) || crossSpan <= 0) {
186 crossSpan = 1;
187 }
188 }
189
InitIrregularItemsPosition(std::map<int32_t,int32_t> & irregularItemsPosition,const GridLayoutOptions & options,int32_t firstIrregularIndex,Axis axis,int32_t crossCount)190 static void InitIrregularItemsPosition(std::map<int32_t, int32_t>& irregularItemsPosition,
191 const GridLayoutOptions& options, int32_t firstIrregularIndex, Axis axis, int32_t crossCount)
192 {
193 if (irregularItemsPosition.empty()) {
194 auto sum = firstIrregularIndex;
195 auto crossSpan = options.getSizeByIndex(firstIrregularIndex).GetCrossSize(axis);
196 ResetInvalidCrossSpan(crossCount, crossSpan);
197 // first irregular item in new line
198 if (crossCount != 0) {
199 auto crossStart = sum % crossCount;
200 if (crossStart + crossSpan > crossCount) {
201 sum += (crossCount - crossStart);
202 }
203 }
204 irregularItemsPosition.emplace(firstIrregularIndex, sum + crossSpan);
205 }
206 }
207
GetCrossStartAndSpanWithUserFunction(int32_t itemIndex,const GridLayoutOptions & options,int32_t firstIrregularIndex)208 std::pair<int32_t, int32_t> GridScrollWithOptionsLayoutAlgorithm::GetCrossStartAndSpanWithUserFunction(
209 int32_t itemIndex, const GridLayoutOptions& options, int32_t firstIrregularIndex)
210 {
211 auto crossCount = static_cast<int32_t>(crossCount_);
212 InitIrregularItemsPosition(
213 gridLayoutInfo_.irregularItemsPosition_, options, firstIrregularIndex, gridLayoutInfo_.axis_, crossCount);
214 auto sum = firstIrregularIndex;
215 auto lastIndex = firstIrregularIndex;
216 JumpToLastIrregularItem(gridLayoutInfo_.irregularItemsPosition_, sum, lastIndex, itemIndex);
217 auto iter = options.irregularIndexes.find(lastIndex);
218 if (iter == options.irregularIndexes.end()) {
219 iter = options.irregularIndexes.begin();
220 }
221 for (; iter != options.irregularIndexes.end(); ++iter) {
222 auto index = *iter;
223 if (index >= itemIndex) {
224 break;
225 }
226
227 if (index == lastIndex) {
228 continue;
229 }
230
231 auto crossSpan = options.getSizeByIndex(index).GetCrossSize(gridLayoutInfo_.axis_);
232 ResetInvalidCrossSpan(crossCount_, crossSpan);
233 auto irregularStart = (sum + index - lastIndex - 1) % crossCount;
234 // put it into next line
235 if (irregularStart + crossSpan > crossCount) {
236 sum += (crossCount - irregularStart);
237 }
238 sum += (index - lastIndex - 1);
239 sum += crossSpan;
240 lastIndex = index;
241 gridLayoutInfo_.irregularItemsPosition_.emplace(index, sum);
242 }
243 sum += ((itemIndex > lastIndex) ? (itemIndex - lastIndex - 1) : 0);
244 auto crossStart = sum % crossCount;
245 bool isRegularItem = (options.irregularIndexes.find(itemIndex) == options.irregularIndexes.end());
246 auto crossSpan = isRegularItem ? 1 : options.getSizeByIndex(itemIndex).GetCrossSize(gridLayoutInfo_.axis_);
247 ResetInvalidCrossSpan(crossCount_, crossSpan);
248 if (crossStart + crossSpan > crossCount) {
249 sum += (crossCount - crossStart);
250 crossStart = 0;
251 }
252 if (!isRegularItem) {
253 sum += crossSpan;
254 gridLayoutInfo_.irregularItemsPosition_.emplace(itemIndex, sum);
255 }
256 return std::make_pair(crossStart, crossSpan);
257 }
258
SkipLargeOffset(float mainSize,LayoutWrapper * layoutWrapper)259 void GridScrollWithOptionsLayoutAlgorithm::SkipLargeOffset(float mainSize, LayoutWrapper* layoutWrapper)
260 {
261 SkipForwardLines(mainSize, layoutWrapper);
262 SkipBackwardLines(mainSize, layoutWrapper);
263 }
264
SkipIrregularLines(LayoutWrapper * layoutWrapper,bool forward)265 void GridScrollWithOptionsLayoutAlgorithm::SkipIrregularLines(LayoutWrapper* layoutWrapper, bool forward)
266 {
267 auto layoutProperty = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
268 CHECK_NULL_VOID(layoutProperty);
269 const auto& options = *layoutProperty->GetLayoutOptions();
270 if (options.irregularIndexes.empty()) {
271 return SkipRegularLines(forward);
272 }
273 if (options.getSizeByIndex) {
274 return GridScrollLayoutAlgorithm::SkipIrregularLines(layoutWrapper, forward);
275 }
276
277 gridLayoutInfo_.SkipStartIndexByOffset(options, mainGap_);
278 }
279 } // namespace OHOS::Ace::NG
280