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/gauge/gauge_layout_algorithm.h"
17
18 #include "core/common/container.h"
19 #include "core/components/progress/progress_theme.h"
20 #include "core/components_ng/base/frame_node.h"
21 #include "core/components_ng/layout/layout_wrapper.h"
22 #include "core/components_ng/pattern/gauge/gauge_layout_property.h"
23 #include "core/components_ng/pattern/gauge/gauge_pattern.h"
24 #include "core/components_ng/pattern/gauge/gauge_theme.h"
25 #include "core/components_ng/pattern/text/text_layout_algorithm.h"
26 #include "core/components_ng/pattern/text/text_layout_property.h"
27
28 namespace OHOS::Ace::NG {
29 namespace {
30 constexpr float HALF_CIRCLE = 180.0f;
31 constexpr float QUARTER_CIRCLE = 90.0f;
32 } // namespace
33
34 GaugeLayoutAlgorithm::GaugeLayoutAlgorithm() = default;
35
OnReset()36 void GaugeLayoutAlgorithm::OnReset() {}
37
Measure(LayoutWrapper * layoutWrapper)38 void GaugeLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
39 {
40 CHECK_NULL_VOID(layoutWrapper);
41 auto host = layoutWrapper->GetHostNode();
42 CHECK_NULL_VOID(host);
43 auto pattern = host->GetPattern<GaugePattern>();
44 CHECK_NULL_VOID(pattern);
45 if (pattern->UseContentModifier()) {
46 auto childList = layoutWrapper->GetAllChildrenWithBuild();
47 std::list<RefPtr<LayoutWrapper>> list;
48 for (const auto& child : childList) {
49 if (pattern->GetContentModifierNode()->GetId() != child->GetHostNode()->GetId()) {
50 child->GetGeometryNode()->SetContentSize(SizeF());
51 } else {
52 auto layoutConstraint = layoutWrapper->GetLayoutProperty()->CreateChildConstraint();
53 child->Measure(layoutConstraint);
54 list.push_back(child);
55 }
56 }
57 BoxLayoutAlgorithm::PerformMeasureSelfWithChildList(layoutWrapper, list);
58 return;
59 }
60 BoxLayoutAlgorithm::Measure(layoutWrapper);
61 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
62 MeasureLimitValueTextWidth(layoutWrapper);
63 auto geometryNode = layoutWrapper->GetGeometryNode();
64 CHECK_NULL_VOID(geometryNode);
65 auto idealSize = geometryNode->GetContentSize();
66 MeasureLimitValueText(layoutWrapper, idealSize, true);
67 MeasureLimitValueText(layoutWrapper, idealSize, false);
68 MeasureFontSize(layoutWrapper);
69 MeasureDescription(layoutWrapper, idealSize);
70 MeasureTitleChild(layoutWrapper, idealSize);
71 }
72 }
73
MeasureContent(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)74 std::optional<SizeF> GaugeLayoutAlgorithm::MeasureContent(
75 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
76 {
77 auto host = layoutWrapper->GetHostNode();
78 CHECK_NULL_RETURN(host, std::nullopt);
79 auto pattern = host->GetPattern<GaugePattern>();
80 CHECK_NULL_RETURN(pattern, std::nullopt);
81 if (pattern->UseContentModifier()) {
82 host->GetGeometryNode()->Reset();
83 return std::nullopt;
84 }
85 if (contentConstraint.selfIdealSize.IsValid()) {
86 auto len =
87 std::min(contentConstraint.selfIdealSize.Height().value(), contentConstraint.selfIdealSize.Width().value());
88 return SizeF(len, len);
89 }
90 if (contentConstraint.selfIdealSize.Width().has_value() &&
91 NonNegative(contentConstraint.selfIdealSize.Width().value())) {
92 auto width = contentConstraint.selfIdealSize.Width().value();
93 return SizeF(width, width);
94 }
95 if (contentConstraint.selfIdealSize.Height().has_value() &&
96 NonNegative(contentConstraint.selfIdealSize.Height().value())) {
97 auto height = contentConstraint.selfIdealSize.Height().value();
98 return SizeF(height, height);
99 }
100 auto pipeline = PipelineBase::GetCurrentContext();
101 CHECK_NULL_RETURN(pipeline, std::nullopt);
102 auto gaugeTheme = pipeline->GetTheme<ProgressTheme>();
103 CHECK_NULL_RETURN(gaugeTheme, std::nullopt);
104 auto defaultThickness = gaugeTheme->GetTrackWidth().ConvertToPx();
105 auto size = SizeF(defaultThickness, defaultThickness);
106 auto layoutConstraint = layoutWrapper->GetLayoutProperty()->GetLayoutConstraint();
107 size.Constrain(layoutConstraint->minSize, layoutConstraint->maxSize);
108 auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
109 MinusPaddingToSize(padding, size);
110 if (!NearEqual(size.Width(), size.Height())) {
111 auto length = std::min(size.Width(), size.Height());
112 size.SetWidth(length);
113 size.SetHeight(length);
114 }
115 return size;
116 }
117
MeasureLimitValueTextWidth(LayoutWrapper * layoutWrapper)118 void GaugeLayoutAlgorithm::MeasureLimitValueTextWidth(LayoutWrapper* layoutWrapper)
119 {
120 CHECK_NULL_VOID(layoutWrapper);
121 auto hostNode = AceType::DynamicCast<FrameNode>(layoutWrapper->GetHostNode());
122 CHECK_NULL_VOID(hostNode);
123 auto pattern = hostNode->GetPattern<GaugePattern>();
124 CHECK_NULL_VOID(pattern);
125 auto hasLimitValueNode = pattern->HasMinValueTextNode();
126 CHECK_NULL_VOID(hasLimitValueNode);
127 auto geometryNode = layoutWrapper->GetGeometryNode();
128 CHECK_NULL_VOID(geometryNode);
129
130 auto offset = geometryNode->GetContentOffset();
131 auto paddingSize = geometryNode->GetPaddingSize();
132 auto left = 0.0f;
133 auto top = 0.0f;
134 if (geometryNode->GetPadding()) {
135 left = geometryNode->GetPadding()->left.value_or(0.0f);
136 top = geometryNode->GetPadding()->top.value_or(0.0f);
137 }
138 auto radius = std::min(paddingSize.Width(), paddingSize.Height()) / 2.0f;
139 auto center = Offset(offset.GetX() + left + radius, offset.GetY() + top + radius);
140 auto layoutProperty = AceType::DynamicCast<GaugeLayoutProperty>(layoutWrapper->GetLayoutProperty());
141 CHECK_NULL_VOID(layoutProperty);
142 auto startAngle = layoutProperty->GetStartAngleValue(DEFAULT_START_DEGREE);
143 auto endAngle = layoutProperty->GetEndAngleValue(DEFAULT_END_DEGREE);
144
145 auto pipelineContext = PipelineBase::GetCurrentContext();
146 CHECK_NULL_VOID(pipelineContext);
147 auto theme = pipelineContext->GetTheme<GaugeTheme>();
148 CHECK_NULL_VOID(theme);
149 auto strokeWidthValue = layoutProperty->GetStrokeWidthValue(theme->GetTrackThickness()).ConvertToPx();
150 if (Negative(strokeWidthValue)) {
151 strokeWidthValue = theme->GetTrackThickness().ConvertToPx();
152 }
153
154 startAngle -= QUARTER_CIRCLE;
155 endAngle -= QUARTER_CIRCLE;
156 auto startDegree = startAngle * M_PI / HALF_CIRCLE;
157 auto endDegree = endAngle * M_PI / HALF_CIRCLE;
158 startAngleOffsetX_ = center.GetX() + (radius - strokeWidthValue) * std::cos(startDegree);
159 endAngleOffsetX_ = center.GetX() + (radius - strokeWidthValue) * std::cos(endDegree);
160 auto diameter = radius * 2.0f;
161 auto textSafeDistance = LIMIT_VALUE_MIN_SAFE_DISTANCE_RATIO * diameter +
162 LIMIT_VALUE_MAX_SAFE_DISTANCE_RATIO * diameter +
163 LIMIT_VALUE_SPACE_SAFE_DISTANCE_RATIO * diameter;
164 limitValueTextWidth_ = (endAngleOffsetX_ - startAngleOffsetX_ - textSafeDistance) * 0.5f;
165 }
166
MeasureLimitValueText(LayoutWrapper * layoutWrapper,const SizeF & parentSize,bool isMin)167 void GaugeLayoutAlgorithm::MeasureLimitValueText(LayoutWrapper* layoutWrapper, const SizeF& parentSize, bool isMin)
168 {
169 auto hostNode = AceType::DynamicCast<FrameNode>(layoutWrapper->GetHostNode());
170 CHECK_NULL_VOID(hostNode);
171 auto pattern = hostNode->GetPattern<GaugePattern>();
172 CHECK_NULL_VOID(pattern);
173 auto hasLimitValueNode = isMin ? pattern->HasMinValueTextNode() : pattern->HasMaxValueTextNode();
174 CHECK_NULL_VOID(hasLimitValueNode);
175 auto layoutProperty = AceType::DynamicCast<GaugeLayoutProperty>(layoutWrapper->GetLayoutProperty());
176 CHECK_NULL_VOID(layoutProperty);
177 auto childLayoutConstraint = layoutProperty->CreateChildConstraint();
178 childLayoutConstraint.parentIdealSize = OptionalSizeF(parentSize);
179 auto layoutGeometryNode = layoutWrapper->GetGeometryNode();
180 CHECK_NULL_VOID(layoutGeometryNode);
181 auto paddingSize = layoutGeometryNode->GetPaddingSize();
182 auto diameter = std::min(paddingSize.Width(), paddingSize.Height());
183 auto width = limitValueTextWidth_;
184 auto height = diameter * LIMIT_VALUE_MIN_OR_MAX_HEIGHT_RATIO;
185 if (!layoutProperty->GetIsShowLimitValueValue(false)) {
186 width = 0.0f;
187 height = 0.0f;
188 }
189 childLayoutConstraint.selfIdealSize = { width, height };
190
191 auto hasLimitValueNodeId = isMin ? pattern->GetMinValueTextId() : pattern->GetMaxValueTextId();
192 auto index = hostNode->GetChildIndexById(hasLimitValueNodeId);
193 auto limitValueTextWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
194 CHECK_NULL_VOID(limitValueTextWrapper);
195 limitValueTextWrapper->Measure(childLayoutConstraint);
196 }
197
MeasureDescription(LayoutWrapper * layoutWrapper,const SizeF & parentSize)198 void GaugeLayoutAlgorithm::MeasureDescription(LayoutWrapper* layoutWrapper, const SizeF& parentSize)
199 {
200 auto hostNode = AceType::DynamicCast<FrameNode>(layoutWrapper->GetHostNode());
201 CHECK_NULL_VOID(hostNode);
202 auto pattern = hostNode->GetPattern<GaugePattern>();
203 CHECK_NULL_VOID(pattern);
204 CHECK_NULL_VOID(pattern->HasDescriptionNode());
205 auto layoutGeometryNode = layoutWrapper->GetGeometryNode();
206 CHECK_NULL_VOID(layoutGeometryNode);
207 auto layoutProperty = AceType::DynamicCast<GaugeLayoutProperty>(layoutWrapper->GetLayoutProperty());
208 CHECK_NULL_VOID(layoutProperty);
209 auto childLayoutConstraint = layoutProperty->CreateChildConstraint();
210 childLayoutConstraint.parentIdealSize = OptionalSizeF(parentSize);
211 auto paddingSize = layoutGeometryNode->GetPaddingSize();
212 auto diameter = std::min(paddingSize.Width(), paddingSize.Height());
213 auto width = diameter * DESCRIPTION_NODE_WIDTH_RATIO;
214 auto height = diameter * DESCRIPTION_NODE_HEIGHT_RATIO;
215
216 auto index = hostNode->GetChildIndexById(pattern->GetDescriptionNodeId());
217 auto descriptionLayoutWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
218 CHECK_NULL_VOID(descriptionLayoutWrapper);
219
220 if (CheckDescriptionIsImageNode(descriptionLayoutWrapper)) {
221 width = diameter * DESCRIPTION_IMAGE_NODE_WIDTH_RATIO;
222 height = diameter * DESCRIPTION_IMAGE_NODE_HEIGHT_RATIO;
223 }
224 if (!layoutProperty->GetIsShowDescriptionValue(false)) {
225 width = 0.0f;
226 height = 0.0f;
227 }
228 childLayoutConstraint.selfIdealSize = { width, height };
229 descriptionLayoutWrapper->Measure(childLayoutConstraint);
230 }
231
MeasureTitleChild(LayoutWrapper * layoutWrapper,const SizeF & parentSize)232 void GaugeLayoutAlgorithm::MeasureTitleChild(LayoutWrapper* layoutWrapper, const SizeF& parentSize)
233 {
234 auto hostNode = AceType::DynamicCast<FrameNode>(layoutWrapper->GetHostNode());
235 CHECK_NULL_VOID(hostNode);
236 auto pattern = hostNode->GetPattern<GaugePattern>();
237 CHECK_NULL_VOID(pattern);
238 CHECK_NULL_VOID(pattern->HasTitleChildNode());
239 auto layoutGeometryNode = layoutWrapper->GetGeometryNode();
240 CHECK_NULL_VOID(layoutGeometryNode);
241 auto layoutProperty = layoutWrapper->GetLayoutProperty();
242 CHECK_NULL_VOID(layoutProperty);
243 auto childLayoutConstraint = layoutProperty->CreateChildConstraint();
244 childLayoutConstraint.parentIdealSize = OptionalSizeF(parentSize);
245 auto paddingSize = layoutGeometryNode->GetPaddingSize();
246 auto diameter = std::min(paddingSize.Width(), paddingSize.Height());
247 childLayoutConstraint.minSize = { 0.0f, 0.0f };
248 childLayoutConstraint.maxSize = { diameter, diameter };
249 childLayoutConstraint.selfIdealSize = { diameter, diameter };
250 auto index = hostNode->GetChildIndexById(pattern->GetTitleChildId());
251 auto titleChildLayoutWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
252 CHECK_NULL_VOID(titleChildLayoutWrapper);
253 titleChildLayoutWrapper->Measure(childLayoutConstraint);
254 }
255
Layout(LayoutWrapper * layoutWrapper)256 void GaugeLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
257 {
258 if (Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
259 BoxLayoutAlgorithm::Layout(layoutWrapper);
260 return;
261 }
262 auto hostNode = layoutWrapper->GetHostNode();
263 CHECK_NULL_VOID(hostNode);
264 auto gaugePattern = hostNode->GetPattern<GaugePattern>();
265 CHECK_NULL_VOID(gaugePattern);
266 if (gaugePattern->UseContentModifier()) {
267 BoxLayoutAlgorithm::Layout(layoutWrapper);
268 return;
269 }
270 auto layoutGeometryNode = layoutWrapper->GetGeometryNode();
271 CHECK_NULL_VOID(layoutGeometryNode);
272 CHECK_NULL_VOID(layoutGeometryNode->GetPadding());
273 auto paddingSize = layoutGeometryNode->GetPaddingSize();
274 auto left = layoutGeometryNode->GetPadding()->left.value_or(0.0f);
275 auto top = layoutGeometryNode->GetPadding()->top.value_or(0.0f);
276 auto diameter = std::min(paddingSize.Width(), paddingSize.Height());
277 auto offset = layoutGeometryNode->GetContentOffset();
278 OffsetF circularOffset = offset + OffsetF(left, top);
279 auto allChildrenWrapperList = layoutWrapper->GetAllChildrenWithBuild();
280 for (const auto& child : allChildrenWrapperList) {
281 auto childNode = child->GetHostNode();
282 CHECK_NULL_VOID(childNode);
283 auto nodeId = childNode->GetId();
284 OffsetF childOffset;
285 if (nodeId == gaugePattern->GetDescriptionNodeId()) {
286 if (CheckDescriptionIsImageNode(child)) {
287 childOffset = circularOffset + OffsetF(DESCRIPTION_IMAGE_X * diameter, DESCRIPTION_IMAGE_Y * diameter);
288 } else {
289 childOffset = circularOffset + OffsetF(DESCRIPTION_X * diameter, DESCRIPTION_Y * diameter);
290 }
291 } else if (nodeId == gaugePattern->GetMinValueTextId()) {
292 childOffset =
293 circularOffset + OffsetF(startAngleOffsetX_ + LIMIT_VALUE_MIN_SAFE_DISTANCE_RATIO * diameter - left,
294 LIMIT_VALUE_Y * diameter);
295 } else if (nodeId == gaugePattern->GetMaxValueTextId()) {
296 childOffset = circularOffset + OffsetF(endAngleOffsetX_ - limitValueTextWidth_ -
297 LIMIT_VALUE_MAX_SAFE_DISTANCE_RATIO * diameter - left,
298 LIMIT_VALUE_Y * diameter);
299 } else if (nodeId == gaugePattern->GetTitleChildId()) {
300 childOffset = circularOffset;
301 }
302
303 auto childGeometryNode = child->GetGeometryNode();
304 CHECK_NULL_VOID(childGeometryNode);
305 childGeometryNode->SetMarginFrameOffset(childOffset);
306 child->Layout();
307 }
308 CHECK_NULL_VOID(indicatorIconLoadingCtx_);
309 indicatorIconLoadingCtx_->MakeCanvasImage(
310 SizeF(INDICATOR_WIDTH_RADIO * diameter, INDICATOR_HEIGHT_RADIO * diameter), true, ImageFit::FILL);
311 }
312
CheckDescriptionIsImageNode(const RefPtr<LayoutWrapper> & layoutWrapper) const313 bool GaugeLayoutAlgorithm::CheckDescriptionIsImageNode(const RefPtr<LayoutWrapper>& layoutWrapper) const
314 {
315 if (layoutWrapper->GetTotalChildCount() <= 0) {
316 return false;
317 }
318 auto childLayoutWrapper = layoutWrapper->GetChildByIndex(0);
319 CHECK_NULL_RETURN(childLayoutWrapper, false);
320 return childLayoutWrapper->GetHostTag() == V2::IMAGE_ETS_TAG;
321 }
322
MeasureFontSize(LayoutWrapper * layoutWrapper)323 void GaugeLayoutAlgorithm::MeasureFontSize(LayoutWrapper* layoutWrapper)
324 {
325 Dimension minFontSize;
326 auto hasMinFontSize = GetLimitFontSize(layoutWrapper, true, minFontSize);
327 Dimension maxFontSize;
328 auto hasMaxFontSize = GetLimitFontSize(layoutWrapper, false, maxFontSize);
329
330 if (hasMinFontSize && hasMaxFontSize) {
331 auto fontSize = minFontSize < maxFontSize ? minFontSize : maxFontSize;
332 SetLimitFontSize(layoutWrapper, true, fontSize);
333 SetLimitFontSize(layoutWrapper, false, fontSize);
334 }
335 }
336
GetLimitFontSize(LayoutWrapper * layoutWrapper,bool isMin,Dimension & fontSize)337 bool GaugeLayoutAlgorithm::GetLimitFontSize(LayoutWrapper* layoutWrapper, bool isMin, Dimension& fontSize)
338 {
339 CHECK_NULL_RETURN(layoutWrapper, false);
340 auto hostNode = AceType::DynamicCast<FrameNode>(layoutWrapper->GetHostNode());
341 CHECK_NULL_RETURN(hostNode, false);
342 auto pattern = hostNode->GetPattern<GaugePattern>();
343 CHECK_NULL_RETURN(pattern, false);
344 auto hasLimitValueNode = isMin ? pattern->HasMinValueTextNode() : pattern->HasMaxValueTextNode();
345 CHECK_NULL_RETURN(hasLimitValueNode, false);
346 auto hasLimitValueNodeId = isMin ? pattern->GetMinValueTextId() : pattern->GetMaxValueTextId();
347 auto index = hostNode->GetChildIndexById(hasLimitValueNodeId);
348 auto limitValueTextWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
349 CHECK_NULL_RETURN(limitValueTextWrapper, false);
350 auto textLayoutTextWrapper = limitValueTextWrapper->GetLayoutAlgorithm();
351 CHECK_NULL_RETURN(textLayoutTextWrapper, false);
352 auto textLayoutAlgorithm = DynamicCast<TextLayoutAlgorithm>(textLayoutTextWrapper->GetLayoutAlgorithm());
353 CHECK_NULL_RETURN(textLayoutAlgorithm, false);
354 auto limitTextStyle = textLayoutAlgorithm->GetTextStyle();
355 if (!limitTextStyle.has_value()) {
356 return false;
357 }
358 fontSize = limitTextStyle->GetFontSize();
359 return true;
360 }
361
SetLimitFontSize(LayoutWrapper * layoutWrapper,bool isMin,const Dimension & fontSize)362 void GaugeLayoutAlgorithm::SetLimitFontSize(LayoutWrapper* layoutWrapper, bool isMin, const Dimension& fontSize)
363 {
364 CHECK_NULL_VOID(layoutWrapper);
365 auto hostNode = AceType::DynamicCast<FrameNode>(layoutWrapper->GetHostNode());
366 CHECK_NULL_VOID(hostNode);
367 auto pattern = hostNode->GetPattern<GaugePattern>();
368 CHECK_NULL_VOID(pattern);
369 auto hasLimitValueNode = isMin ? pattern->HasMinValueTextNode() : pattern->HasMaxValueTextNode();
370 CHECK_NULL_VOID(hasLimitValueNode);
371 auto hasLimitValueNodeId = isMin ? pattern->GetMinValueTextId() : pattern->GetMaxValueTextId();
372 auto index = hostNode->GetChildIndexById(hasLimitValueNodeId);
373 auto limitValueTextWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
374 CHECK_NULL_VOID(limitValueTextWrapper);
375 auto textLayoutProperty = AceType::DynamicCast<TextLayoutProperty>(limitValueTextWrapper->GetLayoutProperty());
376 CHECK_NULL_VOID(textLayoutProperty);
377 auto layoutConstraint = textLayoutProperty->GetLayoutConstraint();
378 CHECK_NULL_VOID(layoutConstraint);
379 textLayoutProperty->UpdateAdaptMaxFontSize(fontSize);
380 limitValueTextWrapper->Measure(layoutConstraint);
381 }
382 } // namespace OHOS::Ace::NG
383