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