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