• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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_richeditor.h"
17 
18 #include <optional>
19 #include <string>
20 
21 #include "base/geometry/dimension.h"
22 #include "base/geometry/ng/size_t.h"
23 #include "base/log/ace_scoring_log.h"
24 #include "bridge/common/utils/utils.h"
25 #include "bridge/declarative_frontend/engine/functions/js_click_function.h"
26 #include "bridge/declarative_frontend/engine/functions/js_function.h"
27 #include "bridge/declarative_frontend/engine/js_ref_ptr.h"
28 #include "bridge/declarative_frontend/engine/js_types.h"
29 #include "bridge/declarative_frontend/engine/jsi/jsi_types.h"
30 #include "bridge/declarative_frontend/jsview/js_container_base.h"
31 #include "bridge/declarative_frontend/jsview/js_image.h"
32 #include "bridge/declarative_frontend/jsview/js_interactable_view.h"
33 #include "bridge/declarative_frontend/jsview/js_shape_abstract.h"
34 #include "bridge/declarative_frontend/jsview/js_textfield.h"
35 #include "bridge/declarative_frontend/jsview/js_utils.h"
36 #include "bridge/declarative_frontend/jsview/js_view_abstract.h"
37 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
38 #include "bridge/declarative_frontend/jsview/models/richeditor_model_impl.h"
39 #include "core/common/resource/resource_object.h"
40 #include "core/components/text/text_theme.h"
41 #include "core/components_ng/base/view_stack_model.h"
42 #include "core/components_ng/pattern/rich_editor/rich_editor_model.h"
43 #include "core/components_ng/pattern/rich_editor/rich_editor_model_ng.h"
44 #include "core/components_ng/pattern/rich_editor/selection_info.h"
45 #include "core/components_v2/inspector/utils.h"
46 #include "frameworks/bridge/common/utils/engine_helper.h"
47 #include "frameworks/bridge/declarative_frontend/jsview/js_text.h"
48 
49 namespace OHOS::Ace {
50 std::unique_ptr<RichEditorModel> RichEditorModel::instance_ = nullptr;
51 std::mutex RichEditorModel::mutex_;
GetInstance()52 RichEditorModel* RichEditorModel::GetInstance()
53 {
54     if (!instance_) {
55         std::lock_guard<std::mutex> lock(mutex_);
56         if (!instance_) {
57 #ifdef NG_BUILD
58             instance_.reset(new NG::RichEditorModelNG());
59 #else
60             if (Container::IsCurrentUseNewPipeline()) {
61                 instance_.reset(new NG::RichEditorModelNG());
62             } else {
63                 // empty implementation
64                 instance_.reset(new Framework::RichEditorModelImpl());
65             }
66 #endif
67         }
68     }
69     return instance_.get();
70 }
71 } // namespace OHOS::Ace
72 
73 namespace OHOS::Ace::Framework {
74 
75 namespace {
76 
ParseMarginAttr(JsiRef<JSVal> marginAttr)77 std::optional<NG::MarginProperty> ParseMarginAttr(JsiRef<JSVal> marginAttr)
78 {
79     std::optional<NG::MarginProperty> marginProp = std::nullopt;
80     CalcDimension length;
81     if (!marginAttr->IsObject() && !marginAttr->IsNumber() && !marginAttr->IsString()) {
82         length.Reset();
83         marginProp = NG::ConvertToCalcPaddingProperty(length, length, length, length);
84         return marginProp;
85     }
86     if (JSViewAbstract::ParseJsDimensionVp(marginAttr, length)) {
87         marginProp = NG::ConvertToCalcPaddingProperty(length, length, length, length);
88     } else if (marginAttr->IsObject()) {
89         auto marginObj = JSRef<JSObject>::Cast(marginAttr);
90         std::optional<CalcDimension> left;
91         std::optional<CalcDimension> right;
92         std::optional<CalcDimension> top;
93         std::optional<CalcDimension> bottom;
94         JSViewAbstract::ParseMarginOrPaddingCorner(marginObj, top, bottom, left, right);
95         marginProp = NG::ConvertToCalcPaddingProperty(top, bottom, left, right);
96     }
97     return marginProp;
98 }
99 
ParseBorderRadiusAttr(JsiRef<JSVal> args)100 std::optional<NG::BorderRadiusProperty> ParseBorderRadiusAttr(JsiRef<JSVal> args)
101 {
102     std::optional<NG::BorderRadiusProperty> prop = std::nullopt;
103     CalcDimension radiusDim;
104     if (!args->IsObject() && !args->IsNumber() && !args->IsString()) {
105         radiusDim.Reset();
106         NG::BorderRadiusProperty borderRadius;
107         borderRadius.SetRadius(radiusDim);
108         borderRadius.multiValued = false;
109         prop = borderRadius;
110         return prop;
111     }
112     if (JSViewAbstract::ParseJsDimensionVp(args, radiusDim)) {
113         if (radiusDim.Unit() == DimensionUnit::PERCENT) {
114             radiusDim.Reset();
115         }
116         NG::BorderRadiusProperty borderRadius;
117         borderRadius.SetRadius(radiusDim);
118         borderRadius.multiValued = false;
119         prop = borderRadius;
120     } else if (args->IsObject()) {
121         JSRef<JSObject> object = JSRef<JSObject>::Cast(args);
122         CalcDimension topLeft;
123         CalcDimension topRight;
124         CalcDimension bottomLeft;
125         CalcDimension bottomRight;
126         JSViewAbstract::ParseAllBorderRadiuses(object, topLeft, topRight, bottomLeft, bottomRight);
127         NG::BorderRadiusProperty borderRadius;
128         borderRadius.radiusTopLeft = topLeft;
129         borderRadius.radiusTopRight = topRight;
130         borderRadius.radiusBottomLeft = bottomLeft;
131         borderRadius.radiusBottomRight = bottomRight;
132         borderRadius.multiValued = true;
133         prop = borderRadius;
134     }
135     return prop;
136 }
137 
138 } // namespace
139 
Create(const JSCallbackInfo & info)140 void JSRichEditor::Create(const JSCallbackInfo& info)
141 {
142     JSRichEditorController* jsController = nullptr;
143     if (info[0]->IsObject()) {
144         auto paramObject = JSRef<JSObject>::Cast(info[0]);
145         auto controllerObj = paramObject->GetProperty("controller");
146         if (!controllerObj->IsUndefined() && !controllerObj->IsNull()) {
147             jsController = JSRef<JSObject>::Cast(controllerObj)->Unwrap<JSRichEditorController>();
148         }
149     }
150     RichEditorModel::GetInstance()->Create();
151     RefPtr<RichEditorControllerBase> controller = RichEditorModel::GetInstance()->GetRichEditorController();
152     if (jsController) {
153         jsController->SetInstanceId(Container::CurrentId());
154         jsController->SetController(controller);
155     }
156 }
157 
SetOnReady(const JSCallbackInfo & args)158 void JSRichEditor::SetOnReady(const JSCallbackInfo& args)
159 {
160     if (!args[0]->IsFunction()) {
161         return;
162     }
163     JsEventCallback<void()> callback(args.GetExecutionContext(), JSRef<JSFunc>::Cast(args[0]));
164     RichEditorModel::GetInstance()->SetOnReady(callback);
165 }
166 
CreateJSTextStyleResult(const TextStyleResult & textStyleResult)167 JSRef<JSObject> JSRichEditor::CreateJSTextStyleResult(const TextStyleResult& textStyleResult)
168 {
169     JSRef<JSObject> textStyleObj = JSRef<JSObject>::New();
170     textStyleObj->SetProperty<std::string>("fontColor", textStyleResult.fontColor);
171     textStyleObj->SetProperty<double>("fontSize", textStyleResult.fontSize);
172     textStyleObj->SetProperty<int32_t>("fontStyle", textStyleResult.fontStyle);
173     textStyleObj->SetProperty<int32_t>("fontWeight", textStyleResult.fontWeight);
174     textStyleObj->SetProperty<std::string>("fontFamily", textStyleResult.fontFamily);
175     JSRef<JSObject> decorationObj = JSRef<JSObject>::New();
176     decorationObj->SetProperty<int32_t>("type", textStyleResult.decorationType);
177     decorationObj->SetProperty<std::string>("color", textStyleResult.decorationColor);
178     textStyleObj->SetPropertyObject("decoration", decorationObj);
179     textStyleObj->SetProperty<int32_t>("textAlign", textStyleResult.textAlign);
180     JSRef<JSArray> leadingMarginArray = JSRef<JSArray>::New();
181     leadingMarginArray->SetValueAt(0, JSRef<JSVal>::Make(ToJSValue(textStyleResult.leadingMarginSize[0])));
182     leadingMarginArray->SetValueAt(1, JSRef<JSVal>::Make(ToJSValue(textStyleResult.leadingMarginSize[1])));
183     textStyleObj->SetPropertyObject("leadingMarginSize", leadingMarginArray);
184 
185     return textStyleObj;
186 }
187 
CreateJSSymbolSpanStyleResult(const SymbolSpanStyle & symbolSpanStyle)188 JSRef<JSObject> JSRichEditor::CreateJSSymbolSpanStyleResult(const SymbolSpanStyle& symbolSpanStyle)
189 {
190     JSRef<JSObject> symbolSpanStyleObj = JSRef<JSObject>::New();
191     symbolSpanStyleObj->SetProperty<std::string>("fontColor", symbolSpanStyle.symbolColor);
192     symbolSpanStyleObj->SetProperty<double>("fontSize", symbolSpanStyle.fontSize);
193     symbolSpanStyleObj->SetProperty<int32_t>("fontWeight", symbolSpanStyle.fontWeight);
194     symbolSpanStyleObj->SetProperty<uint32_t>("renderingStrategy", symbolSpanStyle.renderingStrategy);
195     symbolSpanStyleObj->SetProperty<uint32_t>("effectStrategy", symbolSpanStyle.effectStrategy);
196 
197     return symbolSpanStyleObj;
198 }
199 
CreateJSValueResource(const RefPtr<ResourceObject> & valueResource)200 JSRef<JSObject> JSRichEditor::CreateJSValueResource(const RefPtr<ResourceObject>& valueResource)
201 {
202     JSRef<JSObject> valueResourceObj = JSRef<JSObject>::New();
203     valueResourceObj->SetProperty<std::string>("bundleName", valueResource->GetBundleName());
204     valueResourceObj->SetProperty<std::string>("moduleName", valueResource->GetModuleName());
205     valueResourceObj->SetProperty<uint32_t>("id", valueResource->GetId());
206     valueResourceObj->SetProperty<std::vector<ResourceObjectParams>>("params", valueResource->GetParams());
207     valueResourceObj->SetProperty<uint32_t>("type", valueResource->GetType());
208 
209     return valueResourceObj;
210 }
211 
CreateJSImageStyleResult(const ImageStyleResult & imageStyleResult)212 JSRef<JSObject> JSRichEditor::CreateJSImageStyleResult(const ImageStyleResult& imageStyleResult)
213 {
214     JSRef<JSObject> imageSpanStyleObj = JSRef<JSObject>::New();
215 
216     JSRef<JSArray> sizeArray = JSRef<JSArray>::New();
217     sizeArray->SetValueAt(0, JSRef<JSVal>::Make(ToJSValue(imageStyleResult.size[0])));
218     sizeArray->SetValueAt(1, JSRef<JSVal>::Make(ToJSValue(imageStyleResult.size[1])));
219     imageSpanStyleObj->SetPropertyObject("size", sizeArray);
220     imageSpanStyleObj->SetProperty<int32_t>("verticalAlign", imageStyleResult.verticalAlign);
221     imageSpanStyleObj->SetProperty<int32_t>("objectFit", imageStyleResult.objectFit);
222     imageSpanStyleObj->SetProperty<std::string>("borderRadius", imageStyleResult.borderRadius);
223     imageSpanStyleObj->SetProperty<std::string>("margin", imageStyleResult.margin);
224 
225     return imageSpanStyleObj;
226 }
227 
CreateParagraphStyleResult(const ParagraphInfo & info)228 JSRef<JSObject> JSRichEditor::CreateParagraphStyleResult(const ParagraphInfo& info)
229 {
230     auto obj = JSRef<JSObject>::New();
231     obj->SetProperty<int32_t>("textAlign", info.textAlign);
232 
233     auto lmObj = JSRef<JSObject>::New();
234     auto size = JSRef<JSArray>::New();
235     size->SetValueAt(0, JSRef<JSVal>::Make(ToJSValue(info.leadingMarginSize[0])));
236     size->SetValueAt(1, JSRef<JSVal>::Make(ToJSValue(info.leadingMarginSize[1])));
237     lmObj->SetPropertyObject("size", size);
238 #ifdef PIXEL_MAP_SUPPORTED
239     if (info.leadingMarginPixmap) {
240         lmObj->SetPropertyObject("pixelMap", ConvertPixmap(info.leadingMarginPixmap));
241     }
242 #endif
243     obj->SetPropertyObject("leadingMargin", lmObj);
244     return obj;
245 }
246 
CreateJSSpanResultObject(const ResultObject & resultObject)247 JSRef<JSObject> JSRichEditor::CreateJSSpanResultObject(const ResultObject& resultObject)
248 {
249     JSRef<JSArray> offsetArray = JSRef<JSArray>::New();
250     JSRef<JSArray> spanRangeArray = JSRef<JSArray>::New();
251     JSRef<JSObject> resultObj = JSRef<JSObject>::New();
252     JSRef<JSObject> spanPositionObj = JSRef<JSObject>::New();
253     offsetArray->SetValueAt(0, JSRef<JSVal>::Make(ToJSValue(resultObject.offsetInSpan[0])));
254     offsetArray->SetValueAt(1, JSRef<JSVal>::Make(ToJSValue(resultObject.offsetInSpan[1])));
255     spanRangeArray->SetValueAt(0, JSRef<JSVal>::Make(ToJSValue(resultObject.spanPosition.spanRange[0])));
256     spanRangeArray->SetValueAt(1, JSRef<JSVal>::Make(ToJSValue(resultObject.spanPosition.spanRange[1])));
257     spanPositionObj->SetProperty<int32_t>("spanIndex", resultObject.spanPosition.spanIndex);
258     spanPositionObj->SetPropertyObject("spanRange", spanRangeArray);
259     resultObj->SetPropertyObject("offsetInSpan", offsetArray);
260     resultObj->SetPropertyObject("spanPosition", spanPositionObj);
261     if (resultObject.type == SelectSpanType::TYPESPAN) {
262         resultObj->SetProperty<std::string>("value", resultObject.valueString);
263         resultObj->SetPropertyObject("textStyle", CreateJSTextStyleResult(resultObject.textStyle));
264     } else if (resultObject.type == SelectSpanType::TYPESYMBOLSPAN) {
265         resultObj->SetProperty<std::string>("value", resultObject.valueString);
266         resultObj->SetPropertyObject("symbolSpanStyle", CreateJSSymbolSpanStyleResult(resultObject.symbolSpanStyle));
267         resultObj->SetPropertyObject("valueResource", CreateJSValueResource(resultObject.valueResource));
268     } else if (resultObject.type == SelectSpanType::TYPEIMAGE) {
269         if (resultObject.valuePixelMap) {
270 #ifdef PIXEL_MAP_SUPPORTED
271             auto jsPixmap = ConvertPixmap(resultObject.valuePixelMap);
272             if (!jsPixmap->IsUndefined()) {
273                 resultObj->SetPropertyObject("valuePixelMap", jsPixmap);
274             }
275 #endif
276         } else {
277             resultObj->SetProperty<std::string>("valueResourceStr", resultObject.valueString);
278         }
279         resultObj->SetPropertyObject("imageStyle", CreateJSImageStyleResult(resultObject.imageStyle));
280     }
281 
282     return resultObj;
283 }
284 
CreateJSSelection(const SelectionInfo & selectInfo)285 JSRef<JSVal> JSRichEditor::CreateJSSelection(const SelectionInfo& selectInfo)
286 {
287     uint32_t idx = 0;
288 
289     JSRef<JSArray> selectionArray = JSRef<JSArray>::New();
290     JSRef<JSArray> spanObjectArray = JSRef<JSArray>::New();
291     JSRef<JSObject> selectionObject = JSRef<JSObject>::New();
292 
293     const std::list<ResultObject>& spanObjectList = selectInfo.GetSelection().resultObjects;
294     for (const ResultObject& spanObject : spanObjectList) {
295         spanObjectArray->SetValueAt(idx++, CreateJSSpanResultObject(spanObject));
296     }
297 
298     selectionArray->SetValueAt(0, JSRef<JSVal>::Make(ToJSValue(selectInfo.GetSelection().selection[0])));
299     selectionArray->SetValueAt(1, JSRef<JSVal>::Make(ToJSValue(selectInfo.GetSelection().selection[1])));
300 
301     selectionObject->SetPropertyObject("selection", selectionArray);
302     selectionObject->SetPropertyObject("spans", spanObjectArray);
303     return JSRef<JSVal>::Cast(selectionObject);
304 }
305 
SetOnSelect(const JSCallbackInfo & args)306 void JSRichEditor::SetOnSelect(const JSCallbackInfo& args)
307 {
308     if (!args[0]->IsFunction()) {
309         return;
310     }
311     auto jsSelectFunc =
312         AceType::MakeRefPtr<JsEventFunction<SelectionInfo, 1>>(JSRef<JSFunc>::Cast(args[0]), CreateJSSelection);
313     auto onSelect = [execCtx = args.GetExecutionContext(), func = std::move(jsSelectFunc)](const BaseEventInfo* info) {
314         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
315         const auto* eventInfo = TypeInfoHelper::DynamicCast<SelectionInfo>(info);
316         func->Execute(*eventInfo);
317     };
318     NG::RichEditorModelNG::GetInstance()->SetOnSelect(std::move(onSelect));
319 }
320 
CreateJSSelectionRange(const SelectionRangeInfo & selectRange)321 JSRef<JSVal> JSRichEditor::CreateJSSelectionRange(const SelectionRangeInfo& selectRange)
322 {
323     JSRef<JSObject> selectionRangeObject = JSRef<JSObject>::New();
324 
325     JSRef<JSVal> start = JSRef<JSVal>::Make(ToJSValue(selectRange.start_));
326     JSRef<JSVal> end = JSRef<JSVal>::Make(ToJSValue(selectRange.end_));
327 
328     selectionRangeObject->SetPropertyObject("start", start);
329     selectionRangeObject->SetPropertyObject("end", end);
330     return JSRef<JSVal>::Cast(selectionRangeObject);
331 }
332 
SetOnSelectionChange(const JSCallbackInfo & args)333 void JSRichEditor::SetOnSelectionChange(const JSCallbackInfo& args)
334 {
335     if (args.Length() < 1 || !args[0]->IsFunction()) {
336         return;
337     }
338     auto jsSelectFunc =
339         AceType::MakeRefPtr<JsEventFunction<SelectionRangeInfo, 1>>(JSRef<JSFunc>::Cast(args[0]),
340         CreateJSSelectionRange);
341     auto onSelectionChange =
342         [execCtx = args.GetExecutionContext(), func = std::move(jsSelectFunc)](const BaseEventInfo* info) {
343         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
344         const auto* eventInfo = TypeInfoHelper::DynamicCast<SelectionRangeInfo>(info);
345         func->Execute(*eventInfo);
346     };
347     NG::RichEditorModelNG::GetInstance()->SetOnSelectionChange(std::move(onSelectionChange));
348 }
349 
SetAboutToIMEInput(const JSCallbackInfo & args)350 void JSRichEditor::SetAboutToIMEInput(const JSCallbackInfo& args)
351 {
352     if (!args[0]->IsFunction()) {
353         return;
354     }
355     auto jsAboutToIMEInputFunc = AceType::MakeRefPtr<JsEventFunction<NG::RichEditorInsertValue, 1>>(
356         JSRef<JSFunc>::Cast(args[0]), CreateJsAboutToIMEInputObj);
357     auto callback = [execCtx = args.GetExecutionContext(), func = std::move(jsAboutToIMEInputFunc)](
358                         const NG::RichEditorInsertValue& insertValue) -> bool {
359         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, true);
360         auto ret = func->ExecuteWithValue(insertValue);
361         if (ret->IsBoolean()) {
362             return ret->ToBoolean();
363         }
364         return true;
365     };
366     RichEditorModel::GetInstance()->SetAboutToIMEInput(std::move(callback));
367 }
368 
SetOnIMEInputComplete(const JSCallbackInfo & args)369 void JSRichEditor::SetOnIMEInputComplete(const JSCallbackInfo& args)
370 {
371     if (!args[0]->IsFunction()) {
372         return;
373     }
374     auto jsOnIMEInputCompleteFunc = AceType::MakeRefPtr<JsEventFunction<NG::RichEditorAbstractSpanResult, 1>>(
375         JSRef<JSFunc>::Cast(args[0]), CreateJsOnIMEInputComplete);
376     auto callback = [execCtx = args.GetExecutionContext(), func = std::move(jsOnIMEInputCompleteFunc)](
377                         const NG::RichEditorAbstractSpanResult& textSpanResult) {
378         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
379         func->Execute(textSpanResult);
380     };
381     RichEditorModel::GetInstance()->SetOnIMEInputComplete(std::move(callback));
382 }
SetAboutToDelete(const JSCallbackInfo & args)383 void JSRichEditor::SetAboutToDelete(const JSCallbackInfo& args)
384 {
385     if (!args[0]->IsFunction()) {
386         return;
387     }
388     auto jsAboutToDeleteFunc = AceType::MakeRefPtr<JsEventFunction<NG::RichEditorDeleteValue, 1>>(
389         JSRef<JSFunc>::Cast(args[0]), CreateJsAboutToDelet);
390     auto callback = [execCtx = args.GetExecutionContext(), func = std::move(jsAboutToDeleteFunc)](
391                         const NG::RichEditorDeleteValue& deleteValue) -> bool {
392         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, true);
393         auto ret = func->ExecuteWithValue(deleteValue);
394         if (ret->IsBoolean()) {
395             return ret->ToBoolean();
396         }
397         return true;
398     };
399     RichEditorModel::GetInstance()->SetAboutToDelete(std::move(callback));
400 }
401 
SetOnDeleteComplete(const JSCallbackInfo & args)402 void JSRichEditor::SetOnDeleteComplete(const JSCallbackInfo& args)
403 {
404     if (!args[0]->IsFunction()) {
405         return;
406     }
407     JsEventCallback<void()> callback(args.GetExecutionContext(), JSRef<JSFunc>::Cast(args[0]));
408     RichEditorModel::GetInstance()->SetOnDeleteComplete(callback);
409 }
410 
SetCustomKeyboard(const JSCallbackInfo & args)411 void JSRichEditor::SetCustomKeyboard(const JSCallbackInfo& args)
412 {
413     if (args.Length() > 0 && (args[0]->IsUndefined() || args[0]->IsNull())) {
414         RichEditorModel::GetInstance()->SetCustomKeyboard(nullptr);
415         return;
416     }
417     if (!args[0]->IsObject()) {
418         return;
419     }
420     std::function<void()> buildFunc;
421     if (JSTextField::ParseJsCustomKeyboardBuilder(args, 0, buildFunc)) {
422         RichEditorModel::GetInstance()->SetCustomKeyboard(std::move(buildFunc));
423     }
424 }
425 
CreateJsAboutToIMEInputObj(const NG::RichEditorInsertValue & insertValue)426 JSRef<JSVal> JSRichEditor::CreateJsAboutToIMEInputObj(const NG::RichEditorInsertValue& insertValue)
427 {
428     JSRef<JSObject> aboutToIMEInputObj = JSRef<JSObject>::New();
429     aboutToIMEInputObj->SetProperty<int32_t>("insertOffset", insertValue.GetInsertOffset());
430     aboutToIMEInputObj->SetProperty<std::string>("insertValue", insertValue.GetInsertValue());
431     return JSRef<JSVal>::Cast(aboutToIMEInputObj);
432 }
433 
CreateJsOnIMEInputComplete(const NG::RichEditorAbstractSpanResult & textSpanResult)434 JSRef<JSVal> JSRichEditor::CreateJsOnIMEInputComplete(const NG::RichEditorAbstractSpanResult& textSpanResult)
435 {
436     JSRef<JSObject> onIMEInputCompleteObj = JSRef<JSObject>::New();
437     JSRef<JSObject> spanPositionObj = JSRef<JSObject>::New();
438     JSRef<JSArray> spanRange = JSRef<JSArray>::New();
439     JSRef<JSObject> textStyleObj = JSRef<JSObject>::New();
440     JSRef<JSObject> decorationObj = JSRef<JSObject>::New();
441     JSRef<JSArray> offsetInSpan = JSRef<JSArray>::New();
442     spanRange->SetValueAt(0, JSRef<JSVal>::Make(ToJSValue(textSpanResult.GetSpanRangeStart())));
443     spanRange->SetValueAt(1, JSRef<JSVal>::Make(ToJSValue(textSpanResult.GetSpanRangeEnd())));
444     offsetInSpan->SetValueAt(0, JSRef<JSVal>::Make(ToJSValue(textSpanResult.OffsetInSpan())));
445     offsetInSpan->SetValueAt(
446         1, JSRef<JSVal>::Make(ToJSValue(textSpanResult.OffsetInSpan() + textSpanResult.GetEraseLength())));
447     spanPositionObj->SetPropertyObject("spanRange", spanRange);
448     spanPositionObj->SetProperty<int32_t>("spanIndex", textSpanResult.GetSpanIndex());
449     decorationObj->SetProperty<TextDecoration>("type", textSpanResult.GetTextDecoration());
450     decorationObj->SetProperty<std::string>("color", textSpanResult.GetColor());
451     textStyleObj->SetProperty<std::string>("fontColor", textSpanResult.GetFontColor());
452     textStyleObj->SetProperty<double>("fontSize", textSpanResult.GetFontSize());
453     textStyleObj->SetProperty<int32_t>("fontStyle", static_cast<int32_t>(textSpanResult.GetFontStyle()));
454     textStyleObj->SetProperty<int32_t>("fontWeight", textSpanResult.GetFontWeight());
455     textStyleObj->SetProperty<std::string>("fontFamily", textSpanResult.GetFontFamily());
456     textStyleObj->SetPropertyObject("decoration", decorationObj);
457     onIMEInputCompleteObj->SetPropertyObject("spanPosition", spanPositionObj);
458     onIMEInputCompleteObj->SetProperty<std::string>("value", textSpanResult.GetValue());
459     onIMEInputCompleteObj->SetPropertyObject("textStyle", textStyleObj);
460     onIMEInputCompleteObj->SetPropertyObject("offsetInSpan", offsetInSpan);
461     return JSRef<JSVal>::Cast(onIMEInputCompleteObj);
462 }
463 
CreateJsAboutToDelet(const NG::RichEditorDeleteValue & deleteValue)464 JSRef<JSVal> JSRichEditor::CreateJsAboutToDelet(const NG::RichEditorDeleteValue& deleteValue)
465 {
466     JSRef<JSObject> AboutToDeletObj = JSRef<JSObject>::New();
467     AboutToDeletObj->SetProperty<int32_t>("offset", deleteValue.GetOffset());
468     AboutToDeletObj->SetProperty<int32_t>(
469         "direction", static_cast<int32_t>(deleteValue.GetRichEditorDeleteDirection()));
470     AboutToDeletObj->SetProperty<int32_t>("length", deleteValue.GetLength());
471     JSRef<JSArray> richEditorDeleteSpans = JSRef<JSArray>::New();
472     auto list = deleteValue.GetRichEditorDeleteSpans();
473     int32_t index = 0;
474     for (const auto& it : list) {
475         JSRef<JSObject> spanResultObj = JSRef<JSObject>::New();
476         JSRef<JSObject> spanPositionObj = JSRef<JSObject>::New();
477         JSRef<JSArray> spanRange = JSRef<JSArray>::New();
478         JSRef<JSArray> offsetInSpan = JSRef<JSArray>::New();
479         spanRange->SetValueAt(0, JSRef<JSVal>::Make(ToJSValue(it.GetSpanRangeStart())));
480         spanRange->SetValueAt(1, JSRef<JSVal>::Make(ToJSValue(it.GetSpanRangeEnd())));
481         offsetInSpan->SetValueAt(0, JSRef<JSVal>::Make(ToJSValue(it.OffsetInSpan())));
482         offsetInSpan->SetValueAt(1, JSRef<JSVal>::Make(ToJSValue(it.OffsetInSpan() + it.GetEraseLength())));
483         spanPositionObj->SetPropertyObject("spanRange", spanRange);
484         spanPositionObj->SetProperty<int32_t>("spanIndex", it.GetSpanIndex());
485         spanResultObj->SetPropertyObject("spanPosition", spanPositionObj);
486         spanResultObj->SetPropertyObject("offsetInSpan", offsetInSpan);
487         switch (it.GetType()) {
488             case NG::SpanResultType::TEXT: {
489                 JSRef<JSObject> textStyleObj = JSRef<JSObject>::New();
490                 CreateTextStyleObj(textStyleObj, it);
491                 spanResultObj->SetProperty<std::string>("value", it.GetValue());
492                 spanResultObj->SetPropertyObject("textStyle", textStyleObj);
493                 break;
494             }
495             case NG::SpanResultType::IMAGE: {
496                 JSRef<JSObject> imageStyleObj = JSRef<JSObject>::New();
497                 CreateImageStyleObj(imageStyleObj, spanResultObj, it);
498                 spanResultObj->SetPropertyObject("imageStyle", imageStyleObj);
499                 break;
500             }
501             default:
502                 break;
503         }
504         richEditorDeleteSpans->SetValueAt(index++, spanResultObj);
505     }
506     AboutToDeletObj->SetPropertyObject("richEditorDeleteSpans", richEditorDeleteSpans);
507     return JSRef<JSVal>::Cast(AboutToDeletObj);
508 }
509 
CreateTextStyleObj(JSRef<JSObject> & textStyleObj,const NG::RichEditorAbstractSpanResult & spanResult)510 void JSRichEditor::CreateTextStyleObj(JSRef<JSObject>& textStyleObj, const NG::RichEditorAbstractSpanResult& spanResult)
511 {
512     JSRef<JSObject> decorationObj = JSRef<JSObject>::New();
513     decorationObj->SetProperty<int32_t>("type", (int32_t)(spanResult.GetTextDecoration()));
514     decorationObj->SetProperty<std::string>("color", spanResult.GetColor());
515     textStyleObj->SetProperty<std::string>("fontColor", spanResult.GetFontColor());
516     textStyleObj->SetProperty<double>("fontSize", spanResult.GetFontSize());
517     textStyleObj->SetProperty<int32_t>("fontStyle", static_cast<int32_t>(spanResult.GetFontStyle()));
518     textStyleObj->SetProperty<int32_t>("fontWeight", spanResult.GetFontWeight());
519     textStyleObj->SetProperty<std::string>("fontFamily", spanResult.GetFontFamily());
520     textStyleObj->SetPropertyObject("decoration", decorationObj);
521 }
522 
CreateImageStyleObj(JSRef<JSObject> & imageStyleObj,JSRef<JSObject> & spanResultObj,const NG::RichEditorAbstractSpanResult & spanResult)523 void JSRichEditor::CreateImageStyleObj(
524     JSRef<JSObject>& imageStyleObj, JSRef<JSObject>& spanResultObj, const NG::RichEditorAbstractSpanResult& spanResult)
525 {
526     JSRef<JSArray> imageSize = JSRef<JSArray>::New();
527     imageSize->SetValueAt(0, JSRef<JSVal>::Make(ToJSValue(spanResult.GetSizeWidth())));
528     imageSize->SetValueAt(1, JSRef<JSVal>::Make(ToJSValue(spanResult.GetSizeHeight())));
529     imageStyleObj->SetPropertyObject("size", imageSize);
530     imageStyleObj->SetProperty<int32_t>("verticalAlign", static_cast<int32_t>(spanResult.GetVerticalAlign()));
531     imageStyleObj->SetProperty<int32_t>("objectFit", static_cast<int32_t>(spanResult.GetObjectFit()));
532     if (spanResult.GetValuePixelMap()) {
533 #ifdef PIXEL_MAP_SUPPORTED
534         auto jsPixmap = ConvertPixmap(spanResult.GetValuePixelMap());
535         if (!jsPixmap->IsUndefined()) {
536             spanResultObj->SetPropertyObject("value", jsPixmap);
537         }
538 #endif
539     } else {
540         spanResultObj->SetProperty<std::string>("valueResourceStr", spanResult.GetValueResourceStr());
541     }
542 }
543 
JsClip(const JSCallbackInfo & info)544 void JSRichEditor::JsClip(const JSCallbackInfo& info)
545 {
546     if (info[0]->IsUndefined()) {
547         ViewAbstractModel::GetInstance()->SetClipEdge(true);
548         return;
549     }
550     if (info[0]->IsObject()) {
551         JSShapeAbstract* clipShape = JSRef<JSObject>::Cast(info[0])->Unwrap<JSShapeAbstract>();
552         if (clipShape == nullptr) {
553             return;
554         }
555         ViewAbstractModel::GetInstance()->SetClipShape(clipShape->GetBasicShape());
556     } else if (info[0]->IsBoolean()) {
557         ViewAbstractModel::GetInstance()->SetClipEdge(info[0]->ToBoolean());
558     }
559 }
560 
JsFocusable(const JSCallbackInfo & info)561 void JSRichEditor::JsFocusable(const JSCallbackInfo& info)
562 {
563     if (info.Length() != 1 || !info[0]->IsBoolean()) {
564         return;
565     }
566     JSInteractableView::SetFocusable(info[0]->ToBoolean());
567     JSInteractableView::SetFocusNode(false);
568 }
569 
SetCopyOptions(const JSCallbackInfo & info)570 void JSRichEditor::SetCopyOptions(const JSCallbackInfo& info)
571 {
572     if (info.Length() == 0) {
573         return;
574     }
575     auto copyOptions = CopyOptions::Distributed;
576     auto tmpInfo = info[0];
577     if (tmpInfo->IsNumber()) {
578         auto emunNumber = tmpInfo->ToNumber<int>();
579         copyOptions = static_cast<CopyOptions>(emunNumber);
580     }
581     RichEditorModel::GetInstance()->SetCopyOption(copyOptions);
582 }
583 
BindSelectionMenu(const JSCallbackInfo & info)584 void JSRichEditor::BindSelectionMenu(const JSCallbackInfo& info)
585 {
586     NG::TextSpanType editorType = NG::TextSpanType::NONE;
587     if (info.Length() >= 1 && info[0]->IsUndefined()) {
588         editorType = NG::TextSpanType::TEXT;
589     }
590     if (info.Length() >= 1 && info[0]->IsNumber()) {
591         auto spanType = info[0]->ToNumber<int32_t>();
592         editorType = static_cast<NG::TextSpanType>(spanType);
593     }
594 
595     // Builder
596     if (info.Length() < 2 || !info[1]->IsObject()) {
597         return;
598     }
599 
600     JSRef<JSObject> menuObj = JSRef<JSObject>::Cast(info[1]);
601     auto builder = menuObj->GetProperty("builder");
602     if (!builder->IsFunction()) {
603         return;
604     }
605     auto builderFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSFunc>::Cast(builder));
606     CHECK_NULL_VOID(builderFunc);
607 
608     // responseType
609     NG::TextResponseType responseType = NG::TextResponseType::LONG_PRESS;
610     if (info.Length() >= 3 && info[2]->IsNumber()) {
611         auto response = info[2]->ToNumber<int32_t>();
612         responseType = static_cast<NG::TextResponseType>(response);
613     }
614     std::function<void()> buildFunc = [execCtx = info.GetExecutionContext(), func = std::move(builderFunc)]() {
615         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
616         ACE_SCORING_EVENT("BindSelectionMenu");
617         func->Execute();
618     };
619     NG::SelectMenuParam menuParam;
620     int32_t requiredParamCount = 3;
621     if (info.Length() > requiredParamCount && info[requiredParamCount]->IsObject()) {
622         JSText::ParseMenuParam(info, info[requiredParamCount], menuParam);
623     }
624     RichEditorModel::GetInstance()->BindSelectionMenu(editorType, responseType, buildFunc, menuParam);
625 }
626 
CreateJSTextCommonEvent(NG::TextCommonEvent & event)627 JSRef<JSVal> JSRichEditor::CreateJSTextCommonEvent(NG::TextCommonEvent& event)
628 {
629     JSRef<JSObjTemplate> objectTemplate = JSRef<JSObjTemplate>::New();
630     objectTemplate->SetInternalFieldCount(1);
631     JSRef<JSObject> object = objectTemplate->NewInstance();
632     object->SetPropertyObject("preventDefault", JSRef<JSFunc>::New<FunctionCallback>(JsPreventDefault));
633     object->Wrap<NG::TextCommonEvent>(&event);
634     return JSRef<JSVal>::Cast(object);
635 }
636 
SetOnPaste(const JSCallbackInfo & info)637 void JSRichEditor::SetOnPaste(const JSCallbackInfo& info)
638 {
639     CHECK_NULL_VOID(info[0]->IsFunction());
640     auto jsTextFunc = AceType::MakeRefPtr<JsCitedEventFunction<NG::TextCommonEvent, 1>>(
641         JSRef<JSFunc>::Cast(info[0]), CreateJSTextCommonEvent);
642     WeakPtr<NG::FrameNode> targetNode = NG::ViewStackProcessor::GetInstance()->GetMainFrameNode();
643     auto onPaste = [execCtx = info.GetExecutionContext(), func = std::move(jsTextFunc), node = targetNode](
644                        NG::TextCommonEvent& info) {
645         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
646         ACE_SCORING_EVENT("onPaste");
647         PipelineContext::SetCallBackNode(node);
648         func->Execute(info);
649     };
650     RichEditorModel::GetInstance()->SetOnPaste(std::move(onPaste));
651 }
652 
JsEnableDataDetector(const JSCallbackInfo & info)653 void JSRichEditor::JsEnableDataDetector(const JSCallbackInfo& info)
654 {
655     if (info.Length() < 1) {
656         return;
657     }
658     auto tmpInfo = info[0];
659     if (!tmpInfo->IsBoolean()) {
660         RichEditorModel::GetInstance()->SetTextDetectEnable(false);
661         return;
662     }
663     auto enable = tmpInfo->ToBoolean();
664     RichEditorModel::GetInstance()->SetTextDetectEnable(enable);
665 }
666 
JsDataDetectorConfig(const JSCallbackInfo & info)667 void JSRichEditor::JsDataDetectorConfig(const JSCallbackInfo& info)
668 {
669     if (info.Length() < 1) {
670         return;
671     }
672     if (!info[0]->IsObject()) {
673         return;
674     }
675 
676     std::string textTypes;
677     std::function<void(const std::string&)> onResult;
678     if (!ParseDataDetectorConfig(info, textTypes, onResult)) {
679         return;
680     }
681     RichEditorModel::GetInstance()->SetTextDetectConfig(textTypes, std::move(onResult));
682 }
683 
JSBind(BindingTarget globalObj)684 void JSRichEditor::JSBind(BindingTarget globalObj)
685 {
686     JSClass<JSRichEditor>::Declare("RichEditor");
687     JSClass<JSRichEditor>::StaticMethod("create", &JSRichEditor::Create);
688     JSClass<JSRichEditor>::StaticMethod("onReady", &JSRichEditor::SetOnReady);
689     JSClass<JSRichEditor>::StaticMethod("onSelect", &JSRichEditor::SetOnSelect);
690     JSClass<JSRichEditor>::StaticMethod("onSelectionChange", &JSRichEditor::SetOnSelectionChange);
691     JSClass<JSRichEditor>::StaticMethod("aboutToIMEInput", &JSRichEditor::SetAboutToIMEInput);
692     JSClass<JSRichEditor>::StaticMethod("onIMEInputComplete", &JSRichEditor::SetOnIMEInputComplete);
693     JSClass<JSRichEditor>::StaticMethod("aboutToDelete", &JSRichEditor::SetAboutToDelete);
694     JSClass<JSRichEditor>::StaticMethod("onDeleteComplete", &JSRichEditor::SetOnDeleteComplete);
695     JSClass<JSRichEditor>::StaticMethod("customKeyboard", &JSRichEditor::SetCustomKeyboard);
696     JSClass<JSRichEditor>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
697     JSClass<JSRichEditor>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
698     JSClass<JSRichEditor>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
699     JSClass<JSRichEditor>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
700     JSClass<JSRichEditor>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
701     JSClass<JSRichEditor>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
702     JSClass<JSRichEditor>::StaticMethod("clip", &JSRichEditor::JsClip);
703     JSClass<JSRichEditor>::StaticMethod("focusable", &JSRichEditor::JsFocusable);
704     JSClass<JSRichEditor>::StaticMethod("copyOptions", &JSRichEditor::SetCopyOptions);
705     JSClass<JSRichEditor>::StaticMethod("bindSelectionMenu", &JSRichEditor::BindSelectionMenu);
706     JSClass<JSRichEditor>::StaticMethod("onPaste", &JSRichEditor::SetOnPaste);
707     JSClass<JSRichEditor>::StaticMethod("enableDataDetector", &JSRichEditor::JsEnableDataDetector);
708     JSClass<JSRichEditor>::StaticMethod("dataDetectorConfig", &JSRichEditor::JsDataDetectorConfig);
709     JSClass<JSRichEditor>::InheritAndBind<JSViewAbstract>(globalObj);
710 }
711 
ParseJsImageSpanAttribute(JSRef<JSObject> imageAttribute)712 ImageSpanAttribute JSRichEditorController::ParseJsImageSpanAttribute(JSRef<JSObject> imageAttribute)
713 {
714     ContainerScope scope(instanceId_ < 0 ? Container::CurrentId() : instanceId_);
715     ImageSpanAttribute imageStyle;
716     auto sizeObj = imageAttribute->GetProperty("size");
717     if (sizeObj->IsArray()) {
718         ImageSpanSize imageSize;
719         JSRef<JSArray> size = JSRef<JSArray>::Cast(sizeObj);
720         JSRef<JSVal> width = size->GetValueAt(0);
721         CalcDimension imageSpanWidth;
722         if (!width->IsNull() && JSContainerBase::ParseJsDimensionVp(width, imageSpanWidth)) {
723             imageSize.width = imageSpanWidth;
724             updateSpanStyle_.updateImageWidth = imageSpanWidth;
725         }
726         JSRef<JSVal> height = size->GetValueAt(1);
727         CalcDimension imageSpanHeight;
728         if (!height->IsNull() && JSContainerBase::ParseJsDimensionVp(height, imageSpanHeight)) {
729             imageSize.height = imageSpanHeight;
730             updateSpanStyle_.updateImageHeight = imageSpanHeight;
731         }
732         imageStyle.size = imageSize;
733     }
734     JSRef<JSVal> verticalAlign = imageAttribute->GetProperty("verticalAlign");
735     if (!verticalAlign->IsNull()) {
736         auto align = static_cast<VerticalAlign>(verticalAlign->ToNumber<int32_t>());
737         if (align < VerticalAlign::TOP || align > VerticalAlign::NONE) {
738             align = VerticalAlign::BOTTOM;
739         }
740         imageStyle.verticalAlign = align;
741         updateSpanStyle_.updateImageVerticalAlign = align;
742     }
743     JSRef<JSVal> objectFit = imageAttribute->GetProperty("objectFit");
744     if (!objectFit->IsNull() && objectFit->IsNumber()) {
745         auto fit = static_cast<ImageFit>(objectFit->ToNumber<int32_t>());
746         if (fit < ImageFit::FILL || fit > ImageFit::SCALE_DOWN) {
747             fit = ImageFit::COVER;
748         }
749         imageStyle.objectFit = fit;
750         updateSpanStyle_.updateImageFit = fit;
751     } else {
752         imageStyle.objectFit = ImageFit::COVER;
753     }
754     auto layoutStyleObj = imageAttribute->GetProperty("layoutStyle");
755     auto layoutStyleObject = JSRef<JSObject>::Cast(layoutStyleObj);
756     if (!layoutStyleObject->IsUndefined()) {
757         auto marginAttr = layoutStyleObject->GetProperty("margin");
758         imageStyle.marginProp = ParseMarginAttr(marginAttr);
759         updateSpanStyle_.marginProp = imageStyle.marginProp;
760         auto borderRadiusAttr = layoutStyleObject->GetProperty("borderRadius");
761         imageStyle.borderRadius = ParseBorderRadiusAttr(borderRadiusAttr);
762         updateSpanStyle_.borderRadius = imageStyle.borderRadius;
763     }
764     return imageStyle;
765 }
766 
ParseJsTextStyle(const JSRef<JSObject> & styleObject,TextStyle & style,struct UpdateSpanStyle & updateSpanStyle)767 void JSRichEditorController::ParseJsTextStyle(
768     const JSRef<JSObject>& styleObject, TextStyle& style, struct UpdateSpanStyle& updateSpanStyle)
769 {
770     ContainerScope scope(instanceId_ < 0 ? Container::CurrentId() : instanceId_);
771     JSRef<JSVal> fontColor = styleObject->GetProperty("fontColor");
772     Color textColor;
773     if (!fontColor->IsNull() && JSContainerBase::ParseJsColor(fontColor, textColor)) {
774         updateSpanStyle.updateTextColor = textColor;
775         style.SetTextColor(textColor);
776         updateSpanStyle.hasResourceFontColor = fontColor->IsObject();
777     }
778     JSRef<JSVal> fontSize = styleObject->GetProperty("fontSize");
779     CalcDimension size;
780     if (!fontSize->IsNull() && JSContainerBase::ParseJsDimensionFpNG(fontSize, size) &&
781         !size.IsNegative() && size.Unit() != DimensionUnit::PERCENT) {
782         updateSpanStyle.updateFontSize = size;
783         style.SetFontSize(size);
784     } else if (size.IsNegative() || size.Unit() == DimensionUnit::PERCENT) {
785         auto theme = JSContainerBase::GetTheme<TextTheme>();
786         CHECK_NULL_VOID(theme);
787         size = theme->GetTextStyle().GetFontSize();
788         updateSpanStyle.updateFontSize = size;
789         style.SetFontSize(size);
790     }
791     JSRef<JSVal> fontStyle = styleObject->GetProperty("fontStyle");
792     if (!fontStyle->IsNull() && fontStyle->IsNumber()) {
793         updateSpanStyle.updateItalicFontStyle = static_cast<FontStyle>(fontStyle->ToNumber<int32_t>());
794         style.SetFontStyle(static_cast<FontStyle>(fontStyle->ToNumber<int32_t>()));
795     }
796     JSRef<JSVal> fontWeight = styleObject->GetProperty("fontWeight");
797     std::string weight;
798     if (!fontWeight->IsNull() && (fontWeight->IsNumber() || JSContainerBase::ParseJsString(fontWeight, weight))) {
799         if (fontWeight->IsNumber()) {
800             weight = std::to_string(fontWeight->ToNumber<int32_t>());
801         }
802         updateSpanStyle.updateFontWeight = ConvertStrToFontWeight(weight);
803         style.SetFontWeight(ConvertStrToFontWeight(weight));
804     }
805     JSRef<JSVal> fontFamily = styleObject->GetProperty("fontFamily");
806     std::vector<std::string> family;
807     if (!fontFamily->IsNull() && JSContainerBase::ParseJsFontFamilies(fontFamily, family)) {
808         updateSpanStyle.updateFontFamily = family;
809         style.SetFontFamilies(family);
810     }
811     ParseTextDecoration(styleObject, style, updateSpanStyle);
812     ParseTextShadow(styleObject, style, updateSpanStyle);
813 }
814 
ParseJsSymbolSpanStyle(const JSRef<JSObject> & styleObject,TextStyle & style,struct UpdateSpanStyle & updateSpanStyle)815 void JSRichEditorController::ParseJsSymbolSpanStyle(
816     const JSRef<JSObject>& styleObject, TextStyle& style, struct UpdateSpanStyle& updateSpanStyle)
817 {
818     ContainerScope scope(instanceId_ < 0 ? Container::CurrentId() : instanceId_);
819     updateSpanStyle.isSymbolStyle = true;
820     JSRef<JSVal> fontColor = styleObject->GetProperty("fontColor");
821     std::vector<Color> symbolColor;
822     if (!fontColor->IsNull() && JSContainerBase::ParseJsSymbolColor(fontColor, symbolColor)) {
823         updateSpanStyle.updateSymbolColor = symbolColor;
824         style.SetSymbolColorList(symbolColor);
825         updateSpanStyle.hasResourceFontColor = fontColor->IsObject();
826     }
827     JSRef<JSVal> fontSize = styleObject->GetProperty("fontSize");
828     CalcDimension size;
829     if (!fontSize->IsNull() && JSContainerBase::ParseJsDimensionFpNG(fontSize, size, false) &&
830         !size.IsNegative() && size.Unit() != DimensionUnit::PERCENT) {
831         updateSpanStyle.updateFontSize = size;
832         style.SetFontSize(size);
833     } else if (size.IsNegative() || size.Unit() == DimensionUnit::PERCENT) {
834         auto theme = JSContainerBase::GetTheme<TextTheme>();
835         CHECK_NULL_VOID(theme);
836         size = theme->GetTextStyle().GetFontSize();
837         updateSpanStyle.updateFontSize = size;
838         style.SetFontSize(size);
839     }
840     JSRef<JSVal> fontWeight = styleObject->GetProperty("fontWeight");
841     std::string weight;
842     if (!fontWeight->IsNull() && (fontWeight->IsNumber() || JSContainerBase::ParseJsString(fontWeight, weight))) {
843         if (fontWeight->IsNumber()) {
844             weight = std::to_string(fontWeight->ToNumber<int32_t>());
845         }
846         updateSpanStyle.updateFontWeight = ConvertStrToFontWeight(weight);
847         style.SetFontWeight(ConvertStrToFontWeight(weight));
848     }
849     JSRef<JSVal> renderingStrategy = styleObject->GetProperty("renderingStrategy");
850     uint32_t symbolRenderStrategy;
851     if (!renderingStrategy->IsNull() && JSContainerBase::ParseJsInteger(renderingStrategy, symbolRenderStrategy)) {
852         updateSpanStyle.updateSymbolRenderingStrategy = symbolRenderStrategy;
853         style.SetRenderStrategy(symbolRenderStrategy);
854     }
855     JSRef<JSVal> effectStrategy = styleObject->GetProperty("effectStrategy");
856     uint32_t symbolEffectStrategy;
857     if (!effectStrategy->IsNull() && JSContainerBase::ParseJsInteger(effectStrategy, symbolEffectStrategy)) {
858         updateSpanStyle.updateSymbolEffectStrategy = symbolEffectStrategy;
859         style.SetEffectStrategy(symbolEffectStrategy);
860     }
861 }
862 
ParseTextShadow(const JSRef<JSObject> & styleObject,TextStyle & style,struct UpdateSpanStyle & updateSpanStyle)863 void JSRichEditorController::ParseTextShadow(
864     const JSRef<JSObject>& styleObject, TextStyle& style, struct UpdateSpanStyle& updateSpanStyle)
865 {
866     ContainerScope scope(instanceId_ < 0 ? Container::CurrentId() : instanceId_);
867     auto shadowObject = styleObject->GetProperty("textShadow");
868     if (shadowObject->IsNull()) {
869         return;
870     }
871     std::vector<Shadow> shadows;
872     ParseTextShadowFromShadowObject(shadowObject, shadows);
873     if (!shadows.empty()) {
874         updateSpanStyle.updateTextShadows = shadows;
875         style.SetTextShadows(shadows);
876     }
877 }
878 
ParseTextDecoration(const JSRef<JSObject> & styleObject,TextStyle & style,struct UpdateSpanStyle & updateSpanStyle)879 void JSRichEditorController::ParseTextDecoration(
880     const JSRef<JSObject>& styleObject, TextStyle& style, struct UpdateSpanStyle& updateSpanStyle)
881 {
882     ContainerScope scope(instanceId_ < 0 ? Container::CurrentId() : instanceId_);
883     auto decorationObj = styleObject->GetProperty("decoration");
884     JSRef<JSObject> decorationObject = JSRef<JSObject>::Cast(decorationObj);
885     if (!decorationObject->IsUndefined()) {
886         JSRef<JSVal> type = decorationObject->GetProperty("type");
887         if (!type->IsNull() && !type->IsUndefined()) {
888             updateSpanStyle.updateTextDecoration = static_cast<TextDecoration>(type->ToNumber<int32_t>());
889             style.SetTextDecoration(static_cast<TextDecoration>(type->ToNumber<int32_t>()));
890         }
891         JSRef<JSVal> color = decorationObject->GetProperty("color");
892         Color decorationColor;
893         if (!color->IsNull() && JSContainerBase::ParseJsColor(color, decorationColor)) {
894             updateSpanStyle.updateTextDecorationColor = decorationColor;
895             style.SetTextDecorationColor(decorationColor);
896             updateSpanStyle.hasResourceDecorationColor = color->IsObject();
897         }
898     }
899 }
900 
ParseUserGesture(const JSCallbackInfo & args,UserGestureOptions & gestureOption,const std::string & spanType)901 void ParseUserGesture(
902     const JSCallbackInfo& args, UserGestureOptions& gestureOption, const std::string& spanType)
903 {
904     if (args.Length() < 2) {
905         return;
906     }
907     JSRef<JSObject> object = JSRef<JSObject>::Cast(args[1]);
908     auto gesture = object->GetProperty("gesture");
909     if (!gesture->IsUndefined()) {
910         auto gestureObj = JSRef<JSObject>::Cast(gesture);
911         auto clickFunc = gestureObj->GetProperty("onClick");
912         if (clickFunc->IsUndefined() && IsDisableEventVersion()) {
913             gestureOption.onClick = nullptr;
914         } else if (!clickFunc->IsFunction()) {
915             gestureOption.onClick = nullptr;
916         } else {
917             auto jsOnClickFunc = AceType::MakeRefPtr<JsClickFunction>(JSRef<JSFunc>::Cast(clickFunc));
918             auto onClick = [
919                     execCtx = args.GetExecutionContext(), func = jsOnClickFunc, spanTypeInner = spanType
920                     ](const BaseEventInfo* info) {
921                 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
922                 const auto* clickInfo = TypeInfoHelper::DynamicCast<GestureEvent>(info);
923                 ACE_SCORING_EVENT(spanTypeInner + ".onClick");
924                 func->Execute(*clickInfo);
925             };
926             auto tmpClickFunc = [func = std::move(onClick)](GestureEvent& info) { func(&info); };
927             gestureOption.onClick = std::move(tmpClickFunc);
928         }
929         auto onLongPressFunc = gestureObj->GetProperty("onLongPress");
930         if (onLongPressFunc->IsUndefined() && IsDisableEventVersion()) {
931             gestureOption.onLongPress = nullptr;
932         } else if (!onLongPressFunc->IsFunction()) {
933             gestureOption.onLongPress = nullptr;
934         } else {
935             auto jsLongPressFunc = AceType::MakeRefPtr<JsClickFunction>(JSRef<JSFunc>::Cast(onLongPressFunc));
936             auto onLongPress = [
937                     execCtx = args.GetExecutionContext(), func = jsLongPressFunc, spanTypeInner = spanType
938                     ](const BaseEventInfo* info) {
939                 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
940                 const auto* longPressInfo = TypeInfoHelper::DynamicCast<GestureEvent>(info);
941                 ACE_SCORING_EVENT(spanTypeInner + ".onLongPress");
942                 func->Execute(*longPressInfo);
943             };
944             auto tmpOnLongPressFunc = [func = std::move(onLongPress)](GestureEvent& info) { func(&info); };
945             gestureOption.onLongPress = std::move(tmpOnLongPressFunc);
946         }
947     }
948 }
949 
AddImageSpan(const JSCallbackInfo & args)950 void JSRichEditorController::AddImageSpan(const JSCallbackInfo& args)
951 {
952     ContainerScope scope(instanceId_ < 0 ? Container::CurrentId() : instanceId_);
953     if (args.Length() < 1) {
954         return;
955     }
956     ImageSpanOptions options;
957     if (!args[0]->IsEmpty() && args[0]->ToString() != "") {
958         options = CreateJsImageOptions(args);
959     } else {
960         args.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(-1)));
961         return;
962     }
963     if (options.image.has_value()) {
964         std::string assetSrc = options.image.value();
965         SrcType srcType = ImageSourceInfo::ResolveURIType(assetSrc);
966         if (assetSrc[0] == '/') {
967             assetSrc = assetSrc.substr(1); // get the asset src without '/'.
968         } else if (assetSrc[0] == '.' && assetSrc.size() > 2 && assetSrc[1] == '/') {
969             assetSrc = assetSrc.substr(2); // get the asset src without './'.
970         }
971         if (srcType == SrcType::ASSET) {
972             auto pipelineContext = PipelineBase::GetCurrentContext();
973             CHECK_NULL_VOID(pipelineContext);
974             auto assetManager = pipelineContext->GetAssetManager();
975             CHECK_NULL_VOID(assetManager);
976             auto assetData = assetManager->GetAsset(assetSrc);
977             if (!assetData) {
978                 args.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(-1)));
979                 return;
980             }
981         }
982     }
983     if (args.Length() > 1 && args[1]->IsObject()) {
984         JSRef<JSObject> imageObject = JSRef<JSObject>::Cast(args[1]);
985 
986         JSRef<JSVal> offset = imageObject->GetProperty("offset");
987         int32_t imageOffset = 0;
988         if (!offset->IsNull() && JSContainerBase::ParseJsInt32(offset, imageOffset)) {
989             options.offset = imageOffset;
990         }
991         auto imageStyleObj = imageObject->GetProperty("imageStyle");
992         JSRef<JSObject> imageAttribute = JSRef<JSObject>::Cast(imageStyleObj);
993         if (!imageAttribute->IsUndefined()) {
994             ImageSpanAttribute imageStyle = ParseJsImageSpanAttribute(imageAttribute);
995             options.imageAttribute = imageStyle;
996         }
997         UserGestureOptions gestureOption;
998         ParseUserGesture(args, gestureOption, "ImageSpan");
999         options.userGestureOption = std::move(gestureOption);
1000     }
1001     auto controller = controllerWeak_.Upgrade();
1002     int32_t spanIndex = 0;
1003     if (controller) {
1004         spanIndex = controller->AddImageSpan(options);
1005     }
1006     args.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(spanIndex)));
1007 }
1008 
CreateJsImageOptions(const JSCallbackInfo & args)1009 ImageSpanOptions JSRichEditorController::CreateJsImageOptions(const JSCallbackInfo& args)
1010 {
1011     ContainerScope scope(instanceId_ < 0 ? Container::CurrentId() : instanceId_);
1012     ImageSpanOptions options;
1013     auto context = PipelineBase::GetCurrentContext();
1014     CHECK_NULL_RETURN(context, options);
1015     bool isCard = context->IsFormRender();
1016     std::string image;
1017     std::string bundleName;
1018     std::string moduleName;
1019     bool srcValid = JSContainerBase::ParseJsMedia(args[0], image);
1020     if (isCard && args[0]->IsString()) {
1021         SrcType srcType = ImageSourceInfo::ResolveURIType(image);
1022         bool notSupport = (srcType == SrcType::NETWORK || srcType == SrcType::FILE || srcType == SrcType::DATA_ABILITY);
1023         if (notSupport) {
1024             image.clear();
1025         }
1026     }
1027     JSImage::GetJsMediaBundleInfo(args[0], bundleName, moduleName);
1028     options.image = image;
1029     options.bundleName = bundleName;
1030     options.moduleName = moduleName;
1031     if (!srcValid) {
1032 #if defined(PIXEL_MAP_SUPPORTED)
1033         if (!isCard) {
1034             if (IsDrawable(args[0])) {
1035                 options.imagePixelMap = GetDrawablePixmap(args[0]);
1036             } else {
1037                 options.imagePixelMap = CreatePixelMapFromNapiValue(args[0]);
1038             }
1039         }
1040 #endif
1041     }
1042     return options;
1043 }
1044 
IsDrawable(const JSRef<JSVal> & jsValue)1045 bool JSRichEditorController::IsDrawable(const JSRef<JSVal>& jsValue)
1046 {
1047     if (!jsValue->IsObject()) {
1048         return false;
1049     }
1050     JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(jsValue);
1051     if (jsObj->IsUndefined()) {
1052         return false;
1053     }
1054     JSRef<JSVal> func = jsObj->GetProperty("getPixelMap");
1055     return (!func->IsNull() && func->IsFunction());
1056 }
1057 
IsPixelMap(const JSRef<JSVal> & jsValue)1058 bool JSRichEditorController::IsPixelMap(const JSRef<JSVal>& jsValue)
1059 {
1060     if (!jsValue->IsObject()) {
1061         return false;
1062     }
1063     JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(jsValue);
1064     if (jsObj->IsUndefined()) {
1065         return false;
1066     }
1067     JSRef<JSVal> func = jsObj->GetProperty("readPixelsToBuffer");
1068     return (!func->IsNull() && func->IsFunction());
1069 }
1070 
AddTextSpan(const JSCallbackInfo & args)1071 void JSRichEditorController::AddTextSpan(const JSCallbackInfo& args)
1072 {
1073     ContainerScope scope(instanceId_ < 0 ? Container::CurrentId() : instanceId_);
1074     if (args.Length() < 1) {
1075         return;
1076     }
1077     TextSpanOptions options;
1078     std::string spanValue;
1079     if (!args[0]->IsEmpty() && args[0]->ToString() != "" && JSContainerBase::ParseJsString(args[0], spanValue)) {
1080         options.value = spanValue;
1081     } else {
1082         args.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(-1)));
1083         return;
1084     }
1085     if (args.Length() > 1 && args[1]->IsObject()) {
1086         JSRef<JSObject> spanObject = JSRef<JSObject>::Cast(args[1]);
1087         JSRef<JSVal> offset = spanObject->GetProperty("offset");
1088         int32_t spanOffset = 0;
1089         if (!offset->IsNull() && JSContainerBase::ParseJsInt32(offset, spanOffset)) {
1090             options.offset = spanOffset;
1091         }
1092         auto styleObj = spanObject->GetProperty("style");
1093         JSRef<JSObject> styleObject = JSRef<JSObject>::Cast(styleObj);
1094         if (!styleObject->IsUndefined()) {
1095             auto pipelineContext = PipelineBase::GetCurrentContext();
1096             CHECK_NULL_VOID(pipelineContext);
1097             auto theme = pipelineContext->GetTheme<TextTheme>();
1098             TextStyle style = theme ? theme->GetTextStyle() : TextStyle();
1099             ParseJsTextStyle(styleObject, style, updateSpanStyle_);
1100             options.style = style;
1101             options.hasResourceFontColor = updateSpanStyle_.hasResourceFontColor;
1102             options.hasResourceDecorationColor = updateSpanStyle_.hasResourceDecorationColor;
1103         }
1104         auto paraStyle = spanObject->GetProperty("paragraphStyle");
1105         auto paraStyleObj = JSRef<JSObject>::Cast(paraStyle);
1106         if (!paraStyleObj->IsUndefined()) {
1107             struct UpdateParagraphStyle style;
1108             if (ParseParagraphStyle(paraStyleObj, style)) {
1109                 options.paraStyle = style;
1110             }
1111         }
1112         UserGestureOptions gestureOption;
1113         ParseUserGesture(args, gestureOption, "TextSpan");
1114         options.userGestureOption = std::move(gestureOption);
1115     }
1116     auto controller = controllerWeak_.Upgrade();
1117     int32_t spanIndex = 0;
1118     if (controller) {
1119         spanIndex = controller->AddTextSpan(options);
1120     }
1121     args.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(spanIndex)));
1122 }
1123 
AddSymbolSpan(const JSCallbackInfo & args)1124 void JSRichEditorController::AddSymbolSpan(const JSCallbackInfo& args)
1125 {
1126     ContainerScope scope(instanceId_ < 0 ? Container::CurrentId() : instanceId_);
1127     if (args.Length() < 1) {
1128         return;
1129     }
1130     SymbolSpanOptions options;
1131     uint32_t symbolId;
1132     RefPtr<ResourceObject> resourceObject;
1133     if (!args[0]->IsEmpty() && JSContainerBase::ParseJsSymbolId(args[0], symbolId, resourceObject)) {
1134         options.symbolId = symbolId;
1135         options.resourceObject = resourceObject;
1136     } else {
1137         args.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(-1)));
1138         return;
1139     }
1140 
1141     if (args.Length() > 1 && args[1]->IsObject()) {
1142         JSRef<JSObject> spanObject = JSRef<JSObject>::Cast(args[1]);
1143         JSRef<JSVal> offset = spanObject->GetProperty("offset");
1144         int32_t spanOffset = 0;
1145         if (!offset->IsNull() && JSContainerBase::ParseJsInt32(offset, spanOffset)) {
1146             options.offset = spanOffset;
1147         }
1148         auto styleObj = spanObject->GetProperty("style");
1149         JSRef<JSObject> styleObject = JSRef<JSObject>::Cast(styleObj);
1150         if (!styleObject->IsUndefined()) {
1151             auto pipelineContext = PipelineBase::GetCurrentContext();
1152             CHECK_NULL_VOID(pipelineContext);
1153             auto theme = pipelineContext->GetTheme<TextTheme>();
1154             TextStyle style = theme ? theme->GetTextStyle() : TextStyle();
1155             ParseJsSymbolSpanStyle(styleObject, style, updateSpanStyle_);
1156             options.style = style;
1157         }
1158     }
1159 
1160     auto controller = controllerWeak_.Upgrade();
1161     int32_t spanIndex = 0;
1162     if (controller) {
1163         spanIndex = controller->AddSymbolSpan(options);
1164     }
1165     args.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(spanIndex)));
1166 }
1167 
CreateJSSpansInfo(const SelectionInfo & info)1168 JSRef<JSVal> JSRichEditorController::CreateJSSpansInfo(const SelectionInfo& info)
1169 {
1170     uint32_t idx = 0;
1171 
1172     JSRef<JSArray> spanObjectArray = JSRef<JSArray>::New();
1173     JSRef<JSObject> selectionObject = JSRef<JSObject>::New();
1174 
1175     const std::list<ResultObject>& spanObjectList = info.GetSelection().resultObjects;
1176     for (const ResultObject& spanObject : spanObjectList) {
1177         spanObjectArray->SetValueAt(idx++, JSRichEditor::CreateJSSpanResultObject(spanObject));
1178     }
1179 
1180     return JSRef<JSVal>::Cast(spanObjectArray);
1181 }
1182 
GetSpansInfo(const JSCallbackInfo & args)1183 void JSRichEditorController::GetSpansInfo(const JSCallbackInfo& args)
1184 {
1185     ContainerScope scope(instanceId_ < 0 ? Container::CurrentId() : instanceId_);
1186     int32_t end = -1;
1187     int32_t start = -1;
1188     if (args[0]->IsObject()) {
1189         JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
1190         JSRef<JSVal> startVal = obj->GetProperty("start");
1191         JSRef<JSVal> endVal = obj->GetProperty("end");
1192 
1193         if (!startVal->IsNull() && startVal->IsNumber()) {
1194             start = startVal->ToNumber<int32_t>();
1195         }
1196 
1197         if (!endVal->IsNull() && endVal->IsNumber()) {
1198             end = endVal->ToNumber<int32_t>();
1199         }
1200     }
1201     if (controllerWeak_.Upgrade()) {
1202         SelectionInfo value = controllerWeak_.Upgrade()->GetSpansInfo(start, end);
1203         args.SetReturnValue(CreateJSSpansInfo(value));
1204     }
1205 }
1206 
DeleteSpans(const JSCallbackInfo & args)1207 void JSRichEditorController::DeleteSpans(const JSCallbackInfo& args)
1208 {
1209     ContainerScope scope(instanceId_ < 0 ? Container::CurrentId() : instanceId_);
1210     RangeOptions options;
1211     auto controller = controllerWeak_.Upgrade();
1212     CHECK_NULL_VOID(controller);
1213 
1214     if (args.Length() < 1) {
1215         controller->DeleteSpans(options);
1216         return;
1217     }
1218 
1219     if (!args[0]->IsObject() || !controller) {
1220         return;
1221     }
1222     JSRef<JSObject> spanObject = JSRef<JSObject>::Cast(args[0]);
1223     JSRef<JSVal> startVal = spanObject->GetProperty("start");
1224     int32_t start = 0;
1225     if (!startVal->IsNull() && JSContainerBase::ParseJsInt32(startVal, start)) {
1226         options.start = start;
1227     }
1228     JSRef<JSVal> endVal = spanObject->GetProperty("end");
1229     int32_t end = 0;
1230     if (!startVal->IsNull() && JSContainerBase::ParseJsInt32(endVal, end)) {
1231         options.end = end;
1232     }
1233     controller->DeleteSpans(options);
1234 }
1235 
AddPlaceholderSpan(const JSCallbackInfo & args)1236 void JSRichEditorController::AddPlaceholderSpan(const JSCallbackInfo& args)
1237 {
1238     ContainerScope scope(instanceId_ < 0 ? Container::CurrentId() : instanceId_);
1239     if (args.Length() < 1) {
1240         return;
1241     }
1242     auto customVal = args[0];
1243     if (!customVal->IsFunction() && !customVal->IsObject()) {
1244         return;
1245     }
1246     JSRef<JSVal> funcValue;
1247     auto customObject = JSRef<JSObject>::Cast(customVal);
1248     auto builder = customObject->GetProperty("builder");
1249     // if failed to get builder, parse function directly
1250     if (builder->IsEmpty() || builder->IsNull() || !builder->IsFunction()) {
1251         funcValue = customVal;
1252     } else {
1253         funcValue = builder;
1254     }
1255     SpanOptionBase options;
1256     {
1257         auto builderFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSFunc>::Cast(funcValue));
1258         CHECK_NULL_VOID(builderFunc);
1259         ViewStackModel::GetInstance()->NewScope();
1260         builderFunc->Execute();
1261         auto customNode = AceType::DynamicCast<NG::UINode>(ViewStackModel::GetInstance()->Finish());
1262         auto controller = controllerWeak_.Upgrade();
1263         int32_t spanIndex = 0;
1264         if (controller) {
1265             ParseOptions(args, options);
1266             spanIndex = controller->AddPlaceholderSpan(customNode, options);
1267         }
1268         args.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(spanIndex)));
1269     }
1270 }
1271 
ParseOptions(const JSCallbackInfo & args,SpanOptionBase & placeholderSpan)1272 void JSRichEditorController::ParseOptions(const JSCallbackInfo& args, SpanOptionBase& placeholderSpan)
1273 {
1274     ContainerScope scope(instanceId_ < 0 ? Container::CurrentId() : instanceId_);
1275     if (args.Length() < 2) {
1276         return;
1277     }
1278     if (!args[1]->IsObject()) {
1279         return;
1280     }
1281     JSRef<JSObject> placeholderOptionObject = JSRef<JSObject>::Cast(args[1]);
1282     JSRef<JSVal> offset = placeholderOptionObject->GetProperty("offset");
1283     int32_t placeholderOffset = 0;
1284     if (!offset->IsNull() && JSContainerBase::ParseJsInt32(offset, placeholderOffset)) {
1285         placeholderSpan.offset = placeholderOffset >= 0 ? placeholderOffset : Infinity<int32_t>();
1286     }
1287 }
1288 
CloseSelectionMenu()1289 void JSRichEditorController::CloseSelectionMenu()
1290 {
1291     ContainerScope scope(instanceId_ < 0 ? Container::CurrentId() : instanceId_);
1292     auto controller = controllerWeak_.Upgrade();
1293     CHECK_NULL_VOID(controller);
1294     controller->CloseSelectionMenu();
1295 }
1296 
SetSelection(int32_t selectionStart,int32_t selectionEnd)1297 void JSRichEditorController::SetSelection(int32_t selectionStart, int32_t selectionEnd)
1298 {
1299     ContainerScope scope(instanceId_ < 0 ? Container::CurrentId() : instanceId_);
1300     auto controller = controllerWeak_.Upgrade();
1301     if (controller) {
1302         controller->SetSelection(selectionStart, selectionEnd);
1303     }
1304 }
1305 
GetSelection(const JSCallbackInfo & args)1306 void JSRichEditorController::GetSelection(const JSCallbackInfo& args)
1307 {
1308     ContainerScope scope(instanceId_ < 0 ? Container::CurrentId() : instanceId_);
1309     auto controller = controllerWeak_.Upgrade();
1310     if (controller) {
1311         SelectionInfo value = controller->GetSelectionSpansInfo();
1312         args.SetReturnValue(JSRichEditor::CreateJSSelection(value));
1313     }
1314 }
1315 
JSBind(BindingTarget globalObj)1316 void JSRichEditorController::JSBind(BindingTarget globalObj)
1317 {
1318     JSClass<JSRichEditorController>::Declare("RichEditorController");
1319     JSClass<JSRichEditorController>::CustomMethod("addImageSpan", &JSRichEditorController::AddImageSpan);
1320     JSClass<JSRichEditorController>::CustomMethod("addTextSpan", &JSRichEditorController::AddTextSpan);
1321     JSClass<JSRichEditorController>::CustomMethod("addSymbolSpan", &JSRichEditorController::AddSymbolSpan);
1322     JSClass<JSRichEditorController>::CustomMethod("addBuilderSpan", &JSRichEditorController::AddPlaceholderSpan);
1323     JSClass<JSRichEditorController>::CustomMethod("setCaretOffset", &JSRichEditorController::SetCaretOffset);
1324     JSClass<JSRichEditorController>::CustomMethod("getCaretOffset", &JSRichEditorController::GetCaretOffset);
1325     JSClass<JSRichEditorController>::CustomMethod("updateSpanStyle", &JSRichEditorController::UpdateSpanStyle);
1326     JSClass<JSRichEditorController>::CustomMethod(
1327         "updateParagraphStyle", &JSRichEditorController::UpdateParagraphStyle);
1328     JSClass<JSRichEditorController>::CustomMethod("getTypingStyle", &JSRichEditorController::GetTypingStyle);
1329     JSClass<JSRichEditorController>::CustomMethod("setTypingStyle", &JSRichEditorController::SetTypingStyle);
1330     JSClass<JSRichEditorController>::CustomMethod("getSpans", &JSRichEditorController::GetSpansInfo);
1331     JSClass<JSRichEditorController>::CustomMethod("getParagraphs", &JSRichEditorController::GetParagraphsInfo);
1332     JSClass<JSRichEditorController>::CustomMethod("deleteSpans", &JSRichEditorController::DeleteSpans);
1333     JSClass<JSRichEditorController>::Method("setSelection", &JSRichEditorController::SetSelection);
1334     JSClass<JSRichEditorController>::CustomMethod("getSelection", &JSRichEditorController::GetSelection);
1335     JSClass<JSRichEditorController>::Method("closeSelectionMenu", &JSRichEditorController::CloseSelectionMenu);
1336     JSClass<JSRichEditorController>::Bind(
1337         globalObj, JSRichEditorController::Constructor, JSRichEditorController::Destructor);
1338 }
1339 
GetCaretOffset(const JSCallbackInfo & args)1340 void JSRichEditorController::GetCaretOffset(const JSCallbackInfo& args)
1341 {
1342     ContainerScope scope(instanceId_ < 0 ? Container::CurrentId() : instanceId_);
1343     auto controller = controllerWeak_.Upgrade();
1344     int32_t caretOffset = -1;
1345     if (controller) {
1346         caretOffset = controller->GetCaretOffset();
1347         args.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(caretOffset)));
1348     } else {
1349         args.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(caretOffset)));
1350     }
1351 }
1352 
SetCaretOffset(const JSCallbackInfo & args)1353 void JSRichEditorController::SetCaretOffset(const JSCallbackInfo& args)
1354 {
1355     ContainerScope scope(instanceId_ < 0 ? Container::CurrentId() : instanceId_);
1356     auto controller = controllerWeak_.Upgrade();
1357     int32_t caretPosition = -1;
1358     bool success = false;
1359     JSViewAbstract::ParseJsInteger<int32_t>(args[0], caretPosition);
1360     caretPosition = caretPosition < 0 ? -1 : caretPosition;
1361     if (controller) {
1362         success = controller->SetCaretOffset(caretPosition);
1363         args.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(success)));
1364     } else {
1365         args.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(success)));
1366     }
1367 }
1368 
1369 namespace {
ValidationCheck(const JSCallbackInfo & info)1370 bool ValidationCheck(const JSCallbackInfo& info)
1371 {
1372     if (!info[0]->IsNumber() && !info[0]->IsObject()) {
1373         return false;
1374     }
1375     return true;
1376 }
1377 
ParseRange(const JSRef<JSObject> & object)1378 std::pair<int32_t, int32_t> ParseRange(const JSRef<JSObject>& object)
1379 {
1380     int32_t start = -1;
1381     int32_t end = -1;
1382     if (!JSContainerBase::ParseJsInt32(object->GetProperty("start"), start)) {
1383         start = 0;
1384     }
1385     if (!JSContainerBase::ParseJsInt32(object->GetProperty("end"), end)) {
1386         end = INT_MAX;
1387     }
1388     if (start < 0) {
1389         start = 0;
1390     }
1391     if (end < 0) {
1392         end = INT_MAX;
1393     }
1394     if (start > end) {
1395         start = 0;
1396         end = INT_MAX;
1397     }
1398     return std::make_pair(start, end);
1399 }
1400 } // namespace
1401 
ParseParagraphStyle(const JSRef<JSObject> & styleObject,struct UpdateParagraphStyle & style)1402 bool JSRichEditorController::ParseParagraphStyle(const JSRef<JSObject>& styleObject, struct UpdateParagraphStyle& style)
1403 {
1404     ContainerScope scope(instanceId_ < 0 ? Container::CurrentId() : instanceId_);
1405     auto textAlignObj = styleObject->GetProperty("textAlign");
1406     if (!textAlignObj->IsNull() && textAlignObj->IsNumber()) {
1407         auto align = static_cast<TextAlign>(textAlignObj->ToNumber<int32_t>());
1408         if (align < TextAlign::START || align > TextAlign::JUSTIFY) {
1409             align = TextAlign::START;
1410         }
1411         style.textAlign = align;
1412     }
1413     auto lm = styleObject->GetProperty("leadingMargin");
1414     if (lm->IsObject()) {
1415         // [LeadingMarginPlaceholder]
1416         JSRef<JSObject> leadingMarginObject = JSRef<JSObject>::Cast(lm);
1417         style.leadingMargin = std::make_optional<NG::LeadingMargin>();
1418         JSRef<JSVal> placeholder = leadingMarginObject->GetProperty("pixelMap");
1419         if (IsPixelMap(placeholder)) {
1420 #if defined(PIXEL_MAP_SUPPORTED)
1421             auto pixelMap = CreatePixelMapFromNapiValue(placeholder);
1422             style.leadingMargin->pixmap = pixelMap;
1423 #endif
1424         }
1425 
1426         JSRef<JSVal> sizeVal = leadingMarginObject->GetProperty("size");
1427         if (!sizeVal->IsUndefined() && sizeVal->IsArray()) {
1428             auto rangeArray = JSRef<JSArray>::Cast(sizeVal);
1429             JSRef<JSVal> widthVal = rangeArray->GetValueAt(0);
1430             JSRef<JSVal> heightVal = rangeArray->GetValueAt(1);
1431 
1432             CalcDimension width;
1433             CalcDimension height;
1434             JSContainerBase::ParseJsDimensionVp(widthVal, width);
1435             JSContainerBase::ParseJsDimensionVp(heightVal, height);
1436             style.leadingMargin->size = NG::SizeF(width.ConvertToPx(), height.ConvertToPx());
1437         } else if (sizeVal->IsUndefined()) {
1438             std::string resWidthStr;
1439             if (JSContainerBase::ParseJsString(lm, resWidthStr)) {
1440                 CalcDimension width;
1441                 JSContainerBase::ParseJsDimensionVp(lm, width);
1442                 style.leadingMargin->size = NG::SizeF(width.ConvertToPx(), 0.0);
1443             }
1444         }
1445     } else if (!lm->IsNull()) {
1446         // [Dimension]
1447         style.leadingMargin = std::make_optional<NG::LeadingMargin>();
1448         CalcDimension width;
1449         JSContainerBase::ParseJsDimensionVp(lm, width);
1450         style.leadingMargin->size = NG::SizeF(width.ConvertToPx(), 0.0);
1451     }
1452     return true;
1453 }
1454 
UpdateSpanStyle(const JSCallbackInfo & info)1455 void JSRichEditorController::UpdateSpanStyle(const JSCallbackInfo& info)
1456 {
1457     ContainerScope scope(instanceId_ < 0 ? Container::CurrentId() : instanceId_);
1458     if (!ValidationCheck(info)) {
1459         return;
1460     }
1461     auto jsObject = JSRef<JSObject>::Cast(info[0]);
1462 
1463     auto [start, end] = ParseRange(jsObject);
1464     auto pipelineContext = PipelineBase::GetCurrentContext();
1465     CHECK_NULL_VOID(pipelineContext);
1466     auto theme = pipelineContext->GetTheme<TextTheme>();
1467     TextStyle textStyle = theme ? theme->GetTextStyle() : TextStyle();
1468     ImageSpanAttribute imageStyle;
1469     auto richEditorTextStyle = JSRef<JSObject>::Cast(jsObject->GetProperty("textStyle"));
1470     auto richEditorImageStyle = JSRef<JSObject>::Cast(jsObject->GetProperty("imageStyle"));
1471     auto richEditorSymbolSpanStyle = JSRef<JSObject>::Cast(jsObject->GetProperty("symbolStyle"));
1472     updateSpanStyle_.ResetStyle();
1473     if (!richEditorTextStyle->IsUndefined()) {
1474         ParseJsTextStyle(richEditorTextStyle, textStyle, updateSpanStyle_);
1475     }
1476     if (!richEditorImageStyle->IsUndefined()) {
1477         imageStyle = ParseJsImageSpanAttribute(richEditorImageStyle);
1478     }
1479     if (!richEditorSymbolSpanStyle->IsUndefined()) {
1480         ParseJsSymbolSpanStyle(richEditorSymbolSpanStyle, textStyle, updateSpanStyle_);
1481     }
1482 
1483     auto controller = controllerWeak_.Upgrade();
1484     if (controller) {
1485         controller->SetUpdateSpanStyle(updateSpanStyle_);
1486         controller->UpdateSpanStyle(start, end, textStyle, imageStyle);
1487     }
1488 }
1489 
GetParagraphsInfo(const JSCallbackInfo & args)1490 void JSRichEditorController::GetParagraphsInfo(const JSCallbackInfo& args)
1491 {
1492     ContainerScope scope(instanceId_ < 0 ? Container::CurrentId() : instanceId_);
1493     if (!args[0]->IsObject()) {
1494         return;
1495     }
1496     auto [start, end] = ParseRange(JSRef<JSObject>::Cast(args[0]));
1497     if (start == end) {
1498         return;
1499     }
1500     auto controller = controllerWeak_.Upgrade();
1501     if (controller) {
1502         auto info = controller->GetParagraphsInfo(start, end);
1503         args.SetReturnValue(CreateJSParagraphsInfo(info));
1504     }
1505 }
1506 
UpdateParagraphStyle(const JSCallbackInfo & info)1507 void JSRichEditorController::UpdateParagraphStyle(const JSCallbackInfo& info)
1508 {
1509     ContainerScope scope(instanceId_ < 0 ? Container::CurrentId() : instanceId_);
1510     if (!ValidationCheck(info)) {
1511         return;
1512     }
1513     auto object = JSRef<JSObject>::Cast(info[0]);
1514     auto [start, end] = ParseRange(object);
1515     if (start == end) {
1516         return;
1517     }
1518     auto styleObj = JSRef<JSObject>::Cast(object->GetProperty("style"));
1519 
1520     if (styleObj->IsUndefined()) {
1521         return;
1522     }
1523 
1524     struct UpdateParagraphStyle style;
1525     if (!ParseParagraphStyle(styleObj, style)) {
1526         return;
1527     }
1528     auto controller = controllerWeak_.Upgrade();
1529     if (controller) {
1530         controller->UpdateParagraphStyle(start, end, style);
1531     }
1532 }
1533 
CreateTypingStyleResult(const struct UpdateSpanStyle & typingStyle)1534 JSRef<JSObject> JSRichEditorController::CreateTypingStyleResult(const struct UpdateSpanStyle& typingStyle)
1535 {
1536     auto tyingStyleObj = JSRef<JSObject>::New();
1537     TextStyle textStyle;
1538     if (typingStyle.updateFontFamily.has_value()) {
1539         std::string family = V2::ConvertFontFamily(typingStyle.updateFontFamily.value());
1540         tyingStyleObj->SetProperty<std::string>("fontFamily", family);
1541     }
1542     if (typingStyle.updateFontSize.has_value()) {
1543         tyingStyleObj->SetProperty<double>("fontSize", typingStyle.updateFontSize.value().ConvertToVp());
1544     }
1545     if (typingStyle.updateTextColor.has_value()) {
1546         tyingStyleObj->SetProperty<std::string>("fontColor", typingStyle.updateTextColor.value().ColorToString());
1547     }
1548     if (typingStyle.updateItalicFontStyle.has_value()) {
1549         tyingStyleObj->SetProperty<int32_t>(
1550             "fontStyle", static_cast<int32_t>(typingStyle.updateItalicFontStyle.value()));
1551     }
1552     if (typingStyle.updateFontWeight.has_value()) {
1553         tyingStyleObj->SetProperty<int32_t>("fontWeight", static_cast<int32_t>(typingStyle.updateFontWeight.value()));
1554     }
1555 
1556     JSRef<JSObject> decorationObj = JSRef<JSObject>::New();
1557     if (typingStyle.updateTextDecoration.has_value()) {
1558         decorationObj->SetProperty<int32_t>("type", static_cast<int32_t>(typingStyle.updateTextDecoration.value()));
1559     }
1560     if (typingStyle.updateTextDecorationColor.has_value()) {
1561         decorationObj->SetProperty<std::string>("color", typingStyle.updateTextDecorationColor.value().ColorToString());
1562     }
1563     if (typingStyle.updateTextDecoration.has_value() || typingStyle.updateTextDecorationColor.has_value()) {
1564         tyingStyleObj->SetPropertyObject("decoration", decorationObj);
1565     }
1566     return tyingStyleObj;
1567 }
1568 
GetTypingStyle(const JSCallbackInfo & info)1569 void JSRichEditorController::GetTypingStyle(const JSCallbackInfo& info)
1570 {
1571     ContainerScope scope(instanceId_ < 0 ? Container::CurrentId() : instanceId_);
1572     auto controller = controllerWeak_.Upgrade();
1573     CHECK_NULL_VOID(controller);
1574     auto style = CreateTypingStyleResult(typingStyle_);
1575     info.SetReturnValue(JSRef<JSVal>::Cast(style));
1576 }
1577 
SetTypingStyle(const JSCallbackInfo & info)1578 void JSRichEditorController::SetTypingStyle(const JSCallbackInfo& info)
1579 {
1580     ContainerScope scope(instanceId_ < 0 ? Container::CurrentId() : instanceId_);
1581     auto controller = controllerWeak_.Upgrade();
1582     CHECK_NULL_VOID(controller);
1583     if (!info[0]->IsObject()) {
1584         return;
1585     }
1586     auto pipelineContext = PipelineBase::GetCurrentContext();
1587     CHECK_NULL_VOID(pipelineContext);
1588     auto theme = pipelineContext->GetTheme<TextTheme>();
1589     TextStyle textStyle = theme ? theme->GetTextStyle() : TextStyle();
1590     JSRef<JSObject> richEditorTextStyle = JSRef<JSObject>::Cast(info[0]);
1591     typingStyle_.ResetStyle();
1592     if (!richEditorTextStyle->IsUndefined()) {
1593         ParseJsTextStyle(richEditorTextStyle, textStyle, typingStyle_);
1594     }
1595     controller->SetTypingStyle(typingStyle_, textStyle);
1596 }
1597 
CreateJSParagraphsInfo(const std::vector<ParagraphInfo> & info)1598 JSRef<JSVal> JSRichEditorController::CreateJSParagraphsInfo(const std::vector<ParagraphInfo>& info)
1599 {
1600     auto array = JSRef<JSArray>::New();
1601     for (size_t i = 0; i < info.size(); ++i) {
1602         auto obj = JSRef<JSObject>::New();
1603         obj->SetPropertyObject("style", JSRichEditor::CreateParagraphStyleResult(info[i]));
1604 
1605         auto range = JSRef<JSArray>::New();
1606         range->SetValueAt(0, JSRef<JSVal>::Make(ToJSValue(info[i].range.first)));
1607         range->SetValueAt(1, JSRef<JSVal>::Make(ToJSValue(info[i].range.second)));
1608         obj->SetPropertyObject("range", range);
1609         array->SetValueAt(i, obj);
1610     }
1611     return JSRef<JSVal>::Cast(array);
1612 }
1613 } // namespace OHOS::Ace::Framework
1614