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