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_data_panel.h"
17
18 #include <vector>
19
20 #include "bridge/declarative_frontend/ark_theme/theme_apply/js_data_panel_theme.h"
21 #include "bridge/declarative_frontend/jsview/js_interactable_view.h"
22 #include "bridge/declarative_frontend/jsview/js_linear_gradient.h"
23 #include "bridge/declarative_frontend/jsview/js_utils.h"
24 #include "bridge/declarative_frontend/jsview/js_view_abstract.h"
25 #include "bridge/declarative_frontend/jsview/models/data_panel_model_impl.h"
26 #include "core/common/resource/resource_parse_utils.h"
27 #include "core/components/data_panel/data_panel_theme.h"
28 #include "core/components_ng/base/view_abstract_model.h"
29 #include "core/components_ng/pattern/data_panel/data_panel_model.h"
30 #include "core/components_ng/pattern/data_panel/data_panel_model_ng.h"
31
32 namespace OHOS::Ace {
33
34 std::unique_ptr<DataPanelModel> DataPanelModel::instance_ = nullptr;
35 std::mutex DataPanelModel::mutex_;
36
GetInstance()37 DataPanelModel* DataPanelModel::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::DataPanelModelNG());
44 #else
45 if (Container::IsCurrentUseNewPipeline()) {
46 instance_.reset(new NG::DataPanelModelNG());
47 } else {
48 instance_.reset(new Framework::DataPanelModelImpl());
49 }
50 #endif
51 }
52 }
53 return instance_.get();
54 }
55
56 } // namespace OHOS::Ace
57 namespace OHOS::Ace::Framework {
58 namespace {
59 constexpr uint32_t TYPE_CYCLE = 0;
60 }
61
62 constexpr size_t MAX_COUNT = 9;
63 uint32_t JSDataPanel::dataPanelType_ = 0;
64
JSBind(BindingTarget globalObj)65 void JSDataPanel::JSBind(BindingTarget globalObj)
66 {
67 JSClass<JSDataPanel>::Declare("DataPanel");
68 JSClass<JSDataPanel>::StaticMethod("create", &JSDataPanel::Create);
69 JSClass<JSDataPanel>::StaticMethod("closeEffect", &JSDataPanel::CloseEffect);
70
71 JSClass<JSDataPanel>::StaticMethod("valueColors", &JSDataPanel::ValueColors);
72 JSClass<JSDataPanel>::StaticMethod("trackBackgroundColor", &JSDataPanel::TrackBackground);
73 JSClass<JSDataPanel>::StaticMethod("strokeWidth", &JSDataPanel::StrokeWidth);
74 JSClass<JSDataPanel>::StaticMethod("trackShadow", &JSDataPanel::ShadowOption);
75 JSClass<JSDataPanel>::StaticMethod("borderRadius", &JSDataPanel::BorderRadius);
76
77 JSClass<JSDataPanel>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
78 JSClass<JSDataPanel>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
79 JSClass<JSDataPanel>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
80 JSClass<JSDataPanel>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
81 JSClass<JSDataPanel>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
82 JSClass<JSDataPanel>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
83 JSClass<JSDataPanel>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
84 JSClass<JSDataPanel>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
85 JSClass<JSDataPanel>::StaticMethod("remoteMessage", &JSInteractableView::JsCommonRemoteMessage);
86
87 JSClass<JSDataPanel>::InheritAndBind<JSViewAbstract>(globalObj);
88 }
89
Create(const JSCallbackInfo & info)90 void JSDataPanel::Create(const JSCallbackInfo& info)
91 {
92 if (!info[0]->IsObject()) {
93 return;
94 }
95 JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(info[0]);
96 // max
97 double max = jsObj->GetPropertyValue<double>("max", 100.0);
98 // values
99 JSRef<JSVal> jsValue = jsObj->GetProperty("values");
100 if (!jsValue->IsArray()) {
101 return;
102 }
103 JSRef<JSArray> jsArray = JSRef<JSArray>::Cast(jsValue);
104 size_t length = jsArray->Length();
105 std::vector<double> dateValues;
106 double dataSum = 0.0;
107 size_t count = std::min(length, MAX_COUNT);
108 for (size_t i = 0; i < count; ++i) {
109 JSRef<JSVal> item = jsArray->GetValueAt(i);
110 if (!item->IsNumber()) {
111 continue;
112 }
113 double value = item->ToNumber<double>();
114 if (LessOrEqual(value, 0.0)) {
115 value = 0.0;
116 }
117 // if the sum of values exceeds the maximum value, only fill in to the maximum value
118 if (GreatOrEqual(dataSum + value, max) && GreatNotEqual(max, 0)) {
119 dateValues.emplace_back(max - dataSum);
120 break;
121 }
122 dataSum += value;
123 dateValues.emplace_back(value);
124 }
125 if (LessOrEqual(max, 0.0)) {
126 max = dataSum;
127 }
128
129 size_t dataPanelType = 0;
130 int32_t type = jsObj->GetPropertyValue<int32_t>("type", static_cast<int32_t>(ChartType::RAINBOW));
131 if (type == static_cast<int32_t>(ChartType::LINE)) {
132 dataPanelType = 1;
133 }
134 dataPanelType_ = dataPanelType;
135 DataPanelModel::GetInstance()->Create(dateValues, max, dataPanelType);
136 JSDataPanelTheme::ApplyTheme();
137 }
138
CloseEffect(const JSCallbackInfo & info)139 void JSDataPanel::CloseEffect(const JSCallbackInfo& info)
140 {
141 bool isCloseEffect = true;
142 if (info[0]->IsBoolean()) {
143 isCloseEffect = info[0]->ToBoolean();
144 }
145 DataPanelModel::GetInstance()->SetEffect(isCloseEffect);
146 }
147
ValueColors(const JSCallbackInfo & info)148 void JSDataPanel::ValueColors(const JSCallbackInfo& info)
149 {
150 if (info.Length() < 1) {
151 return;
152 }
153
154 std::vector<OHOS::Ace::NG::Gradient> valueColors;
155 if (!info[0]->IsArray() || info[0]->IsEmpty()) {
156 ConvertThemeColor(valueColors);
157 DataPanelModel::GetInstance()->SetValueColors(valueColors);
158 if (SystemProperties::ConfigChangePerform()) {
159 DataPanelModel::GetInstance()->SetValueColorsSetByUser(false);
160 }
161 return;
162 }
163 bool valueColorsSetByUser = true;
164 auto paramArray = JSRef<JSArray>::Cast(info[0]);
165 size_t length = paramArray->Length();
166 size_t count = std::min(length, MAX_COUNT);
167 for (size_t i = 0; i < count; ++i) {
168 auto item = paramArray->GetValueAt(i);
169 OHOS::Ace::NG::Gradient gradient;
170 if (!ConvertGradientColor(item, gradient)) {
171 valueColors.clear();
172 ConvertThemeColor(valueColors);
173 valueColorsSetByUser = false;
174 break;
175 }
176 valueColors.emplace_back(gradient);
177 }
178 DataPanelModel::GetInstance()->SetValueColors(valueColors);
179 if (SystemProperties::ConfigChangePerform()) {
180 DataPanelModel::GetInstance()->SetValueColorsSetByUser(valueColorsSetByUser);
181 }
182 }
183
TrackBackground(const JSCallbackInfo & info)184 void JSDataPanel::TrackBackground(const JSCallbackInfo& info)
185 {
186 if (info.Length() < 1) {
187 return;
188 }
189 Color color;
190 if (SystemProperties::ConfigChangePerform()) {
191 RefPtr<ResourceObject> resObj;
192 bool state = ParseJsColor(info[0], color, resObj);
193 DataPanelModel::GetInstance()->CreateWithResourceObj(
194 OHOS::Ace::DataPanelResourceType::TRACK_BACKGROUND_COLOR, resObj);
195 if (state) {
196 DataPanelModel::GetInstance()->SetTrackBackground(color);
197 } else {
198 RefPtr<DataPanelTheme> theme = GetTheme<DataPanelTheme>();
199 color = theme->GetBackgroundColor();
200 DataPanelModel::GetInstance()->SetTrackBackground(color);
201 }
202 } else {
203 RefPtr<ResourceObject> resObj;
204 if (!ParseJsColor(info[0], color)) {
205 RefPtr<DataPanelTheme> theme = GetTheme<DataPanelTheme>();
206 color = theme->GetBackgroundColor();
207 }
208 DataPanelModel::GetInstance()->SetTrackBackground(color);
209 }
210 }
211
StrokeWidth(const JSCallbackInfo & info)212 void JSDataPanel::StrokeWidth(const JSCallbackInfo& info)
213 {
214 if (info.Length() < 1) {
215 return;
216 }
217
218 RefPtr<DataPanelTheme> theme = GetTheme<DataPanelTheme>();
219 CalcDimension strokeWidthDimension;
220 RefPtr<ResourceObject> resObj;
221 if (SystemProperties::ConfigChangePerform()) {
222 bool state = ParseJsDimensionVp(info[0], strokeWidthDimension, resObj);
223 DataPanelModel::GetInstance()->CreateWithResourceObj(OHOS::Ace::DataPanelResourceType::STROKE_WIDTH, resObj);
224 if (state) {
225 DataPanelModel::GetInstance()->SetStrokeWidth(strokeWidthDimension);
226 } else {
227 strokeWidthDimension = theme->GetThickness();
228 DataPanelModel::GetInstance()->SetStrokeWidth(strokeWidthDimension);
229 }
230 } else {
231 if (!ParseJsDimensionVp(info[0], strokeWidthDimension)) {
232 strokeWidthDimension = theme->GetThickness();
233 }
234 }
235
236 // If the parameter value is string(''), parse result 0.
237 // The value of 0 is allowed, but the value of string('') is not allowed, so use theme value.
238 if (info[0]->IsString() && (info[0]->ToString().empty() || !StringUtils::StringToDimensionWithUnitNG(
239 info[0]->ToString(), strokeWidthDimension))) {
240 strokeWidthDimension = theme->GetThickness();
241 }
242
243 if (strokeWidthDimension.IsNegative() || strokeWidthDimension.Unit() == DimensionUnit::PERCENT) {
244 strokeWidthDimension = theme->GetThickness();
245 }
246 DataPanelModel::GetInstance()->SetStrokeWidth(strokeWidthDimension);
247 }
248
ShadowOption(const JSCallbackInfo & info)249 void JSDataPanel::ShadowOption(const JSCallbackInfo& info)
250 {
251 OHOS::Ace::NG::DataPanelShadow shadow;
252 if (info[0]->IsNull()) {
253 shadow.isShadowVisible = false;
254 DataPanelModel::GetInstance()->SetShadowOption(shadow);
255 return;
256 }
257 RefPtr<DataPanelTheme> theme = GetTheme<DataPanelTheme>();
258 double radius = theme->GetTrackShadowRadius().ConvertToVp();
259 double offsetX = theme->GetTrackShadowOffsetX().ConvertToVp();
260 double offsetY = theme->GetTrackShadowOffsetY().ConvertToVp();
261 std::vector<OHOS::Ace::NG::Gradient> shadowColors;
262 ConvertThemeColor(shadowColors);
263 if (info[0]->IsObject()) {
264 auto paramObject = JSRef<JSObject>::Cast(info[0]);
265 JSRef<JSVal> jsRadius = paramObject->GetProperty("radius");
266 JSRef<JSVal> jsOffsetX = paramObject->GetProperty("offsetX");
267 JSRef<JSVal> jsOffsetY = paramObject->GetProperty("offsetY");
268 RefPtr<ResourceObject> resR;
269 RefPtr<ResourceObject> resX;
270 RefPtr<ResourceObject> resY;
271 HandleShadowRadius(jsRadius, radius, resR, shadow);
272 HandleShadowOffsetX(jsOffsetX, offsetX, resX, shadow);
273 HandleShadowOffsetY(jsOffsetY, offsetY, resY, shadow);
274 auto colors = paramObject->GetProperty("colors");
275 if (!colors->IsArray()) {
276 shadow.radius = radius;
277 shadow.offsetX = offsetX;
278 shadow.offsetY = offsetY;
279 shadow.colors = shadowColors;
280 DataPanelModel::GetInstance()->SetShadowOption(shadow);
281 return;
282 }
283 ParseShadowColors(colors, shadowColors);
284 }
285
286 shadow.radius = radius;
287 shadow.offsetX = offsetX;
288 shadow.offsetY = offsetY;
289 shadow.colors = shadowColors;
290 DataPanelModel::GetInstance()->SetShadowOption(shadow);
291 }
292
ConvertGradientColor(const JsiRef<JsiValue> & itemParam,OHOS::Ace::NG::Gradient & gradient)293 bool JSDataPanel::ConvertGradientColor(const JsiRef<JsiValue>& itemParam, OHOS::Ace::NG::Gradient& gradient)
294 {
295 if (!itemParam->IsObject()) {
296 return ConvertResourceColor(itemParam, gradient);
297 }
298
299 JSLinearGradient* jsLinearGradient = JSRef<JSObject>::Cast(itemParam)->Unwrap<JSLinearGradient>();
300 if (!jsLinearGradient) {
301 return ConvertResourceColor(itemParam, gradient);
302 }
303
304 size_t colorLength = jsLinearGradient->GetGradient().size();
305 if (colorLength == 0) {
306 return false;
307 }
308 for (size_t colorIndex = 0; colorIndex < colorLength; ++colorIndex) {
309 OHOS::Ace::NG::GradientColor gradientColor;
310 gradientColor.SetLinearColor(LinearColor(jsLinearGradient->GetGradient().at(colorIndex).first));
311 gradientColor.SetDimension(jsLinearGradient->GetGradient().at(colorIndex).second);
312 gradient.AddColor(gradientColor);
313 }
314 return true;
315 }
316
ConvertResourceColor(const JsiRef<JsiValue> & itemParam,OHOS::Ace::NG::Gradient & gradient)317 bool JSDataPanel::ConvertResourceColor(const JsiRef<JsiValue>& itemParam, OHOS::Ace::NG::Gradient& gradient)
318 {
319 Color color;
320 RefPtr<ResourceObject> resObj;
321 if (!ParseJsColor(itemParam, color, resObj)) {
322 return false;
323 }
324
325 if (resObj && SystemProperties::ConfigChangePerform()) {
326 std::string key = "gradient.Color";
327 gradient.AddResource(key, resObj, [](const RefPtr<ResourceObject>& resObj, NG::Gradient& gradient) {
328 Color color;
329 ResourceParseUtils::ParseResColor(resObj, color);
330 gradient.ClearColors();
331 NG::GradientColor startColor;
332 startColor.SetLinearColor(LinearColor(color));
333 startColor.SetDimension(Dimension(0.0));
334 NG::GradientColor endColor;
335 endColor.SetLinearColor(LinearColor(color));
336 endColor.SetDimension(Dimension(1.0));
337 gradient.AddColor(startColor);
338 gradient.AddColor(endColor);
339 });
340 }
341 OHOS::Ace::NG::GradientColor gradientColorStart;
342 gradientColorStart.SetLinearColor(LinearColor(color));
343 gradientColorStart.SetDimension(Dimension(0.0));
344 gradient.AddColor(gradientColorStart);
345 OHOS::Ace::NG::GradientColor gradientColorEnd;
346 gradientColorEnd.SetLinearColor(LinearColor(color));
347 gradientColorEnd.SetDimension(Dimension(1.0));
348 gradient.AddColor(gradientColorEnd);
349 return true;
350 }
351
ConvertThemeColor(std::vector<OHOS::Ace::NG::Gradient> & colors)352 void JSDataPanel::ConvertThemeColor(std::vector<OHOS::Ace::NG::Gradient>& colors)
353 {
354 RefPtr<DataPanelTheme> theme = GetTheme<DataPanelTheme>();
355 auto themeColors = theme->GetColorsArray();
356 for (const auto& item : themeColors) {
357 OHOS::Ace::NG::Gradient gradient;
358 OHOS::Ace::NG::GradientColor gradientColorStart;
359 gradientColorStart.SetLinearColor(LinearColor(item.first));
360 gradientColorStart.SetDimension(Dimension(0.0));
361 gradient.AddColor(gradientColorStart);
362 OHOS::Ace::NG::GradientColor gradientColorEnd;
363 gradientColorEnd.SetLinearColor(LinearColor(item.second));
364 gradientColorEnd.SetDimension(Dimension(1.0));
365 gradient.AddColor(gradientColorEnd);
366 colors.emplace_back(gradient);
367 }
368 }
369
BorderRadius(const JSCallbackInfo & info)370 void JSDataPanel::BorderRadius(const JSCallbackInfo& info)
371 {
372 if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
373 JSViewAbstract::JsBorderRadius(info);
374 } else {
375 std::vector<JSCallbackInfoType> checkList { JSCallbackInfoType::STRING, JSCallbackInfoType::NUMBER,
376 JSCallbackInfoType::OBJECT };
377 if (!CheckJSCallbackInfo("JsBorderRadius", info[0], checkList)) {
378 if (dataPanelType_ != TYPE_CYCLE) {
379 RefPtr<DataPanelTheme> theme = GetTheme<DataPanelTheme>();
380 CHECK_NULL_VOID(theme);
381 ViewAbstractModel::GetInstance()->SetBorderRadius(theme->GetDefaultBorderRadius());
382 } else {
383 ViewAbstractModel::GetInstance()->SetBorderRadius(Dimension {});
384 }
385 return;
386 }
387 JSViewAbstract::ParseBorderRadius(info[0]);
388 }
389 }
390
ParseShadowColors(const JSRef<JSArray> & colorsArray,std::vector<OHOS::Ace::NG::Gradient> & shadowColors)391 void JSDataPanel::ParseShadowColors(
392 const JSRef<JSArray>& colorsArray, std::vector<OHOS::Ace::NG::Gradient>& shadowColors)
393 {
394 shadowColors.clear();
395 for (size_t i = 0; i < colorsArray->Length(); ++i) {
396 auto item = colorsArray->GetValueAt(i);
397 OHOS::Ace::NG::Gradient gradient;
398 if (!ConvertGradientColor(item, gradient)) {
399 shadowColors.clear();
400 ConvertThemeColor(shadowColors);
401 break;
402 }
403 shadowColors.emplace_back(gradient);
404 }
405 }
406
HandleShadowRadius(const JSRef<JSVal> & jsRadius,double & radius,RefPtr<ResourceObject> & resR,OHOS::Ace::NG::DataPanelShadow & shadow)407 void JSDataPanel::HandleShadowRadius(
408 const JSRef<JSVal>& jsRadius, double& radius, RefPtr<ResourceObject>& resR, OHOS::Ace::NG::DataPanelShadow& shadow)
409 {
410 RefPtr<DataPanelTheme> theme = GetTheme<DataPanelTheme>();
411 ParseJsDouble(jsRadius, radius, resR);
412 if (resR && SystemProperties::ConfigChangePerform()) {
413 auto&& updateFunc = [](const RefPtr<ResourceObject>& resRadius, OHOS::Ace::NG::DataPanelShadow& shadow) {
414 RefPtr<DataPanelTheme> theme = GetTheme<DataPanelTheme>();
415 double radius = theme->GetTrackShadowRadius().ConvertToVp();
416 ResourceParseUtils::ParseResDouble(resRadius, radius);
417 if (NonPositive(radius)) {
418 radius = theme->GetTrackShadowRadius().ConvertToVp();
419 }
420 shadow.SetRadius(radius);
421 };
422 shadow.AddResource("shadow.radius", resR, std::move(updateFunc));
423 } else {
424 if (NonPositive(radius)) {
425 radius = theme->GetTrackShadowRadius().ConvertToVp();
426 }
427 }
428 }
429
HandleShadowOffsetX(const JSRef<JSVal> & jsOffsetX,double & offsetX,RefPtr<ResourceObject> & resX,OHOS::Ace::NG::DataPanelShadow & shadow)430 void JSDataPanel::HandleShadowOffsetX(const JSRef<JSVal>& jsOffsetX, double& offsetX, RefPtr<ResourceObject>& resX,
431 OHOS::Ace::NG::DataPanelShadow& shadow)
432 {
433 RefPtr<DataPanelTheme> theme = GetTheme<DataPanelTheme>();
434 ParseJsDouble(jsOffsetX, offsetX, resX);
435 if (resX && SystemProperties::ConfigChangePerform()) {
436 auto&& updateFuncX = [](const RefPtr<ResourceObject>& resObj, OHOS::Ace::NG::DataPanelShadow& shadow) {
437 RefPtr<DataPanelTheme> theme = GetTheme<DataPanelTheme>();
438 double val = theme->GetTrackShadowOffsetX().ConvertToVp();
439 ResourceParseUtils::ParseResDouble(resObj, val);
440 shadow.SetOffsetX(val);
441 };
442 shadow.AddResource("shadow.offsetX", resX, std::move(updateFuncX));
443 }
444 }
445
HandleShadowOffsetY(const JSRef<JSVal> & jsOffsetY,double & offsetY,RefPtr<ResourceObject> & resY,OHOS::Ace::NG::DataPanelShadow & shadow)446 void JSDataPanel::HandleShadowOffsetY(const JSRef<JSVal>& jsOffsetY, double& offsetY, RefPtr<ResourceObject>& resY,
447 OHOS::Ace::NG::DataPanelShadow& shadow)
448 {
449 RefPtr<DataPanelTheme> theme = GetTheme<DataPanelTheme>();
450 ParseJsDouble(jsOffsetY, offsetY, resY);
451 if (resY && SystemProperties::ConfigChangePerform()) {
452 auto&& updateFuncY = [](const RefPtr<ResourceObject>& resObj, OHOS::Ace::NG::DataPanelShadow& shadow) {
453 RefPtr<DataPanelTheme> theme = GetTheme<DataPanelTheme>();
454 double val = theme->GetTrackShadowOffsetY().ConvertToVp();
455 ResourceParseUtils::ParseResDouble(resObj, val);
456 shadow.SetOffsetY(val);
457 };
458 shadow.AddResource("shadow.offsetY", resY, std::move(updateFuncY));
459 }
460 }
461 } // namespace OHOS::Ace::Framework
462