1 /*
2 * Copyright (c) 2022-2025 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/progress/progress_layout_algorithm.h"
17
18 #include <algorithm>
19
20 #include "base/geometry/dimension.h"
21 #include "base/log/log_wrapper.h"
22 #include "base/utils/utils.h"
23 #include "core/components/common/properties/color.h"
24 #include "core/components/progress/progress_component.h"
25 #include "core/components/progress/progress_theme.h"
26 #include "core/components_ng/pattern/progress/progress_date.h"
27 #include "core/components_ng/pattern/progress/progress_layout_property.h"
28 #include "core/components_ng/pattern/progress/progress_paint_property.h"
29 #include "core/components_ng/pattern/progress/progress_pattern.h"
30 #include "core/components_ng/pattern/text/text_layout_property.h"
31
32 namespace OHOS::Ace::NG {
33 namespace {
34 const Dimension DEFALT_RING_DIAMETER = 72.0_vp;
35 const Dimension DEFALT_CAPSULE_WIDTH = 28.0_vp;
36 } // namespace
37 ProgressLayoutAlgorithm::ProgressLayoutAlgorithm() = default;
38
MeasureContent(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)39 std::optional<SizeF> ProgressLayoutAlgorithm::MeasureContent(
40 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
41 {
42 auto host = layoutWrapper->GetHostNode();
43 CHECK_NULL_RETURN(host, std::nullopt);
44 auto pattern = host->GetPattern<ProgressPattern>();
45 CHECK_NULL_RETURN(pattern, std::nullopt);
46 if (pattern->UseContentModifier()) {
47 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)) {
48 host->GetGeometryNode()->ResetContent();
49 } else {
50 host->GetGeometryNode()->Reset();
51 }
52 return std::nullopt;
53 }
54 auto pipeline = PipelineBase::GetCurrentContext();
55 CHECK_NULL_RETURN(pipeline, std::nullopt);
56 if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
57 return MeasureContentForApiNine(contentConstraint, layoutWrapper);
58 }
59 auto progressTheme = pipeline->GetTheme<ProgressTheme>(host->GetThemeScopeId());
60 auto progressLayoutProperty = DynamicCast<ProgressLayoutProperty>(layoutWrapper->GetLayoutProperty());
61 CHECK_NULL_RETURN(progressLayoutProperty, std::nullopt);
62 type_ = progressLayoutProperty->GetType().value_or(ProgressType::LINEAR);
63 Dimension defaultThickness;
64 if (progressTheme) {
65 defaultThickness = (type_ == ProgressType::RING) ? Dimension(progressTheme->GetRingThickness())
66 : Dimension(progressTheme->GetTrackThickness());
67 } else {
68 defaultThickness = Dimension(strokeWidth_);
69 }
70 strokeWidth_ = progressLayoutProperty->GetStrokeWidth().value_or(defaultThickness).ConvertToPx();
71
72 float diameter =
73 progressTheme ? progressTheme->GetRingDiameter().ConvertToPx() : DEFALT_RING_DIAMETER.ConvertToPx();
74 auto selfIdealWidth = contentConstraint.selfIdealSize.Width();
75 auto selfIdealHeight = contentConstraint.selfIdealSize.Height();
76 float width_ = selfIdealWidth.value_or(contentConstraint.percentReference.Width());
77 float height_ = selfIdealHeight.value_or(strokeWidth_);
78 if (type_ == ProgressType::RING || type_ == ProgressType::SCALE || type_ == ProgressType::MOON) {
79 if (!selfIdealHeight) {
80 if (!selfIdealWidth) {
81 width_ = diameter;
82 }
83 height_ = width_;
84 } else {
85 if (selfIdealWidth) {
86 height_ = std::min(width_, height_);
87 }
88 width_ = height_;
89 }
90 } else if (type_ == ProgressType::CAPSULE) {
91 if (!selfIdealWidth) {
92 width_ = contentConstraint.percentReference.Width();
93 }
94 if (!selfIdealHeight) {
95 height_ = contentConstraint.parentIdealSize.Height().value_or(GetChildHeight(layoutWrapper, width_));
96 }
97 auto renderContext = host->GetRenderContext();
98 CHECK_NULL_RETURN(renderContext, std::nullopt);
99 float minSize = std::min(width_, height_);
100 renderContext->UpdateBorderRadius(BorderRadiusProperty(Dimension(minSize / 2.0f)));
101 } else if (type_ == ProgressType::LINEAR) {
102 if (width_ >= height_) {
103 height_ = std::min(height_, strokeWidth_);
104 } else {
105 width_ = std::min(width_, strokeWidth_);
106 }
107 }
108 return SizeF(width_, height_);
109 }
110
MeasureContentForApiNine(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)111 std::optional<SizeF> ProgressLayoutAlgorithm::MeasureContentForApiNine(
112 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
113 {
114 auto pipeline = PipelineBase::GetCurrentContext();
115 CHECK_NULL_RETURN(pipeline, std::nullopt);
116 auto progressLayoutProperty = DynamicCast<ProgressLayoutProperty>(layoutWrapper->GetLayoutProperty());
117 auto progressTheme = pipeline->GetTheme<ProgressTheme>(progressLayoutProperty->GetThemeScopeId());
118 CHECK_NULL_RETURN(progressLayoutProperty, std::nullopt);
119 type_ = progressLayoutProperty->GetType().value_or(ProgressType::LINEAR);
120 strokeWidth_ = progressLayoutProperty->GetStrokeWidth().
121 value_or(progressTheme ? (type_ == ProgressType::SCALE ? progressTheme->GetScaleLength() :
122 progressTheme->GetTrackThickness()): Dimension(strokeWidth_)).ConvertToPx();
123 float diameter =
124 progressTheme ? progressTheme->GetRingDiameter().ConvertToPx() : DEFALT_RING_DIAMETER.ConvertToPx();
125 float width_ = progressTheme ? progressTheme->GetTrackWidth().ConvertToPx() : contentConstraint.maxSize.Width();
126 auto selfIdealWidth = contentConstraint.selfIdealSize.Width();
127 auto selfIdealHeight = contentConstraint.selfIdealSize.Height();
128 if (selfIdealWidth) {
129 width_ = selfIdealWidth.value();
130 }
131 float height_ = strokeWidth_ * 2.0f;
132 if (selfIdealHeight) {
133 height_ = selfIdealHeight.value();
134 }
135 if (type_ == ProgressType::RING || type_ == ProgressType::SCALE || type_ == ProgressType::MOON) {
136 if (!selfIdealWidth && !selfIdealHeight) {
137 width_ = diameter;
138 height_ = width_;
139 } else if (selfIdealWidth && !selfIdealHeight) {
140 height_ = width_;
141 } else if (selfIdealHeight && !selfIdealWidth) {
142 width_ = height_;
143 }
144 } else if (type_ == ProgressType::CAPSULE) {
145 if (!selfIdealHeight) {
146 height_ = diameter;
147 }
148 if (!selfIdealWidth) {
149 width_ = diameter;
150 }
151 }
152 height_ = std::min(height_, static_cast<float>(contentConstraint.maxSize.Height()));
153 width_ = std::min(width_, static_cast<float>(contentConstraint.maxSize.Width()));
154 if (type_ == ProgressType::LINEAR) {
155 strokeWidth_ = std::min(strokeWidth_, height_ / 2.0f);
156 strokeWidth_ = std::min(strokeWidth_, width_ / 2.0f);
157 }
158 return SizeF(width_, height_);
159 }
160
GetType() const161 ProgressType ProgressLayoutAlgorithm::GetType() const
162 {
163 return type_;
164 }
165
GetStrokeWidth() const166 float ProgressLayoutAlgorithm::GetStrokeWidth() const
167 {
168 return strokeWidth_;
169 }
170
GetChildHeight(LayoutWrapper * layoutWrapper,float width) const171 float ProgressLayoutAlgorithm::GetChildHeight(LayoutWrapper* layoutWrapper, float width) const
172 {
173 auto host = layoutWrapper->GetHostNode();
174 CHECK_NULL_RETURN(host, DEFALT_CAPSULE_WIDTH.ConvertToPx());
175 auto paintProperty = host->GetPaintProperty<ProgressPaintProperty>();
176 CHECK_NULL_RETURN(paintProperty, DEFALT_CAPSULE_WIDTH.ConvertToPx());
177 auto pipeline = PipelineBase::GetCurrentContext();
178 CHECK_NULL_RETURN(pipeline, DEFALT_CAPSULE_WIDTH.ConvertToPx());
179 auto progressTheme = pipeline->GetTheme<ProgressTheme>(host->GetThemeScopeId());
180 Dimension margin = progressTheme->GetTextMargin();
181 auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(0);
182 CHECK_NULL_RETURN(childWrapper, DEFALT_CAPSULE_WIDTH.ConvertToPx());
183 auto layoutProperty = AceType::DynamicCast<ProgressLayoutProperty>(layoutWrapper->GetLayoutProperty());
184 CHECK_NULL_RETURN(layoutProperty, DEFALT_CAPSULE_WIDTH.ConvertToPx());
185
186 auto childLayoutProperty = AceType::DynamicCast<TextLayoutProperty>(childWrapper->GetLayoutProperty());
187 CHECK_NULL_RETURN(childLayoutProperty, DEFALT_CAPSULE_WIDTH.ConvertToPx());
188 auto childConstraint = layoutProperty->CreateChildConstraint();
189 childConstraint.maxSize.SetWidth(width);
190 childWrapper->Measure(childConstraint);
191 auto childSize = childWrapper->GetGeometryNode()->GetContentSize();
192 if (childSize.Width() > (width - 2 * margin.ConvertToPx())) {
193 CalcSize defaultCalcSize((CalcLength(width - 2 * margin.ConvertToPx())), std::nullopt);
194 childLayoutProperty->UpdateUserDefinedIdealSize(defaultCalcSize);
195 }
196 float childHeight =
197 paintProperty->GetTextSize().value_or(progressTheme->GetTextSize()).ConvertToPx() + 2 * margin.ConvertToPx();
198
199 auto fontScale = pipeline->GetFontScale();
200 if (GreatOrEqualCustomPrecision(fontScale, progressTheme->GetFontScale()) && (GetType() == ProgressType::CAPSULE)) {
201 const auto& paddingProperty = layoutProperty->GetPaddingProperty();
202 if (!(paddingProperty &&
203 ((paddingProperty->top != std::nullopt) || (paddingProperty->bottom != std::nullopt)))) {
204 childHeight = childHeight + (progressTheme->GetfontScalePadding()).ConvertToPx();
205 }
206 }
207
208 return childHeight;
209 }
210 } // namespace OHOS::Ace::NG
211