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 || 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 CHECK_NULL_RETURN(property, {}); 59 auto layoutConstraint = property->CreateChildConstraint(); 60 layoutConstraint.parentIdealSize = idealSize; 61 auto displayCount = property->GetDisplayCount().value_or(1); 62 if ((!getAutoFill && !IsStretch(property)) || NonPositive(static_cast<double>(displayCount))) { 63 return layoutConstraint; 64 } 65 auto axis = property->GetDirection().value_or(Axis::HORIZONTAL); 66 // re-determine ignoreItemSpace_ based on child calc length 67 property->ResetIgnorePrevMarginAndNextMargin(); 68 property->ResetIgnoreItemSpace(); 69 auto itemSpace = GetItemSpace(property); 70 auto parentMainSize = idealSize.MainSize(axis); 71 if (parentMainSize.has_value() && itemSpace > parentMainSize.value()) { 72 itemSpace = 0.0f; 73 } 74 auto prevMargin = property->GetPrevMarginValue(0.0_px).ConvertToPx(); 75 auto nextMargin = property->GetNextMarginValue(0.0_px).ConvertToPx(); 76 auto itemSpaceCount = CaculateDisplayItemSpaceCount(property, prevMargin, nextMargin); 77 auto childSelfIdealSize = idealSize; 78 float childCalcIdealLength = 0.0f; 79 // Invalid size need not to calculate margin 80 if (!idealSize.IsNonPositive() && ((axis == Axis::HORIZONTAL && idealSize.Width().has_value()) || 81 (axis == Axis::VERTICAL && idealSize.Height().has_value()))) { 82 auto length = axis == Axis::HORIZONTAL ? idealSize.Width().value() : idealSize.Height().value(); 83 childCalcIdealLength = 84 (length - itemSpace * itemSpaceCount - static_cast<float>(prevMargin + nextMargin)) / displayCount; 85 if (LessNotEqual(childCalcIdealLength, 0.0)) { 86 // prioritize margin and displayCount, ignore itemSpace to create a positive idealLength. 87 property->MarkIgnoreItemSpace(); 88 childCalcIdealLength = (length - static_cast<float>(prevMargin + nextMargin)) / displayCount; 89 } 90 if (CheckMarginPropertyExceed(property, childCalcIdealLength)) { 91 prevMargin = 0.0; 92 nextMargin = 0.0; 93 itemSpaceCount = CaculateDisplayItemSpaceCount(property, prevMargin, nextMargin); 94 childCalcIdealLength = (length - itemSpace * itemSpaceCount) / displayCount; 95 if (LessNotEqual(childCalcIdealLength, 0.0)) { 96 childCalcIdealLength = length / displayCount; 97 } else { 98 property->ResetIgnoreItemSpace(); 99 } 100 } 101 axis == Axis::HORIZONTAL ? childSelfIdealSize.SetWidth(childCalcIdealLength) 102 : childSelfIdealSize.SetHeight(childCalcIdealLength); 103 } 104 105 layoutConstraint.selfIdealSize = childSelfIdealSize; 106 return layoutConstraint; 107 } 108 CaculateDisplayItemSpaceCount(const RefPtr<SwiperLayoutProperty> & property,double prevMargin,double nextMargin)109 static int32_t CaculateDisplayItemSpaceCount( 110 const RefPtr<SwiperLayoutProperty>& property, double prevMargin, double nextMargin) 111 { 112 CHECK_NULL_RETURN(property, 0); 113 auto count = property->GetDisplayCountValue(1); 114 count = (Positive(static_cast<double>(count)) ? count : 1); 115 if (Positive(prevMargin) && Positive(nextMargin)) { 116 return count + 1; 117 } else if (NonPositive(prevMargin) && NonPositive(nextMargin)) { 118 return count - 1; 119 } else { 120 return count; 121 } 122 } 123 ComputePageIndex(int32_t index,int32_t displayCount)124 static int32_t ComputePageIndex(int32_t index, int32_t displayCount) 125 { 126 if (displayCount <= 0) { 127 return index; 128 } 129 130 return static_cast<int32_t>(std::floor(static_cast<float>(index) / static_cast<float>(displayCount))) * 131 displayCount; 132 } 133 ComputePageEndIndex(int32_t index,int32_t displayCount)134 static int32_t ComputePageEndIndex(int32_t index, int32_t displayCount) 135 { 136 if (displayCount <= 0) { 137 return index; 138 } 139 140 return static_cast<int32_t>(std::floor(static_cast<float>(index) / static_cast<float>(displayCount))) * 141 displayCount + 142 displayCount - 1; 143 } 144 CheckAutoFillDisplayCount(RefPtr<SwiperLayoutProperty> & swiperLayoutProperty,float contentWidth,int32_t totalCount)145 static void CheckAutoFillDisplayCount( 146 RefPtr<SwiperLayoutProperty>& swiperLayoutProperty, float contentWidth, int32_t totalCount) 147 { 148 CHECK_NULL_VOID(swiperLayoutProperty); 149 bool isAutoFill = swiperLayoutProperty->GetMinSize().has_value(); 150 if (!isAutoFill) { 151 return; 152 } 153 auto minSize = swiperLayoutProperty->GetMinSize()->ConvertToPx(); 154 auto displayCount = 155 static_cast<int32_t>(floor((contentWidth - 2 * SWIPER_MARGIN.ConvertToPx() + SWIPER_GUTTER.ConvertToPx()) / 156 (minSize + SWIPER_GUTTER.ConvertToPx()))); 157 if (LessOrEqual(minSize, 0)) { 158 displayCount = 1; 159 } 160 displayCount = displayCount > 0 ? displayCount : 1; 161 displayCount = displayCount > totalCount ? totalCount : displayCount; 162 163 auto displayCountProperty = swiperLayoutProperty->GetDisplayCount().value_or(1); 164 if (displayCountProperty != displayCount) { 165 swiperLayoutProperty->UpdateDisplayCount(displayCount); 166 } 167 } 168 CheckIsSingleCase(const RefPtr<SwiperLayoutProperty> & property)169 static bool CheckIsSingleCase(const RefPtr<SwiperLayoutProperty>& property) 170 { 171 bool hasMinSize = property->GetMinSize().has_value() && !LessOrEqual(property->GetMinSizeValue().Value(), 0); 172 bool hasPrevMargin = Positive(property->GetCalculatedPrevMargin()); 173 bool hasNextMargin = Positive(property->GetCalculatedNextMargin()); 174 175 return !hasMinSize && (!hasPrevMargin && !hasNextMargin) && 176 ((property->GetDisplayCount().has_value() && property->GetDisplayCountValue() == 1) || 177 (!property->GetDisplayCount().has_value() && SwiperUtils::IsStretch(property))); 178 } 179 180 private: CheckMarginPropertyExceed(const RefPtr<SwiperLayoutProperty> & property,float childCalcIdealLength)181 static bool CheckMarginPropertyExceed(const RefPtr<SwiperLayoutProperty>& property, float childCalcIdealLength) 182 { 183 CHECK_NULL_RETURN(property, false); 184 auto prevMargin = property->GetPrevMarginValue(0.0_px).ConvertToPx(); 185 auto nextMargin = property->GetNextMarginValue(0.0_px).ConvertToPx(); 186 if (GreatNotEqual(prevMargin, childCalcIdealLength) || GreatNotEqual(nextMargin, childCalcIdealLength)) { 187 property->MarkIgnorePrevMarginAndNextMargin(); 188 return true; 189 } 190 return false; 191 } 192 }; 193 194 /** 195 * @brief Helper RAII object. set @c var to @c value when this object goes out of scope. 196 * REQUIRES: the life span of @c var surpasses this object. 197 */ 198 template<typename T> 199 class DestructSetter { 200 public: 201 DestructSetter() = delete; DestructSetter(T & var,T value)202 DestructSetter(T& var, T value) : ref_(var), value_(value) {} ~DestructSetter()203 ~DestructSetter() 204 { 205 ref_ = value_; 206 } 207 208 private: 209 T& ref_; 210 T value_ {}; 211 212 ACE_DISALLOW_COPY_AND_MOVE(DestructSetter); 213 }; 214 } // namespace OHOS::Ace::NG 215 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_SWIPER_SWIPER_UTILS_H 216