1 /*
2 * Copyright (c) 2024 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_indicator.h"
17
18 #include "bridge/declarative_frontend/engine/jsi/js_ui_index.h"
19 #include "core/components/swiper/swiper_indicator_theme.h"
20 #include "core/components_ng/base/view_stack_processor.h"
21 #include "core/components_ng/pattern/swiper_indicator/indicator_common/indicator_model.h"
22 #include "core/components_ng/pattern/swiper_indicator/indicator_common/indicator_model_ng.h"
23
24 namespace OHOS::Ace {
25 namespace {
26 constexpr int32_t DEFAULT_INDICATOR_COUNT = 2;
27 } // namespace
28 std::unique_ptr<IndicatorModel> IndicatorModel::instance_ = nullptr;
29 std::mutex IndicatorModel::mutex_;
30
GetInstance()31 IndicatorModel* IndicatorModel::GetInstance()
32 {
33 if (!instance_) {
34 std::lock_guard<std::mutex> lock(mutex_);
35 if (!instance_) {
36 instance_.reset(new NG::IndicatorModelNG());
37 }
38 }
39 return instance_.get();
40 }
41
42 } // namespace OHOS::Ace
43 namespace OHOS::Ace::Framework {
44 namespace {
SwiperChangeEventToJSValue(const SwiperChangeEvent & eventInfo)45 JSRef<JSVal> SwiperChangeEventToJSValue(const SwiperChangeEvent& eventInfo)
46 {
47 return JSRef<JSVal>::Make(ToJSValue(eventInfo.GetIndex()));
48 }
49
50 } // namespace
51
Create(const JSCallbackInfo & info)52 void JSIndicator::Create(const JSCallbackInfo& info)
53 {
54 auto indicatorController = IndicatorModel::GetInstance()->Create();
55 if (info[0]->IsObject()) {
56 auto* jsController = JSRef<JSObject>::Cast(info[0])->Unwrap<JSIndicatorController>();
57 if (jsController) {
58 jsController->SetInstanceId(Container::CurrentId());
59 WeakPtr<NG::UINode> indicatorNode =
60 AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
61 jsController->SetController(indicatorController, indicatorNode);
62 }
63 }
64 }
65
JSBind(BindingTarget globalObj)66 void JSIndicator::JSBind(BindingTarget globalObj)
67 {
68 JSClass<JSIndicator>::Declare("IndicatorComponent");
69 MethodOptions opt = MethodOptions::NONE;
70 JSClass<JSIndicator>::StaticMethod("create", &JSIndicator::Create, opt);
71 JSClass<JSIndicator>::StaticMethod("count", &JSIndicator::SetCount, opt);
72 JSClass<JSIndicator>::StaticMethod("loop", &JSIndicator::SetLoop, opt);
73 JSClass<JSIndicator>::StaticMethod("vertical", &JSIndicator::SetVertical, opt);
74 JSClass<JSIndicator>::StaticMethod("style", &JSIndicator::SetStyle, opt);
75 JSClass<JSIndicator>::StaticMethod("initialIndex", &JSIndicator::SetInitialIndex, opt);
76 JSClass<JSIndicator>::StaticMethod("onChange", &JSIndicator::SetOnChange);
77 JSClass<JSIndicator>::InheritAndBind<JSViewAbstract>(globalObj);
78 }
79
SetCount(const JSCallbackInfo & info)80 void JSIndicator::SetCount(const JSCallbackInfo& info)
81 {
82 if (!info[0]->IsNumber()) {
83 IndicatorModel::GetInstance()->SetCount(DEFAULT_INDICATOR_COUNT);
84 return;
85 }
86
87 auto count = std::max(DEFAULT_INDICATOR_COUNT, info[0]->ToNumber<int32_t>());
88 IndicatorModel::GetInstance()->SetCount(count);
89 }
90
SetLoop(const JSCallbackInfo & info)91 void JSIndicator::SetLoop(const JSCallbackInfo& info)
92 {
93 if (!info[0]->IsBoolean()) {
94 IndicatorModel::GetInstance()->SetLoop(true);
95 return;
96 }
97
98 IndicatorModel::GetInstance()->SetLoop(info[0]->ToBoolean());
99 }
100
SetVertical(const JSCallbackInfo & info)101 void JSIndicator::SetVertical(const JSCallbackInfo& info)
102 {
103 if (!info[0]->IsBoolean()) {
104 IndicatorModel::GetInstance()->SetDirection(Axis::HORIZONTAL);
105 return;
106 }
107
108 IndicatorModel::GetInstance()->SetDirection(info[0]->ToBoolean() ? Axis::VERTICAL : Axis::HORIZONTAL);
109 }
110
GetFontContent(const JSRef<JSVal> & font,bool isSelected,SwiperDigitalParameters & digitalParameters)111 void JSIndicator::GetFontContent(const JSRef<JSVal>& font, bool isSelected, SwiperDigitalParameters& digitalParameters)
112 {
113 JSRef<JSVal> size;
114 JSRef<JSVal> weight;
115 if (font->IsObject()) {
116 JSRef<JSObject> obj = JSRef<JSObject>::Cast(font);
117 size = obj->GetProperty("size");
118 weight = obj->GetProperty("weight");
119 }
120
121 auto pipelineContext = PipelineBase::GetCurrentContext();
122 CHECK_NULL_VOID(pipelineContext);
123 auto swiperIndicatorTheme = pipelineContext->GetTheme<SwiperIndicatorTheme>();
124 CHECK_NULL_VOID(swiperIndicatorTheme);
125 // set font size, unit FP
126 CalcDimension fontSize;
127 if (!size->IsUndefined() && !size->IsNull() && ParseJsDimensionFpNG(size, fontSize)) {
128 if (LessOrEqual(fontSize.Value(), 0.0) || LessOrEqual(size->ToNumber<double>(), 0.0) ||
129 fontSize.Unit() == DimensionUnit::PERCENT) {
130 fontSize = swiperIndicatorTheme->GetDigitalIndicatorTextStyle().GetFontSize();
131 }
132 } else {
133 fontSize = swiperIndicatorTheme->GetDigitalIndicatorTextStyle().GetFontSize();
134 }
135 if (isSelected) {
136 digitalParameters.selectedFontSize = fontSize;
137 } else {
138 digitalParameters.fontSize = fontSize;
139 }
140
141 if (!weight->IsNull()) {
142 std::string weightValue;
143 if (weight->IsNumber()) {
144 weightValue = std::to_string(weight->ToNumber<int32_t>());
145 } else {
146 ParseJsString(weight, weightValue);
147 }
148 if (isSelected) {
149 digitalParameters.selectedFontWeight = ConvertStrToFontWeight(weightValue);
150 } else {
151 digitalParameters.fontWeight = ConvertStrToFontWeight(weightValue);
152 }
153 } else {
154 if (isSelected) {
155 digitalParameters.selectedFontWeight = swiperIndicatorTheme->GetDigitalIndicatorTextStyle().GetFontWeight();
156 } else {
157 digitalParameters.fontWeight = swiperIndicatorTheme->GetDigitalIndicatorTextStyle().GetFontWeight();
158 }
159 }
160 }
161
ParseIndicatorDimension(const JSRef<JSVal> & value)162 std::optional<Dimension> JSIndicator::ParseIndicatorDimension(const JSRef<JSVal>& value)
163 {
164 std::optional<Dimension> indicatorDimension;
165 if (value->IsUndefined()) {
166 return indicatorDimension;
167 }
168 CalcDimension dimPosition;
169 auto parseOk = ParseJsDimensionVpNG(value, dimPosition);
170 indicatorDimension = parseOk && dimPosition.ConvertToPx() >= 0.0f ? dimPosition : 0.0_vp;
171 return indicatorDimension;
172 }
173
GetDotIndicatorInfo(const JSRef<JSObject> & obj)174 SwiperParameters JSIndicator::GetDotIndicatorInfo(const JSRef<JSObject>& obj)
175 {
176 JSRef<JSVal> leftValue = obj->GetProperty(static_cast<int32_t>(ArkUIIndex::LEFT_VALUE));
177 JSRef<JSVal> topValue = obj->GetProperty(static_cast<int32_t>(ArkUIIndex::TOP_VALUE));
178 JSRef<JSVal> rightValue = obj->GetProperty(static_cast<int32_t>(ArkUIIndex::RIGHT_VALUE));
179 JSRef<JSVal> bottomValue = obj->GetProperty(static_cast<int32_t>(ArkUIIndex::BOTTOM_VALUE));
180 JSRef<JSVal> startValue = obj->GetProperty(static_cast<int32_t>(ArkUIIndex::START_VALUE));
181 JSRef<JSVal> endValue = obj->GetProperty(static_cast<int32_t>(ArkUIIndex::END_VALUE));
182 JSRef<JSVal> itemWidthValue = obj->GetProperty(static_cast<int32_t>(ArkUIIndex::ITEM_WIDTH_VALUE));
183 JSRef<JSVal> itemHeightValue = obj->GetProperty(static_cast<int32_t>(ArkUIIndex::ITEM_HEIGHT_VALUE));
184 JSRef<JSVal> selectedItemWidthValue = obj->GetProperty(static_cast<int32_t>(ArkUIIndex::SELECTED_ITEM_WIDTH_VALUE));
185 JSRef<JSVal> selectedItemHeightValue =
186 obj->GetProperty(static_cast<int32_t>(ArkUIIndex::SELECTED_ITEM_HEIGHT_VALUE));
187 auto pipelineContext = PipelineBase::GetCurrentContext();
188 CHECK_NULL_RETURN(pipelineContext, SwiperParameters());
189 auto swiperIndicatorTheme = pipelineContext->GetTheme<SwiperIndicatorTheme>();
190 CHECK_NULL_RETURN(swiperIndicatorTheme, SwiperParameters());
191 SwiperParameters swiperParameters;
192 swiperParameters.dimLeft = ParseIndicatorDimension(leftValue);
193 swiperParameters.dimTop = ParseIndicatorDimension(topValue);
194 swiperParameters.dimRight = ParseIndicatorDimension(rightValue);
195 swiperParameters.dimBottom = ParseIndicatorDimension(bottomValue);
196 CalcDimension dimStart;
197 CalcDimension dimEnd;
198 std::optional<Dimension> indicatorDimension;
199 swiperParameters.dimStart = ParseLengthMetricsToDimension(startValue, dimStart) ? dimStart : indicatorDimension;
200 swiperParameters.dimEnd = ParseLengthMetricsToDimension(endValue, dimEnd) ? dimEnd : indicatorDimension;
201
202 CalcDimension dimPosition;
203 bool parseItemWOk =
204 ParseJsDimensionVpNG(itemWidthValue, dimPosition) && (dimPosition.Unit() != DimensionUnit::PERCENT);
205 auto defaultSize = swiperIndicatorTheme->GetSize();
206 swiperParameters.itemWidth = parseItemWOk && dimPosition > 0.0_vp ? dimPosition : defaultSize;
207 bool parseItemHOk =
208 ParseJsDimensionVpNG(itemHeightValue, dimPosition) && (dimPosition.Unit() != DimensionUnit::PERCENT);
209 swiperParameters.itemHeight = parseItemHOk && dimPosition > 0.0_vp ? dimPosition : defaultSize;
210 bool parseSelectedItemWOk =
211 ParseJsDimensionVpNG(selectedItemWidthValue, dimPosition) && (dimPosition.Unit() != DimensionUnit::PERCENT);
212 swiperParameters.selectedItemWidth = parseSelectedItemWOk && dimPosition > 0.0_vp ? dimPosition : defaultSize;
213 bool parseSelectedItemHOk =
214 ParseJsDimensionVpNG(selectedItemHeightValue, dimPosition) && (dimPosition.Unit() != DimensionUnit::PERCENT);
215 swiperParameters.selectedItemHeight = parseSelectedItemHOk && dimPosition > 0.0_vp ? dimPosition : defaultSize;
216 IndicatorModel::GetInstance()->SetIsIndicatorCustomSize(
217 parseSelectedItemWOk || parseSelectedItemHOk || parseItemWOk || parseItemHOk);
218 SetDotIndicatorInfo(obj, swiperParameters, swiperIndicatorTheme);
219 return swiperParameters;
220 }
SetDotIndicatorInfo(const JSRef<JSObject> & obj,SwiperParameters & swiperParameters,const RefPtr<SwiperIndicatorTheme> & swiperIndicatorTheme)221 void JSIndicator::SetDotIndicatorInfo(const JSRef<JSObject>& obj, SwiperParameters& swiperParameters,
222 const RefPtr<SwiperIndicatorTheme>& swiperIndicatorTheme)
223 {
224 JSRef<JSVal> maskValue = obj->GetProperty(static_cast<int32_t>(ArkUIIndex::MASK_VALUE));
225 JSRef<JSVal> colorValue = obj->GetProperty(static_cast<int32_t>(ArkUIIndex::COLOR_VALUE));
226 JSRef<JSVal> selectedColorValue = obj->GetProperty(static_cast<int32_t>(ArkUIIndex::SELECTED_COLOR_VALUE));
227 JSRef<JSVal> maxDisplayCountVal = obj->GetProperty(static_cast<int32_t>(ArkUIIndex::MAX_DISPLAY_COUNT_VALUE));
228 JSRef<JSVal> spaceValue = obj->GetProperty(static_cast<int32_t>(ArkUIIndex::SPACE_VALUE));
229 if (maskValue->IsBoolean()) {
230 auto mask = maskValue->ToBoolean();
231 swiperParameters.maskValue = mask;
232 }
233 Color colorVal;
234 auto parseOk = ParseJsColor(colorValue, colorVal);
235 swiperParameters.colorVal = parseOk ? colorVal : swiperIndicatorTheme->GetColor();
236 parseOk = ParseJsColor(selectedColorValue, colorVal);
237 swiperParameters.selectedColorVal = parseOk ? colorVal : swiperIndicatorTheme->GetSelectedColor();
238 auto defalutSpace = swiperIndicatorTheme->GetIndicatorDotItemSpace();
239 CalcDimension dimSpace;
240 auto parseSpaceOk = ParseLengthMetricsToDimension(spaceValue, dimSpace) &&
241 (dimSpace.Unit() != DimensionUnit::PERCENT);
242 swiperParameters.dimSpace = (parseSpaceOk && !(dimSpace < 0.0_vp)) ? dimSpace : defalutSpace;
243 if (maxDisplayCountVal->IsUndefined()) {
244 return;
245 }
246 uint32_t result = 0;
247 auto setMaxDisplayCountVal = ParseJsInteger(maxDisplayCountVal, result);
248 swiperParameters.maxDisplayCountVal = setMaxDisplayCountVal && result > 0 ? result : 0;
249 }
250
GetDigitIndicatorInfo(const JSRef<JSObject> & obj)251 SwiperDigitalParameters JSIndicator::GetDigitIndicatorInfo(const JSRef<JSObject>& obj)
252 {
253 JSRef<JSVal> dotLeftValue = obj->GetProperty("leftValue");
254 JSRef<JSVal> dotTopValue = obj->GetProperty("topValue");
255 JSRef<JSVal> dotRightValue = obj->GetProperty("rightValue");
256 JSRef<JSVal> dotBottomValue = obj->GetProperty("bottomValue");
257 JSRef<JSVal> fontColorValue = obj->GetProperty("fontColorValue");
258 JSRef<JSVal> selectedFontColorValue = obj->GetProperty("selectedFontColorValue");
259 JSRef<JSVal> digitFontValue = obj->GetProperty("digitFontValue");
260 JSRef<JSVal> selectedDigitFontValue = obj->GetProperty("selectedDigitFontValue");
261 auto pipelineContext = PipelineBase::GetCurrentContext();
262 CHECK_NULL_RETURN(pipelineContext, SwiperDigitalParameters());
263 auto swiperIndicatorTheme = pipelineContext->GetTheme<SwiperIndicatorTheme>();
264 CHECK_NULL_RETURN(swiperIndicatorTheme, SwiperDigitalParameters());
265 SwiperDigitalParameters digitalParameters;
266 digitalParameters.dimLeft = ParseIndicatorDimension(dotLeftValue);
267 digitalParameters.dimTop = ParseIndicatorDimension(dotTopValue);
268 digitalParameters.dimRight = ParseIndicatorDimension(dotRightValue);
269 digitalParameters.dimBottom = ParseIndicatorDimension(dotBottomValue);
270 Color fontColor;
271 auto parseOk = JSViewAbstract::ParseJsColor(fontColorValue, fontColor);
272 digitalParameters.fontColor =
273 parseOk ? fontColor : swiperIndicatorTheme->GetDigitalIndicatorTextStyle().GetTextColor();
274 parseOk = JSViewAbstract::ParseJsColor(selectedFontColorValue, fontColor);
275 digitalParameters.selectedFontColor =
276 parseOk ? fontColor : swiperIndicatorTheme->GetDigitalIndicatorTextStyle().GetTextColor();
277 if (!digitFontValue->IsNull() && digitFontValue->IsObject()) {
278 GetFontContent(digitFontValue, false, digitalParameters);
279 }
280 if (!selectedDigitFontValue->IsNull() && selectedDigitFontValue->IsObject()) {
281 GetFontContent(selectedDigitFontValue, true, digitalParameters);
282 }
283 return digitalParameters;
284 }
285
SetStyle(const JSCallbackInfo & info)286 void JSIndicator::SetStyle(const JSCallbackInfo& info)
287 {
288 if (info[0]->IsObject()) {
289 auto obj = JSRef<JSObject>::Cast(info[0]);
290
291 JSRef<JSVal> typeParam = obj->GetProperty("type");
292 if (typeParam->IsString()) {
293 auto type = typeParam->ToString();
294 if (type == "DigitIndicator") {
295 SwiperDigitalParameters digitalParameters = GetDigitIndicatorInfo(obj);
296 IndicatorModel::GetInstance()->SetDigitIndicatorStyle(digitalParameters);
297 IndicatorModel::GetInstance()->SetIndicatorType(SwiperIndicatorType::DIGIT);
298 } else {
299 SwiperParameters swiperParameters = GetDotIndicatorInfo(obj);
300 IndicatorModel::GetInstance()->SetDotIndicatorStyle(swiperParameters);
301 IndicatorModel::GetInstance()->SetIndicatorType(SwiperIndicatorType::DOT);
302 }
303 } else {
304 SwiperParameters swiperParameters = GetDotIndicatorInfo(obj);
305 IndicatorModel::GetInstance()->SetDotIndicatorStyle(swiperParameters);
306 IndicatorModel::GetInstance()->SetIndicatorType(SwiperIndicatorType::DOT);
307 }
308 } else {
309 SwiperParameters swiperParameters = GetDotIndicatorInfo(JSRef<JSObject>::New());
310 IndicatorModel::GetInstance()->SetDotIndicatorStyle(swiperParameters);
311 IndicatorModel::GetInstance()->SetIndicatorType(SwiperIndicatorType::DOT);
312 }
313 }
314
SetOnChange(const JSCallbackInfo & info)315 void JSIndicator::SetOnChange(const JSCallbackInfo& info)
316 {
317 if (!info[0]->IsFunction()) {
318 return;
319 }
320 auto changeHandler = AceType::MakeRefPtr<JsEventFunction<SwiperChangeEvent, 1>>(
321 JSRef<JSFunc>::Cast(info[0]), SwiperChangeEventToJSValue);
322 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
323 auto onChange = [executionContext = info.GetExecutionContext(), func = std::move(changeHandler), node = targetNode](
324 const BaseEventInfo* info) {
325 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext);
326 const auto* swiperInfo = TypeInfoHelper::DynamicCast<SwiperChangeEvent>(info);
327 if (!swiperInfo) {
328 return;
329 }
330 PipelineContext::SetCallBackNode(node);
331 func->Execute(*swiperInfo);
332 };
333
334 IndicatorModel::GetInstance()->SetOnChange(std::move(onChange));
335 }
336
SetInitialIndex(const JSCallbackInfo & info)337 void JSIndicator::SetInitialIndex(const JSCallbackInfo& info)
338 {
339 if (!info[0]->IsNumber()) {
340 IndicatorModel::GetInstance()->SetInitialIndex(0);
341 return;
342 }
343
344 auto index = std::max(0, info[0]->ToNumber<int32_t>());
345 IndicatorModel::GetInstance()->SetInitialIndex(index);
346 }
347
JSBind(BindingTarget globalObj)348 void JSIndicatorController::JSBind(BindingTarget globalObj)
349 {
350 JSClass<JSIndicatorController>::Declare("IndicatorComponentController");
351 JSClass<JSIndicatorController>::CustomMethod("showNext", &JSIndicatorController::ShowNext);
352 JSClass<JSIndicatorController>::CustomMethod("showPrevious", &JSIndicatorController::ShowPrevious);
353 JSClass<JSIndicatorController>::CustomMethod("changeIndex", &JSIndicatorController::ChangeIndex);
354
355 JSClass<JSIndicatorController>::Bind(
356 globalObj, JSIndicatorController::Constructor, JSIndicatorController::Destructor);
357 }
358
Constructor(const JSCallbackInfo & args)359 void JSIndicatorController::Constructor(const JSCallbackInfo& args)
360 {
361 auto scroller = Referenced::MakeRefPtr<JSIndicatorController>();
362 scroller->IncRefCount();
363 args.SetReturnValue(Referenced::RawPtr(scroller));
364 }
365
Destructor(JSIndicatorController * scroller)366 void JSIndicatorController::Destructor(JSIndicatorController* scroller)
367 {
368 if (scroller != nullptr) {
369 scroller->DecRefCount();
370 }
371 }
372
ChangeIndex(const JSCallbackInfo & args)373 void JSIndicatorController::ChangeIndex(const JSCallbackInfo& args)
374 {
375 if (!controller_) {
376 return;
377 }
378 if (!args[0]->IsNumber()) {
379 return;
380 }
381 bool useAnimation = false;
382 if (args.Length() > 1 && args[1]->IsBoolean()) {
383 useAnimation = args[1]->ToBoolean();
384 }
385 auto index = args[0]->ToNumber<int32_t>();
386 controller_->ChangeIndex(index, useAnimation);
387 }
388
389 } // namespace OHOS::Ace::Framework
390