1 /*
2 * Copyright (c) 2022-2024 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 "base/log/dump_log.h"
17 #include "core/components_ng/pattern/progress/progress_pattern.h"
18
19 #include "core/components/progress/progress_theme.h"
20 #include "core/components/theme/app_theme.h"
21 #include "core/components_ng/base/inspector_filter.h"
22 #include "core/components_ng/pattern/progress/progress_layout_algorithm.h"
23 #include "core/components_ng/pattern/text/text_layout_property.h"
24 #include "core/components_ng/pattern/text/text_pattern.h"
25 #include "core/pipeline/pipeline_base.h"
26
27 namespace OHOS::Ace::NG {
28 namespace {
29 constexpr float PROGRESS_DEFAULT_VALUE = 0.0f;
30 }
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)31 bool ProgressPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
32 {
33 if (config.skipMeasure || dirty->SkipMeasureContent()) {
34 return false;
35 }
36 auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
37 CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
38 auto progressLayoutAlgorithm = DynamicCast<ProgressLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
39 CHECK_NULL_RETURN(progressLayoutAlgorithm, false);
40 strokeWidth_ = progressLayoutAlgorithm->GetStrokeWidth();
41 return true;
42 }
43
OnAttachToFrameNode()44 void ProgressPattern::OnAttachToFrameNode()
45 {
46 auto host = GetHost();
47 CHECK_NULL_VOID(host);
48 host->GetRenderContext()->SetClipToFrame(true);
49 }
50
InitAnimatableProperty(ProgressAnimatableProperty & progressAnimatableProperty)51 void ProgressPattern::InitAnimatableProperty(ProgressAnimatableProperty& progressAnimatableProperty)
52 {
53 auto pipeline = PipelineBase::GetCurrentContext();
54 CHECK_NULL_VOID(pipeline);
55 auto progressTheme = pipeline->GetTheme<ProgressTheme>();
56 CHECK_NULL_VOID(progressTheme);
57 auto progressLayoutProperty = GetLayoutProperty<ProgressLayoutProperty>();
58 CHECK_NULL_VOID(progressLayoutProperty);
59 auto paintProperty = GetPaintProperty<ProgressPaintProperty>();
60 CHECK_NULL_VOID(paintProperty);
61 auto color = progressTheme->GetTrackSelectedColor();
62 auto bgColor = progressTheme->GetTrackBgColor();
63 if (progressType_ == ProgressType::CAPSULE) {
64 color = progressTheme->GetCapsuleSelectColor();
65 bgColor = progressTheme->GetCapsuleBgColor();
66 } else if (progressType_ == ProgressType::RING) {
67 bgColor = progressTheme->GetRingProgressBgColor();
68 }
69 color = paintProperty->GetColor().value_or(color);
70 bgColor = paintProperty->GetBackgroundColor().value_or(bgColor);
71 auto borderColor = paintProperty->GetBorderColor().value_or(progressTheme->GetBorderColor());
72 auto host = GetHost();
73 CHECK_NULL_VOID(host);
74 auto geometryNode = host->GetGeometryNode();
75 CHECK_NULL_VOID(geometryNode);
76 auto contentSize = geometryNode->GetContentSize();
77 CalculateStrokeWidth(contentSize);
78 auto strokeRadius = static_cast<float>(
79 paintProperty->GetStrokeRadiusValue(Dimension(strokeWidth_ / 2, DimensionUnit::VP)).ConvertToPx());
80 strokeRadius = std::min(strokeWidth_ / 2, strokeRadius);
81 auto smoothEffect = paintProperty->GetEnableSmoothEffectValue(true);
82 if (!smoothEffect) {
83 auto value = paintProperty->GetValueValue(PROGRESS_DEFAULT_VALUE);
84 progressAnimatableProperty.value = value;
85 }
86 progressAnimatableProperty.color = color;
87 progressAnimatableProperty.bgColor = bgColor;
88 progressAnimatableProperty.borderColor = borderColor;
89 progressAnimatableProperty.strokeWidth = strokeWidth_;
90 progressAnimatableProperty.strokeRadius = strokeRadius;
91
92 if (paintProperty->HasGradientColor()) {
93 progressAnimatableProperty.ringProgressColor = paintProperty->GetGradientColorValue();
94 } else {
95 progressAnimatableProperty.ringProgressColor = convertGradient(color);
96 }
97 }
98
CalculateStrokeWidth(const SizeF & contentSize)99 void ProgressPattern::CalculateStrokeWidth(const SizeF& contentSize)
100 {
101 auto length = std::min(contentSize.Width(), contentSize.Height());
102 auto radius = length / 2;
103 switch (progressType_) {
104 case ProgressType::LINEAR:
105 strokeWidth_ = std::min(strokeWidth_, length);
106 break;
107 case ProgressType::RING:
108 case ProgressType::SCALE:
109 if (strokeWidth_ >= radius) {
110 strokeWidth_ = radius / 2;
111 }
112 break;
113 default:
114 break;
115 }
116 }
117
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const118 void ProgressPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
119 {
120 /* no fixed attr below, just return */
121 if (filter.IsFastFilter()) {
122 ToJsonValueForRingStyleOptions(json, filter);
123 ToJsonValueForLinearStyleOptions(json, filter);
124 return;
125 }
126 auto layoutProperty = GetLayoutProperty<ProgressLayoutProperty>();
127 CHECK_NULL_VOID(layoutProperty);
128 auto paintProperty = GetPaintProperty<ProgressPaintProperty>();
129 CHECK_NULL_VOID(paintProperty);
130 auto pipeline = PipelineBase::GetCurrentContext();
131 CHECK_NULL_VOID(pipeline);
132 auto theme = pipeline->GetTheme<ProgressTheme>();
133 CHECK_NULL_VOID(theme);
134 auto jsonValue = JsonUtil::Create(true);
135 jsonValue->Put("strokeWidth", layoutProperty->GetStrokeWidthValue(theme->GetTrackThickness()).ToString().c_str());
136 jsonValue->Put("scaleCount", std::to_string(paintProperty->GetScaleCountValue(theme->GetScaleNumber())).c_str());
137 jsonValue->Put("scaleWidth", paintProperty->GetScaleWidthValue(theme->GetScaleWidth()).ToString().c_str());
138 json->PutExtAttr("style", jsonValue->ToString().c_str(), filter);
139 ToJsonValueForRingStyleOptions(json, filter);
140 ToJsonValueForLinearStyleOptions(json, filter);
141 json->PutExtAttr("enableSmoothEffect",
142 paintProperty->GetEnableSmoothEffectValue(true) ? "true" : "false", filter);
143 }
144
InitTouchEvent()145 void ProgressPattern::InitTouchEvent()
146 {
147 if (touchListener_) {
148 return;
149 }
150 auto host = GetHost();
151 CHECK_NULL_VOID(host);
152 auto gesture = host->GetOrCreateGestureEventHub();
153 CHECK_NULL_VOID(gesture);
154 auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
155 auto buttonPattern = weak.Upgrade();
156 CHECK_NULL_VOID(buttonPattern);
157 buttonPattern->OnPress(info);
158 };
159 touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
160 gesture->AddTouchEvent(touchListener_);
161 }
162
RemoveTouchEvent()163 void ProgressPattern::RemoveTouchEvent()
164 {
165 if (touchListener_) {
166 auto host = GetHost();
167 CHECK_NULL_VOID(host);
168 auto gesture = host->GetOrCreateGestureEventHub();
169 CHECK_NULL_VOID(gesture);
170 gesture->RemoveTouchEvent(touchListener_);
171 touchListener_ = nullptr;
172 }
173 }
HandleEnabled()174 void ProgressPattern::HandleEnabled()
175 {
176 auto host = GetHost();
177 CHECK_NULL_VOID(host);
178 auto eventHub = host->GetEventHub<EventHub>();
179 CHECK_NULL_VOID(eventHub);
180 auto enabled = eventHub->IsEnabled();
181 auto renderContext = host->GetRenderContext();
182 CHECK_NULL_VOID(renderContext);
183 auto pipeline = PipelineBase::GetCurrentContext();
184 CHECK_NULL_VOID(pipeline);
185 auto theme = pipeline->GetTheme<ProgressTheme>();
186 CHECK_NULL_VOID(theme);
187 auto alpha = theme->GetProgressDisable();
188 auto originalOpacity = renderContext->GetOpacityValue(1.0);
189 renderContext->OnOpacityUpdate(enabled ? originalOpacity : alpha * originalOpacity);
190 }
191
OnPress(const TouchEventInfo & info)192 void ProgressPattern::OnPress(const TouchEventInfo& info)
193 {
194 auto touchType = info.GetTouches().front().GetTouchType();
195 auto pipeline = PipelineBase::GetCurrentContext();
196 CHECK_NULL_VOID(pipeline);
197 auto theme = pipeline->GetTheme<ProgressTheme>();
198 CHECK_NULL_VOID(theme);
199 auto host = GetHost();
200 CHECK_NULL_VOID(host);
201 auto paintProperty = host->GetPaintProperty<ProgressPaintProperty>();
202 CHECK_NULL_VOID(paintProperty);
203 auto textHost = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(0));
204 CHECK_NULL_VOID(textHost);
205 auto textLayoutProperty = textHost->GetLayoutProperty<TextLayoutProperty>();
206 CHECK_NULL_VOID(textLayoutProperty);
207
208 if (touchType == TouchType::DOWN) {
209 backgroundColor_ = paintProperty->GetBackgroundColor().value_or(theme->GetCapsuleBgColor());
210 selectColor_ = paintProperty->GetColor().value_or(theme->GetCapsuleSelectColor());
211 borderColor_ = paintProperty->GetBorderColor().value_or(theme->GetBorderColor());
212 fontColor_ = textLayoutProperty->GetTextColor().value_or(theme->GetTextColor());
213 Color touchEffect = theme->GetClickEffect();
214 Color touchColorDown = backgroundColor_.BlendColor(touchEffect);
215 Color touchSelectColorDown = selectColor_.BlendColor(touchEffect);
216 Color touchBorderColorDown = borderColor_.BlendColor(touchEffect);
217 Color touchFontColorDown = fontColor_.BlendColor(touchEffect);
218 paintProperty->UpdateBackgroundColor(touchColorDown);
219 paintProperty->UpdateColor(touchSelectColorDown);
220 paintProperty->UpdateBorderColor(touchBorderColorDown);
221 textLayoutProperty->UpdateTextColor(touchFontColorDown);
222 } else if (touchType == TouchType::UP || touchType == TouchType::CANCEL) {
223 paintProperty->UpdateBackgroundColor(backgroundColor_);
224 paintProperty->UpdateColor(selectColor_);
225 paintProperty->UpdateBorderColor(borderColor_);
226 textLayoutProperty->UpdateTextColor(fontColor_);
227 }
228 textHost->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
229 host->MarkDirtyNode();
230 }
231
InitOnKeyEvent(const RefPtr<FocusHub> & focusHub)232 void ProgressPattern::InitOnKeyEvent(const RefPtr<FocusHub>& focusHub)
233 {
234 auto getInnerPaintRectCallback = [wp = WeakClaim(this)](RoundRect& paintRect) {
235 auto pattern = wp.Upgrade();
236 CHECK_NULL_VOID(pattern);
237 pattern->GetInnerFocusPaintRect(paintRect);
238 };
239 focusHub->SetInnerFocusPaintRectCallback(getInnerPaintRectCallback);
240 }
241
GetInnerFocusPaintRect(RoundRect & paintRect)242 void ProgressPattern::GetInnerFocusPaintRect(RoundRect& paintRect)
243 {
244 auto host = GetHost();
245 CHECK_NULL_VOID(host);
246 const auto& content = host->GetGeometryNode()->GetContent();
247 CHECK_NULL_VOID(content);
248 auto contentOffset = content->GetRect().GetOffset();
249 auto contentSize = content->GetRect().GetSize();
250 auto currentContext = PipelineBase::GetCurrentContext();
251 CHECK_NULL_VOID(currentContext);
252 auto appTheme = currentContext->GetTheme<AppTheme>();
253 CHECK_NULL_VOID(appTheme);
254 auto paintWidth = appTheme->GetFocusWidthVp();
255 auto focusPadding = appTheme->GetFocusOutPaddingVp();
256 auto focusDistance = paintWidth / 2 + focusPadding;
257 auto focusRadius =
258 std::min(contentSize.Width(), contentSize.Height()) * 0.5 + static_cast<float>(focusDistance.ConvertToPx());
259 paintRect.SetRect(RectF(contentOffset.GetX() - focusDistance.ConvertToPx(),
260 contentOffset.GetY() - focusDistance.ConvertToPx(), contentSize.Width() + 2 * focusDistance.ConvertToPx(),
261 contentSize.Height() + 2 * focusDistance.ConvertToPx()));
262 paintRect.SetCornerRadius(focusRadius);
263 }
264
OnModifyDone()265 void ProgressPattern::OnModifyDone()
266 {
267 Pattern::OnModifyDone();
268 FireBuilder();
269 auto host = GetHost();
270 CHECK_NULL_VOID(host);
271 auto progressLayoutProperty = GetLayoutProperty<ProgressLayoutProperty>();
272 CHECK_NULL_VOID(progressLayoutProperty);
273 if (progressLayoutProperty->GetType() == ProgressType::CAPSULE) {
274 auto hub = host->GetEventHub<EventHub>();
275 HandleEnabled();
276 InitTouchEvent();
277 auto focusHub = hub->GetFocusHub();
278 CHECK_NULL_VOID(focusHub);
279 InitOnKeyEvent(focusHub);
280 } else {
281 RemoveTouchEvent();
282 }
283 }
284
DumpInfo()285 void ProgressPattern::DumpInfo()
286 {
287 auto paintProperty = GetPaintProperty<ProgressPaintProperty>();
288 CHECK_NULL_VOID(paintProperty);
289 DumpLog::GetInstance().AddDesc(
290 std::string("EnableSmoothEffect: ")
291 .append(paintProperty->GetEnableSmoothEffectValue(true) ? "true" : "false"));
292 }
293
OnLanguageConfigurationUpdate()294 void ProgressPattern::OnLanguageConfigurationUpdate()
295 {
296 auto host = GetHost();
297 CHECK_NULL_VOID(host);
298 auto progressLayoutProperty = GetLayoutProperty<ProgressLayoutProperty>();
299 CHECK_NULL_VOID(progressLayoutProperty);
300 bool isRtl = progressLayoutProperty->GetNonAutoLayoutDirection() == TextDirection::RTL;
301 if (isRightToLeft_ == isRtl) {
302 return;
303 }
304 CHECK_NULL_VOID(progressModifier_);
305 progressModifier_->SetIsRightToLeft(isRtl);
306 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
307 isRightToLeft_ = isRtl;
308 }
309
OnVisibleChange(bool isVisible)310 void ProgressPattern::OnVisibleChange(bool isVisible)
311 {
312 auto host = GetHost();
313 CHECK_NULL_VOID(host);
314 visibilityProp_ = isVisible;
315 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
316 }
317
ToJsonValueForRingStyleOptions(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const318 void ProgressPattern::ToJsonValueForRingStyleOptions(std::unique_ptr<JsonValue>& json,
319 const InspectorFilter& filter) const
320 {
321 /* no fixed attr below, just return */
322 if (filter.IsFastFilter()) {
323 return;
324 }
325 auto layoutProperty = GetLayoutProperty<ProgressLayoutProperty>();
326 auto paintProperty = GetPaintProperty<ProgressPaintProperty>();
327 auto pipeline = PipelineBase::GetCurrentContext();
328 auto theme = pipeline->GetTheme<ProgressTheme>();
329
330 auto jsonValue = JsonUtil::Create(true);
331 jsonValue->Put("strokeWidth", layoutProperty->GetStrokeWidthValue(theme->GetTrackThickness()).ToString().c_str());
332 jsonValue->Put("enableScanEffect", (paintProperty->GetEnableRingScanEffect().value_or(false)) ? "true" : "false");
333 jsonValue->Put("shadow", paintProperty->GetPaintShadowValue(false) ? "true" : "false");
334 jsonValue->Put("status",
335 ConvertProgressStatusToString(paintProperty->GetProgressStatusValue(ProgressStatus::PROGRESSING)).c_str());
336 json->PutExtAttr("ringStyle", jsonValue, filter);
337 }
338
ToJsonValueForLinearStyleOptions(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const339 void ProgressPattern::ToJsonValueForLinearStyleOptions(
340 std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
341 {
342 /* no fixed attr below, just return */
343 if (filter.IsFastFilter()) {
344 return;
345 }
346 auto layoutProperty = GetLayoutProperty<ProgressLayoutProperty>();
347 auto paintProperty = GetPaintProperty<ProgressPaintProperty>();
348 auto pipeline = PipelineBase::GetCurrentContext();
349 auto theme = pipeline->GetTheme<ProgressTheme>();
350
351 auto jsonValue = JsonUtil::Create(true);
352 auto strokeWidth = layoutProperty->GetStrokeWidthValue(theme->GetTrackThickness());
353 jsonValue->Put("strokeWidth", strokeWidth.ToString().c_str());
354 auto strokeRadius = paintProperty->GetStrokeRadiusValue(strokeWidth / 2);
355 strokeRadius = std::min(strokeWidth / 2, strokeRadius);
356 jsonValue->Put("strokeRadius", strokeRadius.ToString().c_str());
357 jsonValue->Put("enableScanEffect", (paintProperty->GetEnableLinearScanEffect().value_or(false)) ? "true" : "false");
358 json->PutExtAttr("linearStyle", jsonValue, filter);
359 }
360
ConvertProgressStatusToString(const ProgressStatus status)361 std::string ProgressPattern::ConvertProgressStatusToString(const ProgressStatus status)
362 {
363 std::string str;
364
365 switch (status) {
366 case ProgressStatus::LOADING:
367 str = "ProgressStatus.LOADING";
368 break;
369 case ProgressStatus::PROGRESSING:
370 default:
371 str = "ProgressStatus.PROGRESSING";
372 break;
373 }
374
375 return str;
376 }
377
ObscureText(bool isSensitive)378 void ProgressPattern::ObscureText(bool isSensitive)
379 {
380 auto frameNode = GetHost();
381 CHECK_NULL_VOID(frameNode);
382 auto textHost = AceType::DynamicCast<FrameNode>(frameNode->GetChildAtIndex(0));
383 CHECK_NULL_VOID(textHost);
384 auto textPattern = textHost->GetPattern<TextPattern>();
385 CHECK_NULL_VOID(textPattern);
386 textPattern->OnSensitiveStyleChange(isSensitive);
387 textHost->SetPrivacySensitive(isSensitive);
388 textHost->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
389 }
390
OnSensitiveStyleChange(bool isSensitive)391 void ProgressPattern::OnSensitiveStyleChange(bool isSensitive)
392 {
393 auto frameNode = GetHost();
394 CHECK_NULL_VOID(frameNode);
395 auto progressPaintProperty = frameNode->GetPaintProperty<NG::ProgressPaintProperty>();
396 CHECK_NULL_VOID(progressPaintProperty);
397 progressPaintProperty->UpdateIsSensitive(isSensitive);
398 ObscureText(isSensitive);
399 frameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
400 }
401
FireBuilder()402 void ProgressPattern::FireBuilder()
403 {
404 auto host = GetHost();
405 CHECK_NULL_VOID(host);
406 if (!makeFunc_.has_value()) {
407 CHECK_NULL_VOID(contentModifierNode_);
408 host->RemoveChildAndReturnIndex(contentModifierNode_);
409 contentModifierNode_ = nullptr;
410 host->GetRenderContext()->SetClipToFrame(true);
411 host->MarkNeedFrameFlushDirty(PROPERTY_UPDATE_MEASURE);
412 return;
413 }
414 auto node = BuildContentModifierNode();
415 if (contentModifierNode_ == node) {
416 return;
417 }
418 host->GetRenderContext()->SetClipToFrame(false);
419 host->RemoveChildAndReturnIndex(contentModifierNode_);
420 contentModifierNode_ = node;
421 CHECK_NULL_VOID(contentModifierNode_);
422 host->AddChild(contentModifierNode_, 0);
423 host->MarkNeedFrameFlushDirty(PROPERTY_UPDATE_MEASURE);
424 }
425
BuildContentModifierNode()426 RefPtr<FrameNode> ProgressPattern::BuildContentModifierNode()
427 {
428 if (!makeFunc_.has_value()) {
429 return nullptr;
430 }
431 auto renderProperty = GetPaintProperty<ProgressPaintProperty>();
432 CHECK_NULL_RETURN(renderProperty, nullptr);
433 auto value = renderProperty->GetValue().value_or(0);
434 auto total = renderProperty->GetMaxValue().value_or(0);
435 auto host = GetHost();
436 CHECK_NULL_RETURN(host, nullptr);
437 auto eventHub = host->GetEventHub<EventHub>();
438 CHECK_NULL_RETURN(eventHub, nullptr);
439 auto enabled = eventHub->IsEnabled();
440 return (makeFunc_.value())(ProgressConfiguration{value, total, enabled});
441 }
442 } // namespace OHOS::Ace::NG
443