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