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