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