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/slider/slider_layout_algorithm.h"
17
18 #include "core/components_ng/pattern/slider/slider_pattern.h"
19 #include "core/pipeline_ng/pipeline_context.h"
20
21 namespace OHOS::Ace::NG {
22 namespace {
23 constexpr float HALF = 0.5f;
24 const Dimension shrinkWidth = 16.0_vp;
25 const Dimension thresholdWidth = 34.5_vp; //The slider shortens the width of the threshold
JudgeTrackness(Axis direction,float blockDiameter,float trackThickness,float width,float height)26 bool JudgeTrackness(Axis direction, float blockDiameter, float trackThickness, float width, float height)
27 {
28 if (direction == Axis::HORIZONTAL) {
29 return blockDiameter > height || trackThickness > height;
30 }
31 return blockDiameter > width || trackThickness > width;
32 }
33
GetTheme()34 RefPtr<SliderTheme> GetTheme()
35 {
36 auto pipeline = PipelineBase::GetCurrentContext();
37 CHECK_NULL_RETURN(pipeline, nullptr);
38 return pipeline->GetTheme<SliderTheme>();
39 }
40 } // namespace
41
CalculateHotSize(LayoutWrapper * layoutWrapper,const SizeF & blockSize,float themeBlockHotSize)42 SizeF SliderLayoutAlgorithm::CalculateHotSize(
43 LayoutWrapper* layoutWrapper, const SizeF& blockSize, float themeBlockHotSize)
44 {
45 CHECK_NULL_RETURN(layoutWrapper, SizeF());
46 auto frameNode = layoutWrapper->GetHostNode();
47 CHECK_NULL_RETURN(frameNode, SizeF());
48 auto sliderLayoutProperty = DynamicCast<SliderLayoutProperty>(layoutWrapper->GetLayoutProperty());
49 CHECK_NULL_RETURN(sliderLayoutProperty, SizeF());
50 auto sliderMode = sliderLayoutProperty->GetSliderMode().value_or(SliderModel::SliderMode::OUTSET);
51 SizeF blockHotSize = blockSize;
52 if (sliderMode == SliderModel::SliderMode::NONE) {
53 auto hotSize = std::max(themeBlockHotSize, trackThickness_);
54 blockHotSize = SizeF(hotSize, hotSize);
55 } else {
56 if (LessNotEqual(blockHotSize.Width(), themeBlockHotSize)) {
57 blockHotSize.SetWidth(themeBlockHotSize);
58 }
59 if (LessNotEqual(blockHotSize.Height(), themeBlockHotSize)) {
60 blockHotSize.SetHeight(themeBlockHotSize);
61 }
62 }
63 return blockHotSize;
64 }
65
MeasureContent(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)66 std::optional<SizeF> SliderLayoutAlgorithm::MeasureContent(
67 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
68 {
69 auto frameNode = layoutWrapper->GetHostNode();
70 CHECK_NULL_RETURN(frameNode, std::nullopt);
71 auto pattern = frameNode->GetPattern<SliderPattern>();
72 CHECK_NULL_RETURN(pattern, std::nullopt);
73 if (pattern->UseContentModifier()) {
74 if (frameNode->GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)) {
75 frameNode->GetGeometryNode()->ResetContent();
76 } else {
77 frameNode->GetGeometryNode()->Reset();
78 }
79 return std::nullopt;
80 }
81 auto sliderLayoutProperty = DynamicCast<SliderLayoutProperty>(layoutWrapper->GetLayoutProperty());
82 CHECK_NULL_RETURN(sliderLayoutProperty, std::nullopt);
83 auto theme = GetTheme();
84 CHECK_NULL_RETURN(theme, std::nullopt);
85
86 float width = contentConstraint.selfIdealSize.Width().value_or(contentConstraint.maxSize.Width());
87 float height = contentConstraint.selfIdealSize.Height().value_or(contentConstraint.maxSize.Height());
88 auto layoutPolicy = GetLayoutPolicy(layoutWrapper);
89 if (layoutPolicy.has_value() && layoutPolicy->IsMatch()) {
90 if (layoutPolicy->IsWidthMatch()) {
91 width = contentConstraint.parentIdealSize.Width().value();
92 }
93 if (layoutPolicy->IsHeightMatch()) {
94 height = contentConstraint.parentIdealSize.Height().value();
95 }
96 }
97 Axis direction = sliderLayoutProperty->GetDirection().value_or(Axis::HORIZONTAL);
98 if (direction == Axis::HORIZONTAL && GreaterOrEqualToInfinity(width)) {
99 width = static_cast<float>(theme->GetLayoutMaxLength().ConvertToPx());
100 }
101 if (direction == Axis::VERTICAL && GreaterOrEqualToInfinity(height)) {
102 height = static_cast<float>(theme->GetLayoutMaxLength().ConvertToPx());
103 }
104
105 Dimension themeTrackThickness;
106 Dimension themeBlockSize;
107 Dimension hotBlockShadowWidth;
108 Dimension themeBlockHotSize;
109 GetStyleThemeValue(layoutWrapper, themeTrackThickness, themeBlockSize, hotBlockShadowWidth, themeBlockHotSize);
110 auto thickness = sliderLayoutProperty->GetThickness().value_or(themeTrackThickness);
111 trackThickness_ =
112 static_cast<float>(thickness.Unit() == DimensionUnit::PERCENT
113 ? thickness.ConvertToPxWithSize(direction == Axis::HORIZONTAL ? height : width)
114 : thickness.ConvertToPx());
115 // this scaleValue ensure that the size ratio of the block and trackThickness is consistent
116 float scaleValue = trackThickness_ / static_cast<float>(themeTrackThickness.ConvertToPx());
117 auto blockDiameter = scaleValue * static_cast<float>(themeBlockSize.ConvertToPx());
118 // trackThickness and blockDiameter will get from theme when they are greater than slider component height or width
119 if (JudgeTrackness(direction, blockDiameter, trackThickness_, width, height)) {
120 trackThickness_ = static_cast<float>(themeTrackThickness.ConvertToPx());
121 scaleValue = 1.0;
122 blockDiameter = static_cast<float>(themeBlockSize.ConvertToPx());
123 }
124 blockSize_ = sliderLayoutProperty->GetBlockSizeValue(SizeF(blockDiameter, blockDiameter));
125 blockHotSize_ = CalculateHotSize(layoutWrapper, blockSize_, static_cast<float>(themeBlockHotSize.ConvertToPx()));
126 pattern->UpdateSliderParams(trackThickness_, blockSize_, blockHotSize_);
127 auto mode = sliderLayoutProperty->GetSliderMode().value_or(SliderModel::SliderMode::OUTSET);
128 auto sliderWidth = CalculateSliderWidth(width, height, direction, hotBlockShadowWidth, mode);
129 float sliderLength =
130 CalculateSliderLength(width, height, direction, mode, (pattern->HasPrefix() || pattern->HasSuffix()));
131 return direction == Axis::HORIZONTAL ? SizeF(sliderLength, sliderWidth) : SizeF(sliderWidth, sliderLength);
132 }
133
CalculateSliderLength(float width,float height,Axis direction,SliderModel::SliderMode mode,bool Ends)134 float SliderLayoutAlgorithm::CalculateSliderLength(
135 float width, float height, Axis direction, SliderModel::SliderMode mode, bool Ends)
136 {
137 auto sliderLength = direction == Axis::HORIZONTAL ? width : height;
138 if (mode == SliderModel::SliderMode::OUTSET && Ends) {
139 sliderLength = sliderLength - static_cast<float>(shrinkWidth.ConvertToPx()) / HALF -
140 static_cast<float>(thresholdWidth.ConvertToPx());
141 }
142 return sliderLength;
143 }
144
CalculateSliderWidth(float width,float height,Axis direction,const Dimension & hotBlockShadowWidth,SliderModel::SliderMode mode)145 float SliderLayoutAlgorithm::CalculateSliderWidth(
146 float width, float height, Axis direction, const Dimension& hotBlockShadowWidth, SliderModel::SliderMode mode)
147 {
148 auto theme = GetTheme();
149 CHECK_NULL_RETURN(theme, 0.0f);
150 auto blockWidth = direction == Axis::HORIZONTAL ? blockSize_.Height() : blockSize_.Width();
151 auto blockHotWidth = direction == Axis::HORIZONTAL ? blockHotSize_.Height() : blockHotSize_.Width();
152 auto sliderWidth = static_cast<float>(theme->GetMeasureContentDefaultWidth().ConvertToPx());
153 if (mode == SliderModel::SliderMode::NONE || mode == SliderModel::SliderMode::OUTSET) {
154 sliderWidth = static_cast<float>(theme->GetMeasureContentOutsetWidth().ConvertToPx());
155 }
156 sliderWidth = std::max(sliderWidth, trackThickness_);
157 if (mode == SliderModel::SliderMode::OUTSET) {
158 sliderWidth = std::max(sliderWidth, blockHotWidth);
159 sliderWidth = std::max(sliderWidth, blockWidth + static_cast<float>(hotBlockShadowWidth.ConvertToPx()) / HALF);
160 }
161 sliderWidth = std::clamp(sliderWidth, 0.0f, direction == Axis::HORIZONTAL ? height : width);
162 return sliderWidth;
163 }
164
GetStyleThemeValue(LayoutWrapper * layoutWrapper,Dimension & themeTrackThickness,Dimension & themeBlockSize,Dimension & hotBlockShadowWidth,Dimension & themeBlockHotSize)165 void SliderLayoutAlgorithm::GetStyleThemeValue(LayoutWrapper* layoutWrapper, Dimension& themeTrackThickness,
166 Dimension& themeBlockSize, Dimension& hotBlockShadowWidth, Dimension& themeBlockHotSize)
167 {
168 CHECK_NULL_VOID(layoutWrapper);
169 auto frameNode = layoutWrapper->GetHostNode();
170 CHECK_NULL_VOID(frameNode);
171 auto sliderLayoutProperty = DynamicCast<SliderLayoutProperty>(layoutWrapper->GetLayoutProperty());
172 CHECK_NULL_VOID(sliderLayoutProperty);
173 auto pipeline = frameNode->GetContext();
174 CHECK_NULL_VOID(pipeline);
175 auto theme = pipeline->GetTheme<SliderTheme>();
176 CHECK_NULL_VOID(theme);
177 auto sliderMode = sliderLayoutProperty->GetSliderMode().value_or(SliderModel::SliderMode::OUTSET);
178 if (sliderMode == SliderModel::SliderMode::OUTSET) {
179 themeTrackThickness = theme->GetOutsetTrackThickness();
180 themeBlockSize = theme->GetOutsetBlockSize();
181 hotBlockShadowWidth = theme->GetOutsetHotBlockShadowWidth();
182 themeBlockHotSize = theme->GetOutsetBlockHotSize();
183 } else if (sliderMode == SliderModel::SliderMode::INSET) {
184 themeTrackThickness = theme->GetInsetTrackThickness();
185 themeBlockSize = theme->GetInsetBlockSize();
186 hotBlockShadowWidth = theme->GetInsetHotBlockShadowWidth();
187 themeBlockHotSize = theme->GetInsetBlockHotSize();
188 } else {
189 themeTrackThickness = theme->GetNoneTrackThickness();
190 themeBlockSize = Dimension(0);
191 hotBlockShadowWidth = Dimension(0);
192 themeBlockHotSize = theme->GetNoneBlockHotSize();
193 }
194 }
195
Measure(LayoutWrapper * layoutWrapper)196 void SliderLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
197 {
198 CHECK_NULL_VOID(layoutWrapper);
199 auto layoutProperty = layoutWrapper->GetLayoutProperty();
200 CHECK_NULL_VOID(layoutProperty);
201 auto layoutConstraint = layoutProperty->CreateChildConstraint();
202 auto sliderLayoutProperty = DynamicCast<SliderLayoutProperty>(layoutProperty);
203 CHECK_NULL_VOID(sliderLayoutProperty);
204 auto frameNode = layoutWrapper->GetHostNode();
205 CHECK_NULL_VOID(frameNode);
206 auto pattern = frameNode->GetPattern<SliderPattern>();
207 CHECK_NULL_VOID(pattern);
208 if (!pattern->UseContentModifier()) {
209 layoutConstraint.UpdateSelfMarginSizeWithCheck(OptionalSizeF(blockSize_.Width(), blockSize_.Height()));
210 }
211 if (pattern->HasPrefix() || pattern->HasSuffix()) {
212 const auto& children = layoutWrapper->GetAllChildrenWithBuild();
213 auto contentRect = layoutWrapper->GetGeometryNode()->GetContentRect();
214 auto axis = sliderLayoutProperty->GetDirection().value_or(Axis::HORIZONTAL);
215 if (axis == Axis::HORIZONTAL) {
216 maxWidth = contentRect.Width() * langRatio;
217 maxHeight = maxWidth * shortRatio;
218 } else {
219 maxHeight = contentRect.Height() * langRatio;
220 maxWidth = maxHeight * shortRatio;
221 }
222 if (pattern->HasPrefix()) {
223 auto prefixChild = layoutWrapper->GetOrCreateChildByIndex(0);
224 CHECK_NULL_VOID(prefixChild);
225 SetChildConstraint(prefixChild, maxWidth, maxHeight);
226 }
227 if (pattern->HasSuffix()) {
228 auto suffixChild = layoutWrapper->GetOrCreateChildByIndex(0);
229 if (pattern->HasPrefix()) {
230 suffixChild = layoutWrapper->GetOrCreateChildByIndex(1);
231 }
232 CHECK_NULL_VOID(suffixChild);
233 SetChildConstraint(suffixChild, maxWidth, maxHeight);
234 }
235 } else {
236 if (layoutWrapper->GetTotalChildCount() != 0) {
237 auto child = layoutWrapper->GetOrCreateChildByIndex(0);
238 CHECK_NULL_VOID(child);
239 child->Measure(layoutConstraint);
240 }
241 }
242 PerformMeasureSelf(layoutWrapper);
243 }
244
SetChildConstraint(RefPtr<LayoutWrapper> child,float maxWidth,float maxHeight)245 void SliderLayoutAlgorithm::SetChildConstraint(RefPtr<LayoutWrapper> child, float maxWidth, float maxHeight)
246 {
247 auto childLayoutProperty = child->GetLayoutProperty();
248 CHECK_NULL_VOID(childLayoutProperty);
249 LayoutConstraintF childConstraint = childLayoutProperty->CreateChildConstraint();
250 childConstraint.maxSize.SetWidth(maxWidth);
251 childConstraint.maxSize.SetHeight(maxHeight);
252 child->Measure(childConstraint);
253 }
254
Layout(LayoutWrapper * layoutWrapper)255 void SliderLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
256 {
257 auto host = layoutWrapper->GetHostNode();
258 CHECK_NULL_VOID(host);
259 auto pattern = DynamicCast<SliderPattern>(host->GetPattern());
260 CHECK_NULL_VOID(pattern);
261 if (pattern->UseContentModifier()) {
262 BoxLayoutAlgorithm::Layout(layoutWrapper);
263 return;
264 }
265 PerformLayout(layoutWrapper);
266 const auto& children = layoutWrapper->GetAllChildrenWithBuild();
267 if (children.empty()) {
268 return;
269 }
270
271 auto sliderLayoutProperty = host->GetLayoutProperty<SliderLayoutProperty>();
272 CHECK_NULL_VOID(sliderLayoutProperty);
273 auto pipeline = host->GetContext();
274 CHECK_NULL_VOID(pipeline);
275 auto theme = pipeline->GetTheme<SliderTheme>();
276 CHECK_NULL_VOID(theme);
277
278 auto contentRect = layoutWrapper->GetGeometryNode()->GetContentRect();
279 auto axis = sliderLayoutProperty->GetDirection().value_or(Axis::HORIZONTAL);
280 auto paintReverse = sliderLayoutProperty->GetReverseValue(false);
281 auto direction = sliderLayoutProperty->GetLayoutDirection();
282 if (axis == Axis::HORIZONTAL) {
283 auto isRTL = direction == TextDirection::AUTO ? AceApplicationInfo::GetInstance().IsRightToLeft()
284 : direction == TextDirection::RTL;
285 paintReverse = isRTL ? !paintReverse : paintReverse;
286 }
287 auto mode = sliderLayoutProperty->GetSliderMode().value_or(SliderModel::SliderMode::OUTSET);
288 Dimension hotBlockShadowWidth = mode == SliderModel::SliderMode::OUTSET ? theme->GetOutsetHotBlockShadowWidth()
289 : theme->GetInsetHotBlockShadowWidth();
290 auto length = axis == Axis::HORIZONTAL ? contentRect.Width() : contentRect.Height();
291 float BlockShadowWidth = static_cast<float>(hotBlockShadowWidth.ConvertToPx());
292 auto blockSize = axis == Axis::HORIZONTAL ? blockSize_.Width() : blockSize_.Height();
293 auto borderBlank = std::max(trackThickness_, blockSize + BlockShadowWidth / HALF);
294 auto sliderLength = length >= borderBlank ? length - borderBlank : 1;
295 borderBlank = (length - sliderLength) * HALF;
296 auto selectOffset = borderBlank + pattern->GetValueRatio() * sliderLength;
297 auto insetModeOffset = borderBlank + blockSize * HALF;
298
299 CalculateBlockOffset(layoutWrapper, contentRect, selectOffset, axis, paintReverse);
300 if (pattern->HasPrefix()) {
301 CalculatePrefixOffset(layoutWrapper, contentRect, insetModeOffset, axis, paintReverse);
302 }
303 if (pattern->HasSuffix()) {
304 CalculateSuffixOffset(layoutWrapper, contentRect, insetModeOffset, axis, paintReverse);
305 }
306 }
307
CalculatePrefixOffset(LayoutWrapper * layoutWrapper,const RectF & contentRect,float borderBlank,Axis axis,bool reverse)308 void SliderLayoutAlgorithm::CalculatePrefixOffset(
309 LayoutWrapper* layoutWrapper, const RectF& contentRect, float borderBlank, Axis axis, bool reverse)
310 {
311 auto host = layoutWrapper->GetHostNode();
312 CHECK_NULL_VOID(host);
313 auto pattern = DynamicCast<SliderPattern>(host->GetPattern());
314 CHECK_NULL_VOID(pattern);
315
316 const auto& children = layoutWrapper->GetAllChildrenWithBuild();
317 if (children.size() < 1) {
318 return;
319 }
320 auto prefixIter = std::next(children.begin(), 0);
321 auto prefixChild = *prefixIter;
322 prefixChild->Layout();
323 }
324
CalculateSuffixOffset(LayoutWrapper * layoutWrapper,const RectF & contentRect,float borderBlank,Axis axis,bool reverse)325 void SliderLayoutAlgorithm::CalculateSuffixOffset(
326 LayoutWrapper* layoutWrapper, const RectF& contentRect, float borderBlank, Axis axis, bool reverse)
327 {
328 auto host = layoutWrapper->GetHostNode();
329 CHECK_NULL_VOID(host);
330 auto pattern = DynamicCast<SliderPattern>(host->GetPattern());
331 CHECK_NULL_VOID(pattern);
332
333 const auto& children = layoutWrapper->GetAllChildrenWithBuild();
334 if (children.size() < 1) {
335 return;
336 }
337 auto suffixIter = std::next(children.begin(), 0);
338 if (children.size() > 1) {
339 suffixIter = std::next(children.begin(), 1);
340 }
341 auto suffixChild = *suffixIter;
342 suffixChild->Layout();
343 }
344
CalculateBlockOffset(LayoutWrapper * layoutWrapper,const RectF & contentRect,float selectOffset,Axis axis,bool reverse)345 void SliderLayoutAlgorithm::CalculateBlockOffset(
346 LayoutWrapper* layoutWrapper, const RectF& contentRect, float selectOffset, Axis axis, bool reverse)
347 {
348 auto host = layoutWrapper->GetHostNode();
349 CHECK_NULL_VOID(host);
350 auto pattern = DynamicCast<SliderPattern>(host->GetPattern());
351 CHECK_NULL_VOID(pattern);
352
353 const auto& children = layoutWrapper->GetAllChildrenWithBuild();
354 auto child = children.front();
355 auto childSize_ = child->GetGeometryNode()->GetMarginFrameSize();
356 OffsetF circleCenter;
357 auto animatableBlockCenter = pattern->GetAnimatableBlockCenter();
358 if (animatableBlockCenter.has_value()) {
359 circleCenter = animatableBlockCenter.value();
360 } else {
361 if (!reverse) {
362 if (axis == Axis::HORIZONTAL) {
363 circleCenter.SetX(selectOffset);
364 circleCenter.SetY(contentRect.Height() * HALF);
365 } else {
366 circleCenter.SetX(contentRect.Width() * HALF);
367 circleCenter.SetY(selectOffset);
368 }
369 } else {
370 if (axis == Axis::HORIZONTAL) {
371 circleCenter.SetX(contentRect.Width() - selectOffset);
372 circleCenter.SetY(contentRect.Height() * HALF);
373 } else {
374 circleCenter.SetX(contentRect.Width() * HALF);
375 circleCenter.SetY(contentRect.Height() - selectOffset);
376 }
377 }
378 circleCenter += OffsetF(contentRect.GetX(), contentRect.GetY());
379 }
380
381 OffsetF imageNodeOffset(
382 circleCenter.GetX() - childSize_.Width() * HALF, circleCenter.GetY() - childSize_.Height() * HALF);
383
384 child->GetGeometryNode()->SetMarginFrameOffset(imageNodeOffset);
385 child->Layout();
386 }
387
GetLayoutPolicy(LayoutWrapper * layoutWrapper)388 std::optional<NG::LayoutPolicyProperty> SliderLayoutAlgorithm::GetLayoutPolicy(LayoutWrapper* layoutWrapper)
389 {
390 CHECK_NULL_RETURN(layoutWrapper, NG::LayoutPolicyProperty());
391 auto layoutProperty = layoutWrapper->GetLayoutProperty();
392 CHECK_NULL_RETURN(layoutProperty, NG::LayoutPolicyProperty());
393 auto layoutPolicy = layoutProperty->GetLayoutPolicyProperty();
394 CHECK_NULL_RETURN(layoutPolicy, NG::LayoutPolicyProperty());
395 return layoutPolicy;
396 }
397 } // namespace OHOS::Ace::NG
398