• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-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 #ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_GRID_GRID_LAYOUT_INFO_H
17 #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_GRID_GRID_LAYOUT_INFO_H
18 
19 #include <map>
20 #include <optional>
21 
22 #include "base/geometry/axis.h"
23 #include "base/geometry/ng/rect_t.h"
24 #include "core/components/scroll/scroll_controller_base.h"
25 #include "core/components_ng/pattern/grid/grid_layout_options.h"
26 #include "core/components_ng/property/layout_constraint.h"
27 
28 namespace OHOS::Ace::NG {
29 
30 struct GridPredictLayoutParam {
31     LayoutConstraintF layoutConstraint;
32     std::map<int32_t, float> itemsCrossSizes;
33     float crossGap = 0.0f;
34 };
35 
36 struct GridPreloadItem {
GridPreloadItemGridPreloadItem37     explicit GridPreloadItem(int32_t idx) : idx(idx) {}
GridPreloadItemGridPreloadItem38     GridPreloadItem(int32_t idx, bool buildOnly) : idx(idx), buildOnly(buildOnly) {}
39 
40     bool operator==(const GridPreloadItem& other) const
41     {
42         return idx == other.idx && buildOnly == other.buildOnly;
43     }
44 
45     int32_t idx = -1;
46     bool buildOnly = false; // true if item only needs to be created, not measure / layout
47 };
48 
49 /**
50  * @brief callback to build a grid item at [itemIdx] in an IdleTask
51  * @returns true if the built item needs Grid to perform a layout.
52  */
53 using BuildGridItemCallback = std::function<bool(const RefPtr<FrameNode>& host, int32_t itemIdx)>;
54 
55 constexpr int32_t EMPTY_JUMP_INDEX = -2;
56 constexpr int32_t JUMP_TO_BOTTOM_EDGE = -3;
57 constexpr float HALF = 0.5f;
58 
59 // Try not to add more variables in [GridLayoutInfo] because the more state variables, the more problematic and the
60 // harder it is to maintain
61 struct GridLayoutInfo {
62     /**
63      * @param prune
64      * @if true, try eliminate lines that are above viewport.
65      * @else trust startMainLineIndex_ to determine the viewport.
66      *
67      * @return height in view range.
68      */
69     float GetTotalHeightOfItemsInView(float mainGap, bool prune = false) const;
70 
71     using HeightMapIt = std::map<int32_t, float>::const_iterator;
72     /**
73      * @brief skip starting lines that are outside viewport in LayoutIrregular
74      *
75      * @return [iterator to the first line in view, offset of that first line]
76      */
77     std::pair<HeightMapIt, float> SkipLinesAboveView(float mainGap) const;
78 
UpdateStartIndexByStartLineGridLayoutInfo79     void UpdateStartIndexByStartLine()
80     {
81         auto startLine = gridMatrix_.find(startMainLineIndex_);
82         if (startLine == gridMatrix_.end()) {
83             return;
84         }
85         if (startLine->second.empty()) {
86             return;
87         }
88         startIndex_ = startLine->second.begin()->second;
89     }
90 
91     void UpdateStartIndexForExtralOffset(float mainGap, float mainSize);
92 
93     void UpdateEndLine(float mainSize, float mainGap);
94     // for overScroll at top
95     void UpdateEndIndex(float overScrollOffset, float mainSize, float mainGap);
96     bool IsOutOfStart() const;
97     /**
98      * @brief Determine out of boundary condition for overScroll
99      *
100      * @param irregular whether running irregular layout.
101      * @return true if the end of content is above viewport.
102      */
103     bool IsOutOfEnd(float mainGap, bool irregular) const;
104 
105     void SwapItems(int32_t itemIndex, int32_t insertIndex);
106     int32_t GetOriginalIndex() const;
107     void ClearDragState();
108 
GetAverageLineHeightGridLayoutInfo109     float GetAverageLineHeight()
110     {
111         float totalHeight = 0;
112         int32_t totalRow = 0;
113         for (const auto& record : lineHeightMap_) {
114             if (record.second > 0) {
115                 totalRow++;
116                 totalHeight += record.second;
117             }
118         }
119         return totalRow > 0 ? totalHeight / totalRow : 0;
120     }
121 
122     // should only be used when all children of Grid are in gridMatrix_
GetStartLineOffsetGridLayoutInfo123     float GetStartLineOffset(float mainGap) const
124     {
125         float totalHeight = 0;
126         for (auto iter = lineHeightMap_.begin(); iter != lineHeightMap_.end() && iter->first < startMainLineIndex_;
127              ++iter) {
128             totalHeight += (iter->second + mainGap);
129         }
130         return totalHeight - currentOffset_;
131     }
132 
133     bool IsAllItemsMeasured() const;
134 
135     float GetTotalLineHeight(float mainGap, bool removeLastGap = true) const
136     {
137         float totalHeight = 0.0f;
138         for (auto iter : lineHeightMap_) {
139             totalHeight += (iter.second + mainGap);
140         }
141         return (removeLastGap) ? totalHeight - mainGap : totalHeight;
142     }
143 
144     /**
145      * @brief set up jumpIndex_ and align_ to jump to the bottom edge of content.
146      */
147     void PrepareJumpToBottom();
148 
149     /**
150      * @brief optimized function (early exit) to compare total height to [other].
151      * @param other height to compare to.
152      * @return true if total height is less than [other].
153      */
154     bool HeightSumSmaller(float other, float mainGap) const;
155 
156     /**
157      * @return height sum of lines in range [startLine, endLine).
158      */
159     float GetHeightInRange(int32_t startLine, int32_t endLine, float mainGap) const;
160 
161     struct EndIndexInfo {
162         int32_t itemIdx = -1; /**< Index of the last item. */
163         int32_t y = -1;       /**< Main-axis position (line index) of the item. */
164         int32_t x = -1;       /**< Cross-axis position (column index) of the item. */
165     };
166     /**
167      * @brief Traverse the matrix backward to find the last item index, starting from Line [endLine].
168      *
169      * Intended to work on irregular layout.
170      * @param endLine index of the line to start traversing.
171      * @return last item index above endLine (inclusive) and the position it resides in.
172      */
173     EndIndexInfo FindEndIdx(int32_t endLine) const;
174 
175     /**
176      * REQUIRES: Item is between startIndex_ and endIndex_. Otherwise, the result is undefined.
177      *
178      * @param line starting line of the item.
179      * @param mainGap The gap between lines.
180      * @return position of the item's top edge relative to the viewport.
181      */
GetItemTopPosGridLayoutInfo182     inline float GetItemTopPos(int32_t line, float mainGap) const
183     {
184         return currentOffset_ + GetHeightInRange(startMainLineIndex_, line, mainGap);
185     }
186 
187     /**
188      * REQUIRES: Item is between startIndex_ and endIndex_. Otherwise, the result is undefined.
189      *
190      * @param line starting line of the item.
191      * @param itemHeight The number of rows the item occupies.
192      * @param mainGap The gap between items in the main axis.
193      * @return position of the item's bottom edge relative to the viewport.
194      */
GetItemBottomPosGridLayoutInfo195     inline float GetItemBottomPos(int32_t line, int32_t itemHeight, float mainGap) const
196     {
197         return currentOffset_ + GetHeightInRange(startMainLineIndex_, line + itemHeight, mainGap) - mainGap;
198     }
199 
200     /**
201      * @brief Perform a binary search to find item with [index] in the gridMatrix_.
202      *
203      * @param index target item index
204      * @return iterator to that item, or map::end if not found.
205      */
206     std::map<int32_t, std::map<int32_t, int32_t>>::const_iterator FindInMatrix(int32_t index) const;
207 
208     /**
209      * @param itemIdx
210      * @return position [col, row] of the item. [-1, -1] if item is not in matrix.
211      */
212     std::pair<int32_t, int32_t> GetItemPos(int32_t itemIdx) const;
213 
214     /**
215      * @brief Tries to find the item between startMainLine and endMainLine.
216      *
217      * @param target The target item to find.
218      * @return The line index and column index of the found item.
219      */
220     std::pair<int32_t, int32_t> FindItemInRange(int32_t target) const;
221 
222     /**
223      * @brief Find the offset and line index of an item's center point.
224      *
225      * @param startLine starting line index of this item.
226      * @param lineCnt number of rows the item occupies.
227      * @return [lineIdx, offset relative to this line] of the center point.
228      */
229     std::pair<int32_t, float> FindItemCenter(int32_t startLine, int32_t lineCnt, float mainGap) const;
230 
231     /**
232      * @brief clears lineHeightMap_ and gridMatrix_ starting from line [idx]
233      *
234      * @param idx starting line index
235      */
236     void ClearMapsToEnd(int32_t idx);
237 
238     /**
239      * @brief clears lineHeightMap_ and gridMatrix_ in range [0, idx)
240      *
241      * @param idx ending line index, exclusive.
242      */
243     void ClearMapsFromStart(int32_t idx);
244 
245     /**
246      * @brief clears lineHeightMap_ starting from line [idx]
247      *
248      * @param idx starting line index
249      */
250     void ClearHeightsToEnd(int32_t idx);
251 
252     /**
253      * @brief clear gridMatrix_ in range [idx, end)
254      *
255      * REQUIRES: idx and lineIdx have to match each other.
256      * @param idx item index to start clearing from.
257      * @param lineIdx already-found line index of the target item.
258      */
259     void ClearMatrixToEnd(int32_t idx, int32_t lineIdx);
260 
ResetPositionFlagsGridLayoutInfo261     void ResetPositionFlags()
262     {
263         reachEnd_ = false;
264         reachStart_ = false;
265         offsetEnd_ = false;
266     }
267 
IsResettedGridLayoutInfo268     bool IsResetted() const
269     {
270         return startIndex_ != 0 && gridMatrix_.empty();
271     }
272 
SetScrollAlignGridLayoutInfo273     void SetScrollAlign(ScrollAlign align)
274     {
275         scrollAlign_ = align;
276     }
277 
278     float GetContentOffset(float mainGap) const;
279     /**
280      * @brief Get the total height of grid content. Use estimation when lineHeights are not available. Can handle
281      * bigItems.
282      *
283      * @param mainGap
284      * @return total height
285      */
286     float GetContentHeight(float mainGap) const;
287     float GetContentOffset(const GridLayoutOptions& options, float mainGap) const;
288 
289     /**
290      * @brief Get the content height of Grid in range [0, endIdx).
291      *
292      * IF: Irregular items always take up the whole line (no getSizeByIdx callback).
293      * THEN: Assumes that all irregular lines have the same height, and all other regular lines have the same height.
294      * ELSE: Call a more versatile algorithm.
295      * REQUIRES:
296      * 1. all irregular lines must have the same height.
297      * 2. all regular items must have the same height.
298      *
299      * @param options contains irregular item.
300      * @param endIdx ending item index (exclusive).
301      * @param mainGap gap between lines.
302      * @return total height of the content.
303      */
304     float GetContentHeight(const GridLayoutOptions& options, int32_t endIdx, float mainGap) const;
305     void SkipStartIndexByOffset(const GridLayoutOptions& options, float mainGap);
306     float GetCurrentLineHeight() const;
307 
308     /**
309      * @brief Get Content Offset when using irregular layout.
310      */
311     float GetIrregularOffset(float mainGap) const;
312     /**
313      * @brief Get total content height when using irregular layout.
314      */
315     float GetIrregularHeight(float mainGap) const;
316 
317     bool GetLineIndexByIndex(int32_t targetIndex, int32_t& targetLineIndex) const;
318     float GetTotalHeightFromZeroIndex(int32_t targetLineIndex, float mainGap) const;
319 
320     /**
321      * @brief Calculate the distance from content's end to the viewport bottom.
322      *
323      * REQUIRES: currentOffset_ is valid from last layout.
324      * @param mainSize of the viewport.
325      * @param heightInView total height of items inside the viewport.
326      * @param mainGap gap between lines along the main axis.
327      * @return Positive when content's end is below viewport. Return [mainSize] if last line is not in viewport.
328      */
329     float GetDistanceToBottom(float mainSize, float heightInView, float mainGap) const;
330 
331     /**
332      * @brief Transforms scrollAlign_ into other ScrollAlign values, based on current position of
333      * target item.
334      *
335      * @param height number of rows the item occupies.
336      * @param mainSize The main-axis length of the grid.
337      * @return ScrollAlign value transformed from AUTO.
338      */
339     ScrollAlign TransformAutoScrollAlign(int32_t itemIdx, int32_t height, float mainSize, float mainGap) const;
340 
341     /**
342      * @param targetIdx target item's index.
343      * @param height number of rows the item occupies.
344      * @return item position to scroll to through animation.
345      */
346     float GetAnimatePosIrregular(int32_t targetIdx, int32_t height, ScrollAlign align, float mainGap) const;
347 
348     bool GetGridItemAnimatePos(const GridLayoutInfo& currentGridLayoutInfo, int32_t targetIndex, ScrollAlign align,
349         float mainGap, float& targetPos);
350 
351     using MatIter = std::map<int32_t, std::map<int32_t, int32_t>>::const_iterator;
352     MatIter FindStartLineInMatrix(MatIter iter, int32_t index) const;
353     void ClearHeightsFromMatrix(int32_t lineIdx);
354 
355     void UpdateDefaultCachedCount();
356 
357     int32_t FindInMatrixByMainIndexAndCrossIndex(int32_t mainIndex, int32_t crossIndex) const;
358 
359     // Only used for debugging.
360     void PrintMatrix();
361     void PrintLineHeight();
362 
363     bool CheckGridMatrix(int32_t cachedCount);
364 
GetChildrenCountGridLayoutInfo365     int32_t GetChildrenCount() const
366     {
367         return firstRepeatCount_ > 0 ? firstRepeatCount_ : childrenCount_;
368     }
369 
370     std::string ToString() const;
371 
372     Axis axis_ = Axis::VERTICAL;
373 
374     float currentOffset_ = 0.0f; // offset on the current top GridItem on [startMainLineIndex_]
375     float prevOffset_ = 0.0f;
376     float currentHeight_ = 0.0f; // height from first item to current top GridItem on [startMainLineIndex_]
377     float prevHeight_ = 0.0f;
378     float lastMainSize_ = 0.0f;
379     float lastCrossSize_ = 0.0f;
380     float totalHeightOfItemsInView_ = 0.0f;
381     float avgLineHeight_ = 0.0f;
382 
383     // additional padding to accommodate navigation bar when SafeArea is expanded
384     float contentEndPadding_ = 0.0f;
385 
386     std::optional<int32_t> lastCrossCount_;
387     // index of first and last GridItem in viewport
388     int32_t startIndex_ = 0;
389     int32_t endIndex_ = -1;
390 
391     // index of first row and last row in viewport (assuming it's a vertical Grid)
392     int32_t startMainLineIndex_ = 0;
393     int32_t endMainLineIndex_ = 0;
394 
395     int32_t jumpIndex_ = EMPTY_JUMP_INDEX;
396     int32_t jumpForRecompose_ = EMPTY_JUMP_INDEX; // new mark index to notify frontend recomposition
397     std::optional<float> extraOffset_;
398     int32_t crossCount_ = 0;
399     int32_t childrenCount_ = 0;
400     int32_t firstRepeatCount_ = 0;
401     int32_t repeatDifference_ = 0;
402     ScrollAlign scrollAlign_ = ScrollAlign::AUTO;
403 
404     // Map structure: [mainIndex, [crossIndex, index]],
405     // when vertical, mainIndex is rowIndex and crossIndex is columnIndex.
406     std::map<int32_t, std::map<int32_t, int32_t>> gridMatrix_;
407     // in vertical grid, this map is like: [rowIndex: rowHeight]
408     std::map<int32_t, float> lineHeightMap_;
409 
410     // Map structure: [index, last cell]
411     std::map<int32_t, int32_t> irregularItemsPosition_;
412 
413     // rect of grid item dragged in
414     RectF currentRect_;
415 
416     bool reachEnd_ = false; // true if last GridItem appears in the viewPort
417     bool reachStart_ = false;
418 
419     bool offsetEnd_ = false; // true if content bottom is truly reached
420 
421     // Grid has GridItem whose columnEnd - columnStart > 0
422     bool hasBigItem_;
423 
424     // Grid has GridItem whose rowEnd - rowStart > 0
425     bool hasMultiLineItem_;
426     // false when offset is updated but layout hasn't happened, so data is out of sync
427     bool synced_ = false;
428 
429     std::optional<int32_t> targetIndex_;
430 
431     std::map<int32_t, bool> irregularLines_;
432 
433     bool clearStretch_ = false;
434 
435     // default cached count
436     int32_t defCachedCount_ = 1;
437 
438     int32_t times_ = 0;
439 
440 private:
441     float GetCurrentOffsetOfRegularGrid(float mainGap) const;
442     float GetContentHeightOfRegularGrid(float mainGap) const;
443     int32_t GetItemIndexByPosition(int32_t position);
444     int32_t GetPositionByItemIndex(int32_t itemIndex);
445     void MoveItemsBack(int32_t from, int32_t to, int32_t itemIndex);
446     void MoveItemsForward(int32_t from, int32_t to, int32_t itemIndex);
447     void GetLineHeights(
448         const GridLayoutOptions& options, float mainGap, float& regularHeight, float& irregularHeight) const;
449 
450     /**
451      * @brief Find the number of GridItems in range [startLine, endLine].
452      *
453      * REQUIRES: gridMatrix_ is valid in range [startLine, endLine].
454      * @return number of GridItems
455      */
456     int32_t FindItemCount(int32_t startLine, int32_t endLine) const;
457 
458     int32_t currentMovingItemPosition_ = -1;
459     std::map<int32_t, int32_t> positionItemIndexMap_;
460     float lastIrregularMainSize_ = 0.0f; // maybe no irregular item in current gridMatrix_
461     float lastRegularMainSize_ = 0.0f;
462 };
463 
464 } // namespace OHOS::Ace::NG
465 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_GRID_GRID_LAYOUT_ALGORITHM_H
466