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 "base/utils/utils.h"
19 #include "core/components_ng/layout/layout_wrapper.h"
20 #include "core/components_ng/pattern/slider/slider_layout_property.h"
21 #include "core/components_ng/pattern/slider/slider_pattern.h"
22 #include "core/pipeline_ng/pipeline_context.h"
23 
24 namespace OHOS::Ace::NG {
25 namespace {
26 constexpr float HALF = 0.5f;
JudgeTrackness(Axis direction,float blockDiameter,float trackThickness,float width,float height)27 bool JudgeTrackness(Axis direction, float blockDiameter, float trackThickness, float width, float height)
28 {
29     if (direction == Axis::HORIZONTAL) {
30         return blockDiameter > height || trackThickness > height;
31     }
32     return blockDiameter > width || trackThickness > width;
33 }
34 
GetTheme()35 RefPtr<SliderTheme> GetTheme()
36 {
37     auto pipeline = PipelineBase::GetCurrentContext();
38     CHECK_NULL_RETURN(pipeline, nullptr);
39     return pipeline->GetTheme<SliderTheme>();
40 }
41 } // namespace
42 
CalculateHotSize(LayoutWrapper * layoutWrapper,const SizeF & blockSize,float themeBlockHotSize)43 SizeF SliderLayoutAlgorithm::CalculateHotSize(
44     LayoutWrapper* layoutWrapper, const SizeF& blockSize, float themeBlockHotSize)
45 {
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         frameNode->GetGeometryNode()->Reset();
75         return std::nullopt;
76     }
77     auto sliderLayoutProperty = DynamicCast<SliderLayoutProperty>(layoutWrapper->GetLayoutProperty());
78     CHECK_NULL_RETURN(sliderLayoutProperty, std::nullopt);
79     auto theme = GetTheme();
80     CHECK_NULL_RETURN(theme, std::nullopt);
81 
82     float width = contentConstraint.selfIdealSize.Width().value_or(contentConstraint.maxSize.Width());
83     float height = contentConstraint.selfIdealSize.Height().value_or(contentConstraint.maxSize.Height());
84     Axis direction = sliderLayoutProperty->GetDirection().value_or(Axis::HORIZONTAL);
85     if (direction == Axis::HORIZONTAL && GreaterOrEqualToInfinity(width)) {
86         width = static_cast<float>(theme->GetLayoutMaxLength().ConvertToPx());
87     }
88     if (direction == Axis::VERTICAL && GreaterOrEqualToInfinity(height)) {
89         height = static_cast<float>(theme->GetLayoutMaxLength().ConvertToPx());
90     }
91 
92     Dimension themeTrackThickness;
93     Dimension themeBlockSize;
94     Dimension hotBlockShadowWidth;
95     Dimension themeBlockHotSize;
96     GetStyleThemeValue(layoutWrapper, themeTrackThickness, themeBlockSize, hotBlockShadowWidth, themeBlockHotSize);
97     auto thickness = sliderLayoutProperty->GetThickness().value_or(themeTrackThickness);
98     trackThickness_ =
99         static_cast<float>(thickness.Unit() == DimensionUnit::PERCENT
100                                ? thickness.ConvertToPxWithSize(direction == Axis::HORIZONTAL ? height : width)
101                                : thickness.ConvertToPx());
102     // this scaleValue ensure that the size ratio of the block and trackThickness is consistent
103     float scaleValue = trackThickness_ / static_cast<float>(themeTrackThickness.ConvertToPx());
104     auto blockDiameter = scaleValue * static_cast<float>(themeBlockSize.ConvertToPx());
105     // trackThickness and blockDiameter will get from theme when they are greater than slider component height or width
106     if (JudgeTrackness(direction, blockDiameter, trackThickness_, width, height)) {
107         trackThickness_ = static_cast<float>(themeTrackThickness.ConvertToPx());
108         scaleValue = 1.0;
109         blockDiameter = static_cast<float>(themeBlockSize.ConvertToPx());
110     }
111     blockSize_ = sliderLayoutProperty->GetBlockSizeValue(SizeF(blockDiameter, blockDiameter));
112     blockHotSize_ = CalculateHotSize(layoutWrapper, blockSize_, static_cast<float>(themeBlockHotSize.ConvertToPx()));
113     auto mode = sliderLayoutProperty->GetSliderMode().value_or(SliderModel::SliderMode::OUTSET);
114     auto sliderWidth = CalculateSliderWidth(width, height, direction, hotBlockShadowWidth, mode);
115     float sliderLength = direction == Axis::HORIZONTAL ? width : height;
116     return direction == Axis::HORIZONTAL ? SizeF(sliderLength, sliderWidth) : SizeF(sliderWidth, sliderLength);
117 }
118 
CalculateSliderWidth(float width,float height,Axis direction,const Dimension & hotBlockShadowWidth,SliderModel::SliderMode mode)119 float SliderLayoutAlgorithm::CalculateSliderWidth(
120     float width, float height, Axis direction, const Dimension& hotBlockShadowWidth, SliderModel::SliderMode mode)
121 {
122     auto theme = GetTheme();
123     auto blockWidth = direction == Axis::HORIZONTAL ? blockSize_.Height() : blockSize_.Width();
124     auto blockHotWidth = direction == Axis::HORIZONTAL ? blockHotSize_.Height() : blockHotSize_.Width();
125     auto sliderWidth = static_cast<float>(theme->GetMeasureContentDefaultWidth().ConvertToPx());
126     sliderWidth = std::max(sliderWidth, trackThickness_);
127     if (mode == SliderModel::SliderMode::OUTSET) {
128         sliderWidth = std::max(sliderWidth, blockHotWidth);
129         sliderWidth = std::max(sliderWidth, blockWidth + static_cast<float>(hotBlockShadowWidth.ConvertToPx()) / HALF);
130     }
131     sliderWidth = std::clamp(sliderWidth, 0.0f, direction == Axis::HORIZONTAL ? height : width);
132     return sliderWidth;
133 }
134 
GetStyleThemeValue(LayoutWrapper * layoutWrapper,Dimension & themeTrackThickness,Dimension & themeBlockSize,Dimension & hotBlockShadowWidth,Dimension & themeBlockHotSize)135 void SliderLayoutAlgorithm::GetStyleThemeValue(LayoutWrapper* layoutWrapper, Dimension& themeTrackThickness,
136     Dimension& themeBlockSize, Dimension& hotBlockShadowWidth, Dimension& themeBlockHotSize)
137 {
138     auto frameNode = layoutWrapper->GetHostNode();
139     CHECK_NULL_VOID(frameNode);
140     auto sliderLayoutProperty = DynamicCast<SliderLayoutProperty>(layoutWrapper->GetLayoutProperty());
141     CHECK_NULL_VOID(sliderLayoutProperty);
142     auto pipeline = PipelineBase::GetCurrentContext();
143     CHECK_NULL_VOID(pipeline);
144     auto theme = pipeline->GetTheme<SliderTheme>();
145     CHECK_NULL_VOID(theme);
146     auto sliderMode = sliderLayoutProperty->GetSliderMode().value_or(SliderModel::SliderMode::OUTSET);
147     if (sliderMode == SliderModel::SliderMode::OUTSET) {
148         themeTrackThickness = theme->GetOutsetTrackThickness();
149         themeBlockSize = theme->GetOutsetBlockSize();
150         hotBlockShadowWidth = theme->GetOutsetHotBlockShadowWidth();
151         themeBlockHotSize = theme->GetOutsetBlockHotSize();
152     } else if (sliderMode == SliderModel::SliderMode::INSET) {
153         themeTrackThickness = theme->GetInsetTrackThickness();
154         themeBlockSize = theme->GetInsetBlockSize();
155         hotBlockShadowWidth = theme->GetInsetHotBlockShadowWidth();
156         themeBlockHotSize = theme->GetInsetBlockHotSize();
157     } else {
158         themeTrackThickness = theme->GetNoneTrackThickness();
159         themeBlockSize = Dimension(0);
160         hotBlockShadowWidth = Dimension(0);
161         themeBlockHotSize = theme->GetNoneBlockHotSize();
162     }
163 }
164 
Measure(LayoutWrapper * layoutWrapper)165 void SliderLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
166 {
167     auto layoutConstraint = layoutWrapper->GetLayoutProperty()->CreateChildConstraint();
168     auto frameNode = layoutWrapper->GetHostNode();
169     CHECK_NULL_VOID(frameNode);
170     auto pattern = frameNode->GetPattern<SliderPattern>();
171     CHECK_NULL_VOID(pattern);
172     if (!pattern->UseContentModifier()) {
173         layoutConstraint.UpdateSelfMarginSizeWithCheck(OptionalSizeF(blockSize_.Width(), blockSize_.Height()));
174     }
175     if (layoutWrapper->GetTotalChildCount() != 0) {
176         auto child = layoutWrapper->GetOrCreateChildByIndex(0);
177         child->Measure(layoutConstraint);
178     }
179     PerformMeasureSelf(layoutWrapper);
180 }
181 
Layout(LayoutWrapper * layoutWrapper)182 void SliderLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
183 {
184     auto host = layoutWrapper->GetHostNode();
185     CHECK_NULL_VOID(host);
186     auto pattern = DynamicCast<SliderPattern>(host->GetPattern());
187     CHECK_NULL_VOID(pattern);
188     if (pattern->UseContentModifier()) {
189         BoxLayoutAlgorithm::Layout(layoutWrapper);
190         return;
191     }
192     PerformLayout(layoutWrapper);
193     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
194     if (children.empty()) {
195         return;
196     }
197 
198     auto sliderLayoutProperty = host->GetLayoutProperty<SliderLayoutProperty>();
199     CHECK_NULL_VOID(sliderLayoutProperty);
200     auto pipeline = PipelineBase::GetCurrentContext();
201     CHECK_NULL_VOID(pipeline);
202     auto theme = pipeline->GetTheme<SliderTheme>();
203     CHECK_NULL_VOID(theme);
204 
205     auto contentRect = layoutWrapper->GetGeometryNode()->GetContentRect();
206     auto axis = sliderLayoutProperty->GetDirection().value_or(Axis::HORIZONTAL);
207     auto paintReverse = sliderLayoutProperty->GetReverseValue(false);
208     auto direction = sliderLayoutProperty->GetLayoutDirection();
209     if (axis == Axis::HORIZONTAL) {
210         auto isRTL = direction == TextDirection::AUTO ? AceApplicationInfo::GetInstance().IsRightToLeft()
211                                                       : direction == TextDirection::RTL;
212         paintReverse = isRTL ? !paintReverse : paintReverse;
213     }
214     auto mode = sliderLayoutProperty->GetSliderMode().value_or(SliderModel::SliderMode::OUTSET);
215     Dimension hotBlockShadowWidth = mode == SliderModel::SliderMode::OUTSET ? theme->GetOutsetHotBlockShadowWidth()
216                                                                             : theme->GetInsetHotBlockShadowWidth();
217     auto length = axis == Axis::HORIZONTAL ? contentRect.Width() : contentRect.Height();
218     float BlockShadowWidth = static_cast<float>(hotBlockShadowWidth.ConvertToPx());
219     auto blockSize = axis == Axis::HORIZONTAL ? blockSize_.Width() : blockSize_.Height();
220     auto borderBlank = std::max(trackThickness_, blockSize + BlockShadowWidth / HALF);
221     auto sliderLength = length >= borderBlank ? length - borderBlank : 1;
222     borderBlank = (length - sliderLength) * HALF;
223     auto selectOffset = borderBlank + pattern->GetValueRatio() * sliderLength;
224 
225     CalculateBlockOffset(layoutWrapper, contentRect, selectOffset, axis, paintReverse);
226 }
227 
CalculateBlockOffset(LayoutWrapper * layoutWrapper,const RectF & contentRect,float selectOffset,Axis axis,bool reverse)228 void SliderLayoutAlgorithm::CalculateBlockOffset(
229     LayoutWrapper* layoutWrapper, const RectF& contentRect, float selectOffset, Axis axis, bool reverse)
230 {
231     auto host = layoutWrapper->GetHostNode();
232     CHECK_NULL_VOID(host);
233     auto pattern = DynamicCast<SliderPattern>(host->GetPattern());
234     CHECK_NULL_VOID(pattern);
235 
236     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
237     auto child = children.front();
238     auto childSize_ = child->GetGeometryNode()->GetMarginFrameSize();
239     OffsetF circleCenter;
240     auto animatableBlockCenter = pattern->GetAnimatableBlockCenter();
241     if (animatableBlockCenter.has_value()) {
242         circleCenter = animatableBlockCenter.value();
243     } else {
244         if (!reverse) {
245             if (axis == Axis::HORIZONTAL) {
246                 circleCenter.SetX(selectOffset);
247                 circleCenter.SetY(contentRect.Height() * HALF);
248             } else {
249                 circleCenter.SetX(contentRect.Width() * HALF);
250                 circleCenter.SetY(selectOffset);
251             }
252         } else {
253             if (axis == Axis::HORIZONTAL) {
254                 circleCenter.SetX(contentRect.Width() - selectOffset);
255                 circleCenter.SetY(contentRect.Height() * HALF);
256             } else {
257                 circleCenter.SetX(contentRect.Width() * HALF);
258                 circleCenter.SetY(contentRect.Height() - selectOffset);
259             }
260         }
261         circleCenter += OffsetF(contentRect.GetX(), contentRect.GetY());
262     }
263 
264     OffsetF imageNodeOffset(
265         circleCenter.GetX() - childSize_.Width() * HALF, circleCenter.GetY() - childSize_.Height() * HALF);
266 
267     child->GetGeometryNode()->SetMarginFrameOffset(imageNodeOffset);
268     child->Layout();
269 }
270 
271 } // namespace OHOS::Ace::NG
272