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/jsview/js_interactable_view.h"
21 #include "bridge/declarative_frontend/jsview/js_linear_gradient.h"
22 #include "bridge/declarative_frontend/jsview/js_utils.h"
23 #include "bridge/declarative_frontend/jsview/js_view_abstract.h"
24 #include "bridge/declarative_frontend/jsview/models/data_panel_model_impl.h"
25 #include "bridge/declarative_frontend/ark_theme/theme_apply/js_data_panel_theme.h"
26 #include "core/components/data_panel/data_panel_theme.h"
27 #include "core/components_ng/base/view_abstract_model.h"
28 #include "core/components_ng/pattern/data_panel/data_panel_model_ng.h"
29 
30 namespace OHOS::Ace {
31 
32 std::unique_ptr<DataPanelModel> DataPanelModel::instance_ = nullptr;
33 std::mutex DataPanelModel::mutex_;
34 
GetInstance()35 DataPanelModel* DataPanelModel::GetInstance()
36 {
37     if (!instance_) {
38         std::lock_guard<std::mutex> lock(mutex_);
39         if (!instance_) {
40 #ifdef NG_BUILD
41             instance_.reset(new NG::DataPanelModelNG());
42 #else
43             if (Container::IsCurrentUseNewPipeline()) {
44                 instance_.reset(new NG::DataPanelModelNG());
45             } else {
46                 instance_.reset(new Framework::DataPanelModelImpl());
47             }
48 #endif
49         }
50     }
51     return instance_.get();
52 }
53 
54 } // namespace OHOS::Ace
55 namespace OHOS::Ace::Framework {
56 namespace {
57 constexpr uint32_t TYPE_CYCLE = 0;
58 }
59 
60 constexpr size_t MAX_COUNT = 9;
61 uint32_t JSDataPanel::dataPanelType_ = 0;
62 
JSBind(BindingTarget globalObj)63 void JSDataPanel::JSBind(BindingTarget globalObj)
64 {
65     JSClass<JSDataPanel>::Declare("DataPanel");
66     JSClass<JSDataPanel>::StaticMethod("create", &JSDataPanel::Create);
67     JSClass<JSDataPanel>::StaticMethod("closeEffect", &JSDataPanel::CloseEffect);
68 
69     JSClass<JSDataPanel>::StaticMethod("valueColors", &JSDataPanel::ValueColors);
70     JSClass<JSDataPanel>::StaticMethod("trackBackgroundColor", &JSDataPanel::TrackBackground);
71     JSClass<JSDataPanel>::StaticMethod("strokeWidth", &JSDataPanel::StrokeWidth);
72     JSClass<JSDataPanel>::StaticMethod("trackShadow", &JSDataPanel::ShadowOption);
73     JSClass<JSDataPanel>::StaticMethod("borderRadius", &JSDataPanel::BorderRadius);
74 
75     JSClass<JSDataPanel>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
76     JSClass<JSDataPanel>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
77     JSClass<JSDataPanel>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
78     JSClass<JSDataPanel>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
79     JSClass<JSDataPanel>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
80     JSClass<JSDataPanel>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
81     JSClass<JSDataPanel>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
82     JSClass<JSDataPanel>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
83     JSClass<JSDataPanel>::StaticMethod("remoteMessage", &JSInteractableView::JsCommonRemoteMessage);
84 
85     JSClass<JSDataPanel>::InheritAndBind<JSViewAbstract>(globalObj);
86 }
87 
Create(const JSCallbackInfo & info)88 void JSDataPanel::Create(const JSCallbackInfo& info)
89 {
90     if (!info[0]->IsObject()) {
91         return;
92     }
93     JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(info[0]);
94     // max
95     double max = jsObj->GetPropertyValue<double>("max", 100.0);
96     // values
97     JSRef<JSVal> jsValue = jsObj->GetProperty("values");
98     if (!jsValue->IsArray()) {
99         return;
100     }
101     JSRef<JSArray> jsArray = JSRef<JSArray>::Cast(jsValue);
102     size_t length = jsArray->Length();
103     std::vector<double> dateValues;
104     double dataSum = 0.0;
105     size_t count = std::min(length, MAX_COUNT);
106     for (size_t i = 0; i < count; ++i) {
107         JSRef<JSVal> item = jsArray->GetValueAt(i);
108         if (!item->IsNumber()) {
109             continue;
110         }
111         double value = item->ToNumber<double>();
112         if (LessOrEqual(value, 0.0)) {
113             value = 0.0;
114         }
115         // if the sum of values exceeds the maximum value, only fill in to the maximum value
116         if (GreatOrEqual(dataSum + value, max) && GreatNotEqual(max, 0)) {
117             dateValues.emplace_back(max - dataSum);
118             break;
119         }
120         dataSum += value;
121         dateValues.emplace_back(value);
122     }
123     if (LessOrEqual(max, 0.0)) {
124         max = dataSum;
125     }
126 
127     size_t dataPanelType = 0;
128     int32_t type = jsObj->GetPropertyValue<int32_t>("type", static_cast<int32_t>(ChartType::RAINBOW));
129     if (type == static_cast<int32_t>(ChartType::LINE)) {
130         dataPanelType = 1;
131     }
132     dataPanelType_ = dataPanelType;
133     DataPanelModel::GetInstance()->Create(dateValues, max, dataPanelType);
134     JSDataPanelTheme::ApplyTheme();
135 }
136 
CloseEffect(const JSCallbackInfo & info)137 void JSDataPanel::CloseEffect(const JSCallbackInfo& info)
138 {
139     bool isCloseEffect = true;
140     if (info[0]->IsBoolean()) {
141         isCloseEffect = info[0]->ToBoolean();
142     }
143     DataPanelModel::GetInstance()->SetEffect(isCloseEffect);
144 }
145 
ValueColors(const JSCallbackInfo & info)146 void JSDataPanel::ValueColors(const JSCallbackInfo& info)
147 {
148     if (info.Length() < 1) {
149         return;
150     }
151 
152     std::vector<OHOS::Ace::NG::Gradient> valueColors;
153     if (!info[0]->IsArray() || info[0]->IsEmpty()) {
154         ConvertThemeColor(valueColors);
155         DataPanelModel::GetInstance()->SetValueColors(valueColors);
156         return;
157     }
158 
159     auto paramArray = JSRef<JSArray>::Cast(info[0]);
160     size_t length = paramArray->Length();
161     size_t count = std::min(length, MAX_COUNT);
162     for (size_t i = 0; i < count; ++i) {
163         auto item = paramArray->GetValueAt(i);
164         OHOS::Ace::NG::Gradient gradient;
165         if (!ConvertGradientColor(item, gradient)) {
166             valueColors.clear();
167             ConvertThemeColor(valueColors);
168             break;
169         }
170         valueColors.emplace_back(gradient);
171     }
172     DataPanelModel::GetInstance()->SetValueColors(valueColors);
173 }
174 
TrackBackground(const JSCallbackInfo & info)175 void JSDataPanel::TrackBackground(const JSCallbackInfo& info)
176 {
177     if (info.Length() < 1) {
178         return;
179     }
180     Color color;
181     if (!ParseJsColor(info[0], color)) {
182         RefPtr<DataPanelTheme> theme = GetTheme<DataPanelTheme>();
183         color = theme->GetBackgroundColor();
184     }
185 
186     DataPanelModel::GetInstance()->SetTrackBackground(color);
187 }
188 
StrokeWidth(const JSCallbackInfo & info)189 void JSDataPanel::StrokeWidth(const JSCallbackInfo& info)
190 {
191     if (info.Length() < 1) {
192         return;
193     }
194 
195     RefPtr<DataPanelTheme> theme = GetTheme<DataPanelTheme>();
196     CalcDimension strokeWidthDimension;
197     if (!ParseJsDimensionVp(info[0], strokeWidthDimension)) {
198         strokeWidthDimension = theme->GetThickness();
199     }
200 
201     // If the parameter value is string(''), parse result 0.
202     // The value of 0 is allowed, but the value of string('') is not allowed, so use theme value.
203     if (info[0]->IsString() && (info[0]->ToString().empty() || !StringUtils::StringToDimensionWithUnitNG(
204         info[0]->ToString(), strokeWidthDimension))) {
205         strokeWidthDimension = theme->GetThickness();
206     }
207 
208     if (strokeWidthDimension.IsNegative() || strokeWidthDimension.Unit() == DimensionUnit::PERCENT) {
209         strokeWidthDimension = theme->GetThickness();
210     }
211     DataPanelModel::GetInstance()->SetStrokeWidth(strokeWidthDimension);
212 }
213 
ShadowOption(const JSCallbackInfo & info)214 void JSDataPanel::ShadowOption(const JSCallbackInfo& info)
215 {
216     OHOS::Ace::NG::DataPanelShadow shadow;
217     if (info[0]->IsNull()) {
218         shadow.isShadowVisible = false;
219         DataPanelModel::GetInstance()->SetShadowOption(shadow);
220         return;
221     }
222     RefPtr<DataPanelTheme> theme = GetTheme<DataPanelTheme>();
223     double radius = theme->GetTrackShadowRadius().ConvertToVp();
224     double offsetX = theme->GetTrackShadowOffsetX().ConvertToVp();
225     double offsetY = theme->GetTrackShadowOffsetY().ConvertToVp();
226     std::vector<OHOS::Ace::NG::Gradient> shadowColors;
227     ConvertThemeColor(shadowColors);
228     if (info[0]->IsObject()) {
229         auto paramObject = JSRef<JSObject>::Cast(info[0]);
230         JSRef<JSVal> jsRadius = paramObject->GetProperty("radius");
231         JSRef<JSVal> jsOffsetX = paramObject->GetProperty("offsetX");
232         JSRef<JSVal> jsOffsetY = paramObject->GetProperty("offsetY");
233         ParseJsDouble(jsRadius, radius);
234         if (NonPositive(radius)) {
235             radius = theme->GetTrackShadowRadius().ConvertToVp();
236         }
237         ParseJsDouble(jsOffsetX, offsetX);
238         ParseJsDouble(jsOffsetY, offsetY);
239 
240         auto colors = paramObject->GetProperty("colors");
241         if (!colors->IsArray()) {
242             shadow.radius = radius;
243             shadow.offsetX = offsetX;
244             shadow.offsetY = offsetY;
245             shadow.colors = shadowColors;
246             DataPanelModel::GetInstance()->SetShadowOption(shadow);
247             return;
248         }
249         shadowColors.clear();
250         auto colorsArray = JSRef<JSArray>::Cast(colors);
251         for (size_t i = 0; i < colorsArray->Length(); ++i) {
252             auto item = colorsArray->GetValueAt(i);
253             OHOS::Ace::NG::Gradient gradient;
254             if (!ConvertGradientColor(item, gradient)) {
255                 shadowColors.clear();
256                 ConvertThemeColor(shadowColors);
257                 break;
258             }
259             shadowColors.emplace_back(gradient);
260         }
261     }
262 
263     shadow.radius = radius;
264     shadow.offsetX = offsetX;
265     shadow.offsetY = offsetY;
266     shadow.colors = shadowColors;
267     DataPanelModel::GetInstance()->SetShadowOption(shadow);
268 }
269 
ConvertGradientColor(const JsiRef<JsiValue> & itemParam,OHOS::Ace::NG::Gradient & gradient)270 bool JSDataPanel::ConvertGradientColor(const JsiRef<JsiValue>& itemParam, OHOS::Ace::NG::Gradient& gradient)
271 {
272     if (!itemParam->IsObject()) {
273         return ConvertResourceColor(itemParam, gradient);
274     }
275 
276     JSLinearGradient* jsLinearGradient = JSRef<JSObject>::Cast(itemParam)->Unwrap<JSLinearGradient>();
277     if (!jsLinearGradient) {
278         return ConvertResourceColor(itemParam, gradient);
279     }
280 
281     size_t colorLength = jsLinearGradient->GetGradient().size();
282     if (colorLength == 0) {
283         return false;
284     }
285     for (size_t colorIndex = 0; colorIndex < colorLength; ++colorIndex) {
286         OHOS::Ace::NG::GradientColor gradientColor;
287         gradientColor.SetLinearColor(LinearColor(jsLinearGradient->GetGradient().at(colorIndex).first));
288         gradientColor.SetDimension(jsLinearGradient->GetGradient().at(colorIndex).second);
289         gradient.AddColor(gradientColor);
290     }
291     return true;
292 }
293 
ConvertResourceColor(const JsiRef<JsiValue> & itemParam,OHOS::Ace::NG::Gradient & gradient)294 bool JSDataPanel::ConvertResourceColor(const JsiRef<JsiValue>& itemParam, OHOS::Ace::NG::Gradient& gradient)
295 {
296     Color color;
297     if (!ParseJsColor(itemParam, color)) {
298         return false;
299     }
300     OHOS::Ace::NG::GradientColor gradientColorStart;
301     gradientColorStart.SetLinearColor(LinearColor(color));
302     gradientColorStart.SetDimension(Dimension(0.0));
303     gradient.AddColor(gradientColorStart);
304     OHOS::Ace::NG::GradientColor gradientColorEnd;
305     gradientColorEnd.SetLinearColor(LinearColor(color));
306     gradientColorEnd.SetDimension(Dimension(1.0));
307     gradient.AddColor(gradientColorEnd);
308     return true;
309 }
310 
ConvertThemeColor(std::vector<OHOS::Ace::NG::Gradient> & colors)311 void JSDataPanel::ConvertThemeColor(std::vector<OHOS::Ace::NG::Gradient>& colors)
312 {
313     RefPtr<DataPanelTheme> theme = GetTheme<DataPanelTheme>();
314     auto themeColors = theme->GetColorsArray();
315     for (const auto& item : themeColors) {
316         OHOS::Ace::NG::Gradient gradient;
317         OHOS::Ace::NG::GradientColor gradientColorStart;
318         gradientColorStart.SetLinearColor(LinearColor(item.first));
319         gradientColorStart.SetDimension(Dimension(0.0));
320         gradient.AddColor(gradientColorStart);
321         OHOS::Ace::NG::GradientColor gradientColorEnd;
322         gradientColorEnd.SetLinearColor(LinearColor(item.second));
323         gradientColorEnd.SetDimension(Dimension(1.0));
324         gradient.AddColor(gradientColorEnd);
325         colors.emplace_back(gradient);
326     }
327 }
328 
BorderRadius(const JSCallbackInfo & info)329 void JSDataPanel::BorderRadius(const JSCallbackInfo& info)
330 {
331     if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
332         JSViewAbstract::JsBorderRadius(info);
333     } else {
334         std::vector<JSCallbackInfoType> checkList { JSCallbackInfoType::STRING, JSCallbackInfoType::NUMBER,
335             JSCallbackInfoType::OBJECT };
336         if (!CheckJSCallbackInfo("JsBorderRadius", info[0], checkList)) {
337             if (dataPanelType_ != TYPE_CYCLE) {
338                 RefPtr<DataPanelTheme> theme = GetTheme<DataPanelTheme>();
339                 CHECK_NULL_VOID(theme);
340                 ViewAbstractModel::GetInstance()->SetBorderRadius(theme->GetDefaultBorderRadius());
341             } else {
342                 ViewAbstractModel::GetInstance()->SetBorderRadius(Dimension {});
343             }
344             return;
345         }
346         JSViewAbstract::ParseBorderRadius(info[0]);
347     }
348 }
349 } // namespace OHOS::Ace::Framework
350