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