1 /*
2  * Copyright (c) 2021-2022 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 "frameworks/bridge/common/dom/dom_progress.h"
17 
18 #include "frameworks/bridge/common/utils/utils.h"
19 
20 namespace OHOS::Ace::Framework {
21 
22 
IsGradient(const std::string & value)23 bool IsGradient(const std::string& value)
24 {
25     auto gradientJson = JsonUtil::ParseJsonString(value);
26     if (!gradientJson->IsObject()) {
27         return false;
28     }
29     return true;
30 }
31 
ParseGradient(const DOMProgress & progress,const std::string & value)32 Gradient ParseGradient(const DOMProgress& progress, const std::string& value)
33 {
34     // only support linear gradient
35     auto gradientJson = JsonUtil::ParseJsonString(value);
36     Gradient gradient = Gradient();
37     if (!gradientJson->IsObject()) {
38         return gradient;
39     }
40     auto gradientValue = gradientJson->GetValue(DOM_VALUES);
41     if ((gradientValue == nullptr) || (!gradientValue->IsArray()) || (gradientValue->GetArraySize() <= 0)) {
42         return gradient;
43     }
44     auto values = gradientValue->GetArrayItem(0);
45     gradient.SetDirection(GradientDirection::START_TO_END);
46     auto colors = values->GetValue(DOM_GRADIENT_VALUES);
47     if (colors != nullptr && colors->IsArray()) {
48         for (int32_t index = 0; index < colors->GetArraySize(); index++) {
49             // remove the " at front and end. check the color string longer than ""
50             // "#FFFFFF" -> #FFFFFF
51             if (colors->GetArrayItem(index)->ToString().length() > 2) {
52                 gradient.AddColor(GradientColor(progress.ParseColor(colors->GetArrayItem(index)->ToString().substr(
53                     1, colors->GetArrayItem(index)->ToString().length() - 2))));
54             }
55         }
56     }
57     return gradient;
58 }
59 
CreateProgressComponent(double min,double percent,double cachedValue,double max,ProgressType type)60 RefPtr<ProgressComponent> DOMProgress::CreateProgressComponent(
61     double min, double percent, double cachedValue, double max, ProgressType type)
62 {
63     if (type == ProgressType::CIRCLE) {
64         return AceType::MakeRefPtr<ProgressComponent>(min, percent, cachedValue, max, ProgressType::LINEAR);
65     }
66     return AceType::MakeRefPtr<ProgressComponent>(min, percent, cachedValue, max, type);
67 }
68 
DOMProgress(NodeId nodeId,const std::string & nodeName)69 DOMProgress::DOMProgress(NodeId nodeId, const std::string& nodeName) : DOMNode(nodeId, nodeName) {}
70 
SetSpecializedAttr(const std::pair<std::string,std::string> & attr)71 bool DOMProgress::SetSpecializedAttr(const std::pair<std::string, std::string>& attr)
72 {
73     static const LinearMapNode<void (*)(const std::string&, DOMProgress&)> progressAttrsOperators[] = {
74         { DOM_RING_CLOCKWISH_DIRECTION,
75             [](const std::string& val, DOMProgress& progress) { progress.clockwiseDirection_ = StringToBool(val); } },
76         { DOM_EFFECTS_ON,
77             [](const std::string& val, DOMProgress& progress) { progress.showAnimationEffect_ = StringToBool(val); } },
78         { DOM_PROGRESS_PERCENT,
79             [](const std::string& val, DOMProgress& progress) {
80                 progress.percent_ = StringToDouble(val);
81                 if (progress.percent_ > progress.max_) {
82                     progress.percent_ = progress.max_;
83                 }
84                 if (progress.percent_ < progress.min_) {
85                     progress.percent_ = progress.min_;
86                 }
87             } },
88         { DOM_PROGRESS_SECONDARY_PERCENT,
89             [](const std::string& val, DOMProgress& progress) {
90                 progress.cachedValue_ = StringToDouble(val);
91                 if (progress.cachedValue_ > progress.max_) {
92                     progress.cachedValue_ = progress.max_;
93                 }
94                 if (progress.cachedValue_ < progress.min_) {
95                     progress.cachedValue_ = progress.min_;
96                 }
97             } },
98         { DOM_PROGRESS_TYPE,
99             [](const std::string& val, DOMProgress& progress) {
100                 if (val == DOM_PROGRESS_TYPE_CIRCULAR) {
101                     progress.type_ = ProgressType::CIRCLE;
102                 } else if (val == DOM_PROGRESS_TYPE_RING) {
103                     progress.type_ = ProgressType::RING;
104                 } else if (val == DOM_PROGRESS_TYPE_HORIZONTAL) {
105                     progress.type_ = ProgressType::LINEAR;
106                 } else if (val == DOM_PROGRESS_TYPE_SCALE) {
107                     progress.type_ = ProgressType::SCALE;
108                 } else if (val == DOM_PROGRESS_TYPE_MOON) {
109                     progress.type_ = ProgressType::MOON;
110                 } else if ((val == DOM_PROGRESS_TYPE_ARC)) {
111                     progress.type_ = ProgressType::ARC;
112                 } else if ((val == DOM_PROGRESS_TYPE_BUBBLE)) {
113                     progress.type_ = ProgressType::BUBBLE;
114                 } else {
115                     progress.type_ = ProgressType::LINEAR;
116                 }
117             } },
118     };
119     auto operatorIter =
120         BinarySearchFindIndex(progressAttrsOperators, ArraySize(progressAttrsOperators), attr.first.c_str());
121     if (operatorIter != -1) {
122         progressAttrsOperators[operatorIter].value(attr.second, *this);
123         return true;
124     }
125     return false;
126 }
127 
SetSpecializedStyle(const std::pair<std::string,std::string> & style)128 bool DOMProgress::SetSpecializedStyle(const std::pair<std::string, std::string>& style)
129 {
130     // static linear map must be sorted by key.
131     static const LinearMapNode<void (*)(const std::string&, DOMProgress&)> progressStylesOperators[] = {
132         { DOM_PROGRESS_BACKGROUND_COLOR,
133             [](const std::string& val, DOMProgress& progress) {
134                 progress.backgroundColor_.first = progress.ParseColor(val);
135                 progress.backgroundColor_.second = true;
136             } },
137         { DOM_PROGRESS_BUBBLE_RADIUS,
138             [](const std::string& val, DOMProgress& progress) {
139                 progress.diameter_ = progress.ParseDimension(val);
140             } },
141         { DOM_CENTER_X,
142             [](const std::string& val, DOMProgress& progress) {
143                 progress.centerX_.first = StringToDouble(val);
144                 progress.centerX_.second = true;
145             } },
146         { DOM_CENTER_Y,
147             [](const std::string& val, DOMProgress& progress) {
148                 progress.centerY_.first = StringToDouble(val);
149                 progress.centerY_.second = true;
150             } },
151         { DOM_PROGRESS_COLOR,
152             [](const std::string& val, DOMProgress& progress) {
153                 if (IsGradient(val)) {
154                     progress.gradient_ = ParseGradient(progress, val);
155                 } else {
156                     progress.color_.first = progress.ParseColor(val);
157                     progress.color_.second = true;
158                 }
159             } },
160         { DOM_PROGRESS_DIAMETER,
161             [](const std::string& val, DOMProgress& progress) {
162                 progress.bubbleRadius_ = progress.ParseDimension(val);
163             } },
164         { DOM_DIRECTION,
165             [](const std::string& val, DOMProgress& progress) { progress.isStartToEnd_ = val == DOM_START_TO_END; } },
166         { DOM_PROGRESS_RADIUS,
167             [](const std::string& val, DOMProgress& progress) {
168                 progress.radius_.first = StringToDouble(val);
169                 progress.radius_.second = true;
170             } },
171         { DOM_SCALE_NUMBER,
172             [](const std::string& val, DOMProgress& progress) {
173                 progress.scaleNumber_.first = StringUtils::StringToInt(val);
174                 progress.scaleNumber_.second = true;
175             } },
176         { DOM_SCALE_WIDTH,
177             [](const std::string& val, DOMProgress& progress) {
178                 progress.scaleWidth_.first = progress.ParseDimension(val);
179                 progress.scaleWidth_.second = true;
180             } },
181         { DOM_PROGRESS_SECONDARY_COLOR,
182             [](const std::string& val, DOMProgress& progress) {
183                 progress.cachedColor_.first = progress.ParseColor(val);
184                 progress.cachedColor_.second = true;
185             } },
186         { DOM_START_DEGREE,
187             [](const std::string& val, DOMProgress& progress) { progress.startDegree_ = StringToDouble(val); } },
188         { DOM_PROGRESS_STROKE_WIDTH,
189             [](const std::string& val, DOMProgress& progress) {
190                 progress.strokeWidth_.first = progress.ParseDimension(val);
191                 progress.strokeWidth_.second = true;
192             } },
193         { DOM_SWEEP_DEGREE,
194             [](const std::string& val, DOMProgress& progress) { progress.sweepDegree_ = StringToDouble(val); } },
195     };
196     auto operatorIter =
197         BinarySearchFindIndex(progressStylesOperators, ArraySize(progressStylesOperators), style.first.c_str());
198     if (operatorIter != -1) {
199         progressStylesOperators[operatorIter].value(style.second, *this);
200         return true;
201     }
202     return false;
203 }
204 
PrepareSpecializedComponent()205 void DOMProgress::PrepareSpecializedComponent()
206 {
207     InitProgressIfNeed();
208     if (type_ == ProgressType::CIRCLE) {
209         loadingProgressChild_->SetProgressColor(color_.first);
210         return;
211     }
212     if (type_ == ProgressType::BUBBLE) {
213         bubbleProgressChild_->SetBubbleRadius(bubbleRadius_);
214         bubbleProgressChild_->SetDiameter(diameter_);
215         return;
216     }
217     progressChild_->SetMaxValue(max_);
218     progressChild_->SetMinValue(min_);
219     progressChild_->SetValue(percent_);
220     progressChild_->SetCachedValue(cachedValue_);
221     progressChild_->GetTrack()->SetSelectColor(color_.first);
222     progressChild_->GetTrack()->SetCachedColor(cachedColor_.first);
223     progressChild_->GetTrack()->SetBackgroundColor(backgroundColor_.first);
224     progressChild_->GetTrack()->SetTrackThickness(strokeWidth_.first);
225     progressChild_->GetTrack()->SetShowAnimation(showAnimationEffect_);
226     progressChild_->SetAnimationPlay(showAnimationEffect_);
227     progressChild_->GetTrack()->SetTextDirection(
228         IsRightToLeft() && isStartToEnd_ ? TextDirection::RTL : TextDirection::LTR);
229     if (gradient_.IsValid()) {
230         progressChild_->GetTrack()->SetSelectGradient(gradient_);
231     }
232     if (type_ == ProgressType::RING) {
233         auto info = progressChild_->GetTrack()->GetTrackInfo();
234         info->SetClockwise(clockwiseDirection_);
235     } else if (type_ == ProgressType::SCALE) {
236         auto info = progressChild_->GetTrack()->GetTrackInfo();
237         info->SetScaleWidth(scaleWidth_.first);
238         info->SetScaleNumber(scaleNumber_.first);
239         info->SetClockwise(clockwiseDirection_);
240     } else if (type_ == ProgressType::ARC) {
241         // draw arc progress
242         progressChild_->GetTrack()->GetTrackInfo()->SetStartDegree(startDegree_);
243         progressChild_->GetTrack()->GetTrackInfo()->SetSweepDegree(sweepDegree_);
244         if (radius_.second) {
245             progressChild_->GetTrack()->SetRadius(radius_.first);
246         }
247         if (centerX_.second) {
248             progressChild_->GetTrack()->SetCenterX(centerX_.first);
249         }
250         if (centerY_.second) {
251             progressChild_->GetTrack()->SetCenterY(centerY_.first);
252         }
253     }
254 }
255 
OnSetStyleFinished()256 void DOMProgress::OnSetStyleFinished()
257 {
258     // the range is from -360 to 360 degree
259     static constexpr double defaultStartDegree = -120;
260     static constexpr double defaultSweepDegree = 240;
261     if (startDegree_ > 360.0 || startDegree_ < -360.0) {
262         startDegree_ = defaultStartDegree;
263         sweepDegree_ = defaultSweepDegree;
264         return;
265     }
266     if (sweepDegree_ > 360.0 || sweepDegree_ < -360.0) {
267         startDegree_ = defaultStartDegree;
268         sweepDegree_ = defaultSweepDegree;
269         return;
270     }
271 }
272 
GetSpecializedComponent()273 RefPtr<Component> DOMProgress::GetSpecializedComponent()
274 {
275     if (type_ == ProgressType::CIRCLE) {
276         return loadingProgressChild_;
277     } else if (type_ == ProgressType::BUBBLE) {
278         return bubbleProgressChild_;
279     } else {
280         return progressChild_;
281     }
282 }
283 
InitProgressIfNeed()284 void DOMProgress::InitProgressIfNeed()
285 {
286     auto theme = GetTheme<ProgressTheme>();
287     if (type_ == ProgressType::CIRCLE) {
288         // Width_ and height_ in circular progress are usually the same with diameter in loading progress component.
289         // If width_ and height_ are different, choose smaller one as diameter.
290         if (!loadingProgressChild_) {
291             loadingProgressChild_ = AceType::MakeRefPtr<LoadingProgressComponent>();
292         }
293         if (theme) {
294             if (!color_.second) {
295                 color_.first = theme->GetProgressColor();
296             }
297             loadingProgressChild_->SetMoveRatio(theme->GetMoveRatio());
298             loadingProgressChild_->SetRingRadius(theme->GetRingRadius());
299             loadingProgressChild_->SetOrbitRadius(theme->GetOrbitRadius());
300             loadingProgressChild_->SetCometTailLen(theme->GetCometTailLen());
301         }
302         return;
303     }
304 
305     if (type_ == ProgressType::BUBBLE) {
306         if (!bubbleProgressChild_) {
307             bubbleProgressChild_ = AceType::MakeRefPtr<BubbleProgressComponent>();
308         }
309         return;
310     }
311 
312     if (!progressChild_ || progressChild_->GetType() != type_) {
313         progressChild_ = CreateProgressComponent(min_, percent_, cachedValue_, max_, type_);
314     }
315     if (!theme) {
316         return;
317     }
318     if (!color_.second) {
319         color_.first = type_ == ProgressType::MOON ? theme->GetMoonFrontColor() : theme->GetTrackSelectedColor();
320     }
321     if (!backgroundColor_.second) {
322         backgroundColor_.first =
323             type_ == ProgressType::MOON ? theme->GetMoonBackgroundColor() : theme->GetTrackBgColor();
324     }
325     if (!cachedColor_.second) {
326         cachedColor_.first = theme->GetTrackCachedColor();
327     }
328     if (!strokeWidth_.second) {
329         if (type_ == ProgressType::SCALE) {
330             strokeWidth_.first = theme->GetScaleLength();
331         } else if (type_ == ProgressType::RING) {
332             strokeWidth_.first = theme->GetRingThickness();
333         } else {
334             strokeWidth_.first = theme->GetTrackThickness();
335         }
336     }
337     if (!scaleWidth_.second) {
338         scaleWidth_.first = theme->GetScaleWidth();
339     }
340     if (!scaleNumber_.second) {
341         scaleNumber_.first = theme->GetScaleNumber();
342     }
343 }
344 
345 } // namespace OHOS::Ace::Framework
346