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