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 #ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_SWIPER_SWIPER_UTILS_H
17 #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_SWIPER_SWIPER_UTILS_H
18 
19 #include <optional>
20 
21 #include "base/geometry/axis.h"
22 #include "base/memory/referenced.h"
23 #include "base/utils/utils.h"
24 #include "core/components/common/layout/constants.h"
25 #include "core/components_ng/pattern/swiper/swiper_layout_property.h"
26 #include "core/components_ng/pattern/swiper/swiper_paint_property.h"
27 #include "core/components_ng/property/measure_utils.h"
28 
29 namespace OHOS::Ace::NG {
30 namespace {
31 constexpr Dimension SWIPER_MARGIN = 16.0_vp;
32 constexpr Dimension SWIPER_GUTTER = 16.0_vp;
33 } // namespace
34 
35 class SwiperUtils {
36 public:
37     SwiperUtils() = delete;
38     ~SwiperUtils() = delete;
39 
IsStretch(const RefPtr<SwiperLayoutProperty> & property)40     static bool IsStretch(const RefPtr<SwiperLayoutProperty>& property)
41     {
42         // If display count is setted, use stretch mode.
43         CHECK_NULL_RETURN(property, true);
44         return property->IsStretch();
45     }
46 
GetItemSpace(const RefPtr<SwiperLayoutProperty> & property)47     static float GetItemSpace(const RefPtr<SwiperLayoutProperty>& property)
48     {
49         if (property->IgnoreItemSpace()) {
50             return 0.0f;
51         }
52         return property->GetItemSpace().value_or(0.0_px).ConvertToPx();
53     }
54 
CreateChildConstraint(const RefPtr<SwiperLayoutProperty> & property,const OptionalSizeF & idealSize,bool getAutoFill)55     static LayoutConstraintF CreateChildConstraint(
56         const RefPtr<SwiperLayoutProperty>& property, const OptionalSizeF& idealSize, bool getAutoFill)
57     {
58         auto layoutConstraint = property->CreateChildConstraint();
59         layoutConstraint.parentIdealSize = idealSize;
60         auto displayCount = property->GetDisplayCount().value_or(1);
61         if ((!getAutoFill && !IsStretch(property)) || NonPositive(static_cast<double>(displayCount))) {
62             return layoutConstraint;
63         }
64         auto axis = property->GetDirection().value_or(Axis::HORIZONTAL);
65         // re-determine ignoreItemSpace_ based on child calc length
66         property->ResetIgnorePrevMarginAndNextMargin();
67         property->ResetIgnoreItemSpace();
68         auto itemSpace = GetItemSpace(property);
69         auto parentMainSize = idealSize.MainSize(axis);
70         if (parentMainSize.has_value() && itemSpace > parentMainSize.value()) {
71             itemSpace = 0.0f;
72         }
73         auto prevMargin = property->GetPrevMarginValue(0.0_px).ConvertToPx();
74         auto nextMargin = property->GetNextMarginValue(0.0_px).ConvertToPx();
75         auto itemSpaceCount = CaculateDisplayItemSpaceCount(property, prevMargin, nextMargin);
76         auto childSelfIdealSize = idealSize;
77         float childCalcIdealLength = 0.0f;
78         // Invalid size need not to calculate margin
79         if (!idealSize.IsNonPositive() && ((axis == Axis::HORIZONTAL && idealSize.Width().has_value()) ||
80                                               (axis == Axis::VERTICAL && idealSize.Height().has_value()))) {
81             auto length = axis == Axis::HORIZONTAL ? idealSize.Width().value() : idealSize.Height().value();
82             childCalcIdealLength =
83                 (length - itemSpace * itemSpaceCount - static_cast<float>(prevMargin + nextMargin)) / displayCount;
84             if (LessNotEqual(childCalcIdealLength, 0.0)) {
85                 // prioritize margin and displayCount, ignore itemSpace to create a positive idealLength.
86                 property->MarkIgnoreItemSpace();
87                 childCalcIdealLength = (length - static_cast<float>(prevMargin + nextMargin)) / displayCount;
88             }
89             if (CheckMarginPropertyExceed(property, childCalcIdealLength)) {
90                 prevMargin = 0.0;
91                 nextMargin = 0.0;
92                 itemSpaceCount = CaculateDisplayItemSpaceCount(property, prevMargin, nextMargin);
93                 childCalcIdealLength = (length - itemSpace * itemSpaceCount) / displayCount;
94                 if (LessNotEqual(childCalcIdealLength, 0.0)) {
95                     childCalcIdealLength = length / displayCount;
96                 } else {
97                     property->ResetIgnoreItemSpace();
98                 }
99             }
100             axis == Axis::HORIZONTAL ? childSelfIdealSize.SetWidth(childCalcIdealLength)
101                                      : childSelfIdealSize.SetHeight(childCalcIdealLength);
102         }
103 
104         layoutConstraint.selfIdealSize = childSelfIdealSize;
105         return layoutConstraint;
106     }
107 
CaculateDisplayItemSpaceCount(const RefPtr<SwiperLayoutProperty> & property,double prevMargin,double nextMargin)108     static int32_t CaculateDisplayItemSpaceCount(
109         const RefPtr<SwiperLayoutProperty>& property, double prevMargin, double nextMargin)
110     {
111         CHECK_NULL_RETURN(property, 0);
112         auto count = property->GetDisplayCountValue(1);
113         count = (Positive(static_cast<double>(count)) ? count : 1);
114         if (Positive(prevMargin) && Positive(nextMargin)) {
115             return count + 1;
116         } else if (NonPositive(prevMargin) && NonPositive(nextMargin)) {
117             return count - 1;
118         } else {
119             return count;
120         }
121     }
122 
ComputePageIndex(int32_t index,int32_t displayCount)123     static int32_t ComputePageIndex(int32_t index, int32_t displayCount)
124     {
125         if (displayCount <= 0) {
126             return index;
127         }
128 
129         return static_cast<int32_t>(std::floor(static_cast<float>(index) / static_cast<float>(displayCount))) *
130                displayCount;
131     }
132 
ComputePageEndIndex(int32_t index,int32_t displayCount)133     static int32_t ComputePageEndIndex(int32_t index, int32_t displayCount)
134     {
135         if (displayCount <= 0) {
136             return index;
137         }
138 
139         return static_cast<int32_t>(std::floor(static_cast<float>(index) / static_cast<float>(displayCount))) *
140                    displayCount +
141                displayCount - 1;
142     }
143 
CheckAutoFillDisplayCount(RefPtr<SwiperLayoutProperty> & swiperLayoutProperty,float contentWidth,int32_t totalCount)144     static void CheckAutoFillDisplayCount(
145         RefPtr<SwiperLayoutProperty>& swiperLayoutProperty, float contentWidth, int32_t totalCount)
146     {
147         bool isAutoFill = swiperLayoutProperty->GetMinSize().has_value();
148         if (!isAutoFill) {
149             return;
150         }
151         auto minSize = swiperLayoutProperty->GetMinSize()->ConvertToPx();
152         auto displayCount =
153             static_cast<int32_t>(floor((contentWidth - 2 * SWIPER_MARGIN.ConvertToPx() + SWIPER_GUTTER.ConvertToPx()) /
154                                        (minSize + SWIPER_GUTTER.ConvertToPx())));
155         if (LessOrEqual(minSize, 0)) {
156             displayCount = 1;
157         }
158         displayCount = displayCount > 0 ? displayCount : 1;
159         displayCount = displayCount > totalCount ? totalCount : displayCount;
160 
161         auto displayCountProperty = swiperLayoutProperty->GetDisplayCount().value_or(1);
162         if (displayCountProperty != displayCount) {
163             swiperLayoutProperty->UpdateDisplayCount(displayCount);
164         }
165     }
166 
167 private:
CheckMarginPropertyExceed(const RefPtr<SwiperLayoutProperty> & property,float childCalcIdealLength)168     static bool CheckMarginPropertyExceed(const RefPtr<SwiperLayoutProperty>& property, float childCalcIdealLength)
169     {
170         CHECK_NULL_RETURN(property, false);
171         auto prevMargin = property->GetPrevMarginValue(0.0_px).ConvertToPx();
172         auto nextMargin = property->GetNextMarginValue(0.0_px).ConvertToPx();
173         if (GreatNotEqual(prevMargin, childCalcIdealLength) || GreatNotEqual(nextMargin, childCalcIdealLength)) {
174             property->MarkIgnorePrevMarginAndNextMargin();
175             return true;
176         }
177         return false;
178     }
179 };
180 
181 /**
182  * @brief Helper RAII object. set @c var to @c value when this object goes out of scope.
183  * REQUIRES: the life span of @c var surpasses this object.
184  */
185 template<typename T>
186 class DestructSetter {
187 public:
188     DestructSetter() = delete;
DestructSetter(T & var,T value)189     DestructSetter(T& var, T value) : ref_(var), value_(value) {}
~DestructSetter()190     ~DestructSetter()
191     {
192         ref_ = value_;
193     }
194 
195 private:
196     T& ref_;
197     T value_ {};
198 
199     ACE_DISALLOW_COPY_AND_MOVE(DestructSetter);
200 };
201 } // namespace OHOS::Ace::NG
202 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_SWIPER_SWIPER_UTILS_H
203