• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-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 #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     float GetTotalLineHeight(float mainGap, bool removeLastGap = true) const
134     {
135         float totalHeight = 0.0f;
136         for (auto iter : lineHeightMap_) {
137             totalHeight += (iter.second + mainGap);
138         }
139         return (removeLastGap) ? totalHeight - mainGap : totalHeight;
140     }
141 
142     /**
143      * @brief set up jumpIndex_ and align_ to jump to the bottom edge of content.
144      */
145     void PrepareJumpToBottom();
146 
147     /**
148      * @brief optimized function (early exit) to compare total height to [other].
149      * @param other height to compare to.
150      * @return true if total height is less than [other].
151      */
152     bool HeightSumSmaller(float other, float mainGap) const;
153 
154     /**
155      * @return height sum of lines in range [startLine, endLine).
156      */
157     float GetHeightInRange(int32_t startLine, int32_t endLine, float mainGap) const;
158 
159     struct EndIndexInfo {
160         int32_t itemIdx = -1; /**< Index of the last item. */
161         int32_t y = -1;       /**< Main-axis position (line index) of the item. */
162         int32_t x = -1;       /**< Cross-axis position (column index) of the item. */
163     };
164     /**
165      * @brief Traverse the matrix backward to find the last item index, starting from Line [endLine].
166      *
167      * Intended to work on irregular layout.
168      * @param endLine index of the line to start traversing.
169      * @return last item index above endLine (inclusive) and the position it resides in.
170      */
171     EndIndexInfo FindEndIdx(int32_t endLine) const;
172 
173     /**
174      * REQUIRES: Item is between startIndex_ and endIndex_. Otherwise, the result is undefined.
175      *
176      * @param line starting line of the item.
177      * @param mainGap The gap between lines.
178      * @return position of the item's top edge relative to the viewport.
179      */
GetItemTopPosGridLayoutInfo180     inline float GetItemTopPos(int32_t line, float mainGap) const
181     {
182         return currentOffset_ + GetHeightInRange(startMainLineIndex_, line, mainGap);
183     }
184 
185     /**
186      * REQUIRES: Item is between startIndex_ and endIndex_. Otherwise, the result is undefined.
187      *
188      * @param line starting line of the item.
189      * @param itemHeight The number of rows the item occupies.
190      * @param mainGap The gap between items in the main axis.
191      * @return position of the item's bottom edge relative to the viewport.
192      */
GetItemBottomPosGridLayoutInfo193     inline float GetItemBottomPos(int32_t line, int32_t itemHeight, float mainGap) const
194     {
195         return currentOffset_ + GetHeightInRange(startMainLineIndex_, line + itemHeight, mainGap) - mainGap;
196     }
197 
198     /**
199      * @brief Perform a binary search to find item with [index] in the gridMatrix_.
200      *
201      * @param index target item index
202      * @return iterator to that item, or map::end if not found.
203      */
204     std::map<int32_t, std::map<int32_t, int32_t>>::const_iterator FindInMatrix(int32_t index) const;
205 
206     /**
207      * @param itemIdx
208      * @return position [col, row] of the item. [-1, -1] if item is not in matrix.
209      */
210     std::pair<int32_t, int32_t> GetItemPos(int32_t itemIdx) const;
211 
212     /**
213      * @brief Tries to find the item between startMainLine and endMainLine.
214      *
215      * @param target The target item to find.
216      * @return The line index and column index of the found item.
217      */
218     std::pair<int32_t, int32_t> FindItemInRange(int32_t target) const;
219 
220     /**
221      * @brief Find the offset and line index of an item's center point.
222      *
223      * @param startLine starting line index of this item.
224      * @param lineCnt number of rows the item occupies.
225      * @return [lineIdx, offset relative to this line] of the center point.
226      */
227     std::pair<int32_t, float> FindItemCenter(int32_t startLine, int32_t lineCnt, float mainGap) const;
228 
229     /**
230      * @brief clears lineHeightMap_ and gridMatrix_ starting from line [idx]
231      *
232      * @param idx starting line index
233      */
234     void ClearMapsToEnd(int32_t idx);
235 
236     /**
237      * @brief clears lineHeightMap_ and gridMatrix_ in range [0, idx)
238      *
239      * @param idx ending line index, exclusive.
240      */
241     void ClearMapsFromStart(int32_t idx);
242 
243     /**
244      * @brief clears lineHeightMap_ starting from line [idx]
245      *
246      * @param idx starting line index
247      */
248     void ClearHeightsToEnd(int32_t idx);
249 
250     /**
251      * @brief clear gridMatrix_ in range [idx, end)
252      *
253      * REQUIRES: idx and lineIdx have to match each other.
254      * @param idx item index to start clearing from.
255      * @param lineIdx already-found line index of the target item.
256      */
257     void ClearMatrixToEnd(int32_t idx, int32_t lineIdx);
258 
ResetPositionFlagsGridLayoutInfo259     void ResetPositionFlags()
260     {
261         reachEnd_ = false;
262         reachStart_ = false;
263         offsetEnd_ = false;
264     }
265 
IsResettedGridLayoutInfo266     bool IsResetted() const
267     {
268         return startIndex_ != 0 && gridMatrix_.empty();
269     }
270 
SetScrollAlignGridLayoutInfo271     void SetScrollAlign(ScrollAlign align)
272     {
273         scrollAlign_ = align;
274     }
275 
276     float GetContentOffset(float mainGap) const;
277     /**
278      * @brief Get the total height of grid content. Use estimation when lineHeights are not available. Can handle
279      * bigItems.
280      *
281      * @param mainGap
282      * @return total height
283      */
284     float GetContentHeight(float mainGap) const;
285     float GetContentOffset(const GridLayoutOptions& options, float mainGap) const;
286 
287     /**
288      * @brief Get the content height of Grid in range [0, endIdx).
289      *
290      * IF: Irregular items always take up the whole line (no getSizeByIdx callback).
291      * THEN: Assumes that all irregular lines have the same height, and all other regular lines have the same height.
292      * ELSE: Call a more versatile algorithm.
293      * REQUIRES:
294      * 1. all irregular lines must have the same height.
295      * 2. all regular items must have the same height.
296      *
297      * @param options contains irregular item.
298      * @param endIdx ending item index (exclusive).
299      * @param mainGap gap between lines.
300      * @return total height of the content.
301      */
302     float GetContentHeight(const GridLayoutOptions& options, int32_t endIdx, float mainGap) const;
303     void SkipStartIndexByOffset(const GridLayoutOptions& options, float mainGap);
304     float GetCurrentLineHeight() const;
305 
306     /**
307      * @brief Get Content Offset when using irregular layout.
308      */
309     float GetIrregularOffset(float mainGap) const;
310     /**
311      * @brief Get total content height when using irregular layout.
312      */
313     float GetIrregularHeight(float mainGap) const;
314 
315     bool GetLineIndexByIndex(int32_t targetIndex, int32_t& targetLineIndex) const;
316     float GetTotalHeightFromZeroIndex(int32_t targetLineIndex, float mainGap) const;
317 
318     /**
319      * @brief Calculate the distance from content's end to the viewport bottom.
320      *
321      * REQUIRES: currentOffset_ is valid from last layout.
322      * @param mainSize of the viewport.
323      * @param heightInView total height of items inside the viewport.
324      * @param mainGap gap between lines along the main axis.
325      * @return Positive when content's end is below viewport. Return [mainSize] if last line is not in viewport.
326      */
327     float GetDistanceToBottom(float mainSize, float heightInView, float mainGap) const;
328 
329     /**
330      * @brief Transforms scrollAlign_ into other ScrollAlign values, based on current position of
331      * target item.
332      *
333      * @param height number of rows the item occupies.
334      * @param mainSize The main-axis length of the grid.
335      * @return ScrollAlign value transformed from AUTO.
336      */
337     ScrollAlign TransformAutoScrollAlign(int32_t itemIdx, int32_t height, float mainSize, float mainGap) const;
338 
339     /**
340      * @param targetIdx target item's index.
341      * @param height number of rows the item occupies.
342      * @return item position to scroll to through animation.
343      */
344     float GetAnimatePosIrregular(int32_t targetIdx, int32_t height, ScrollAlign align, float mainGap) const;
345 
346     bool GetGridItemAnimatePos(const GridLayoutInfo& currentGridLayoutInfo, int32_t targetIndex, ScrollAlign align,
347         float mainGap, float& targetPos);
348 
349     using MatIter = std::map<int32_t, std::map<int32_t, int32_t>>::const_iterator;
350     MatIter FindStartLineInMatrix(MatIter iter, int32_t index) const;
351     void ClearHeightsFromMatrix(int32_t lineIdx);
352 
353     void UpdateDefaultCachedCount();
354 
355     int32_t FindInMatrixByMainIndexAndCrossIndex(int32_t mainIndex, int32_t crossIndex) const;
356 
357     // Only used for debugging.
358     void PrintMatrix();
359     void PrintLineHeight();
360 
361     bool CheckGridMatrix(int32_t cachedCount);
362 
363     Axis axis_ = Axis::VERTICAL;
364 
365     float currentOffset_ = 0.0f; // offset on the current top GridItem on [startMainLineIndex_]
366     float prevOffset_ = 0.0f;
367     float currentHeight_ = 0.0f; // height from first item to current top GridItem on [startMainLineIndex_]
368     float prevHeight_ = 0.0f;
369     float lastMainSize_ = 0.0f;
370     float lastCrossSize_ = 0.0f;
371     float totalHeightOfItemsInView_ = 0.0f;
372     float avgLineHeight_ = 0.0f;
373 
374     // additional padding to accommodate navigation bar when SafeArea is expanded
375     float contentEndPadding_ = 0.0f;
376 
377     std::optional<int32_t> lastCrossCount_;
378     // index of first and last GridItem in viewport
379     int32_t startIndex_ = 0;
380     int32_t endIndex_ = -1;
381 
382     // index of first row and last row in viewport (assuming it's a vertical Grid)
383     int32_t startMainLineIndex_ = 0;
384     int32_t endMainLineIndex_ = 0;
385 
386     int32_t jumpIndex_ = EMPTY_JUMP_INDEX;
387     std::optional<float> extraOffset_;
388     int32_t crossCount_ = 0;
389     int32_t childrenCount_ = 0;
390     ScrollAlign scrollAlign_ = ScrollAlign::AUTO;
391 
392     // Map structure: [mainIndex, [crossIndex, index]],
393     // when vertical, mainIndex is rowIndex and crossIndex is columnIndex.
394     std::map<int32_t, std::map<int32_t, int32_t>> gridMatrix_;
395     // in vertical grid, this map is like: [rowIndex: rowHeight]
396     std::map<int32_t, float> lineHeightMap_;
397 
398     // Map structure: [index, last cell]
399     std::map<int32_t, int32_t> irregularItemsPosition_;
400 
401     // rect of grid item dragged in
402     RectF currentRect_;
403 
404     bool reachEnd_ = false; // true if last GridItem appears in the viewPort
405     bool reachStart_ = false;
406 
407     bool offsetEnd_ = false; // true if content bottom is truly reached
408 
409     // Grid has GridItem whose columnEnd - columnStart > 0
410     bool hasBigItem_;
411 
412     // Grid has GridItem whose rowEnd - rowStart > 0
413     bool hasMultiLineItem_;
414     // false when offset is updated but layout hasn't happened, so data is out of sync
415     bool synced_ = false;
416 
417     std::optional<int32_t> targetIndex_;
418 
419     std::map<int32_t, bool> irregularLines_;
420 
421     bool clearStretch_ = false;
422 
423     // default cached count
424     int32_t defCachedCount_ = 1;
425 
426     int32_t times_ = 0;
427 
428 private:
429     float GetCurrentOffsetOfRegularGrid(float mainGap) const;
430     float GetContentHeightOfRegularGrid(float mainGap) const;
431     int32_t GetItemIndexByPosition(int32_t position);
432     int32_t GetPositionByItemIndex(int32_t itemIndex);
433     void MoveItemsBack(int32_t from, int32_t to, int32_t itemIndex);
434     void MoveItemsForward(int32_t from, int32_t to, int32_t itemIndex);
435     void GetLineHeights(
436         const GridLayoutOptions& options, float mainGap, float& regularHeight, float& irregularHeight) const;
437 
438     /**
439      * @brief Find the number of GridItems in range [startLine, endLine].
440      *
441      * REQUIRES: gridMatrix_ is valid in range [startLine, endLine].
442      * @return number of GridItems
443      */
444     int32_t FindItemCount(int32_t startLine, int32_t endLine) const;
445 
446     int32_t currentMovingItemPosition_ = -1;
447     std::map<int32_t, int32_t> positionItemIndexMap_;
448     float lastIrregularMainSize_ = 0.0f; // maybe no irregular item in current gridMatrix_
449     float lastRegularMainSize_ = 0.0f;
450 };
451 
452 } // namespace OHOS::Ace::NG
453 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_GRID_GRID_LAYOUT_ALGORITHM_H
454