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