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