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