• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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 #include "core/components_ng/pattern/grid/irregular/grid_layout_range_solver.h"
17 
18 #include "core/components_ng/pattern/grid/grid_layout_property.h"
19 #include "core/components_ng/pattern/grid/irregular/grid_layout_utils.h"
20 
21 namespace OHOS::Ace::NG {
GridLayoutRangeSolver(GridLayoutInfo * info,LayoutWrapper * wrapper)22 GridLayoutRangeSolver::GridLayoutRangeSolver(GridLayoutInfo* info, LayoutWrapper* wrapper)
23     : info_(info), wrapper_(wrapper)
24 {
25     auto props = AceType::DynamicCast<GridLayoutProperty>(wrapper_->GetLayoutProperty());
26     opts_ = &props->GetLayoutOptions().value();
27 };
28 
29 using Result = GridLayoutRangeSolver::StartingRowInfo;
FindStartingRow(float mainGap)30 Result GridLayoutRangeSolver::FindStartingRow(float mainGap)
31 {
32     if (info_->gridMatrix_.empty() || info_->lineHeightMap_.empty()) {
33         return { 0, 0, 0.0f };
34     }
35     if (NearZero(info_->currentOffset_)) {
36         return { info_->startMainLineIndex_, info_->startIndex_, 0.0f };
37     }
38     if (Negative(info_->currentOffset_)) {
39         return SolveForward(mainGap, -info_->currentOffset_, info_->startMainLineIndex_);
40     }
41     return SolveBackward(mainGap, info_->currentOffset_, info_->startMainLineIndex_);
42 }
43 
44 using RangeInfo = GridLayoutRangeSolver::RangeInfo;
FindRangeOnJump(int32_t jumpIdx,int32_t jumpLineIdx,float mainGap)45 RangeInfo GridLayoutRangeSolver::FindRangeOnJump(int32_t jumpIdx, int32_t jumpLineIdx, float mainGap)
46 {
47     auto mainSize = wrapper_->GetGeometryNode()->GetContentSize().MainSize(info_->axis_);
48     /*
49     Notice that  finding the first line in ScrollAlign::END is the same as having the jumpLine matching the top of the
50     viewport and applying a positive whole-page offset, so we can directly use SolveBackward. But for
51     ScrollAlign::START, we have to change SolveForward a bit to find the ending row.
52     */
53     switch (info_->scrollAlign_) {
54         case ScrollAlign::START: {
55             auto [startRow, startIdx] = CheckMultiRow(jumpLineIdx);
56             float offset = -info_->GetHeightInRange(startRow, jumpLineIdx, mainGap);
57             auto [endLineIdx, endIdx] = SolveForwardForEndIdx(mainGap, mainSize, jumpLineIdx);
58             return { startRow, startIdx, offset, endLineIdx, endIdx };
59         }
60         case ScrollAlign::CENTER: {
61             // align by item center
62             auto size = GridLayoutUtils::GetItemSize(info_, wrapper_, jumpIdx);
63             const auto [centerLine, offset] = info_->FindItemCenter(jumpLineIdx, size.rows, mainGap);
64             const float halfMainSize = mainSize / 2.0f;
65             auto [endLineIdx, endIdx] = SolveForwardForEndIdx(mainGap, halfMainSize + offset, centerLine);
66             auto res = SolveBackward(mainGap, halfMainSize - offset, centerLine);
67             return { res.row, res.idx, res.pos, endLineIdx, endIdx };
68         }
69         case ScrollAlign::END: {
70             auto it = info_->lineHeightMap_.find(jumpLineIdx);
71             if (it == info_->lineHeightMap_.end()) {
72                 TAG_LOGW(AceLogTag::ACE_GRID, "line height at %{public}d not prepared during jump", jumpLineIdx);
73                 return {};
74             }
75             auto res = SolveBackward(mainGap, mainSize - it->second, jumpLineIdx);
76             return { res.row, res.idx, res.pos, jumpLineIdx, info_->FindEndIdx(jumpLineIdx).itemIdx };
77         }
78         default:
79             return {};
80     }
81 }
82 
SolveForward(float mainGap,float targetLen,const int32_t idx)83 Result GridLayoutRangeSolver::SolveForward(float mainGap, float targetLen, const int32_t idx)
84 {
85     float len = -mainGap;
86     auto it = info_->lineHeightMap_.find(idx);
87     for (; it != info_->lineHeightMap_.end(); ++it) {
88         if (GreatNotEqual(len + it->second + mainGap, targetLen)) {
89             break;
90         }
91         len += it->second + mainGap;
92     }
93     if (it == info_->lineHeightMap_.end()) {
94         len -= (--it)->second + mainGap;
95     }
96     auto [startRow, startIdx] = CheckMultiRow(it->first);
97     for (int32_t i = it->first; i > startRow; --i) {
98         --it;
99         len -= it->second + mainGap;
100     }
101     return { startRow, startIdx, len - targetLen + mainGap };
102 }
103 
SolveForwardForEndIdx(float mainGap,float targetLen,int32_t line)104 std::pair<int32_t, int32_t> GridLayoutRangeSolver::SolveForwardForEndIdx(float mainGap, float targetLen, int32_t line)
105 {
106     if (Negative(targetLen)) {
107         return { -1, -1 };
108     }
109     float len = 0.0f;
110     auto it = info_->lineHeightMap_.find(line);
111     if (it == info_->lineHeightMap_.end()) {
112         return { -1, -1 };
113     }
114 
115     for (; LessNotEqual(len, targetLen) && it != info_->lineHeightMap_.end(); ++it) {
116         len += it->second + mainGap;
117     }
118     --it;
119     return { it->first, info_->FindEndIdx(it->first).itemIdx };
120 }
121 
SolveBackward(float mainGap,float targetLen,int32_t idx)122 Result GridLayoutRangeSolver::SolveBackward(float mainGap, float targetLen, int32_t idx)
123 {
124     float len = mainGap;
125     while (idx > 0 && LessNotEqual(len, targetLen)) {
126         auto it = info_->lineHeightMap_.find(--idx);
127         if (it == info_->lineHeightMap_.end()) {
128             return { 0, 0, 0.0f };
129         }
130         len += it->second + mainGap;
131     }
132 
133     auto [startLine, startItem] = CheckMultiRow(idx);
134     float newOffset = targetLen - len + mainGap;
135     newOffset -= info_->GetHeightInRange(startLine, idx, mainGap);
136     return { startLine, startItem, newOffset };
137 }
138 
CheckMultiRow(const int32_t idx)139 std::pair<int32_t, int32_t> GridLayoutRangeSolver::CheckMultiRow(const int32_t idx)
140 {
141     // check multi-row item that occupies Row [idx]
142     auto it = info_->gridMatrix_.find(idx);
143     if (it == info_->gridMatrix_.end()) {
144         return { -1, -1 };
145     }
146 
147     const auto& row = it->second;
148     if (row.empty()) {
149         return { -1, -1 };
150     }
151     int32_t startLine = idx;
152     int32_t startItem = row.begin()->second;
153     for (int32_t c = 0; c < info_->crossCount_; ++c) {
154         auto it = row.find(c);
155         if (it == row.end()) {
156             continue;
157         }
158 
159         int32_t r = idx;
160         if (it->second == 0) {
161             // Item 0 always start at [0, 0]
162             return { 0, 0 };
163         }
164         if (it->second < 0) {
165             // traverse upwards to find the first row occupied by this item
166             while (r > 0 && info_->gridMatrix_.at(r).at(c) < 0) {
167                 --r;
168             }
169             if (r < startLine) {
170                 startLine = r;
171                 startItem = -it->second;
172             }
173         }
174 
175         // skip the columns occupied by this item
176         const int32_t itemIdx = std::abs(it->second);
177         if (opts_->irregularIndexes.find(itemIdx) != opts_->irregularIndexes.end()) {
178             if (opts_->getSizeByIndex) {
179                 auto size = opts_->getSizeByIndex(itemIdx);
180                 c += (info_->axis_ == Axis::VERTICAL ? size.columns : size.rows) - 1;
181             } else {
182                 // no getSizeByIndex implies itemWidth == crossCount
183                 break;
184             }
185         }
186     }
187     return { startLine, startItem };
188 }
189 } // namespace OHOS::Ace::NG
190