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