/* * Copyright (c) 2021-2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "frameworks/bridge/declarative_frontend/jsview/js_slider.h" #include "bridge/declarative_frontend/jsview/js_linear_gradient.h" #include "bridge/declarative_frontend/jsview/js_view_common_def.h" #include "bridge/declarative_frontend/jsview/models/slider_model_impl.h" #include "bridge/declarative_frontend/ark_theme/theme_apply/js_slider_theme.h" #include "core/components/slider/render_slider.h" #include "core/components/slider/slider_element.h" #include "core/components_ng/pattern/slider/slider_model_ng.h" #include "frameworks/bridge/declarative_frontend/engine/functions/js_function.h" #include "frameworks/bridge/declarative_frontend/jsview/js_shape_abstract.h" namespace OHOS::Ace { namespace { constexpr int SLIDER_SHOW_TIPS_MAX_PARAMS = 2; } // namespace std::unique_ptr SliderModel::instance_ = nullptr; std::mutex SliderModel::mutex_; SliderModel* SliderModel::GetInstance() { if (!instance_) { std::lock_guard lock(mutex_); if (!instance_) { #ifdef NG_BUILD instance_.reset(new NG::SliderModelNG()); #else if (Container::IsCurrentUseNewPipeline()) { instance_.reset(new NG::SliderModelNG()); } else { instance_.reset(new Framework::SliderModelImpl()); } #endif } } return instance_.get(); } } // namespace OHOS::Ace namespace OHOS::Ace::Framework { void JSSlider::JSBind(BindingTarget globalObj) { JSClass::Declare("Slider"); MethodOptions opt = MethodOptions::NONE; JSClass::StaticMethod("create", &JSSlider::Create, opt); JSClass::StaticMethod("blockColor", &JSSlider::SetBlockColor); JSClass::StaticMethod("trackColor", &JSSlider::SetTrackColor); JSClass::StaticMethod("trackThickness", &JSSlider::SetThickness); JSClass::StaticMethod("selectedColor", &JSSlider::SetSelectedColor); JSClass::StaticMethod("minLabel", &JSSlider::SetMinLabel); JSClass::StaticMethod("maxLabel", &JSSlider::SetMaxLabel); JSClass::StaticMethod("minResponsiveDistance", &JSSlider::SetMinResponsiveDistance); JSClass::StaticMethod("showSteps", &JSSlider::SetShowSteps); JSClass::StaticMethod("showTips", &JSSlider::SetShowTips); JSClass::StaticMethod("blockBorderColor", &JSSlider::SetBlockBorderColor); JSClass::StaticMethod("blockBorderWidth", &JSSlider::SetBlockBorderWidth); JSClass::StaticMethod("stepColor", &JSSlider::SetStepColor); JSClass::StaticMethod("trackBorderRadius", &JSSlider::SetTrackBorderRadius); JSClass::StaticMethod("selectedBorderRadius", &JSSlider::SetSelectedBorderRadius); JSClass::StaticMethod("blockSize", &JSSlider::SetBlockSize); JSClass::StaticMethod("blockStyle", &JSSlider::SetBlockStyle); JSClass::StaticMethod("stepSize", &JSSlider::SetStepSize); JSClass::StaticMethod("sliderInteractionMode", &JSSlider::SetSliderInteractionMode); JSClass::StaticMethod("slideRange", &JSSlider::SetValidSlideRange); JSClass::StaticMethod("onChange", &JSSlider::OnChange); JSClass::StaticMethod("onAttach", &JSInteractableView::JsOnAttach); JSClass::StaticMethod("onAppear", &JSInteractableView::JsOnAppear); JSClass::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear); JSClass::StaticMethod("onDetach", &JSInteractableView::JsOnDetach); JSClass::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey); JSClass::StaticMethod("onTouch", &JSInteractableView::JsOnTouch); JSClass::InheritAndBind(globalObj); } double GetStep(double step, double max, double min) { if (LessOrEqual(step, 0.0) || step > max - min) { step = 1; } return step; } double GetValue(double value, double max, double min) { if (value < min) { value = min; } if (value > max) { value = max; } return value; } void ParseSliderValueObject(const JSCallbackInfo& info, const JSRef& changeEventVal) { CHECK_NULL_VOID(changeEventVal->IsFunction()); JsEventCallback onChangeEvent(info.GetExecutionContext(), JSRef::Cast(changeEventVal)); SliderModel::GetInstance()->SetOnChangeEvent(std::move(onChangeEvent)); } void JSSlider::Create(const JSCallbackInfo& info) { static const double valueMin = -1000000.0f; double value = valueMin; // value:Current progress value. The default value is min. double min = 0; // min:Set the minimum value. The default value is 0. double max = 100; // max:Set the maximum value. The default value is 100. double step = 1; // step:Sets the sliding jump value of the slider. The default value is 1. bool reverse = false; if (!info[0]->IsObject()) { SliderModel::GetInstance()->Create( static_cast(value), static_cast(step), static_cast(min), static_cast(max)); JSSliderTheme::ApplyTheme(); return; } auto paramObject = JSRef::Cast(info[0]); auto getValue = paramObject->GetProperty("value"); auto getMin = paramObject->GetProperty("min"); auto getMax = paramObject->GetProperty("max"); auto getStep = paramObject->GetProperty("step"); auto getStyle = paramObject->GetProperty("style"); auto direction = paramObject->GetProperty("direction"); auto isReverse = paramObject->GetProperty("reverse"); JSRef changeEventVal; if (!getValue->IsNull() && getValue->IsNumber()) { value = getValue->ToNumber(); } else if (!getValue->IsNull() && getValue->IsObject()) { JSRef valueObj = JSRef::Cast(getValue); changeEventVal = valueObj->GetProperty("changeEvent"); auto valueProperty = valueObj->GetProperty("value"); value = valueProperty->ToNumber(); } if (!getMin->IsNull() && getMin->IsNumber()) { min = getMin->ToNumber(); } if (!getMax->IsNull() && getMax->IsNumber()) { max = getMax->ToNumber(); } if (!getStep->IsNull() && getStep->IsNumber()) { step = getStep->ToNumber(); } if (!isReverse->IsNull() && isReverse->IsBoolean()) { reverse = isReverse->ToBoolean(); } if (GreatOrEqual(min, max)) { min = 0; max = 100; } step = GetStep(step, max, min); if (!Container::IsCurrentUseNewPipeline()) { value = GetValue(value, max, min); } auto sliderStyle = SliderStyle::OUTSET; auto sliderMode = SliderModel::SliderMode::OUTSET; if (!getStyle->IsNull() && getStyle->IsNumber()) { sliderStyle = static_cast(getStyle->ToNumber()); } if (sliderStyle == SliderStyle::INSET) { sliderMode = SliderModel::SliderMode::INSET; } else if (sliderStyle == SliderStyle::CAPSULE) { sliderMode = SliderModel::SliderMode::CAPSULE; } else if (sliderStyle == SliderStyle::NONE) { sliderMode = SliderModel::SliderMode::NONE; } else { sliderMode = SliderModel::SliderMode::OUTSET; } auto sliderDirection = Axis::HORIZONTAL; if (!direction->IsNull() && direction->IsNumber()) { sliderDirection = static_cast(direction->ToNumber()); } if (sliderDirection != Axis::VERTICAL) { sliderDirection = Axis::HORIZONTAL; } SliderModel::GetInstance()->Create( static_cast(value), static_cast(step), static_cast(min), static_cast(max)); SliderModel::GetInstance()->SetSliderMode(sliderMode); SliderModel::GetInstance()->SetDirection(sliderDirection); SliderModel::GetInstance()->SetReverse(reverse); if (!changeEventVal->IsUndefined() && changeEventVal->IsFunction()) { ParseSliderValueObject(info, changeEventVal); } JSSliderTheme::ApplyTheme(); } void JSSlider::SetThickness(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } CalcDimension value; if (!ParseJsDimensionVp(info[0], value)) { value = CalcDimension(0.0); } SliderModel::GetInstance()->SetThickness(value); } void JSSlider::SetBlockColor(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } Color colorVal; if (!ParseJsColor(info[0], colorVal)) { auto theme = GetTheme(); CHECK_NULL_VOID(theme); colorVal = theme->GetBlockColor(); } SliderModel::GetInstance()->SetBlockColor(colorVal); } void JSSlider::SetTrackColor(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } NG::Gradient gradient; bool isResourceColor = false; if (!ConvertGradientColor(info[0], gradient)) { Color colorVal; if (info[0]->IsNull() || info[0]->IsUndefined() || !ParseJsColor(info[0], colorVal)) { auto theme = GetTheme(); CHECK_NULL_VOID(theme); colorVal = theme->GetTrackBgColor(); } isResourceColor = true; gradient = NG::SliderModelNG::CreateSolidGradient(colorVal); // Set track color to Framework::SliderModelImpl. Need to backward compatibility with old pipeline. SliderModel::GetInstance()->SetTrackBackgroundColor(colorVal); } // Set track gradient color to NG::SliderModelNG SliderModel::GetInstance()->SetTrackBackgroundColor(gradient, isResourceColor); } bool JSSlider::ConvertGradientColor(const JsiRef& param, NG::Gradient& gradient) { if (param->IsNull() || param->IsUndefined() || !param->IsObject()) { return false; } JSLinearGradient* jsLinearGradient = JSRef::Cast(param)->Unwrap(); if (!jsLinearGradient || jsLinearGradient->GetGradient().empty()) { return false; } size_t size = jsLinearGradient->GetGradient().size(); if (size == 1) { // If there is only one color, then this color is used for both the begin and end side. NG::GradientColor gradientColor; gradientColor.SetLinearColor(LinearColor(jsLinearGradient->GetGradient().front().first)); gradientColor.SetDimension(jsLinearGradient->GetGradient().front().second); gradient.AddColor(gradientColor); gradient.AddColor(gradientColor); return true; } for (size_t colorIndex = 0; colorIndex < size; colorIndex++) { NG::GradientColor gradientColor; gradientColor.SetLinearColor(LinearColor(jsLinearGradient->GetGradient().at(colorIndex).first)); gradientColor.SetDimension(jsLinearGradient->GetGradient().at(colorIndex).second); gradient.AddColor(gradientColor); } return true; } void JSSlider::SetSelectedColor(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } Color colorVal; if (!ParseJsColor(info[0], colorVal)) { auto theme = GetTheme(); CHECK_NULL_VOID(theme); colorVal = theme->GetTrackSelectedColor(); } SliderModel::GetInstance()->SetSelectColor(colorVal); } void JSSlider::SetMinLabel(const JSCallbackInfo& info) { if (!info[0]->IsString() && !info[0]->IsNumber()) { return; } SliderModel::GetInstance()->SetMinLabel(info[0]->ToNumber()); } void JSSlider::SetMaxLabel(const JSCallbackInfo& info) { if (!info[0]->IsString() && !info[0]->IsNumber()) { return; } SliderModel::GetInstance()->SetMaxLabel(info[0]->ToNumber()); } void JSSlider::SetValidSlideRange(const JSCallbackInfo& info) { if (!info[0]->IsObject()) { SliderModel::GetInstance()->ResetValidSlideRange(); return; } auto paramObject = JSRef::Cast(info[0]); auto getValueRangeFrom = paramObject->GetProperty("from"); auto getValueRangeTo = paramObject->GetProperty("to"); float rangeFromValue = std::numeric_limits::quiet_NaN(); float rangeToValue = std::numeric_limits::quiet_NaN(); if (getValueRangeFrom->IsEmpty()) { rangeFromValue = std::numeric_limits::infinity(); } else if (getValueRangeFrom->IsNumber()) { rangeFromValue = getValueRangeFrom->ToNumber(); } if (getValueRangeTo->IsEmpty()) { rangeToValue = std::numeric_limits::infinity(); } else if (getValueRangeTo->IsNumber()) { rangeToValue = getValueRangeTo->ToNumber(); } if (std::isnan(rangeFromValue) || std::isnan(rangeToValue) || (std::isinf(rangeFromValue) && std::isinf(rangeToValue))) { SliderModel::GetInstance()->ResetValidSlideRange(); return; } SliderModel::GetInstance()->SetValidSlideRange(rangeFromValue, rangeToValue); } void JSSlider::SetMinResponsiveDistance(const JSCallbackInfo& info) { if (info.Length() < 1) { SliderModel::GetInstance()->ResetMinResponsiveDistance(); return; } float value = 0.0f; if (info[0]->IsString() || info[0]->IsNumber()) { value = info[0]->ToNumber(); value = std::isfinite(value) ? value : 0.0f; SliderModel::GetInstance()->SetMinResponsiveDistance(value); } else { SliderModel::GetInstance()->ResetMinResponsiveDistance(); } } void JSSlider::SetShowSteps(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } bool showSteps = false; if (info[0]->IsBoolean()) { showSteps = info[0]->ToBoolean(); } SliderModel::GetInstance()->SetShowSteps(showSteps); } void JSSlider::SetSliderInteractionMode(const JSCallbackInfo& info) { if (info.Length() < 1) { SliderModel::GetInstance()->ResetSliderInteractionMode(); return; } if (!info[0]->IsNull() && info[0]->IsNumber()) { auto mode = static_cast(info[0]->ToNumber()); SliderModel::GetInstance()->SetSliderInteractionMode(mode); } else { SliderModel::GetInstance()->ResetSliderInteractionMode(); } } void JSSlider::SetShowTips(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } bool showTips = false; if (info[0]->IsBoolean()) { showTips = info[0]->ToBoolean(); } std::optional content; if (info.Length() == SLIDER_SHOW_TIPS_MAX_PARAMS) { std::string str; if (ParseJsString(info[1], str)) { content = str; } } SliderModel::GetInstance()->SetShowTips(showTips, content); } void JSSlider::SetBlockBorderColor(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } Color colorVal; if (!ParseJsColor(info[0], colorVal)) { SliderModel::GetInstance()->ResetBlockBorderColor(); return; } SliderModel::GetInstance()->SetBlockBorderColor(colorVal); } void JSSlider::SetBlockBorderWidth(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } CalcDimension blockBorderWidth; if (!ParseJsDimensionVp(info[0], blockBorderWidth)) { SliderModel::GetInstance()->ResetBlockBorderWidth(); return; } if (LessNotEqual(blockBorderWidth.Value(), 0.0)) { SliderModel::GetInstance()->ResetBlockBorderWidth(); return; } SliderModel::GetInstance()->SetBlockBorderWidth(blockBorderWidth); } void JSSlider::SetStepColor(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } Color colorVal; if (!ParseJsColor(info[0], colorVal)) { SliderModel::GetInstance()->ResetStepColor(); return; } SliderModel::GetInstance()->SetStepColor(colorVal); } void JSSlider::SetTrackBorderRadius(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } CalcDimension trackBorderRadius; if (!ParseJsDimensionVpNG(info[0], trackBorderRadius, true)) { SliderModel::GetInstance()->ResetTrackBorderRadius(); return; } if (LessNotEqual(trackBorderRadius.Value(), 0.0)) { SliderModel::GetInstance()->ResetTrackBorderRadius(); return; } SliderModel::GetInstance()->SetTrackBorderRadius(trackBorderRadius); } void JSSlider::SetSelectedBorderRadius(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } CalcDimension selectedBorderRadius; if (!ParseJsDimensionVpNG(info[0], selectedBorderRadius, false)) { SliderModel::GetInstance()->ResetSelectedBorderRadius(); return; } if (LessNotEqual(selectedBorderRadius.Value(), 0.0)) { SliderModel::GetInstance()->ResetSelectedBorderRadius(); return; } SliderModel::GetInstance()->SetSelectedBorderRadius(selectedBorderRadius); } void JSSlider::SetBlockSize(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } if (!info[0]->IsObject()) { SliderModel::GetInstance()->ResetBlockSize(); return; } JSRef sizeObj = JSRef::Cast(info[0]); CalcDimension width; JSRef jsWidth = sizeObj->GetProperty("width"); if (!ParseJsDimensionVp(jsWidth, width)) { width.SetValue(0.0); } if (LessNotEqual(width.Value(), 0.0)) { width.SetValue(0.0); } CalcDimension height; JSRef jsHeight = sizeObj->GetProperty("height"); if (!ParseJsDimensionVp(jsHeight, height)) { height.SetValue(0.0); } if (LessNotEqual(height.Value(), 0.0)) { height.SetValue(0.0); } SliderModel::GetInstance()->SetBlockSize(width, height); } void JSSlider::SetBlockStyle(const JSCallbackInfo& info) { if (!info[0]->IsObject()) { ResetBlockStyle(); return; } auto jsObj = JSRef::Cast(info[0]); auto getType = jsObj->GetProperty("type"); if (getType->IsNull() || !getType->IsNumber()) { ResetBlockStyle(); return; } auto type = static_cast(getType->ToNumber()); if (type == SliderModel::BlockStyleType::IMAGE) { std::string src; auto image = jsObj->GetProperty("image"); if (!ParseJsMedia(image, src)) { ResetBlockStyle(); return; } std::string bundleName; std::string moduleName; GetJsMediaBundleInfo(image, bundleName, moduleName); SliderModel::GetInstance()->SetBlockImage(src, bundleName, moduleName); } else if (type == SliderModel::BlockStyleType::SHAPE) { auto shape = jsObj->GetProperty("shape"); if (!shape->IsObject()) { ResetBlockStyle(); return; } JSShapeAbstract* shapeAbstract = JSRef::Cast(shape)->Unwrap(); if (shapeAbstract == nullptr) { ResetBlockStyle(); return; } SliderModel::GetInstance()->SetBlockShape(shapeAbstract->GetBasicShape()); } SliderModel::GetInstance()->SetBlockType(type); } void JSSlider::SetStepSize(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } CalcDimension stepSize; if (!ParseJsDimensionVp(info[0], stepSize)) { SliderModel::GetInstance()->ResetStepSize(); return; } if (LessNotEqual(stepSize.Value(), 0.0)) { auto theme = GetTheme(); CHECK_NULL_VOID(theme); stepSize = theme->GetMarkerSize(); } SliderModel::GetInstance()->SetStepSize(stepSize); } void JSSlider::OnChange(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } SliderModel::GetInstance()->SetOnChange( JsEventCallback(info.GetExecutionContext(), JSRef::Cast(info[0]))); info.ReturnSelf(); } void JSSlider::ResetBlockStyle() { SliderModel::GetInstance()->ResetBlockType(); SliderModel::GetInstance()->ResetBlockImage(); SliderModel::GetInstance()->ResetBlockShape(); } } // namespace OHOS::Ace::Framework