1 /*
2 * Copyright (c) 2022 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_layout/grid_layout_algorithm.h"
17
18 #include <cstdint>
19
20 #include "base/geometry/ng/offset_t.h"
21 #include "base/geometry/ng/size_t.h"
22 #include "base/utils/utils.h"
23 #include "core/components_ng/layout/layout_wrapper.h"
24 #include "core/components_ng/pattern/grid/grid_item_pattern.h"
25 #include "core/components_ng/pattern/grid/grid_utils.h"
26 #include "core/components_ng/property/measure_utils.h"
27 #include "core/components_ng/property/templates_parser.h"
28
29 namespace OHOS::Ace::NG {
30 namespace {
OffsetByAlign(const SizeF & size,float rowLen,float colLen,float & positionX,float & positionY)31 void OffsetByAlign(const SizeF& size, float rowLen, float colLen, float& positionX, float& positionY)
32 {
33 // only support Alignment.Center now
34 auto width = size.Width();
35 positionX += (colLen - width) / 2;
36 auto height = size.Height();
37 positionY += (rowLen - height) / 2;
38 }
39 } // namespace
40
CreateChildConstraint(const SizeF & idealSize,const RefPtr<GridLayoutProperty> & layoutProperty,int32_t row,int32_t col,int32_t & rowSpan,int32_t & colSpan,const RefPtr<LayoutProperty> & childLayoutProperty) const41 LayoutConstraintF GridLayoutAlgorithm::CreateChildConstraint(const SizeF& idealSize,
42 const RefPtr<GridLayoutProperty>& layoutProperty, int32_t row, int32_t col, int32_t& rowSpan, int32_t& colSpan,
43 const RefPtr<LayoutProperty>& childLayoutProperty) const
44 {
45 LayoutConstraintF layoutConstraint = layoutProperty->CreateChildConstraint();
46
47 float rowLen = 0.0;
48 for (int32_t i = 0; i < rowSpan; ++i) {
49 rowLen += GetItemSize(row + i, col, true);
50 }
51 rowLen += (rowSpan - 1) * rowsGap_;
52
53 float colLen = 0.0;
54 for (int32_t i = 0; i < colSpan; ++i) {
55 colLen += GetItemSize(row, col + i, false);
56 }
57 colLen += (colSpan - 1) * columnsGap_;
58
59 layoutConstraint.maxSize = SizeF(colLen, rowLen);
60 layoutConstraint.percentReference = SizeF(colLen, rowLen);
61 if (!childLayoutProperty || !childLayoutProperty->GetCalcLayoutConstraint()) {
62 layoutConstraint.selfIdealSize.UpdateIllegalSizeWithCheck(layoutConstraint.maxSize);
63 }
64 return layoutConstraint;
65 }
66
InitGridCeils(LayoutWrapper * layoutWrapper,const SizeF & idealSize)67 void GridLayoutAlgorithm::InitGridCeils(LayoutWrapper* layoutWrapper, const SizeF& idealSize)
68 {
69 auto layoutProperty = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
70 CHECK_NULL_VOID(layoutProperty);
71 auto scale = layoutProperty->GetLayoutConstraint()->scaleProperty;
72 rowsGap_ = ConvertToPx(layoutProperty->GetRowsGap().value_or(0.0_vp), scale, idealSize.Height()).value_or(0);
73 columnsGap_ = ConvertToPx(layoutProperty->GetColumnsGap().value_or(0.0_vp), scale, idealSize.Width()).value_or(0);
74 auto rows = ParseTemplateArgs(GridUtils::ParseArgs(layoutProperty->GetRowsTemplate().value_or("")),
75 idealSize.Height(), rowsGap_, layoutWrapper->GetTotalChildCount());
76 auto cols = ParseTemplateArgs(GridUtils::ParseArgs(layoutProperty->GetColumnsTemplate().value_or("")),
77 idealSize.Width(), columnsGap_, layoutWrapper->GetTotalChildCount());
78 auto rowsLen = rows.first;
79 auto colsLen = cols.first;
80 if (rowsLen.empty()) {
81 rowsLen.push_back(idealSize.Height());
82 }
83 rowsGap_ = rows.second;
84
85 if (colsLen.empty()) {
86 colsLen.push_back(idealSize.Width());
87 }
88 columnsGap_ = cols.second;
89
90 if (static_cast<uint32_t>(mainCount_) != rowsLen.size()) {
91 mainCount_ = static_cast<int32_t>(rowsLen.size());
92 }
93 if (static_cast<uint32_t>(crossCount_) != colsLen.size()) {
94 crossCount_ = static_cast<int32_t>(colsLen.size());
95 gridLayoutInfo_.crossCount_ = crossCount_;
96 }
97
98 gridCells_.clear();
99 int32_t row = 0;
100 for (const auto& height : rowsLen) {
101 int32_t col = 0;
102 for (const auto& width : colsLen) {
103 gridCells_[row][col] = SizeF(width, height);
104 ++col;
105 }
106 ++row;
107 }
108 }
109
CheckGridPlaced(int32_t index,int32_t row,int32_t col,int32_t & rowSpan,int32_t & colSpan)110 bool GridLayoutAlgorithm::CheckGridPlaced(int32_t index, int32_t row, int32_t col, int32_t& rowSpan, int32_t& colSpan)
111 {
112 auto rowIter = gridLayoutInfo_.gridMatrix_.find(row);
113 if (rowIter != gridLayoutInfo_.gridMatrix_.end()) {
114 auto colIter = rowIter->second.find(col);
115 if (colIter != rowIter->second.end()) {
116 return false;
117 }
118 }
119 rowSpan = std::min(mainCount_ - row, rowSpan);
120 colSpan = std::min(crossCount_ - col, colSpan);
121 int32_t rSpan = 0;
122 int32_t cSpan = 0;
123 int32_t retColSpan = 1;
124 while (rSpan < rowSpan) {
125 rowIter = gridLayoutInfo_.gridMatrix_.find(rSpan + row);
126 if (rowIter != gridLayoutInfo_.gridMatrix_.end()) {
127 cSpan = 0;
128 while (cSpan < colSpan) {
129 if (rowIter->second.find(cSpan + col) != rowIter->second.end()) {
130 colSpan = cSpan;
131 break;
132 }
133 ++cSpan;
134 }
135 } else {
136 cSpan = colSpan;
137 }
138 if (retColSpan > cSpan) {
139 break;
140 }
141 retColSpan = cSpan;
142 ++rSpan;
143 }
144
145 rowSpan = rSpan;
146 colSpan = retColSpan;
147 for (int32_t i = row; i < row + rowSpan; ++i) {
148 std::map<int32_t, int32_t> rowMap;
149 auto iter = gridLayoutInfo_.gridMatrix_.find(i);
150 if (iter != gridLayoutInfo_.gridMatrix_.end()) {
151 rowMap = iter->second;
152 }
153 for (int32_t j = col; j < col + colSpan; ++j) {
154 rowMap.emplace(std::make_pair(j, index));
155 }
156 gridLayoutInfo_.gridMatrix_[i] = rowMap;
157 }
158 return true;
159 }
160
GetNextGrid(int32_t & curRow,int32_t & curCol) const161 void GridLayoutAlgorithm::GetNextGrid(int32_t& curRow, int32_t& curCol) const
162 {
163 if (isVertical_) {
164 ++curCol;
165 if (curCol >= crossCount_) {
166 curCol = 0;
167 ++curRow;
168 }
169 } else {
170 ++curRow;
171 if (curRow >= mainCount_) {
172 curRow = 0;
173 ++curCol;
174 }
175 }
176 }
177
ComputeItemPosition(LayoutWrapper * layoutWrapper,int32_t row,int32_t col,int32_t & rowSpan,int32_t & colSpan,const RefPtr<LayoutWrapper> & childLayoutWrapper) const178 OffsetF GridLayoutAlgorithm::ComputeItemPosition(LayoutWrapper* layoutWrapper, int32_t row, int32_t col,
179 int32_t& rowSpan, int32_t& colSpan, const RefPtr<LayoutWrapper>& childLayoutWrapper) const
180 {
181 auto layoutProperty = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
182 CHECK_NULL_RETURN(layoutProperty, OffsetF());
183 auto frameSize = layoutWrapper->GetGeometryNode()->GetFrameSize();
184 MinusPaddingToSize(layoutProperty->CreatePaddingAndBorder(), frameSize);
185
186 // Calculate the position for current child.
187 float positionX = 0.0f;
188 float positionY = 0.0f;
189 for (int32_t i = 0; i < row; ++i) {
190 positionY += GetItemSize(i, 0, true);
191 }
192 positionY += row * rowsGap_;
193 for (int32_t i = 0; i < col; ++i) {
194 positionX += GetItemSize(0, i, false);
195 }
196 positionX += col * columnsGap_;
197
198 // Calculate the size for current child.
199 float rowLen = 0.0f;
200 float colLen = 0.0f;
201 for (int32_t i = 0; i < rowSpan; ++i) {
202 rowLen += GetItemSize(row + i, col, true);
203 }
204 rowLen += (rowSpan - 1) * rowsGap_;
205 for (int32_t i = 0; i < colSpan; ++i) {
206 colLen += GetItemSize(row, col + i, false);
207 }
208 colLen += (colSpan - 1) * columnsGap_;
209
210 if (childLayoutWrapper) {
211 auto childSize = childLayoutWrapper->GetGeometryNode()->GetMarginFrameSize();
212 OffsetByAlign(childSize, rowLen, colLen, positionX, positionY);
213 }
214
215 // If RTL, place the item from right.
216 if (rightToLeft_) {
217 positionX = frameSize.Width() - positionX - colLen;
218 }
219 return OffsetF(positionX, positionY);
220 }
221
GetItemSize(int32_t row,int32_t col,bool height) const222 float GridLayoutAlgorithm::GetItemSize(int32_t row, int32_t col, bool height) const
223 {
224 auto nextC = gridCells_.find(row);
225 if (nextC != gridCells_.end()) {
226 auto nextCol = nextC->second;
227 auto nextColRow = nextCol.find(col);
228 if (nextColRow != nextCol.end()) {
229 return height ? nextColRow->second.Height() : nextColRow->second.Width();
230 }
231 }
232 return 0.0;
233 }
234
GetItemRect(const RefPtr<GridLayoutProperty> & gridLayoutProperty,const RefPtr<GridItemLayoutProperty> & childLayoutProperty,int32_t index) const235 GridItemRect GridLayoutAlgorithm::GetItemRect(const RefPtr<GridLayoutProperty>& gridLayoutProperty,
236 const RefPtr<GridItemLayoutProperty>& childLayoutProperty, int32_t index) const
237 {
238 GridItemRect rect;
239 if (gridLayoutProperty->GetLayoutOptions().has_value()) {
240 const auto& options = *gridLayoutProperty->GetLayoutOptions();
241 if (options.getRectByIndex) {
242 rect = options.getRectByIndex(index);
243 rect.rowSpan = std::max(rect.rowSpan, 1);
244 rect.columnSpan = std::max(rect.columnSpan, 1);
245 }
246 } else {
247 if (childLayoutProperty) {
248 rect.rowStart = childLayoutProperty->GetRowStart().value_or(-1);
249 rect.columnStart = childLayoutProperty->GetColumnStart().value_or(-1);
250 rect.rowSpan = std::max(childLayoutProperty->GetRowEnd().value_or(-1) - rect.rowStart + 1, 1);
251 rect.columnSpan = std::max(childLayoutProperty->GetColumnEnd().value_or(-1) - rect.columnStart + 1, 1);
252 }
253 }
254 return rect;
255 }
256
Measure(LayoutWrapper * layoutWrapper)257 void GridLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
258 {
259 auto gridLayoutProperty = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
260 CHECK_NULL_VOID(gridLayoutProperty);
261 Axis axis = gridLayoutInfo_.axis_;
262 auto idealSize =
263 CreateIdealSize(gridLayoutProperty->GetLayoutConstraint().value(), axis, MeasureType::MATCH_PARENT, true);
264 if (GreatOrEqual(GetMainAxisSize(idealSize, axis), Infinity<float>())) {
265 idealSize = gridLayoutProperty->GetLayoutConstraint().value().percentReference;
266 TAG_LOGI(AceLogTag::ACE_GRID, "size of main axis value is infinity, use percent reference");
267 }
268 rightToLeft_ = layoutWrapper->GetLayoutProperty()->GetNonAutoLayoutDirection() == TextDirection::RTL;
269
270 layoutWrapper->GetGeometryNode()->SetFrameSize(idealSize);
271 MinusPaddingToSize(gridLayoutProperty->CreatePaddingAndBorder(), idealSize);
272 InitGridCeils(layoutWrapper, idealSize);
273
274 int32_t rowIndex = 0;
275 int32_t colIndex = 0;
276 int32_t itemIndex = 0;
277 itemsPosition_.clear();
278 gridLayoutInfo_.gridMatrix_.clear();
279 gridLayoutInfo_.startIndex_ = 0;
280 gridLayoutInfo_.hasBigItem_ = false;
281 for (int32_t index = 0; index < mainCount_ * crossCount_; ++index) {
282 auto childLayoutWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
283 if (!childLayoutWrapper) {
284 break;
285 }
286 auto layoutProperty = childLayoutWrapper->GetLayoutProperty();
287 if (!layoutProperty) {
288 break;
289 }
290
291 auto childLayoutProperty = DynamicCast<GridItemLayoutProperty>(layoutProperty);
292 auto rect = GetItemRect(gridLayoutProperty, childLayoutProperty, index);
293 int32_t itemRowStart = rect.rowStart;
294 int32_t itemColStart = rect.columnStart;
295 int32_t itemRowSpan = rect.rowSpan;
296 int32_t itemColSpan = rect.columnSpan;
297 if (itemRowSpan > 1 || itemColSpan > 1) {
298 gridLayoutInfo_.hasBigItem_ = true;
299 }
300
301 if (itemRowStart >= 0 && itemRowStart < mainCount_ && itemColStart >= 0 && itemColStart < crossCount_ &&
302 CheckGridPlaced(itemIndex, itemRowStart, itemColStart, itemRowSpan, itemColSpan)) {
303 childLayoutWrapper->Measure(CreateChildConstraint(idealSize, gridLayoutProperty, itemRowStart, itemColStart,
304 itemRowSpan, itemColSpan, childLayoutProperty));
305 itemsPosition_.try_emplace(index, ComputeItemPosition(layoutWrapper, itemRowStart, itemColStart,
306 itemRowSpan, itemColSpan, childLayoutWrapper));
307 } else {
308 while (!CheckGridPlaced(itemIndex, rowIndex, colIndex, itemRowSpan, itemColSpan)) {
309 GetNextGrid(rowIndex, colIndex);
310 if (rowIndex >= mainCount_ || colIndex >= crossCount_) {
311 break;
312 }
313 }
314 if (rowIndex >= mainCount_ || colIndex >= crossCount_) {
315 continue;
316 }
317 childLayoutWrapper->Measure(CreateChildConstraint(
318 idealSize, gridLayoutProperty, rowIndex, colIndex, itemRowSpan, itemColSpan, childLayoutProperty));
319 itemsPosition_.try_emplace(index,
320 ComputeItemPosition(layoutWrapper, rowIndex, colIndex, itemRowSpan, itemColSpan, childLayoutWrapper));
321
322 PrintConflictingPositionLog(itemIndex, rect, rowIndex, colIndex, itemRowSpan, itemColSpan);
323 }
324
325 if (childLayoutProperty) {
326 childLayoutProperty->UpdateRealRowSpan(itemRowSpan);
327 childLayoutProperty->UpdateRealColumnSpan(itemColSpan);
328 }
329
330 ++itemIndex;
331 }
332 gridLayoutInfo_.endIndex_ = itemIndex - 1;
333 gridLayoutInfo_.startMainLineIndex_ = 0;
334 gridLayoutInfo_.endMainLineIndex_ = static_cast<int32_t>(gridLayoutInfo_.gridMatrix_.size()) - 1 ;
335 }
336
Layout(LayoutWrapper * layoutWrapper)337 void GridLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
338 {
339 CHECK_NULL_VOID(layoutWrapper);
340 auto layoutProperty = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
341 CHECK_NULL_VOID(layoutProperty);
342 auto frameSize = layoutWrapper->GetGeometryNode()->GetFrameSize();
343 auto padding = layoutProperty->CreatePaddingAndBorder();
344 MinusPaddingToSize(padding, frameSize);
345 layoutWrapper->RemoveAllChildInRenderTree();
346 for (int32_t index = 0; index < mainCount_ * crossCount_; ++index) {
347 OffsetF childOffset;
348 auto childPosition = itemsPosition_.find(index);
349 if (childPosition != itemsPosition_.end()) {
350 auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
351 if (!childWrapper) {
352 break;
353 }
354 childOffset = childPosition->second;
355 childWrapper->GetGeometryNode()->SetMarginFrameOffset(padding.Offset() + childOffset);
356 childWrapper->Layout();
357 }
358 }
359
360 for (const auto& mainLine : gridLayoutInfo_.gridMatrix_) {
361 int32_t itemIndex = -1;
362 for (const auto& crossLine : mainLine.second) {
363 // If item index is the same, must be the same GridItem, needn't layout again.
364 if (itemIndex == crossLine.second) {
365 continue;
366 }
367 itemIndex = crossLine.second;
368 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(itemIndex);
369 if (!wrapper) {
370 break;
371 }
372 auto layoutProperty = wrapper->GetLayoutProperty();
373 CHECK_NULL_VOID(layoutProperty);
374 auto gridItemLayoutProperty = AceType::DynamicCast<GridItemLayoutProperty>(layoutProperty);
375 CHECK_NULL_VOID(gridItemLayoutProperty);
376 gridItemLayoutProperty->UpdateMainIndex(mainLine.first);
377 gridItemLayoutProperty->UpdateCrossIndex(crossLine.first);
378 UpdateRealGridItemPositionInfo(wrapper, mainLine.first, crossLine.first);
379 }
380 }
381 }
382
PrintConflictingPositionLog(int32_t itemIndex,GridItemRect rect,int32_t rowIndex,int32_t colIndex,int32_t rowSpan,int32_t colSpan)383 void GridLayoutAlgorithm::PrintConflictingPositionLog(
384 int32_t itemIndex, GridItemRect rect, int32_t rowIndex, int32_t colIndex, int32_t rowSpan, int32_t colSpan)
385 {
386 if (!(rect.rowStart == -1 && rect.columnStart == -1 && rect.rowSpan == 1 && rect.columnSpan == 1)) {
387 TAG_LOGI(AceLogTag::ACE_GRID,
388 "item index:%{public}d has been set to a conflicting position row:%{public}d, col:%{public}d, "
389 "rowSpan:%{public}d, colSpan:%{public}d. reset to [%{public}d, %{public}d, %{public}d, %{public}d]",
390 itemIndex, rect.rowStart, rect.columnStart, rect.rowSpan, rect.columnSpan, rowIndex, colIndex, rowSpan,
391 colSpan);
392 }
393 }
394 } // namespace OHOS::Ace::NG
395