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