• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023-2025 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_filler.h"
17 
18 #include "core/components_ng/pattern/grid/grid_item_pattern.h"
19 #include "core/components_ng/pattern/grid/irregular/grid_layout_utils.h"
20 
21 namespace OHOS::Ace::NG {
GridIrregularFiller(GridLayoutInfo * info,LayoutWrapper * wrapper)22 GridIrregularFiller::GridIrregularFiller(GridLayoutInfo* info, LayoutWrapper* wrapper) : info_(info), wrapper_(wrapper)
23 {}
24 
InitPos(int32_t lineIdx)25 int32_t GridIrregularFiller::InitPos(int32_t lineIdx)
26 {
27     posX_ = -1;
28     posY_ = lineIdx;
29     return info_->FindEndIdx(lineIdx - 1).itemIdx;
30 }
31 
32 using Result = GridIrregularFiller::FillResult;
Fill(const FillParameters & params,float targetLen,int32_t startingLine)33 Result GridIrregularFiller::Fill(const FillParameters& params, float targetLen, int32_t startingLine)
34 {
35     startingLine = std::max(0, startingLine);
36     int32_t idx = InitPos(startingLine);
37     // no gap on first row
38     float len = -params.mainGap;
39     auto childrenCount = info_->GetChildrenCount();
40     while (idx < (childrenCount - 1)) {
41         int32_t prevRow = posY_;
42         if (!FindNextItem(++idx)) {
43             FillOne(idx);
44         }
45         // (posY_ > prevRow) implies that the previous row has been filled
46 
47         int32_t row = prevRow;
48         if (UpdateLength(len, targetLen, row, posY_, params.mainGap)) {
49             return { len, row, idx - 1 };
50         }
51 
52         MeasureItem(params, idx, posX_, posY_, false);
53     }
54 
55     if (info_->lineHeightMap_.empty()) {
56         return {};
57     }
58     // last child reached
59     int32_t lastRow = info_->lineHeightMap_.rbegin()->first;
60     if (UpdateLength(len, targetLen, posY_, lastRow + 1, params.mainGap)) {
61         return { len, posY_, idx };
62     }
63     return { len, lastRow, idx };
64 }
65 
FillToTarget(const FillParameters & params,int32_t targetIdx,int32_t startingLine)66 void GridIrregularFiller::FillToTarget(const FillParameters& params, int32_t targetIdx, int32_t startingLine)
67 {
68     if (startingLine < 0) {
69         startingLine = 0;
70     }
71     if (targetIdx >= info_->GetChildrenCount()) {
72         targetIdx = info_->GetChildrenCount() - 1;
73     }
74     int32_t idx = InitPos(startingLine);
75     while (idx < targetIdx) {
76         if (!FindNextItem(++idx)) {
77             FillOne(idx);
78         }
79         MeasureItem(params, idx, posX_, posY_, false);
80     }
81 }
82 
FitItem(const decltype(GridLayoutInfo::gridMatrix_) ::iterator & it,int32_t itemWidth)83 int32_t GridIrregularFiller::FitItem(const decltype(GridLayoutInfo::gridMatrix_)::iterator& it, int32_t itemWidth)
84 {
85     if (it == info_->gridMatrix_.end()) {
86         // empty row, can fit
87         return 0;
88     }
89 
90     if (static_cast<int32_t>(it->second.size()) + itemWidth > info_->crossCount_) {
91         // not enough space
92         return -1;
93     }
94 
95     for (int i = 0; i <= info_->crossCount_ - itemWidth; ++i) {
96         bool flag = true;
97         for (int j = 0; j < itemWidth; ++j) {
98             if (it->second.find(i + j) != it->second.end()) {
99                 // spot already filled
100                 flag = false;
101                 break;
102             }
103         }
104 
105         if (flag) {
106             return i;
107         }
108     }
109     return -1;
110 }
111 
FillOne(const int32_t idx)112 void GridIrregularFiller::FillOne(const int32_t idx)
113 {
114     int32_t row = posY_;
115 
116     auto size = GridLayoutUtils::GetItemSize(info_, wrapper_, idx);
117 
118     auto it = info_->gridMatrix_.find(row);
119     int32_t col = FitItem(it, size.columns);
120     while (col == -1) {
121         // can't fill at end, find the next available line
122         it = info_->gridMatrix_.find(++row);
123         col = FitItem(it, size.columns);
124     }
125 
126     if (it == info_->gridMatrix_.end()) {
127         // create a new line
128         info_->gridMatrix_[row] = {};
129     }
130 
131     // top left square should be set to [idx], the rest to -[idx]
132     for (int32_t r = 0; r < size.rows; ++r) {
133         for (int32_t c = 0; c < size.columns; ++c) {
134             info_->gridMatrix_[row + r][col + c] = -idx;
135         }
136     }
137 
138     info_->gridMatrix_[row][col] = idx;
139 
140     posY_ = row;
141     posX_ = col;
142 }
143 
FindNextItem(int32_t target)144 bool GridIrregularFiller::FindNextItem(int32_t target)
145 {
146     const auto& mat = info_->gridMatrix_;
147     // start from first cross everytime, for the current target might be before the previous target
148     posX_ = -1;
149     while (AdvancePos()) {
150         if (mat.at(posY_).at(posX_) == target) {
151             return true;
152         }
153     }
154     // to handle empty tiles in the middle of matrix, check next row
155     auto nextRow = mat.find(posY_ + 1);
156     while (nextRow != mat.end()) {
157         for (const auto [col, item] : nextRow->second) {
158             if (item == target) {
159                 posY_ = nextRow->first;
160                 posX_ = col;
161                 return true;
162             }
163         }
164         ++nextRow;
165     }
166     return false;
167 }
168 
AdvancePos()169 bool GridIrregularFiller::AdvancePos()
170 {
171     ++posX_;
172     if (posX_ == info_->crossCount_) {
173         // go to next row
174         ++posY_;
175         posX_ = 0;
176     }
177 
178     const auto& mat = info_->gridMatrix_;
179     if (mat.find(posY_) == mat.end()) {
180         return false;
181     }
182 
183     const auto& row = mat.at(posY_);
184     return row.find(posX_) != row.end();
185 }
186 
UpdateLength(float & len,float targetLen,int32_t & row,int32_t rowBound,float mainGap) const187 bool GridIrregularFiller::UpdateLength(float& len, float targetLen, int32_t& row, int32_t rowBound, float mainGap) const
188 {
189     for (; row < rowBound; ++row) {
190         auto lineHeightIt = info_->lineHeightMap_.find(row);
191         if (lineHeightIt == info_->lineHeightMap_.end()) {
192             TAG_LOGW(AceLogTag::ACE_GRID, "line height at row %{public}d not prepared after forward measure", posY_);
193             continue;
194         }
195         len += lineHeightIt->second + mainGap;
196         if (GreatOrEqual(len, targetLen)) {
197             return true;
198         }
199     }
200     return false;
201 }
202 
MeasureItem(const FillParameters & params,int32_t itemIdx,int32_t col,int32_t row,bool isCache)203 std::pair<float, LayoutConstraintF> GridIrregularFiller::MeasureItem(
204     const FillParameters& params, int32_t itemIdx, int32_t col, int32_t row, bool isCache)
205 {
206     auto props = AceType::DynamicCast<GridLayoutProperty>(wrapper_->GetLayoutProperty());
207     auto constraint = props->CreateChildConstraint();
208     auto child = wrapper_->GetOrCreateChildByIndex(itemIdx, !isCache, isCache);
209     CHECK_NULL_RETURN(child, {});
210 
211     const auto itemSize = GridLayoutUtils::GetItemSize(info_, wrapper_, itemIdx);
212     float crossLen = 0.0f;
213     for (int32_t i = 0; i < itemSize.columns; ++i) {
214         crossLen += params.crossLens[i + col];
215     }
216     crossLen += params.crossGap * (itemSize.columns - 1);
217     constraint.percentReference.SetCrossSize(crossLen, info_->axis_);
218     if (info_->axis_ == Axis::VERTICAL) {
219         constraint.maxSize = SizeF { crossLen, Infinity<float>() };
220         constraint.parentIdealSize = OptionalSizeF(crossLen, std::nullopt);
221     } else {
222         constraint.maxSize = SizeF { Infinity<float>(), crossLen };
223         constraint.parentIdealSize = OptionalSizeF(std::nullopt, crossLen);
224     }
225 
226     if (isCache) {
227         child->SetActive();
228     }
229     child->Measure(constraint);
230     SetItemInfo(child, itemIdx, row, col, itemSize);
231 
232     float childHeight = child->GetGeometryNode()->GetMarginFrameSize().MainSize(info_->axis_);
233     // spread height to each row.
234     float heightPerRow = (childHeight - (params.mainGap * (itemSize.rows - 1))) / itemSize.rows;
235     for (int32_t i = 0; i < itemSize.rows; ++i) {
236         info_->lineHeightMap_[row + i] = std::max(info_->lineHeightMap_[row + i], heightPerRow);
237     }
238     return { childHeight, constraint };
239 }
240 
InitPosToLastItem(int32_t lineIdx)241 int32_t GridIrregularFiller::InitPosToLastItem(int32_t lineIdx)
242 {
243     auto res = info_->FindEndIdx(lineIdx);
244     posX_ = res.x;
245     posY_ = std::max(0, res.y);
246     return res.itemIdx;
247 }
248 
FillMatrixOnly(int32_t targetIdx)249 int32_t GridIrregularFiller::FillMatrixOnly(int32_t targetIdx)
250 {
251     if (targetIdx >= info_->GetChildrenCount()) {
252         targetIdx = info_->GetChildrenCount() - 1;
253     }
254     int32_t idx = InitPosToLastItem(static_cast<int32_t>(info_->gridMatrix_.size()) - 1);
255     while (idx < targetIdx) {
256         if (!FindNextItem(++idx)) {
257             FillOne(idx);
258         }
259     }
260     return posY_;
261 }
262 
FillMatrixByLine(int32_t startingLine,int32_t targetLine)263 int32_t GridIrregularFiller::FillMatrixByLine(int32_t startingLine, int32_t targetLine)
264 {
265     int32_t idx = InitPosToLastItem(startingLine);
266     auto childrenCount = info_->GetChildrenCount();
267     while (posY_ < targetLine && idx < (childrenCount - 1)) {
268         if (!FindNextItem(++idx)) {
269             FillOne(idx);
270         }
271     }
272     return idx;
273 }
274 
MeasureBackward(const FillParameters & params,float targetLen,int32_t startingLine)275 float GridIrregularFiller::MeasureBackward(const FillParameters& params, float targetLen, int32_t startingLine)
276 {
277     float len = 0.0f;
278     posY_ = startingLine;
279     std::unordered_set<int32_t> measured;
280 
281     for (; posY_ >= 0 && LessNotEqual(len, targetLen); --posY_) {
282         BackwardImpl(measured, params);
283         auto lineHeightIt = info_->lineHeightMap_.find(posY_);
284         if (lineHeightIt == info_->lineHeightMap_.end()) {
285             TAG_LOGW(AceLogTag::ACE_GRID, "line height at row %{public}d not prepared after backward measure", posY_);
286             continue;
287         }
288         len += params.mainGap + lineHeightIt->second;
289     }
290     return len;
291 }
292 
MeasureBackwardToTarget(const FillParameters & params,int32_t targetLine,int32_t startingLine)293 void GridIrregularFiller::MeasureBackwardToTarget(
294     const FillParameters& params, int32_t targetLine, int32_t startingLine)
295 {
296     if (targetLine < 0) {
297         return;
298     }
299     posY_ = startingLine;
300 
301     std::unordered_set<int32_t> measured;
302     for (; posY_ >= targetLine; --posY_) {
303         BackwardImpl(measured, params);
304     }
305 }
306 
MeasureLineWithIrregulars(const FillParameters & params,const int32_t line)307 void GridIrregularFiller::MeasureLineWithIrregulars(const FillParameters& params, const int32_t line)
308 {
309     if (line == 0) {
310         return;
311     }
312     const auto it = info_->gridMatrix_.find(line);
313     if (it == info_->gridMatrix_.end()) {
314         return;
315     }
316     std::unordered_set<int32_t> visited;
317     int32_t topRow = line;
318     for (const auto& [c, itemIdx] : it->second) {
319         if (itemIdx == 0) {
320             topRow = 0;
321             break;
322         }
323         if (itemIdx < 0 && !visited.count(std::abs(itemIdx))) {
324             topRow = std::min(FindItemTopRow(it->first, c), topRow);
325         }
326         visited.insert(std::abs(itemIdx));
327     }
328     if (topRow < line) {
329         MeasureBackwardToTarget(params, topRow, line);
330     }
331 }
332 
BackwardImpl(std::unordered_set<int32_t> & measured,const FillParameters & params)333 void GridIrregularFiller::BackwardImpl(std::unordered_set<int32_t>& measured, const FillParameters& params)
334 {
335     auto it = info_->gridMatrix_.find(posY_);
336     if (it == info_->gridMatrix_.end()) {
337         TAG_LOGW(AceLogTag::ACE_GRID, "positionY %{public}d not found in matrix in backward measure.", posY_);
338         return;
339     }
340     const auto& row = it->second;
341     for (const auto& colIt : row) {
342         const int32_t& c = colIt.first;
343         const int32_t itemIdx = std::abs(colIt.second);
344         if (measured.count(itemIdx)) {
345             continue;
346         }
347 
348         const int32_t topRow = FindItemTopRow(posY_, c);
349         MeasureItem(params, itemIdx, c, topRow, false);
350         // measure irregular items only once from the bottom-left tile
351         measured.insert(itemIdx);
352     }
353 }
354 
FindItemTopRow(int32_t row,int32_t col) const355 int32_t GridIrregularFiller::FindItemTopRow(int32_t row, int32_t col) const
356 {
357     if (info_->gridMatrix_.at(row).at(col) == 0) {
358         return 0;
359     }
360 
361     while (info_->gridMatrix_.at(row).at(col) < 0) {
362         --row;
363     }
364     return row;
365 }
366 
SetItemInfo(const RefPtr<LayoutWrapper> & item,int32_t idx,int32_t row,int32_t col,GridItemSize size)367 void GridIrregularFiller::SetItemInfo(
368     const RefPtr<LayoutWrapper>& item, int32_t idx, int32_t row, int32_t col, GridItemSize size)
369 {
370     CHECK_NULL_VOID(item);
371     if (info_->axis_ == Axis::HORIZONTAL) {
372         std::swap(row, col);
373         std::swap(size.rows, size.columns);
374     }
375     auto pattern = item->GetHostNode()->GetPattern<GridItemPattern>();
376     CHECK_NULL_VOID(pattern);
377     auto props = pattern->GetLayoutProperty<GridItemLayoutProperty>();
378     CHECK_NULL_VOID(props);
379     props->UpdateIndex(idx);
380     props->UpdateMainIndex(row);
381     props->UpdateCrossIndex(col);
382 
383     if (size.rows == 1 && size.columns == 1) {
384         pattern->ResetGridItemInfo();
385         return;
386     }
387     pattern->SetIrregularItemInfo({ .mainIndex = row,
388         .crossIndex = col,
389         .mainSpan = size.rows,
390         .crossSpan = size.columns,
391         .mainStart = row,
392         .mainEnd = row + size.rows - 1,
393         .crossStart = col,
394         .crossEnd = col + size.columns - 1 });
395 }
396 } // namespace OHOS::Ace::NG
397