• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2023 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 "bridge/declarative_frontend/jsview/js_gauge.h"
17 
18 #include <string>
19 
20 #include "base/log/ace_scoring_log.h"
21 #include "base/memory/ace_type.h"
22 #include "bridge/declarative_frontend/jsview/js_interactable_view.h"
23 #include "bridge/declarative_frontend/jsview/js_linear_gradient.h"
24 #include "bridge/declarative_frontend/jsview/js_utils.h"
25 #include "bridge/declarative_frontend/jsview/models/gauge_model_impl.h"
26 #include "core/components_ng/base/view_stack_model.h"
27 #include "core/components_ng/pattern/gauge/gauge_model_ng.h"
28 
29 namespace OHOS::Ace {
30 
31 std::unique_ptr<GaugeModel> GaugeModel::instance_ = nullptr;
32 std::mutex GaugeModel::mutex_;
33 constexpr double DEFAULT_GAUGE_VALUE = 0;
34 constexpr double DEFAULT_GAUGE_MIN = 0;
35 constexpr double DEFAULT_GAUGE_MAX = 100;
36 
GetInstance()37 GaugeModel* GaugeModel::GetInstance()
38 {
39     if (!instance_) {
40         std::lock_guard<std::mutex> lock(mutex_);
41         if (!instance_) {
42 #ifdef NG_BUILD
43             instance_.reset(new NG::GaugeModelNG());
44 #else
45             if (Container::IsCurrentUseNewPipeline()) {
46                 instance_.reset(new NG::GaugeModelNG());
47             } else {
48                 instance_.reset(new Framework::GaugeModelImpl());
49             }
50 #endif
51         }
52     }
53     return instance_.get();
54 }
55 
56 } // namespace OHOS::Ace
57 namespace OHOS::Ace::Framework {
58     constexpr Color ERROR_COLOR = Color(0xFFE84026);
59     constexpr float FIX_ANGLE = 720.0f;
60 
JSBind(BindingTarget globalObj)61 void JSGauge::JSBind(BindingTarget globalObj)
62 {
63     JSClass<JSGauge>::Declare("Gauge");
64     JSClass<JSGauge>::StaticMethod("create", &JSGauge::Create);
65 
66     JSClass<JSGauge>::StaticMethod("value", &JSGauge::SetValue);
67     JSClass<JSGauge>::StaticMethod("startAngle", &JSGauge::SetStartAngle);
68     JSClass<JSGauge>::StaticMethod("endAngle", &JSGauge::SetEndAngle);
69     JSClass<JSGauge>::StaticMethod("colors", &JSGauge::SetColors);
70     JSClass<JSGauge>::StaticMethod("strokeWidth", &JSGauge::SetStrokeWidth);
71     JSClass<JSGauge>::StaticMethod("labelConfig", &JSGauge::SetLabelConfig);
72     JSClass<JSGauge>::StaticMethod("trackShadow", &JSGauge::SetShadowOptions);
73     JSClass<JSGauge>::StaticMethod("indicator", &JSGauge::SetIndicator);
74     JSClass<JSGauge>::StaticMethod("description", &JSGauge::SetDescription);
75     JSClass<JSGauge>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
76     JSClass<JSGauge>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
77     JSClass<JSGauge>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
78     JSClass<JSGauge>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
79     JSClass<JSGauge>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
80     JSClass<JSGauge>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
81     JSClass<JSGauge>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
82     JSClass<JSGauge>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
83 
84     JSClass<JSGauge>::InheritAndBind<JSViewAbstract>(globalObj);
85 }
86 
Create(const JSCallbackInfo & info)87 void JSGauge::Create(const JSCallbackInfo& info)
88 {
89     if (!info[0]->IsObject()) {
90         GaugeModel::GetInstance()->Create(DEFAULT_GAUGE_VALUE, DEFAULT_GAUGE_MIN, DEFAULT_GAUGE_MAX);
91         GaugeModel::GetInstance()->SetIsShowLimitValue(true);
92         return;
93     }
94 
95     auto paramObject = JSRef<JSObject>::Cast(info[0]);
96     auto value = paramObject->GetProperty("value");
97     auto min = paramObject->GetProperty("min");
98     auto max = paramObject->GetProperty("max");
99 
100     double gaugeMin = min->IsNumber() ? min->ToNumber<double>() : DEFAULT_GAUGE_MIN;
101     double gaugeMax = max->IsNumber() ? max->ToNumber<double>() : DEFAULT_GAUGE_MAX;
102     double gaugeValue = value->IsNumber() ? value->ToNumber<double>() : DEFAULT_GAUGE_VALUE;
103     if (LessNotEqual(gaugeMax, gaugeMin)) {
104         gaugeMin = NG::DEFAULT_MIN_VALUE;
105         gaugeMax = NG::DEFAULT_MAX_VALUE;
106     }
107 
108     if (LessNotEqual(gaugeValue, gaugeMin) || GreatNotEqual(gaugeValue, gaugeMax)) {
109         gaugeValue = gaugeMin;
110     }
111     GaugeModel::GetInstance()->Create(gaugeValue, gaugeMin, gaugeMax);
112     if (min->IsNumber() || max->IsNumber()) {
113         GaugeModel::GetInstance()->SetIsShowLimitValue(true);
114     } else {
115         GaugeModel::GetInstance()->SetIsShowLimitValue(false);
116     }
117 }
118 
SetValue(const JSCallbackInfo & info)119 void JSGauge::SetValue(const JSCallbackInfo& info)
120 {
121     float value = NG::DEFAULT_MIN_VALUE;
122     if (info[0]->IsNumber()) {
123         value = info[0]->ToNumber<float>();
124     }
125     GaugeModel::GetInstance()->SetValue(value);
126 }
127 
SetStartAngle(const JSCallbackInfo & info)128 void JSGauge::SetStartAngle(const JSCallbackInfo& info)
129 {
130     float startAngle = NG::DEFAULT_START_DEGREE;
131     if (info[0]->IsNumber()) {
132         startAngle = std::fmod(info[0]->ToNumber<double>(), FIX_ANGLE);
133     }
134     GaugeModel::GetInstance()->SetStartAngle(startAngle);
135 }
136 
SetEndAngle(const JSCallbackInfo & info)137 void JSGauge::SetEndAngle(const JSCallbackInfo& info)
138 {
139     float endAngle = NG::DEFAULT_END_DEGREE;
140     if (info[0]->IsNumber()) {
141         endAngle = std::fmod(info[0]->ToNumber<double>(), FIX_ANGLE);
142     }
143     GaugeModel::GetInstance()->SetEndAngle(endAngle);
144 }
145 
SetColors(const JSCallbackInfo & info)146 void JSGauge::SetColors(const JSCallbackInfo& info)
147 {
148     if (!Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
149         SetGradientColors(info);
150         return;
151     }
152 
153     if (!info[0]->IsArray()) {
154         return;
155     }
156     std::vector<Color> colors;
157     std::vector<double> values;
158     std::vector<float> weights;
159     auto jsColor = JSRef<JSArray>::Cast(info[0]);
160     for (size_t i = 0; i < jsColor->Length(); ++i) {
161         JSRef<JSVal> jsValue = jsColor->GetValueAt(i);
162         if (!jsValue->IsArray()) {
163             return;
164         }
165         JSRef<JSArray> tempColors = jsColor->GetValueAt(i);
166         double value = tempColors->GetValueAt(1)->ToNumber<double>();
167         float weight = tempColors->GetValueAt(1)->ToNumber<float>();
168         Color selectedColor;
169         if (!ParseJsColor(tempColors->GetValueAt(0), selectedColor)) {
170             selectedColor = ERROR_COLOR;
171         }
172         colors.push_back(selectedColor);
173         values.push_back(value);
174         if (weight > 0) {
175             weights.push_back(weight);
176         } else {
177             weights.push_back(0.0f);
178         }
179     }
180     GaugeModel::GetInstance()->SetColors(colors, weights);
181 }
182 
SetGradientColors(const JSCallbackInfo & info)183 void JSGauge::SetGradientColors(const JSCallbackInfo& info)
184 {
185     if (info[0]->IsNull() || info[0]->IsUndefined()) {
186         GaugeModel::GetInstance()->ResetGradientColors();
187         return;
188     }
189 
190     NG::GaugeType type = NG::GaugeType::TYPE_CIRCULAR_MULTI_SEGMENT_GRADIENT;
191     std::vector<NG::ColorStopArray> colors;
192     std::vector<float> weights;
193     if (info[0]->IsArray()) {
194         auto jsColorsArray = JSRef<JSArray>::Cast(info[0]);
195         if (jsColorsArray->Length() == 0) {
196             GaugeModel::GetInstance()->ResetGradientColors();
197             return;
198         }
199 
200         for (size_t i = 0; i < jsColorsArray->Length(); ++i) {
201             if (static_cast<int32_t>(i) >= NG::COLORS_MAX_COUNT) {
202                 break;
203             }
204             JSRef<JSVal> jsValue = jsColorsArray->GetValueAt(i);
205             if (!jsValue->IsArray()) {
206                 continue;
207             }
208             auto tempColors = JSRef<JSArray>::Cast(jsValue);
209             // Get weight
210             float weight = tempColors->GetValueAt(1)->ToNumber<float>();
211             if (NonPositive(weight)) {
212                 continue;
213             }
214             weights.push_back(weight);
215             // Get color
216             JSRef<JSVal> jsColorValue = tempColors->GetValueAt(0);
217             ConvertGradientColor(jsColorValue, colors, type);
218         }
219         type = NG::GaugeType::TYPE_CIRCULAR_MULTI_SEGMENT_GRADIENT;
220         SortColorStopOffset(colors);
221         GaugeModel::GetInstance()->SetGradientColors(colors, weights, type);
222         return;
223     }
224     ConvertGradientColor(info[0], colors, type);
225     SortColorStopOffset(colors);
226     GaugeModel::GetInstance()->SetGradientColors(colors, weights, type);
227 }
228 
SortColorStopOffset(std::vector<NG::ColorStopArray> & colors)229 void JSGauge::SortColorStopOffset(std::vector<NG::ColorStopArray>& colors)
230 {
231     for (auto& colorStopArray : colors) {
232         std::sort(colorStopArray.begin(), colorStopArray.end(),
233             [](const std::pair<Color, Dimension>& left, const std::pair<Color, Dimension>& right) {
234                 return left.second.Value() < right.second.Value();
235             });
236 
237         auto iter = std::unique(colorStopArray.begin(), colorStopArray.end(),
238             [](const std::pair<Color, Dimension>& left, const std::pair<Color, Dimension>& right) {
239                 return left.second.Value() == right.second.Value();
240             });
241         colorStopArray.erase(iter, colorStopArray.end());
242     }
243 }
244 
ConvertGradientColor(const JsiRef<JsiValue> & itemParam,std::vector<NG::ColorStopArray> & colors,NG::GaugeType & type)245 void JSGauge::ConvertGradientColor(
246     const JsiRef<JsiValue>& itemParam, std::vector<NG::ColorStopArray>& colors, NG::GaugeType& type)
247 {
248     if (!itemParam->IsObject()) {
249         type = NG::GaugeType::TYPE_CIRCULAR_MONOCHROME;
250         return ConvertResourceColor(itemParam, colors);
251     }
252 
253     JSLinearGradient* jsLinearGradient = JSRef<JSObject>::Cast(itemParam)->Unwrap<JSLinearGradient>();
254     if (!jsLinearGradient) {
255         type = NG::GaugeType::TYPE_CIRCULAR_MONOCHROME;
256         return ConvertResourceColor(itemParam, colors);
257     }
258 
259     type = NG::GaugeType::TYPE_CIRCULAR_SINGLE_SEGMENT_GRADIENT;
260     if (jsLinearGradient->GetGradient().size() == 0) {
261         NG::ColorStopArray colorStopArray;
262         colorStopArray.emplace_back(std::make_pair(ERROR_COLOR, Dimension(0.0)));
263         colors.emplace_back(colorStopArray);
264     } else {
265         colors.emplace_back(jsLinearGradient->GetGradient());
266     }
267 }
268 
ConvertResourceColor(const JsiRef<JsiValue> & itemParam,std::vector<NG::ColorStopArray> & colors)269 void JSGauge::ConvertResourceColor(const JsiRef<JsiValue>& itemParam, std::vector<NG::ColorStopArray>& colors)
270 {
271     Color color;
272     if (!ParseJsColor(itemParam, color)) {
273         color = ERROR_COLOR;
274     }
275     NG::ColorStopArray colorStopArray;
276     colorStopArray.emplace_back(std::make_pair(color, Dimension(0.0)));
277     colors.emplace_back(colorStopArray);
278 }
279 
SetStrokeWidth(const JSCallbackInfo & info)280 void JSGauge::SetStrokeWidth(const JSCallbackInfo& info)
281 {
282     CalcDimension strokeWidth;
283     if (!ParseJsDimensionVpNG(info[0], strokeWidth) || strokeWidth.Unit() == DimensionUnit::PERCENT) {
284         strokeWidth = CalcDimension(0);
285     }
286     GaugeModel::GetInstance()->SetStrokeWidth(strokeWidth);
287 }
288 
SetLabelConfig(const JSCallbackInfo & info)289 void JSGauge::SetLabelConfig(const JSCallbackInfo& info)
290 {
291     if (!info[0]->IsObject()) {
292         return;
293     }
294     auto paramObject = JSRef<JSObject>::Cast(info[0]);
295     auto labelText = paramObject->GetProperty("text");
296     auto labelColor = paramObject->GetProperty("color");
297     Color currentColor;
298     ParseJsColor(labelColor, currentColor);
299     if (labelText->IsString()) {
300         GaugeModel::GetInstance()->SetLabelMarkedText(labelText->ToString());
301     }
302     if (ParseJsColor(labelColor, currentColor)) {
303         GaugeModel::GetInstance()->SetMarkedTextColor(currentColor);
304     }
305 }
306 
SetShadowOptions(const JSCallbackInfo & info)307 void JSGauge::SetShadowOptions(const JSCallbackInfo& info)
308 {
309     NG::GaugeShadowOptions shadowOptions;
310     if (info[0]->IsNull()) {
311         shadowOptions.isShadowVisible = false;
312         GaugeModel::GetInstance()->SetShadowOptions(shadowOptions);
313         return;
314     }
315 
316     if (!info[0]->IsObject()) {
317         GaugeModel::GetInstance()->ResetShadowOptions();
318         return;
319     }
320 
321     auto paramObject = JSRef<JSObject>::Cast(info[0]);
322     JSRef<JSVal> jsRadius = paramObject->GetProperty("radius");
323     JSRef<JSVal> jsOffsetX = paramObject->GetProperty("offsetX");
324     JSRef<JSVal> jsOffsetY = paramObject->GetProperty("offsetY");
325 
326     double radius = 0.0;
327     if (!ParseJsDouble(jsRadius, radius)) {
328         radius = NG::DEFAULT_GAUGE_SHADOW_RADIUS;
329     }
330 
331     if (NonPositive(radius)) {
332         radius = NG::DEFAULT_GAUGE_SHADOW_RADIUS;
333     }
334 
335     double offsetX = 0.0;
336     if (!ParseJsDouble(jsOffsetX, offsetX)) {
337         offsetX = NG::DEFAULT_GAUGE_SHADOW_OFFSETX;
338     }
339 
340     double offsetY = 0.0;
341     if (!ParseJsDouble(jsOffsetY, offsetY)) {
342         offsetY = NG::DEFAULT_GAUGE_SHADOW_OFFSETY;
343     }
344 
345     shadowOptions.radius = radius;
346     shadowOptions.offsetX = offsetX;
347     shadowOptions.offsetY = offsetY;
348 
349     GaugeModel::GetInstance()->SetShadowOptions(shadowOptions);
350 }
351 
SetDescription(const JSCallbackInfo & info)352 void JSGauge::SetDescription(const JSCallbackInfo& info)
353 {
354     if (info[0]->IsNull()) {
355         GaugeModel::GetInstance()->SetIsShowLimitValue(false);
356         GaugeModel::GetInstance()->SetIsShowDescription(false);
357         return;
358     }
359     if (info[0]->IsUndefined() || !info[0]->IsObject()) {
360         GaugeModel::GetInstance()->SetIsShowLimitValue(true);
361         GaugeModel::GetInstance()->SetIsShowDescription(false);
362         return;
363     }
364 
365     auto builderObject = JSRef<JSObject>::Cast(info[0])->GetProperty("builder");
366     if (builderObject->IsFunction()) {
367         GaugeModel::GetInstance()->SetIsShowLimitValue(false);
368         GaugeModel::GetInstance()->SetIsShowDescription(true);
369         ViewStackModel::GetInstance()->NewScope();
370         JsFunction jsBuilderFunc(info.This(), JSRef<JSFunc>::Cast(builderObject));
371         ACE_SCORING_EVENT("Gauge.description.builder");
372         jsBuilderFunc.Execute();
373         auto customNode = ViewStackModel::GetInstance()->Finish();
374         GaugeModel::GetInstance()->SetDescription(customNode);
375     } else {
376         GaugeModel::GetInstance()->SetIsShowLimitValue(true);
377         GaugeModel::GetInstance()->SetIsShowDescription(false);
378     }
379 }
380 
SetIndicator(const JSCallbackInfo & info)381 void JSGauge::SetIndicator(const JSCallbackInfo& info)
382 {
383     if (info[0]->IsNull()) {
384         GaugeModel::GetInstance()->SetIsShowIndicator(false);
385         return;
386     }
387 
388     if (!info[0]->IsObject()) {
389         GaugeModel::GetInstance()->ResetIndicatorIconPath();
390         GaugeModel::GetInstance()->ResetIndicatorSpace();
391         GaugeModel::GetInstance()->SetIsShowIndicator(true);
392         return;
393     }
394 
395     GaugeModel::GetInstance()->SetIsShowIndicator(true);
396     auto paramObject = JSRef<JSObject>::Cast(info[0]);
397     JSRef<JSVal> jsIcon = paramObject->GetProperty("icon");
398     JSRef<JSVal> jsSpace = paramObject->GetProperty("space");
399 
400     std::string iconPath;
401     if (ParseJsMedia(jsIcon, iconPath)) {
402         std::string bundleName;
403         std::string moduleName;
404         GetJsMediaBundleInfo(jsIcon, bundleName, moduleName);
405         GaugeModel::GetInstance()->SetIndicatorIconPath(iconPath, bundleName, moduleName);
406     } else {
407         GaugeModel::GetInstance()->ResetIndicatorIconPath();
408     }
409 
410     CalcDimension space;
411     if (!ParseJsDimensionVpNG(jsSpace, space, false)) {
412         space = NG::INDICATOR_DISTANCE_TO_TOP;
413     }
414     if (space.IsNegative()) {
415         space = NG::INDICATOR_DISTANCE_TO_TOP;
416     }
417     GaugeModel::GetInstance()->SetIndicatorSpace(space);
418 }
419 } // namespace OHOS::Ace::Framework
420