• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-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/text_picker/textpicker_layout_algorithm.h"
17 
18 #include "core/components_ng/property/measure_utils.h"
19 
20 namespace OHOS::Ace::NG {
21 
22 namespace {
23 const int32_t DIVIDER_SIZE = 2;
24 const float PICKER_HEIGHT_HALF = 3.5f;
25 const float ITEM_HEIGHT_HALF = 2.0f;
26 const int32_t MAX_HALF_DISPLAY_COUNT = 2;
27 const int32_t BUFFER_NODE_NUMBER = 2;
28 const float DOUBLE_VALUE = 2.0f;
29 constexpr double PERCENT_100 = 100.0f;
30 constexpr double PERCENT_120 = 1.2f;
31 constexpr float DEFAULT_FONT_SCALE = 1.f;
32 
CreatePercentGradientColor(float percent,Color color)33 GradientColor CreatePercentGradientColor(float percent, Color color)
34 {
35     NG::GradientColor gredient = GradientColor(color);
36     gredient.SetDimension(CalcDimension(percent * PERCENT_100, DimensionUnit::PERCENT));
37     return gredient;
38 }
39 } // namespace
40 
Measure(LayoutWrapper * layoutWrapper)41 void TextPickerLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
42 {
43     auto pipeline = PipelineContext::GetCurrentContext();
44     CHECK_NULL_VOID(pipeline);
45     auto pickerTheme = pipeline->GetTheme<PickerTheme>();
46     CHECK_NULL_VOID(pickerTheme);
47     auto dialogTheme = pipeline->GetTheme<DialogTheme>();
48     CHECK_NULL_VOID(dialogTheme);
49     SizeF frameSize = { -1.0f, -1.0f };
50 
51     auto columnNode = layoutWrapper->GetHostNode();
52     CHECK_NULL_VOID(columnNode);
53     auto blendNode = DynamicCast<FrameNode>(columnNode->GetParent());
54     CHECK_NULL_VOID(blendNode);
55     auto stackNode = DynamicCast<FrameNode>(blendNode->GetParent());
56     CHECK_NULL_VOID(stackNode);
57     auto pickerNode = DynamicCast<FrameNode>(stackNode->GetParent());
58     CHECK_NULL_VOID(pickerNode);
59     auto layoutProperty = pickerNode->GetLayoutProperty<TextPickerLayoutProperty>();
60     CHECK_NULL_VOID(layoutProperty);
61     auto textPickerPattern = pickerNode->GetPattern<TextPickerPattern>();
62     CHECK_NULL_VOID(textPickerPattern);
63 
64     GetColumnSize(layoutProperty, pickerTheme, dialogTheme, frameSize, pickerNode);
65 
66     textPickerPattern->CheckAndUpdateColumnSize(frameSize, columnNode, NeedAdaptForAging());
67     pickerItemHeight_ = frameSize.Height();
68     layoutWrapper->GetGeometryNode()->SetFrameSize(frameSize);
69     MeasureText(layoutWrapper, pickerTheme, frameSize);
70     float gradientPercent = GetGradientPercent(layoutProperty, textPickerPattern, frameSize, pickerTheme);
71     InitGradient(gradientPercent, blendNode, columnNode);
72 }
73 
GetGradientPercent(const RefPtr<TextPickerLayoutProperty> & layoutProperty,const RefPtr<TextPickerPattern> & textPickerPattern,SizeF & frameSize,const RefPtr<PickerTheme> & pickerTheme)74 float TextPickerLayoutAlgorithm::GetGradientPercent(const RefPtr<TextPickerLayoutProperty>& layoutProperty,
75     const RefPtr<TextPickerPattern>& textPickerPattern, SizeF& frameSize, const RefPtr<PickerTheme>& pickerTheme)
76 {
77     float gradientPercent = 0.0f;
78     bool isGradientHeight = layoutProperty->HasGradientHeight();
79     if (LessNotEqual(textPickerPattern->GetGradientHeight().ConvertToPx(), 0.0f)) {
80         isGradientHeight = false;
81     }
82     if (isGradientHeight) {
83         auto gradientheight = textPickerPattern->GetGradientHeight();
84         float gradientheightValue = 0.0f;
85         if (gradientheight.Unit() == DimensionUnit::PERCENT) {
86             gradientheightValue = frameSize.Height() * gradientheight.Value() / DOUBLE_VALUE;
87         } else {
88             gradientheightValue = gradientheight.ConvertToPx();
89         }
90         if ((frameSize.Height() / DOUBLE_VALUE) < gradientheightValue) {
91             gradientPercent = static_cast<float>
92                 (pickerTheme->GetGradientHeight().ConvertToPx()) * gradientFontScale_ / frameSize.Height();
93         } else {
94             gradientPercent = gradientheightValue / frameSize.Height();
95         }
96     } else {
97         gradientPercent = static_cast<float>(pickerTheme->GetGradientHeight().ConvertToPx()) * gradientFontScale_ /
98                           frameSize.Height();
99     }
100     return gradientPercent;
101 }
102 
GetColumnSize(const RefPtr<TextPickerLayoutProperty> & layoutProperty,const RefPtr<PickerTheme> & pickerTheme,const RefPtr<DialogTheme> & dialogTheme,SizeF & frameSize,const RefPtr<FrameNode> & pickerNode)103 void TextPickerLayoutAlgorithm::GetColumnSize(const RefPtr<TextPickerLayoutProperty>& layoutProperty,
104     const RefPtr<PickerTheme>& pickerTheme, const RefPtr<DialogTheme>& dialogTheme, SizeF& frameSize,
105     const RefPtr<FrameNode>& pickerNode)
106 {
107     float pickerHeight = 0.0f;
108     isDefaultPickerItemHeight_ = layoutProperty->HasDefaultPickerItemHeight();
109     if (isDefaultPickerItemHeight_) {
110         auto defaultPickerItemHeightValue = layoutProperty->GetDefaultPickerItemHeightValue();
111         if (LessOrEqual(defaultPickerItemHeightValue.Value(), 0.0f)) {
112             isDefaultPickerItemHeight_ = false;
113         } else {
114             UpdateDefaultPickerItemHeightLPX(pickerNode, defaultPickerItemHeightValue);
115         }
116     }
117     uint32_t showCount = pickerTheme->GetShowCountPortrait();
118     if (SystemProperties::GetDeviceOrientation() == DeviceOrientation::LANDSCAPE) {
119         showCount = pickerTheme->GetShowCountLandscape();
120     }
121     auto textPickerPattern = pickerNode->GetPattern<TextPickerPattern>();
122     CHECK_NULL_VOID(textPickerPattern);
123 
124     auto isUserSetDividerSpacingFont = textPickerPattern->GetIsUserSetDividerSpacingFont();
125     auto isUserSetGradientFont = textPickerPattern->GetIsUserSetGradientFont();
126     if (isUserSetDividerSpacingFont) {
127         dividerSpacingFontScale_ = ReCalcItemHeightScale(textPickerPattern->GetDividerSpacing());
128         textPickerPattern->SetPaintDividerSpacing(dividerSpacingFontScale_);
129     }
130     if (isUserSetGradientFont) {
131         gradientFontScale_ = ReCalcItemHeightScale(textPickerPattern->GetGradientHeight(), false);
132     }
133     if (isDefaultPickerItemHeight_) {
134         pickerHeight = static_cast<float>(defaultPickerItemHeight_ * showCount);
135     } else {
136         pickerHeight = static_cast<float>(pickerTheme->GetGradientHeight().ConvertToPx() *
137             (static_cast<int32_t>(showCount) - 1) * gradientFontScale_ +
138             pickerTheme->GetDividerSpacing().ConvertToPx() * dividerSpacingFontScale_);
139     }
140     auto layoutConstraint = pickerNode->GetLayoutProperty()->GetLayoutConstraint();
141     float pickerWidth = static_cast<float>((pickerTheme->GetDividerSpacing() * DIVIDER_SIZE).ConvertToPx());
142     if (textPickerPattern->GetIsShowInDialog()) {
143         float dialogButtonHeight =
144             static_cast<float>((pickerTheme->GetButtonHeight() + dialogTheme->GetDividerHeight() +
145                 dialogTheme->GetDividerPadding().Bottom() + pickerTheme->GetContentMarginVertical() * 2).ConvertToPx());
146         pickerHeight = std::min(pickerHeight, layoutConstraint->maxSize.Height() - dialogButtonHeight);
147         if (isDefaultPickerItemHeight_) {
148             defaultPickerItemHeight_ = (showCount == 0) ? defaultPickerItemHeight_ : pickerHeight / showCount;
149             textPickerPattern->SetResizePickerItemHeight(defaultPickerItemHeight_);
150             textPickerPattern->SetResizeFlag(true);
151         }
152     }
153     frameSize.SetWidth(pickerWidth);
154     frameSize.SetHeight(pickerHeight);
155 }
156 
UpdateDefaultPickerItemHeightLPX(const RefPtr<FrameNode> & pickerNode,const Dimension & defaultPickerItemHeightValue)157 void TextPickerLayoutAlgorithm::UpdateDefaultPickerItemHeightLPX(
158     const RefPtr<FrameNode>& pickerNode, const Dimension& defaultPickerItemHeightValue)
159 {
160     if (defaultPickerItemHeight_ != defaultPickerItemHeightValue.Value() &&
161         defaultPickerItemHeightValue.Unit() == DimensionUnit::LPX) {
162         CHECK_NULL_VOID(pickerNode);
163         auto context = pickerNode->GetContext();
164         CHECK_NULL_VOID(context);
165         defaultPickerItemHeight_ = context->NormalizeToPx(defaultPickerItemHeightValue);
166     }
167 }
168 
InitGradient(const float & gradientPercent,const RefPtr<FrameNode> blendNode,const RefPtr<FrameNode> columnNode)169 void TextPickerLayoutAlgorithm::InitGradient(const float& gradientPercent, const RefPtr<FrameNode> blendNode,
170     const RefPtr<FrameNode> columnNode)
171 {
172     auto blendRenderContext = blendNode->GetRenderContext();
173     auto columnRenderContext = columnNode->GetRenderContext();
174     CHECK_NULL_VOID(blendRenderContext);
175     CHECK_NULL_VOID(columnRenderContext);
176 
177     NG::Gradient gradient;
178     gradient.CreateGradientWithType(NG::GradientType::LINEAR);
179     gradient.AddColor(CreatePercentGradientColor(0, Color::TRANSPARENT));
180     gradient.AddColor(CreatePercentGradientColor(gradientPercent, Color::WHITE));
181     gradient.AddColor(CreatePercentGradientColor(1 - gradientPercent, Color::WHITE));
182     gradient.AddColor(CreatePercentGradientColor(1, Color::TRANSPARENT));
183 
184     columnRenderContext->UpdateBackBlendMode(BlendMode::SRC_IN);
185     columnRenderContext->UpdateBackBlendApplyType(BlendApplyType::OFFSCREEN);
186     blendRenderContext->UpdateLinearGradient(gradient);
187     blendRenderContext->UpdateBackBlendMode(BlendMode::SRC_OVER);
188     blendRenderContext->UpdateBackBlendApplyType(BlendApplyType::OFFSCREEN);
189 }
190 
MeasureText(LayoutWrapper * layoutWrapper,const SizeF & size)191 void TextPickerLayoutAlgorithm::MeasureText(LayoutWrapper* layoutWrapper, const SizeF& size)
192 {
193     auto totalChild = layoutWrapper->GetTotalChildCount();
194     for (int32_t index = 0; index < totalChild; index++) {
195         auto child = layoutWrapper->GetOrCreateChildByIndex(index);
196         ChangeTextStyle(index, totalChild, size, child, layoutWrapper);
197     }
198 }
199 
ChangeTextStyle(uint32_t index,uint32_t showOptionCount,const SizeF & size,const RefPtr<LayoutWrapper> & childLayoutWrapper,LayoutWrapper * layoutWrapper)200 void TextPickerLayoutAlgorithm::ChangeTextStyle(uint32_t index, uint32_t showOptionCount, const SizeF& size,
201     const RefPtr<LayoutWrapper>& childLayoutWrapper, LayoutWrapper* layoutWrapper)
202 {
203     SizeF frameSize = { -1.0f, -1.0f };
204     auto pipeline = PipelineContext::GetCurrentContext();
205     CHECK_NULL_VOID(pipeline);
206     auto pickerTheme = pipeline->GetTheme<PickerTheme>();
207     CHECK_NULL_VOID(pickerTheme);
208     frameSize.SetWidth(size.Width());
209     uint32_t selectedIndex = showOptionCount / 2; // the center option is selected.
210     auto layoutChildConstraint = layoutWrapper->GetLayoutProperty()->CreateChildConstraint();
211     if (isDefaultPickerItemHeight_) {
212         frameSize.SetHeight(static_cast<float>(defaultPickerItemHeight_));
213     } else {
214         if (index == selectedIndex) {
215             frameSize.SetHeight(
216                 static_cast<float>(pickerTheme->GetDividerSpacing().ConvertToPx() * dividerSpacingFontScale_));
217         } else {
218             frameSize.SetHeight(
219                 static_cast<float>(pickerTheme->GetGradientHeight().ConvertToPx() * gradientFontScale_));
220         }
221     }
222     layoutChildConstraint.selfIdealSize = { frameSize.Width(), frameSize.Height() };
223     childLayoutWrapper->Measure(layoutChildConstraint);
224 }
225 
Layout(LayoutWrapper * layoutWrapper)226 void TextPickerLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
227 {
228     CHECK_NULL_VOID(layoutWrapper);
229     auto pipeline = PipelineContext::GetCurrentContext();
230     CHECK_NULL_VOID(pipeline);
231     auto pickerTheme = pipeline->GetTheme<PickerTheme>();
232     CHECK_NULL_VOID(pickerTheme);
233     auto layoutProperty = AceType::DynamicCast<LinearLayoutProperty>(layoutWrapper->GetLayoutProperty());
234     CHECK_NULL_VOID(layoutProperty);
235     auto geometryNode = layoutWrapper->GetGeometryNode();
236     CHECK_NULL_VOID(geometryNode);
237 
238     auto size = geometryNode->GetFrameSize();
239     auto padding = layoutProperty->CreatePaddingAndBorder();
240     MinusPaddingToSize(padding, size);
241     auto children = layoutWrapper->GetAllChildrenWithBuild();
242     float childStartCoordinate = 0.0f;
243 
244     if (isDefaultPickerItemHeight_) {
245         childStartCoordinate +=
246             static_cast<float>(size.Height() / ITEM_HEIGHT_HALF - defaultPickerItemHeight_ * PICKER_HEIGHT_HALF);
247         halfDisplayCounts_ =
248             std::clamp(static_cast<int32_t>(
249                            std::ceil((size.Height() / ITEM_HEIGHT_HALF - defaultPickerItemHeight_ / ITEM_HEIGHT_HALF) /
250                                      defaultPickerItemHeight_)),
251                 0, MAX_HALF_DISPLAY_COUNT);
252     } else {
253         childStartCoordinate += static_cast<float>(pickerItemHeight_ / ITEM_HEIGHT_HALF -
254             pickerTheme->GetGradientHeight().ConvertToPx() * gradientFontScale_ * (ITEM_HEIGHT_HALF + 1) -
255             pickerTheme->GetDividerSpacing().ConvertToPx() * dividerSpacingFontScale_ / ITEM_HEIGHT_HALF);
256         halfDisplayCounts_ = std::clamp(static_cast<int32_t>(std::ceil(
257             (pickerItemHeight_ / ITEM_HEIGHT_HALF - pickerTheme->GetDividerSpacing().ConvertToPx() / ITEM_HEIGHT_HALF) /
258             pickerTheme->GetGradientHeight().ConvertToPx())), 0, MAX_HALF_DISPLAY_COUNT);
259     }
260     int32_t i = 0;
261     int32_t showCount = static_cast<int32_t>(pickerTheme->GetShowOptionCount()) + BUFFER_NODE_NUMBER;
262     for (const auto& child : children) {
263         if (i >= showCount || i >= static_cast<int32_t>(currentOffset_.size())) {
264             break;
265         }
266         auto childGeometryNode = child->GetGeometryNode();
267         auto childSize = childGeometryNode->GetMarginFrameSize();
268         auto childOffset =
269             OffsetF(0.0f, childStartCoordinate + static_cast<float>(currentOffset_[i++]) + padding.Offset().GetY());
270         childGeometryNode->SetMarginFrameOffset(childOffset);
271         child->Layout();
272         childStartCoordinate += childSize.Height();
273     }
274 }
275 
NeedAdaptForAging()276 bool TextPickerLayoutAlgorithm::NeedAdaptForAging()
277 {
278     auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
279     CHECK_NULL_RETURN(pipeline, false);
280     auto pickerTheme = pipeline->GetTheme<PickerTheme>();
281     CHECK_NULL_RETURN(pickerTheme, false);
282 
283     return GreatOrEqual(pipeline->GetFontScale(), pickerTheme->GetMaxOneFontScale());
284 }
285 
AdjustFontSizeScale(const Dimension & fontSizeValue,double fontScale)286 const Dimension TextPickerLayoutAlgorithm::AdjustFontSizeScale(const Dimension& fontSizeValue, double fontScale)
287 {
288     auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
289     CHECK_NULL_RETURN(pipeline, fontSizeValue);
290     auto pickerTheme = pipeline->GetTheme<PickerTheme>();
291     CHECK_NULL_RETURN(pickerTheme, fontSizeValue);
292 
293     double adjustedScale = std::clamp(fontScale, pickerTheme->GetNormalFontScale(),
294         pickerTheme->GetMaxTwoFontScale());
295     auto result = 0.0_vp;
296     if (!NearZero(fontScale)) {
297         result =  fontSizeValue / fontScale * adjustedScale;
298     }
299     return result;
300 }
301 
ReCalcItemHeightScale(const Dimension & userSetHeight,bool isDividerSpacing)302 float TextPickerLayoutAlgorithm::ReCalcItemHeightScale(const Dimension& userSetHeight, bool isDividerSpacing)
303 {
304     auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
305     CHECK_NULL_RETURN(pipeline, DEFAULT_FONT_SCALE);
306     auto pickerTheme = pipeline->GetTheme<PickerTheme>();
307     CHECK_NULL_RETURN(pickerTheme, DEFAULT_FONT_SCALE);
308 
309     auto systemFontScale = static_cast<double>(pipeline->GetFontScale());
310     auto themePadding = pickerTheme->GetPickerDialogFontPadding();
311     auto userSetHeightValue = AdjustFontSizeScale(userSetHeight, systemFontScale).ConvertToPx();
312     double adjustedScale =
313         std::clamp(systemFontScale, pickerTheme->GetNormalFontScale(), pickerTheme->GetMaxTwoFontScale());
314     if (NearZero(adjustedScale)) {
315         return DEFAULT_FONT_SCALE;
316     }
317     userSetHeightValue = userSetHeightValue / adjustedScale * PERCENT_120 + (themePadding.ConvertToPx() * DIVIDER_SIZE);
318     auto themeHeightLimit =
319         isDividerSpacing ? pickerTheme->GetDividerSpacingLimit() : pickerTheme->GetGradientHeightLimit();
320     auto themeHeight = isDividerSpacing ? pickerTheme->GetDividerSpacing() : pickerTheme->GetGradientHeight();
321     if (GreatOrEqualCustomPrecision(userSetHeightValue, themeHeightLimit.ConvertToPx())) {
322         userSetHeightValue = themeHeightLimit.ConvertToPx();
323     } else {
324         userSetHeightValue = std::max(userSetHeightValue, themeHeight.ConvertToPx());
325     }
326 
327     if (NearZero(themeHeight.ConvertToPx())) {
328         return DEFAULT_FONT_SCALE;
329     }
330 
331     return std::max(static_cast<float>(userSetHeightValue / themeHeight.ConvertToPx()), DEFAULT_FONT_SCALE);
332 }
333 
MeasureText(LayoutWrapper * layoutWrapper,const RefPtr<PickerTheme> & pickerTheme,const SizeF & size)334 void TextPickerLayoutAlgorithm::MeasureText(LayoutWrapper* layoutWrapper, const RefPtr<PickerTheme>& pickerTheme,
335     const SizeF& size)
336 {
337     auto totalChild = layoutWrapper->GetTotalChildCount();
338     CHECK_EQUAL_VOID(totalChild, 0);
339 
340     auto selectedIndex = totalChild / 2;
341     auto layoutChildConstraint = layoutWrapper->GetLayoutProperty()->CreateChildConstraint();
342     const float dividerHeight = static_cast<float>(pickerTheme->GetDividerSpacing().ConvertToPx() * \
343         dividerSpacingFontScale_);
344     const float gradientHeight = static_cast<float>(pickerTheme->GetGradientHeight().ConvertToPx() * \
345         gradientFontScale_);
346     const float defaultHeight = static_cast<float>(defaultPickerItemHeight_);
347 
348     for (auto index = 0; index < totalChild; index++) {
349         auto child = layoutWrapper->GetOrCreateChildByIndex(index);
350         SizeF frameSize = { size.Width(), -1.0f };
351         if (isDefaultPickerItemHeight_) {
352             frameSize.SetHeight(defaultHeight);
353         } else {
354             if (index == selectedIndex) {
355                 frameSize.SetHeight(dividerHeight);
356             } else {
357                 frameSize.SetHeight(gradientHeight);
358             }
359         }
360         layoutChildConstraint.selfIdealSize = { frameSize.Width(), frameSize.Height() };
361         child->Measure(layoutChildConstraint);
362     }
363 }
364 
365 } // namespace OHOS::Ace::NG
366