• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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/list/list_lanes_layout_algorithm.h"
17 
18 #include "base/utils/utils.h"
19 #include "core/components_ng/base/frame_node.h"
20 #include "core/components_ng/syntax/lazy_for_each_node.h"
21 #include "core/components_v2/inspector/inspector_constants.h"
22 
23 namespace OHOS::Ace::NG {
24 
UpdateListItemConstraint(Axis axis,const OptionalSizeF & selfIdealSize,LayoutConstraintF & contentConstraint)25 void ListLanesLayoutAlgorithm::UpdateListItemConstraint(
26     Axis axis, const OptionalSizeF& selfIdealSize, LayoutConstraintF& contentConstraint)
27 {
28     contentConstraint.parentIdealSize = selfIdealSize;
29     contentConstraint.maxSize.SetMainSize(Infinity<float>(), axis);
30     groupLayoutConstraint_ = contentConstraint;
31     auto crossSizeOptional = selfIdealSize.CrossSize(axis);
32     if (crossSizeOptional.has_value()) {
33         float crossSize = crossSizeOptional.value();
34         groupLayoutConstraint_.maxSize.SetCrossSize(crossSize, axis);
35         if (lanes_ > 1) {
36             float laneGutter = GetLaneGutter();
37             crossSize = (crossSize + laneGutter) / lanes_ - laneGutter;
38             crossSize = crossSize <= 0 ? 1 : crossSize;
39         }
40         if (maxLaneLength_.has_value() && maxLaneLength_.value() < crossSize) {
41             crossSize = maxLaneLength_.value();
42         }
43         contentConstraint.percentReference.SetCrossSize(crossSize, axis);
44         contentConstraint.parentIdealSize.SetCrossSize(crossSize, axis);
45         contentConstraint.maxSize.SetCrossSize(crossSize, axis);
46         if (minLaneLength_.has_value()) {
47             contentConstraint.minSize.SetCrossSize(minLaneLength_.value(), axis);
48         }
49     }
50 }
51 
MeasureAndGetChildHeight(LayoutWrapper * layoutWrapper,int32_t childIndex)52 float ListLanesLayoutAlgorithm::MeasureAndGetChildHeight(LayoutWrapper* layoutWrapper, int32_t childIndex)
53 {
54     auto wrapper = layoutWrapper->GetOrCreateChildByIndex(childIndex);
55     CHECK_NULL_RETURN(wrapper, 0.0f);
56     bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
57     if (isGroup) {
58         auto listLayoutProperty =
59             AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
60         // true: layout forward, true: layout all group items.
61         SetListItemGroupParam(wrapper, 0.0f, true, listLayoutProperty, true);
62         wrapper->Measure(groupLayoutConstraint_);
63     } else {
64         wrapper->Measure(childLayoutConstraint_);
65     }
66     float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
67     return mainLen;
68 }
69 
LayoutALineForward(LayoutWrapper * layoutWrapper,int32_t & currentIndex,float startPos,float & endPos)70 int32_t ListLanesLayoutAlgorithm::LayoutALineForward(LayoutWrapper* layoutWrapper,
71     int32_t& currentIndex, float startPos, float& endPos)
72 {
73     float mainLen = 0.0f;
74     bool isGroup = false;
75     int32_t cnt = 0;
76     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
77     for (int32_t i = 0; i < lanes && currentIndex + 1 <= GetMaxListItemIndex(); i++) {
78         auto wrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex + 1);
79         if (!wrapper) {
80             break;
81         }
82         isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
83         if (isGroup && cnt > 0) {
84             wrapper->SetActive(false);
85             isGroup = false;
86             break;
87         }
88         cnt++;
89         ++currentIndex;
90         if (isGroup) {
91             ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItemGroup:%d", currentIndex);
92             auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
93             SetListItemGroupParam(wrapper, startPos, true, listLayoutProperty, GroupNeedAllLayout());
94             wrapper->Measure(groupLayoutConstraint_);
95         } else {
96             ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
97             wrapper->Measure(childLayoutConstraint_);
98         }
99         mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
100         if (isGroup) {
101             break;
102         }
103     }
104     if (cnt > 0) {
105         endPos = startPos + mainLen;
106         for (int32_t i = 0; i < cnt; i++) {
107             SetItemInfo(currentIndex - i, { startPos, endPos, isGroup });
108         }
109     }
110     return cnt;
111 }
112 
LayoutALineBackward(LayoutWrapper * layoutWrapper,int32_t & currentIndex,float endPos,float & startPos)113 int32_t ListLanesLayoutAlgorithm::LayoutALineBackward(LayoutWrapper* layoutWrapper,
114     int32_t& currentIndex, float endPos, float& startPos)
115 {
116     float mainLen = 0.0f;
117     bool isGroup = false;
118     int32_t cnt = 0;
119     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
120     for (int32_t i = 0; i < lanes && currentIndex - 1 >= 0; i++) {
121         if (currentIndex > GetMaxListItemIndex() + 1) {
122             --currentIndex;
123             continue;
124         }
125         auto wrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex - 1);
126         if (!wrapper) {
127             break;
128         }
129         isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
130         if (isGroup && cnt > 0) {
131             wrapper->SetActive(false);
132             isGroup = false;
133             break;
134         }
135         --currentIndex;
136 
137         cnt++;
138         if (isGroup) {
139             ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItemGroup:%d", currentIndex);
140             auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
141             SetListItemGroupParam(wrapper, endPos, false, listLayoutProperty, GroupNeedAllLayout());
142             wrapper->Measure(groupLayoutConstraint_);
143         } else {
144             ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
145             wrapper->Measure(childLayoutConstraint_);
146         }
147         mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
148         if (isGroup || (currentIndex - FindLanesStartIndex(layoutWrapper, currentIndex)) % lanes == 0) {
149             break;
150         }
151     }
152     if (cnt > 0) {
153         startPos = endPos - mainLen;
154         for (int32_t i = 0; i < cnt; i++) {
155             SetItemInfo(currentIndex + i, { startPos, endPos, isGroup });
156         }
157     }
158     return cnt;
159 }
160 
CalculateLanesParam(std::optional<float> & minLaneLength,std::optional<float> & maxLaneLength,int32_t lanes,std::optional<float> crossSizeOptional,float laneGutter)161 int32_t ListLanesLayoutAlgorithm::CalculateLanesParam(std::optional<float>& minLaneLength,
162     std::optional<float>& maxLaneLength, int32_t lanes, std::optional<float> crossSizeOptional, float laneGutter)
163 {
164     if (lanes < 1) {
165         return 1;
166     }
167     // Case 1: lane length constrain is not set
168     //      1.1: use [lanes] set by user if [lanes] is set
169     //      1.2: set [lanes] to 1 if [lanes] is not set
170     if (!crossSizeOptional.has_value() || GreaterOrEqualToInfinity(crossSizeOptional.value()) ||
171         !minLaneLength.has_value() || !maxLaneLength.has_value()) {
172         maxLaneLength.reset();
173         minLaneLength.reset();
174         return lanes;
175     }
176     // Case 2: lane length constrain is set --> need to calculate [lanes_] according to contraint.
177     // We agreed on such rules (assuming we have a vertical list here):
178     // rule 1: [minLaneLength_] has a higher priority than [maxLaneLength_] when decide [lanes_], for e.g.,
179     //         if [minLaneLength_] is 40, [maxLaneLength_] is 60, list's width is 120,
180     //         the [lanes_] is 3 rather than 2.
181     // rule 2: after [lanes_] is determined by rule 1, the width of lane will be as large as it can be, for
182     // e.g.,
183     //         if [minLaneLength_] is 40, [maxLaneLength_] is 60, list's width is 132, the [lanes_] is 3
184     //         according to rule 1, then the width of lane will be 132 / 3 = 44 rather than 40,
185     //         its [minLaneLength_].
186     auto crossSize = crossSizeOptional.value();
187     ModifyLaneLength(minLaneLength, maxLaneLength, crossSize);
188 
189     // if minLaneLength is 40, maxLaneLength is 60
190     // when list's width is 120, lanes_ = 3
191     // when list's width is 80, lanes_ = 2
192     // when list's width is 70, lanes_ = 1
193     float maxLanes = (crossSize + laneGutter) / (minLaneLength.value() + laneGutter);
194     float minLanes = (crossSize + laneGutter) / (maxLaneLength.value() + laneGutter);
195     // let's considerate scenarios when maxCrossSize > 0
196     // now it's guaranteed that [minLaneLength_] <= [maxLaneLength_], i.e., maxLanes >= minLanes > 0
197     // there are 3 scenarios:
198     // 1. 1 > maxLanes >= minLanes > 0
199     // 2. maxLanes >= 1 >= minLanes > 0
200     // 3. maxLanes >= minLanes > 1
201     // 1. 1 > maxLanes >= minLanes > 0 ---> maxCrossSize < minLaneLength_ =< maxLaneLength
202     if (GreatNotEqual(1, maxLanes) && GreatOrEqual(maxLanes, minLanes)) {
203         lanes = 1;
204         minLaneLength = crossSize;
205         maxLaneLength = crossSize;
206         return lanes;
207     }
208     // 2. maxLanes >= 1 >= minLanes > 0 ---> minLaneLength_ = maxCrossSize < maxLaneLength
209     if (GreatOrEqual(maxLanes, 1) && LessOrEqual(minLanes, 1)) {
210         lanes = std::floor(maxLanes);
211         maxLaneLength = crossSize;
212         return lanes;
213     }
214     // 3. maxLanes >= minLanes > 1 ---> minLaneLength_ <= maxLaneLength < maxCrossSize
215     if (GreatOrEqual(maxLanes, minLanes) && GreatNotEqual(minLanes, 1)) {
216         lanes = std::floor(maxLanes);
217         return lanes;
218     }
219     lanes = 1;
220     LOGE("unexpected situation, set lanes to 1, maxLanes: %{public}f, minLanes: %{public}f, minLaneLength_: "
221          "%{public}f, maxLaneLength_: %{public}f",
222         maxLanes, minLanes, minLaneLength.value(), maxLaneLength.value());
223     return lanes;
224 }
225 
CalculateLanes(const RefPtr<ListLayoutProperty> & layoutProperty,const LayoutConstraintF & layoutConstraint,std::optional<float> crossSizeOptional,Axis axis)226 void ListLanesLayoutAlgorithm::CalculateLanes(const RefPtr<ListLayoutProperty>& layoutProperty,
227     const LayoutConstraintF& layoutConstraint, std::optional<float> crossSizeOptional, Axis axis)
228 {
229     auto contentConstraint = layoutProperty->GetContentLayoutConstraint().value();
230     auto mainPercentRefer = GetMainAxisSize(contentConstraint.percentReference, axis);
231     int32_t lanes = layoutProperty->GetLanes().value_or(1);
232     lanes = lanes > 1 ? lanes : 1;
233     if (layoutProperty->GetLaneMinLength().has_value()) {
234         minLaneLength_ =
235             ConvertToPx(layoutProperty->GetLaneMinLength().value(), layoutConstraint.scaleProperty, mainPercentRefer);
236     }
237     if (layoutProperty->GetLaneMaxLength().has_value()) {
238         maxLaneLength_ =
239             ConvertToPx(layoutProperty->GetLaneMaxLength().value(), layoutConstraint.scaleProperty, mainPercentRefer);
240     }
241     float laneGutter = 0.0f;
242     if (layoutProperty->GetLaneGutter().has_value()) {
243         laneGutter = ConvertToPx(
244             layoutProperty->GetLaneGutter().value(), layoutConstraint.scaleProperty, crossSizeOptional.value_or(0.0))
245                 .value();
246         SetLaneGutter(laneGutter);
247     }
248     lanes_ = CalculateLanesParam(minLaneLength_, maxLaneLength_, lanes, crossSizeOptional, laneGutter);
249 }
250 
ModifyLaneLength(std::optional<float> & minLaneLength,std::optional<float> & maxLaneLength,float crossSize)251 void ListLanesLayoutAlgorithm::ModifyLaneLength(
252     std::optional<float>& minLaneLength, std::optional<float>& maxLaneLength, float crossSize)
253 {
254     if (GreatNotEqual(minLaneLength.value(), maxLaneLength.value())) {
255         LOGI("minLaneLength: %{public}f is greater than maxLaneLength: %{public}f, assign minLaneLength to"
256              " maxLaneLength",
257             minLaneLength.value(), maxLaneLength.value());
258         maxLaneLength = minLaneLength;
259     }
260 }
261 
CalculateLaneCrossOffset(float crossSize,float childCrossSize)262 float ListLanesLayoutAlgorithm::CalculateLaneCrossOffset(float crossSize, float childCrossSize)
263 {
264     if (lanes_ <= 0) {
265         return 0.0f;
266     }
267     return ListLayoutAlgorithm::CalculateLaneCrossOffset(crossSize / lanes_, childCrossSize / lanes_);
268 }
269 
GetLazyForEachIndex(const RefPtr<FrameNode> & host)270 int32_t ListLanesLayoutAlgorithm::GetLazyForEachIndex(const RefPtr<FrameNode>& host)
271 {
272     CHECK_NULL_RETURN(host, -1);
273     auto lazyForEach = AceType::DynamicCast<LazyForEachNode>(host->GetParent());
274     CHECK_NULL_RETURN_NOLOG(lazyForEach, -1);
275     return lazyForEach->GetIndexByUINode(host);
276 }
277 
FindLanesStartIndex(LayoutWrapper * layoutWrapper,int32_t startIndex,int32_t index)278 int32_t ListLanesLayoutAlgorithm::FindLanesStartIndex(LayoutWrapper* layoutWrapper, int32_t startIndex, int32_t index)
279 {
280     auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index, false);
281     CHECK_NULL_RETURN_NOLOG(wrapper, index);
282     if (wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
283         return index;
284     }
285     auto lazyIndex = GetLazyForEachIndex(wrapper->GetHostNode());
286     if (lazyIndex > 0) {
287         index -= lazyIndex;
288     }
289     for (int32_t idx = index; idx > startIndex; idx--) {
290         auto wrapper = layoutWrapper->GetOrCreateChildByIndex(idx - 1, false);
291         CHECK_NULL_RETURN_NOLOG(wrapper, idx);
292         if (wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
293             return idx;
294         }
295     }
296     if (startIndex == 0) {
297         return 0;
298     }
299     return -1;
300 }
301 
FindLanesStartIndex(LayoutWrapper * layoutWrapper,int32_t index)302 int32_t ListLanesLayoutAlgorithm::FindLanesStartIndex(LayoutWrapper* layoutWrapper, int32_t index)
303 {
304     if (lanes_ == 1) {
305         return 0;
306     }
307     auto it = lanesItemRange_.upper_bound(index);
308     if (it == lanesItemRange_.begin()) {
309         int32_t startIdx = FindLanesStartIndex(layoutWrapper, 0, index);
310         lanesItemRange_[startIdx] = index;
311         return startIdx;
312     }
313     it--;
314     if (it->second >= index) {
315         return it->first;
316     }
317     int32_t startIdx = FindLanesStartIndex(layoutWrapper, it->second, index);
318     if (startIdx >= 0) {
319         lanesItemRange_[startIdx] = index;
320         return startIdx;
321     }
322     it->second = index;
323     return it->first;
324 }
325 
GetLanesFloor(LayoutWrapper * layoutWrapper,int32_t index)326 int32_t ListLanesLayoutAlgorithm::GetLanesFloor(LayoutWrapper* layoutWrapper, int32_t index)
327 {
328     if (lanes_ > 1) {
329         int32_t startIndex = FindLanesStartIndex(layoutWrapper, index);
330         return index - (index - startIndex) % lanes_;
331     }
332     return index;
333 }
334 
LayoutCachedALineForward(LayoutWrapper * layoutWrapper,int32_t & index,float & startPos,float crossSize)335 std::list<int32_t> ListLanesLayoutAlgorithm::LayoutCachedALineForward(LayoutWrapper* layoutWrapper,
336     int32_t& index, float& startPos, float crossSize)
337 {
338     std::list<int32_t> predictBuildList;
339     ListLayoutAlgorithm::PositionMap posMap;
340     float mainLen = 0.0f;
341     bool isGroup = false;
342     int32_t cnt = 0;
343     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
344     for (int32_t i = 0; i < lanes && index + i <= GetMaxListItemIndex(); i++) {
345         auto wrapper = layoutWrapper->GetChildByIndex(index + i);
346         if (!wrapper || wrapper->CheckNeedForceMeasureAndLayout()) {
347             predictBuildList.emplace_back(index + i);
348             continue;
349         }
350         isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
351         if (isGroup && cnt > 0) {
352             isGroup = false;
353             break;
354         }
355         cnt++;
356         mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
357         if (isGroup) {
358             break;
359         }
360     }
361     if (cnt > 0) {
362         auto endPos = startPos + mainLen;
363         for (int32_t i = 0; i < cnt; i++) {
364             posMap[index + i] = { startPos, endPos, isGroup };
365         }
366         startPos = endPos + GetSpaceWidth();
367         auto startIndex = index;
368         for (const auto& pos: posMap) {
369             auto wrapper = layoutWrapper->GetChildByIndex(pos.first);
370             if (!wrapper) {
371                 break;
372             }
373             LayoutItem(wrapper, pos.first, pos.second, startIndex, crossSize);
374             SyncGeometry(wrapper);
375             wrapper->SetActive(false);
376         }
377     }
378     index += cnt + static_cast<int32_t>(predictBuildList.size());
379     return predictBuildList;
380 }
381 
LayoutCachedALineBackward(LayoutWrapper * layoutWrapper,int32_t & index,float & endPos,float crossSize)382 std::list<int32_t> ListLanesLayoutAlgorithm::LayoutCachedALineBackward(LayoutWrapper* layoutWrapper,
383     int32_t& index, float& endPos, float crossSize)
384 {
385     std::list<int32_t> predictBuildList;
386     ListLayoutAlgorithm::PositionMap posMap;
387     float mainLen = 0.0f;
388     bool isGroup = false;
389     int32_t cnt = 0;
390     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
391     for (int32_t i = 0; i < lanes && index >= 0; i++) {
392         auto idx = index - i;
393         auto wrapper = layoutWrapper->GetChildByIndex(idx);
394         if (!wrapper || wrapper->CheckNeedForceMeasureAndLayout()) {
395             predictBuildList.emplace_back(idx);
396             continue;
397         }
398         isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
399         if (isGroup && cnt > 0) {
400             isGroup = false;
401             break;
402         }
403 
404         cnt++;
405         mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
406         if (isGroup || (idx - FindLanesStartIndex(layoutWrapper, idx)) % lanes == 0) {
407             break;
408         }
409     }
410     if (cnt > 0) {
411         auto startPos = endPos - mainLen;
412         for (int32_t i = 0; i < cnt; i++) {
413             posMap[index - i] = { startPos, endPos, isGroup };
414         }
415         endPos = startPos - GetSpaceWidth();
416         auto startIndex = index - cnt + 1;
417         for (const auto& pos: posMap) {
418             auto wrapper = layoutWrapper->GetChildByIndex(pos.first);
419             if (!wrapper) {
420                 break;
421             }
422             LayoutItem(wrapper, pos.first, pos.second, startIndex, crossSize);
423             SyncGeometry(wrapper);
424             wrapper->SetActive(false);
425         }
426     }
427     index -= cnt + static_cast<int32_t>(predictBuildList.size());
428     return predictBuildList;
429 }
430 
LayoutCachedItem(LayoutWrapper * layoutWrapper,int32_t cacheCount)431 std::list<int32_t> ListLanesLayoutAlgorithm::LayoutCachedItem(LayoutWrapper* layoutWrapper, int32_t cacheCount)
432 {
433     std::list<int32_t> predictBuildList;
434     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
435     auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
436     MinusPaddingToSize(padding, size);
437     float crossSize = GetCrossAxisSize(size, axis_);
438 
439     auto itemPosition = GetItemPosition();
440     auto currIndex = itemPosition.rbegin()->first + 1;
441     auto currPos = itemPosition.rbegin()->second.endPos + GetSpaceWidth();
442     for (int32_t i = 0; i < cacheCount && currIndex <= GetMaxListItemIndex(); i++) {
443         auto tmpList = LayoutCachedALineForward(layoutWrapper, currIndex, currPos, crossSize);
444         predictBuildList.merge(tmpList);
445     }
446     currIndex = itemPosition.begin()->first - 1;
447     currPos = itemPosition.begin()->second.startPos - GetSpaceWidth();
448     for (int32_t i = 0; i < cacheCount && currIndex >= 0; i++) {
449         auto tmpList = LayoutCachedALineBackward(layoutWrapper, currIndex, currPos, crossSize);
450         predictBuildList.merge(tmpList);
451     }
452     return predictBuildList;
453 }
454 } // namespace OHOS::Ace::NG