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 "frameworks/bridge/declarative_frontend/jsview/js_slider.h"
17
18 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
19 #include "bridge/declarative_frontend/jsview/models/slider_model_impl.h"
20 #include "core/components/slider/render_slider.h"
21 #include "core/components/slider/slider_element.h"
22 #include "core/components_ng/pattern/slider/slider_model_ng.h"
23 #include "frameworks/bridge/declarative_frontend/engine/functions/js_function.h"
24 #include "frameworks/bridge/declarative_frontend/jsview/js_shape_abstract.h"
25
26 namespace OHOS::Ace {
27 namespace {
28 constexpr int SLIDER_SHOW_TIPS_MAX_PARAMS = 2;
29 } // namespace
30
31 std::unique_ptr<SliderModel> SliderModel::instance_ = nullptr;
32 std::mutex SliderModel::mutex_;
33
GetInstance()34 SliderModel* SliderModel::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::SliderModelNG());
41 #else
42 if (Container::IsCurrentUseNewPipeline()) {
43 instance_.reset(new NG::SliderModelNG());
44 } else {
45 instance_.reset(new Framework::SliderModelImpl());
46 }
47 #endif
48 }
49 }
50 return instance_.get();
51 }
52
53 } // namespace OHOS::Ace
54
55 namespace OHOS::Ace::Framework {
56
JSBind(BindingTarget globalObj)57 void JSSlider::JSBind(BindingTarget globalObj)
58 {
59 JSClass<JSSlider>::Declare("Slider");
60 MethodOptions opt = MethodOptions::NONE;
61 JSClass<JSSlider>::StaticMethod("create", &JSSlider::Create, opt);
62 JSClass<JSSlider>::StaticMethod("blockColor", &JSSlider::SetBlockColor);
63 JSClass<JSSlider>::StaticMethod("trackColor", &JSSlider::SetTrackColor);
64 JSClass<JSSlider>::StaticMethod("trackThickness", &JSSlider::SetThickness);
65 JSClass<JSSlider>::StaticMethod("selectedColor", &JSSlider::SetSelectedColor);
66 JSClass<JSSlider>::StaticMethod("minLabel", &JSSlider::SetMinLabel);
67 JSClass<JSSlider>::StaticMethod("maxLabel", &JSSlider::SetMaxLabel);
68 JSClass<JSSlider>::StaticMethod("showSteps", &JSSlider::SetShowSteps);
69 JSClass<JSSlider>::StaticMethod("showTips", &JSSlider::SetShowTips);
70 JSClass<JSSlider>::StaticMethod("blockBorderColor", &JSSlider::SetBlockBorderColor);
71 JSClass<JSSlider>::StaticMethod("blockBorderWidth", &JSSlider::SetBlockBorderWidth);
72 JSClass<JSSlider>::StaticMethod("stepColor", &JSSlider::SetStepColor);
73 JSClass<JSSlider>::StaticMethod("trackBorderRadius", &JSSlider::SetTrackBorderRadius);
74 JSClass<JSSlider>::StaticMethod("blockSize", &JSSlider::SetBlockSize);
75 JSClass<JSSlider>::StaticMethod("blockStyle", &JSSlider::SetBlockStyle);
76 JSClass<JSSlider>::StaticMethod("stepSize", &JSSlider::SetStepSize);
77 JSClass<JSSlider>::StaticMethod("onChange", &JSSlider::OnChange);
78 JSClass<JSSlider>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
79 JSClass<JSSlider>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
80 JSClass<JSSlider>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
81 JSClass<JSSlider>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
82 JSClass<JSSlider>::InheritAndBind<JSViewAbstract>(globalObj);
83 }
84
GetStep(double step,double max,double min)85 double GetStep(double step, double max, double min)
86 {
87 if (LessOrEqual(step, 0.0) || step > max - min) {
88 step = 1;
89 }
90 return step;
91 }
92
GetValue(double value,double max,double min)93 double GetValue(double value, double max, double min)
94 {
95 if (value < min) {
96 value = min;
97 }
98
99 if (value > max) {
100 value = max;
101 }
102 return value;
103 }
104
ParseSliderValueObject(const JSCallbackInfo & info,const JSRef<JSVal> & changeEventVal)105 void ParseSliderValueObject(const JSCallbackInfo& info, const JSRef<JSVal>& changeEventVal)
106 {
107 CHECK_NULL_VOID(changeEventVal->IsFunction());
108
109 JsEventCallback<void(float)> onChangeEvent(info.GetExecutionContext(), JSRef<JSFunc>::Cast(changeEventVal));
110 SliderModel::GetInstance()->SetOnChangeEvent(std::move(onChangeEvent));
111 }
112
Create(const JSCallbackInfo & info)113 void JSSlider::Create(const JSCallbackInfo& info)
114 {
115 static const double valueMin = -1000000.0f;
116 double value = valueMin; // value:Current progress value. The default value is min.
117 double min = 0; // min:Set the minimum value. The default value is 0.
118 double max = 100; // max:Set the maximum value. The default value is 100.
119 double step = 1; // step:Sets the sliding jump value of the slider. The default value is 1.
120 bool reverse = false;
121
122 if (!info[0]->IsObject()) {
123 LOGE("slider create error, info is non-valid");
124 SliderModel::GetInstance()->Create(
125 static_cast<float>(value), static_cast<float>(step), static_cast<float>(min), static_cast<float>(max));
126 return;
127 }
128
129 auto paramObject = JSRef<JSObject>::Cast(info[0]);
130 auto getValue = paramObject->GetProperty("value");
131 auto getMin = paramObject->GetProperty("min");
132 auto getMax = paramObject->GetProperty("max");
133 auto getStep = paramObject->GetProperty("step");
134 auto getStyle = paramObject->GetProperty("style");
135 auto direction = paramObject->GetProperty("direction");
136 auto isReverse = paramObject->GetProperty("reverse");
137 JSRef<JSVal> changeEventVal;
138
139 if (!getValue->IsNull() && getValue->IsNumber()) {
140 value = getValue->ToNumber<double>();
141 } else if (!getValue->IsNull() && getValue->IsObject()) {
142 JSRef<JSObject> valueObj = JSRef<JSObject>::Cast(getValue);
143 changeEventVal = valueObj->GetProperty("changeEvent");
144 auto valueProperty = valueObj->GetProperty("value");
145 value = valueProperty->ToNumber<double>();
146 }
147
148 if (!getMin->IsNull() && getMin->IsNumber()) {
149 min = getMin->ToNumber<double>();
150 }
151
152 if (!getMax->IsNull() && getMax->IsNumber()) {
153 max = getMax->ToNumber<double>();
154 }
155
156 if (!getStep->IsNull() && getStep->IsNumber()) {
157 step = getStep->ToNumber<double>();
158 }
159
160 if (!isReverse->IsNull() && isReverse->IsBoolean()) {
161 reverse = isReverse->ToBoolean();
162 }
163
164 if (GreatOrEqual(min, max)) {
165 min = 0;
166 max = 100;
167 }
168
169 step = GetStep(step, max, min);
170
171 if (!Container::IsCurrentUseNewPipeline()) {
172 value = GetValue(value, max, min);
173 }
174
175 auto sliderStyle = SliderStyle::OUTSET;
176 auto sliderMode = SliderModel::SliderMode::OUTSET;
177 if (!getStyle->IsNull() && getStyle->IsNumber()) {
178 sliderStyle = static_cast<SliderStyle>(getStyle->ToNumber<int32_t>());
179 }
180 if (sliderStyle == SliderStyle::INSET) {
181 sliderMode = SliderModel::SliderMode::INSET;
182 } else if (sliderStyle == SliderStyle::CAPSULE) {
183 sliderMode = SliderModel::SliderMode::CAPSULE;
184 } else {
185 sliderMode = SliderModel::SliderMode::OUTSET;
186 }
187
188 auto sliderDirection = Axis::HORIZONTAL;
189 if (!direction->IsNull() && direction->IsNumber()) {
190 sliderDirection = static_cast<Axis>(direction->ToNumber<int32_t>());
191 }
192 if (sliderDirection != Axis::VERTICAL) {
193 sliderDirection = Axis::HORIZONTAL;
194 }
195
196 SliderModel::GetInstance()->Create(
197 static_cast<float>(value), static_cast<float>(step), static_cast<float>(min), static_cast<float>(max));
198 SliderModel::GetInstance()->SetSliderMode(sliderMode);
199 SliderModel::GetInstance()->SetDirection(sliderDirection);
200 SliderModel::GetInstance()->SetReverse(reverse);
201 if (!changeEventVal->IsUndefined() && changeEventVal->IsFunction()) {
202 ParseSliderValueObject(info, changeEventVal);
203 }
204 }
205
SetThickness(const JSCallbackInfo & info)206 void JSSlider::SetThickness(const JSCallbackInfo& info)
207 {
208 if (info.Length() < 1) {
209 LOGE("The arg is wrong, it is supposed to have at least 1 arguments");
210 return;
211 }
212 CalcDimension value;
213 if (!ParseJsDimensionVp(info[0], value)) {
214 value = CalcDimension(0.0);
215 }
216 SliderModel::GetInstance()->SetThickness(value);
217 }
218
SetBlockColor(const JSCallbackInfo & info)219 void JSSlider::SetBlockColor(const JSCallbackInfo& info)
220 {
221 if (info.Length() < 1) {
222 LOGE("The arg is wrong, it is supposed to have at least 1 arguments");
223 return;
224 }
225 Color colorVal;
226 if (!ParseJsColor(info[0], colorVal)) {
227 auto theme = GetTheme<SliderTheme>();
228 CHECK_NULL_VOID(theme);
229 colorVal = theme->GetBlockColor();
230 }
231 SliderModel::GetInstance()->SetBlockColor(colorVal);
232 }
233
SetTrackColor(const JSCallbackInfo & info)234 void JSSlider::SetTrackColor(const JSCallbackInfo& info)
235 {
236 if (info.Length() < 1) {
237 LOGE("The arg is wrong, it is supposed to have atleast 1 arguments");
238 return;
239 }
240 Color colorVal;
241 if (!ParseJsColor(info[0], colorVal)) {
242 auto theme = GetTheme<SliderTheme>();
243 CHECK_NULL_VOID(theme);
244 colorVal = theme->GetTrackBgColor();
245 }
246 SliderModel::GetInstance()->SetTrackBackgroundColor(colorVal);
247 }
248
SetSelectedColor(const JSCallbackInfo & info)249 void JSSlider::SetSelectedColor(const JSCallbackInfo& info)
250 {
251 if (info.Length() < 1) {
252 LOGE("The arg is wrong, it is supposed to have atleast 1 arguments");
253 return;
254 }
255 Color colorVal;
256 if (!ParseJsColor(info[0], colorVal)) {
257 auto theme = GetTheme<SliderTheme>();
258 CHECK_NULL_VOID(theme);
259 colorVal = theme->GetTrackSelectedColor();
260 }
261 SliderModel::GetInstance()->SetSelectColor(colorVal);
262 }
263
SetMinLabel(const JSCallbackInfo & info)264 void JSSlider::SetMinLabel(const JSCallbackInfo& info)
265 {
266 if (info.Length() < 1) {
267 LOGE("The arg is wrong, it is supposed to have at least 1 arguments");
268 return;
269 }
270
271 if (!info[0]->IsString()) {
272 LOGE("arg is not string.");
273 return;
274 }
275 SliderModel::GetInstance()->SetMinLabel(info[0]->ToNumber<float>());
276 }
277
SetMaxLabel(const JSCallbackInfo & info)278 void JSSlider::SetMaxLabel(const JSCallbackInfo& info)
279 {
280 if (info.Length() < 1) {
281 LOGE("The arg is wrong, it is supposed to have at least 1 arguments");
282 return;
283 }
284
285 if (!info[0]->IsString()) {
286 LOGE("arg is not string.");
287 return;
288 }
289 SliderModel::GetInstance()->SetMaxLabel(info[0]->ToNumber<float>());
290 }
291
SetShowSteps(const JSCallbackInfo & info)292 void JSSlider::SetShowSteps(const JSCallbackInfo& info)
293 {
294 if (info.Length() < 1) {
295 LOGE("The arg is wrong, it is supposed to have at least 1 arguments");
296 return;
297 }
298 bool showSteps = false;
299 if (info[0]->IsBoolean()) {
300 showSteps = info[0]->ToBoolean();
301 }
302 SliderModel::GetInstance()->SetShowSteps(showSteps);
303 }
304
SetShowTips(const JSCallbackInfo & info)305 void JSSlider::SetShowTips(const JSCallbackInfo& info)
306 {
307 if (info.Length() < 1) {
308 LOGE("The arg is wrong, it is supposed to have at least 1 arguments");
309 return;
310 }
311 bool showTips = false;
312 if (info[0]->IsBoolean()) {
313 showTips = info[0]->ToBoolean();
314 }
315
316 std::optional<std::string> content;
317 if (info.Length() == SLIDER_SHOW_TIPS_MAX_PARAMS) {
318 std::string str;
319 if (ParseJsString(info[1], str)) {
320 content = str;
321 }
322 }
323
324 SliderModel::GetInstance()->SetShowTips(showTips, content);
325 }
326
SetBlockBorderColor(const JSCallbackInfo & info)327 void JSSlider::SetBlockBorderColor(const JSCallbackInfo& info)
328 {
329 if (info.Length() < 1) {
330 LOGW("The arg is wrong, it is supposed to have at least 1 arguments");
331 return;
332 }
333
334 Color colorVal;
335 if (!ParseJsColor(info[0], colorVal)) {
336 SliderModel::GetInstance()->ResetBlockBorderColor();
337 return;
338 }
339 SliderModel::GetInstance()->SetBlockBorderColor(colorVal);
340 }
341
SetBlockBorderWidth(const JSCallbackInfo & info)342 void JSSlider::SetBlockBorderWidth(const JSCallbackInfo& info)
343 {
344 if (info.Length() < 1) {
345 LOGW("The arg is wrong, it is supposed to have at least 1 arguments");
346 return;
347 }
348
349 CalcDimension blockBorderWidth;
350 if (!ParseJsDimensionVp(info[0], blockBorderWidth)) {
351 SliderModel::GetInstance()->ResetBlockBorderWidth();
352 return;
353 }
354 if (LessNotEqual(blockBorderWidth.Value(), 0.0)) {
355 SliderModel::GetInstance()->ResetBlockBorderWidth();
356 return;
357 }
358 SliderModel::GetInstance()->SetBlockBorderWidth(blockBorderWidth);
359 }
360
SetStepColor(const JSCallbackInfo & info)361 void JSSlider::SetStepColor(const JSCallbackInfo& info)
362 {
363 if (info.Length() < 1) {
364 LOGW("The arg is wrong, it is supposed to have at least 1 arguments");
365 return;
366 }
367
368 Color colorVal;
369 if (!ParseJsColor(info[0], colorVal)) {
370 SliderModel::GetInstance()->ResetStepColor();
371 return;
372 }
373 SliderModel::GetInstance()->SetStepColor(colorVal);
374 }
375
SetTrackBorderRadius(const JSCallbackInfo & info)376 void JSSlider::SetTrackBorderRadius(const JSCallbackInfo& info)
377 {
378 if (info.Length() < 1) {
379 LOGW("The arg is wrong, it is supposed to have at least 1 arguments");
380 return;
381 }
382
383 CalcDimension trackBorderRadius;
384 if (!ParseJsDimensionVpNG(info[0], trackBorderRadius, true)) {
385 SliderModel::GetInstance()->ResetTrackBorderRadius();
386 return;
387 }
388 if (LessNotEqual(trackBorderRadius.Value(), 0.0)) {
389 SliderModel::GetInstance()->ResetTrackBorderRadius();
390 return;
391 }
392 SliderModel::GetInstance()->SetTrackBorderRadius(trackBorderRadius);
393 }
394
SetBlockSize(const JSCallbackInfo & info)395 void JSSlider::SetBlockSize(const JSCallbackInfo& info)
396 {
397 if (info.Length() < 1) {
398 LOGW("The arg is wrong, it is supposed to have at least 1 arguments");
399 return;
400 }
401 if (!info[0]->IsObject()) {
402 SliderModel::GetInstance()->ResetBlockSize();
403 return;
404 }
405 JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
406
407 CalcDimension width;
408 JSRef<JSVal> jsWidth = sizeObj->GetProperty("width");
409 if (!ParseJsDimensionVp(jsWidth, width)) {
410 width.SetValue(0.0);
411 }
412 if (LessNotEqual(width.Value(), 0.0)) {
413 width.SetValue(0.0);
414 }
415
416 CalcDimension height;
417 JSRef<JSVal> jsHeight = sizeObj->GetProperty("height");
418 if (!ParseJsDimensionVp(jsHeight, height)) {
419 height.SetValue(0.0);
420 }
421 if (LessNotEqual(height.Value(), 0.0)) {
422 height.SetValue(0.0);
423 }
424
425 SliderModel::GetInstance()->SetBlockSize(width, height);
426 }
427
SetBlockStyle(const JSCallbackInfo & info)428 void JSSlider::SetBlockStyle(const JSCallbackInfo& info)
429 {
430 if (info.Length() < 1) {
431 LOGW("The arg is wrong, it is supposed to have at least 1 arguments");
432 return;
433 }
434
435 if (!info[0]->IsObject()) {
436 LOGW("arg is not object.");
437 ResetBlockStyle();
438 return;
439 }
440 auto jsObj = JSRef<JSObject>::Cast(info[0]);
441 auto getType = jsObj->GetProperty("type");
442 if (getType->IsNull() || !getType->IsNumber()) {
443 LOGW("block type is not number.");
444 ResetBlockStyle();
445 return;
446 }
447 auto type = static_cast<SliderModel::BlockStyleType>(getType->ToNumber<int32_t>());
448 if (type == SliderModel::BlockStyleType::IMAGE) {
449 std::string src;
450 if (!ParseJsMedia(jsObj->GetProperty("image"), src)) {
451 ResetBlockStyle();
452 return;
453 }
454 SliderModel::GetInstance()->SetBlockImage(src);
455 } else if (type == SliderModel::BlockStyleType::SHAPE) {
456 auto shape = jsObj->GetProperty("shape");
457 if (!shape->IsObject()) {
458 LOGW("shape param is not an object.");
459 ResetBlockStyle();
460 return;
461 }
462 JSShapeAbstract* shapeAbstract = JSRef<JSObject>::Cast(shape)->Unwrap<JSShapeAbstract>();
463 if (shapeAbstract == nullptr) {
464 LOGW("clipShape is null");
465 ResetBlockStyle();
466 return;
467 }
468 SliderModel::GetInstance()->SetBlockShape(shapeAbstract->GetBasicShape());
469 }
470 SliderModel::GetInstance()->SetBlockType(type);
471 }
472
SetStepSize(const JSCallbackInfo & info)473 void JSSlider::SetStepSize(const JSCallbackInfo& info)
474 {
475 if (info.Length() < 1) {
476 LOGW("The arg is wrong, it is supposed to have at least 1 arguments");
477 return;
478 }
479
480 CalcDimension stepSize;
481 if (!ParseJsDimensionVp(info[0], stepSize)) {
482 SliderModel::GetInstance()->ResetStepSize();
483 return;
484 }
485 if (LessNotEqual(stepSize.Value(), 0.0)) {
486 auto theme = GetTheme<SliderTheme>();
487 CHECK_NULL_VOID(theme);
488 stepSize = theme->GetMarkerSize();
489 }
490 SliderModel::GetInstance()->SetStepSize(stepSize);
491 }
492
OnChange(const JSCallbackInfo & info)493 void JSSlider::OnChange(const JSCallbackInfo& info)
494 {
495 if (info.Length() < 1) {
496 LOGW("Must contain at least 1 argument");
497 return;
498 }
499 if (!info[0]->IsFunction()) {
500 LOGW("Argument is not a function object");
501 return;
502 }
503 SliderModel::GetInstance()->SetOnChange(
504 JsEventCallback<void(float, int32_t)>(info.GetExecutionContext(), JSRef<JSFunc>::Cast(info[0])));
505 info.ReturnSelf();
506 }
507
ResetBlockStyle()508 void JSSlider::ResetBlockStyle()
509 {
510 SliderModel::GetInstance()->ResetBlockType();
511 SliderModel::GetInstance()->ResetBlockImage();
512 SliderModel::GetInstance()->ResetBlockShape();
513 }
514 } // namespace OHOS::Ace::Framework
515