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 constexpr float ZERO_MEASURE_CONTENT_SIZE = 0.0f;
37 } // namespace
38 ProgressLayoutAlgorithm::ProgressLayoutAlgorithm() = default;
39
MeasureContent(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)40 std::optional<SizeF> ProgressLayoutAlgorithm::MeasureContent(
41 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
42 {
43 auto host = layoutWrapper->GetHostNode();
44 CHECK_NULL_RETURN(host, std::nullopt);
45 auto pattern = host->GetPattern<ProgressPattern>();
46 CHECK_NULL_RETURN(pattern, std::nullopt);
47 if (pattern->UseContentModifier()) {
48 if (host->GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)) {
49 host->GetGeometryNode()->ResetContent();
50 } else {
51 host->GetGeometryNode()->Reset();
52 }
53 return std::nullopt;
54 }
55 auto pipeline = PipelineBase::GetCurrentContext();
56 CHECK_NULL_RETURN(pipeline, std::nullopt);
57 if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
58 return MeasureContentForApiNine(contentConstraint, layoutWrapper);
59 }
60 auto progressTheme = pipeline->GetTheme<ProgressTheme>(host->GetThemeScopeId());
61 auto progressLayoutProperty = DynamicCast<ProgressLayoutProperty>(layoutWrapper->GetLayoutProperty());
62 CHECK_NULL_RETURN(progressLayoutProperty, std::nullopt);
63 auto layoutProperty = AceType::DynamicCast<LayoutProperty>(layoutWrapper->GetLayoutProperty());
64 CHECK_NULL_RETURN(layoutProperty, std::nullopt);
65 auto layoutPolicy = layoutProperty->GetLayoutPolicyProperty();
66 if (layoutPolicy.has_value() && (layoutPolicy->IsWrap() || layoutPolicy->IsFix())) {
67 return SizeF(ZERO_MEASURE_CONTENT_SIZE, ZERO_MEASURE_CONTENT_SIZE);
68 }
69 type_ = progressLayoutProperty->GetType().value_or(ProgressType::LINEAR);
70 Dimension defaultThickness;
71 if (progressTheme) {
72 defaultThickness = (type_ == ProgressType::RING) ? Dimension(progressTheme->GetRingThickness())
73 : Dimension(progressTheme->GetTrackThickness());
74 } else {
75 defaultThickness = Dimension(strokeWidth_);
76 }
77 strokeWidth_ = progressLayoutProperty->GetStrokeWidth().value_or(defaultThickness).ConvertToPx();
78
79 float diameter =
80 progressTheme ? progressTheme->GetRingDiameter().ConvertToPx() : DEFALT_RING_DIAMETER.ConvertToPx();
81 auto selfIdealWidth = contentConstraint.selfIdealSize.Width();
82 auto selfIdealHeight = contentConstraint.selfIdealSize.Height();
83 float width_ = selfIdealWidth.value_or(contentConstraint.percentReference.Width());
84 float height_ = selfIdealHeight.value_or(strokeWidth_);
85 if (type_ == ProgressType::RING || type_ == ProgressType::SCALE || type_ == ProgressType::MOON) {
86 if (!selfIdealHeight) {
87 if (!selfIdealWidth) {
88 width_ = diameter;
89 }
90 height_ = width_;
91 } else {
92 if (selfIdealWidth) {
93 height_ = std::min(width_, height_);
94 }
95 width_ = height_;
96 }
97 } else if (type_ == ProgressType::CAPSULE) {
98 if (!selfIdealWidth) {
99 width_ = contentConstraint.percentReference.Width();
100 }
101 if (!selfIdealHeight) {
102 height_ = contentConstraint.parentIdealSize.Height().value_or(GetChildHeight(layoutWrapper, width_));
103 }
104 auto renderContext = host->GetRenderContext();
105 CHECK_NULL_RETURN(renderContext, std::nullopt);
106 float minSize = std::min(width_, height_);
107 renderContext->UpdateBorderRadius(BorderRadiusProperty(Dimension(minSize / 2.0f)));
108 } else if (type_ == ProgressType::LINEAR) {
109 if (width_ >= height_) {
110 height_ = std::min(height_, strokeWidth_);
111 } else {
112 width_ = std::min(width_, strokeWidth_);
113 }
114 }
115 return SizeF(width_, height_);
116 }
117
MeasureContentForApiNine(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)118 std::optional<SizeF> ProgressLayoutAlgorithm::MeasureContentForApiNine(
119 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
120 {
121 auto pipeline = PipelineBase::GetCurrentContext();
122 CHECK_NULL_RETURN(pipeline, std::nullopt);
123 auto progressLayoutProperty = DynamicCast<ProgressLayoutProperty>(layoutWrapper->GetLayoutProperty());
124 auto progressTheme = pipeline->GetTheme<ProgressTheme>(progressLayoutProperty->GetThemeScopeId());
125 CHECK_NULL_RETURN(progressLayoutProperty, std::nullopt);
126 type_ = progressLayoutProperty->GetType().value_or(ProgressType::LINEAR);
127 strokeWidth_ = progressLayoutProperty->GetStrokeWidth().
128 value_or(progressTheme ? (type_ == ProgressType::SCALE ? progressTheme->GetScaleLength() :
129 progressTheme->GetTrackThickness()): Dimension(strokeWidth_)).ConvertToPx();
130 float diameter =
131 progressTheme ? progressTheme->GetRingDiameter().ConvertToPx() : DEFALT_RING_DIAMETER.ConvertToPx();
132 float width_ = progressTheme ? progressTheme->GetTrackWidth().ConvertToPx() : contentConstraint.maxSize.Width();
133 auto selfIdealWidth = contentConstraint.selfIdealSize.Width();
134 auto selfIdealHeight = contentConstraint.selfIdealSize.Height();
135 if (selfIdealWidth) {
136 width_ = selfIdealWidth.value();
137 }
138 float height_ = strokeWidth_ * 2.0f;
139 if (selfIdealHeight) {
140 height_ = selfIdealHeight.value();
141 }
142 if (type_ == ProgressType::RING || type_ == ProgressType::SCALE || type_ == ProgressType::MOON) {
143 if (!selfIdealWidth && !selfIdealHeight) {
144 width_ = diameter;
145 height_ = width_;
146 } else if (selfIdealWidth && !selfIdealHeight) {
147 height_ = width_;
148 } else if (selfIdealHeight && !selfIdealWidth) {
149 width_ = height_;
150 }
151 } else if (type_ == ProgressType::CAPSULE) {
152 if (!selfIdealHeight) {
153 height_ = diameter;
154 }
155 if (!selfIdealWidth) {
156 width_ = diameter;
157 }
158 }
159 height_ = std::min(height_, static_cast<float>(contentConstraint.maxSize.Height()));
160 width_ = std::min(width_, static_cast<float>(contentConstraint.maxSize.Width()));
161 if (type_ == ProgressType::LINEAR) {
162 strokeWidth_ = std::min(strokeWidth_, height_ / 2.0f);
163 strokeWidth_ = std::min(strokeWidth_, width_ / 2.0f);
164 }
165 return SizeF(width_, height_);
166 }
167
GetType() const168 ProgressType ProgressLayoutAlgorithm::GetType() const
169 {
170 return type_;
171 }
172
GetStrokeWidth() const173 float ProgressLayoutAlgorithm::GetStrokeWidth() const
174 {
175 return strokeWidth_;
176 }
177
GetChildHeight(LayoutWrapper * layoutWrapper,float width) const178 float ProgressLayoutAlgorithm::GetChildHeight(LayoutWrapper* layoutWrapper, float width) const
179 {
180 auto host = layoutWrapper->GetHostNode();
181 CHECK_NULL_RETURN(host, DEFALT_CAPSULE_WIDTH.ConvertToPx());
182 auto paintProperty = host->GetPaintProperty<ProgressPaintProperty>();
183 CHECK_NULL_RETURN(paintProperty, DEFALT_CAPSULE_WIDTH.ConvertToPx());
184 auto pipeline = PipelineBase::GetCurrentContext();
185 CHECK_NULL_RETURN(pipeline, DEFALT_CAPSULE_WIDTH.ConvertToPx());
186 auto progressTheme = pipeline->GetTheme<ProgressTheme>(host->GetThemeScopeId());
187 Dimension margin = progressTheme->GetTextMargin();
188 auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(0);
189 CHECK_NULL_RETURN(childWrapper, DEFALT_CAPSULE_WIDTH.ConvertToPx());
190 auto layoutProperty = AceType::DynamicCast<ProgressLayoutProperty>(layoutWrapper->GetLayoutProperty());
191 CHECK_NULL_RETURN(layoutProperty, DEFALT_CAPSULE_WIDTH.ConvertToPx());
192
193 auto childLayoutProperty = AceType::DynamicCast<TextLayoutProperty>(childWrapper->GetLayoutProperty());
194 CHECK_NULL_RETURN(childLayoutProperty, DEFALT_CAPSULE_WIDTH.ConvertToPx());
195 auto childConstraint = layoutProperty->CreateChildConstraint();
196 childConstraint.maxSize.SetWidth(width);
197 childWrapper->Measure(childConstraint);
198 auto childSize = childWrapper->GetGeometryNode()->GetContentSize();
199 if (childSize.Width() > (width - 2 * margin.ConvertToPx())) {
200 CalcSize defaultCalcSize((CalcLength(width - 2 * margin.ConvertToPx())), std::nullopt);
201 childLayoutProperty->UpdateUserDefinedIdealSize(defaultCalcSize);
202 }
203 float childHeight =
204 paintProperty->GetTextSize().value_or(progressTheme->GetTextSize()).ConvertToPx() + 2 * margin.ConvertToPx();
205
206 auto fontScale = pipeline->GetFontScale();
207 if (GreatOrEqualCustomPrecision(fontScale, progressTheme->GetFontScale()) && (GetType() == ProgressType::CAPSULE)) {
208 const auto& paddingProperty = layoutProperty->GetPaddingProperty();
209 if (!(paddingProperty &&
210 ((paddingProperty->top != std::nullopt) || (paddingProperty->bottom != std::nullopt)))) {
211 childHeight = childHeight + (progressTheme->GetfontScalePadding()).ConvertToPx();
212 }
213 }
214
215 return childHeight;
216 }
217 } // namespace OHOS::Ace::NG
218