1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "frameworks/bridge/declarative_frontend/jsview/js_container_base.h"
17 #include "frameworks/bridge/declarative_frontend/jsview/js_text_editable_controller.h"
18 #include "frameworks/core/components_ng/pattern/text_field/text_field_model.h"
19
20 namespace OHOS::Ace::Framework {
21
JSBind(BindingTarget globalObj)22 void JSTextEditableController::JSBind(BindingTarget globalObj)
23 {
24 JSClass<JSTextEditableController>::Method("caretPosition", &JSTextEditableController::CaretPosition);
25 JSClass<JSTextEditableController>::CustomMethod("getCaretOffset", &JSTextEditableController::GetCaretOffset);
26 JSClass<JSTextEditableController>::CustomMethod("setTextSelection", &JSTextEditableController::SetTextSelection);
27 JSClass<JSTextEditableController>::CustomMethod("showPassword", &JSTextEditableController::ShowPassword);
28 JSClass<JSTextEditableController>::CustomMethod("hidePassword", &JSTextEditableController::HidePassword);
29 JSClass<JSTextEditableController>::CustomMethod(
30 "getTextContentRect", &JSTextEditableController::GetTextContentRect);
31 JSClass<JSTextEditableController>::CustomMethod(
32 "getTextContentLineCount", &JSTextEditableController::GetTextContentLinesNum);
33 JSClass<JSTextEditableController>::CustomMethod("addText", &JSTextEditableController::AddText);
34 JSClass<JSTextEditableController>::CustomMethod("deleteText", &JSTextEditableController::DeleteText);
35 JSClass<JSTextEditableController>::CustomMethod("getSelection", &JSTextEditableController::GetSelection);
36 JSClass<JSTextEditableController>::CustomMethod("clearPreviewText", &JSTextEditableController::ClearPreviewText);
37 JSClass<JSTextEditableController>::CustomMethod("getText", &JSTextEditableController::GetText);
38 JSClass<JSTextEditableController>::Method("stopEditing", &JSTextEditableController::StopEditing);
39 JSClass<JSTextEditableController>::Bind(
40 globalObj, JSTextEditableController::Constructor, JSTextEditableController::Destructor);
41 }
42
Constructor(const JSCallbackInfo & args)43 void JSTextEditableController::Constructor(const JSCallbackInfo& args)
44 {
45 auto controller = Referenced::MakeRefPtr<JSTextEditableController>();
46 controller->IncRefCount();
47 args.SetReturnValue(Referenced::RawPtr(controller));
48 }
49
Destructor(JSTextEditableController * controller)50 void JSTextEditableController::Destructor(JSTextEditableController* controller)
51 {
52 if (controller != nullptr) {
53 controller->DecRefCount();
54 }
55 }
56
CaretPosition(int32_t caretPosition)57 void JSTextEditableController::CaretPosition(int32_t caretPosition)
58 {
59 auto controller = controllerWeak_.Upgrade();
60 if (controller) {
61 if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
62 caretPosition = caretPosition < 0 ? 0 : caretPosition;
63 } else {
64 // do nothing
65 }
66 controller->CaretPosition(caretPosition);
67 } else {
68 TAG_LOGW(AceLogTag::ACE_TEXT_FIELD, "CaretPosition: The JSTextEditableController is NULL");
69 }
70 }
71
ShowPassword(const JSCallbackInfo & info)72 void JSTextEditableController::ShowPassword(const JSCallbackInfo& info)
73 {
74 auto controller = controllerWeak_.Upgrade();
75 if (controller) {
76 controller->SetPasswordState(false);
77 } else {
78 TAG_LOGW(AceLogTag::ACE_TEXT_FIELD, "ShowPassword: The JSTextEditableController is NULL");
79 }
80 }
81
HidePassword(const JSCallbackInfo & info)82 void JSTextEditableController::HidePassword(const JSCallbackInfo& info)
83 {
84 auto controller = controllerWeak_.Upgrade();
85 if (controller) {
86 controller->SetPasswordState(true);
87 } else {
88 TAG_LOGW(AceLogTag::ACE_TEXT_FIELD, "HidePassword: The JSTextEditableController is NULL");
89 }
90 }
91
SetTextSelection(const JSCallbackInfo & info)92 void JSTextEditableController::SetTextSelection(const JSCallbackInfo& info)
93 {
94 if (info.Length() < 2) { /* 2:args number */
95 return;
96 }
97 auto controller = controllerWeak_.Upgrade();
98 if (controller) {
99 const auto& start = info[0];
100 const auto& end = info[1];
101 std::optional<SelectionOptions> options = std::nullopt;
102 if (!start->IsNumber() || !end->IsNumber()) {
103 TAG_LOGW(AceLogTag::ACE_TEXT_FIELD, "SetTextSelection: The selectionStart or selectionEnd is NULL");
104 }
105 int32_t selectionStart = start->ToNumber<int32_t>();
106 int32_t selectionEnd = end->ToNumber<int32_t>();
107
108 if (info.Length() == 3 && info[2]->IsObject()) { /* 2, 3:args number */
109 SelectionOptions optionTemp;
110 JSRef<JSObject> optionsObj = JSRef<JSObject>::Cast(info[2]); /* 2:args number */
111 JSRef<JSVal> menuPolicy = optionsObj->GetProperty("menuPolicy");
112 int32_t tempPolicy = 0;
113 if (!menuPolicy->IsNull() && JSContainerBase::ParseJsInt32(menuPolicy, tempPolicy)) {
114 optionTemp.menuPolicy = static_cast<MenuPolicy>(tempPolicy);
115 options = optionTemp;
116 }
117 } else {
118 TAG_LOGI(AceLogTag::ACE_TEXT_FIELD, "SetTextSelection: The selectionOption is null");
119 }
120 controller->SetTextSelection(selectionStart, selectionEnd, options);
121 } else {
122 TAG_LOGW(AceLogTag::ACE_TEXT_FIELD, "SetTextSelection: The JSTextEditableController is NULL");
123 }
124 }
125
CreateRectangle(const Rect & info)126 JSRef<JSObject> JSTextEditableController::CreateRectangle(const Rect& info)
127 {
128 JSRef<JSObject> rectObj = JSRef<JSObject>::New();
129 rectObj->SetProperty<double>("x", info.Left());
130 rectObj->SetProperty<double>("y", info.Top());
131 rectObj->SetProperty<double>("width", info.Width());
132 rectObj->SetProperty<double>("height", info.Height());
133 return rectObj;
134 }
135
GetTextContentRect(const JSCallbackInfo & info)136 void JSTextEditableController::GetTextContentRect(const JSCallbackInfo& info)
137 {
138 auto controller = controllerWeak_.Upgrade();
139 if (controller) {
140 auto rectObj = CreateRectangle(controller->GetTextContentRect());
141 JSRef<JSVal> rect = JSRef<JSObject>::Cast(rectObj);
142 info.SetReturnValue(rect);
143 } else {
144 TAG_LOGW(AceLogTag::ACE_TEXT_FIELD, "GetTextContentRect: The JSTextEditableController is NULL");
145 }
146 }
147
GetTextContentLinesNum(const JSCallbackInfo & info)148 void JSTextEditableController::GetTextContentLinesNum(const JSCallbackInfo& info)
149 {
150 auto controller = controllerWeak_.Upgrade();
151 if (controller) {
152 auto lines = controller->GetTextContentLinesNum();
153 auto linesNum = JSVal(ToJSValue(lines));
154 auto textLines = JSRef<JSVal>::Make(linesNum);
155 info.SetReturnValue(textLines);
156 } else {
157 TAG_LOGW(AceLogTag::ACE_TEXT_FIELD, "GetTextContentLinesNum: The JSTextEditableController is NULL");
158 }
159 }
160
AddText(const JSCallbackInfo & info)161 void JSTextEditableController::AddText(const JSCallbackInfo& info)
162 {
163 auto controller = controllerWeak_.Upgrade();
164 if (controller) {
165 const auto& text = info[0];
166 const auto& options = info[1];
167 std::u16string textValue;
168 if (text->IsString()) {
169 textValue = text->ToU16String();
170 } else {
171 TAG_LOGI(AceLogTag::ACE_TEXT_FIELD, "AddText: The text is null");
172 auto returnValue = JSVal(ToJSValue(controller->GetCaretIndex()));
173 info.SetReturnValue(JSRef<JSVal>::Make(returnValue));
174 return;
175 }
176 int32_t offsetIndex = -1;
177 if (options->IsObject()) {
178 JSRef<JSObject> optionObj = JSRef<JSObject>::Cast(options);
179 JSRef<JSVal> offset = optionObj->GetProperty("offset");
180 if (offset->IsNumber()) {
181 offsetIndex = offset->ToNumber<int32_t>();
182 offsetIndex = std::max(0, offsetIndex);
183 }
184 }
185 // add text
186 int32_t result = controller->AddText(textValue, offsetIndex);
187 auto returnValue = JSVal(ToJSValue(result));
188 info.SetReturnValue(JSRef<JSVal>::Make(returnValue));
189 } else {
190 TAG_LOGW(AceLogTag::ACE_TEXT_FIELD, "AddText: The JSTextEditableController is NULL");
191 }
192 }
193
DeleteText(const JSCallbackInfo & info)194 void JSTextEditableController::DeleteText(const JSCallbackInfo& info)
195 {
196 auto controller = controllerWeak_.Upgrade();
197 if (controller) {
198 const auto& textRange = info[0];
199 if (!textRange->IsObject()) {
200 controller->DeleteText(-1, -1);
201 return;
202 }
203 JSRef<JSObject> rangeObj = JSRef<JSObject>::Cast(textRange);
204
205 int32_t startIndex = -1;
206 int32_t endIndex = -1;
207 JSRef<JSVal> start = rangeObj->GetProperty("start");
208 if (start->IsNumber()) {
209 startIndex = start->ToNumber<int32_t>();
210 startIndex = startIndex < 0 ? 0 : startIndex;
211 }
212 JSRef<JSVal> end = rangeObj->GetProperty("end");
213 if (end->IsNumber()) {
214 endIndex = end->ToNumber<int32_t>();
215 endIndex = endIndex < 0 ? -1 : endIndex;
216 }
217 controller->DeleteText(startIndex, endIndex);
218 } else {
219 TAG_LOGW(AceLogTag::ACE_TEXT_FIELD, "DeleteText: The JSTextEditableController is NULL");
220 }
221 }
222
GetSelection(const JSCallbackInfo & info)223 void JSTextEditableController::GetSelection(const JSCallbackInfo& info)
224 {
225 auto controller = controllerWeak_.Upgrade();
226 if (controller) {
227 SelectionInfo selectInfo = controller->GetSelection();
228 JSRef<JSObject> selectionObject = JSRef<JSObject>::New();
229 selectionObject->SetPropertyObject("start",
230 JSRef<JSVal>::Make(ToJSValue(selectInfo.GetSelection().selection[0])));
231 selectionObject->SetPropertyObject("end",
232 JSRef<JSVal>::Make(ToJSValue(selectInfo.GetSelection().selection[1])));
233 info.SetReturnValue(JSRef<JSVal>::Cast(selectionObject));
234 } else {
235 TAG_LOGW(AceLogTag::ACE_TEXT_FIELD, "GetSelection: The JSTextEditableController is NULL");
236 }
237 }
238
GetCaretOffset(const JSCallbackInfo & info)239 void JSTextEditableController::GetCaretOffset(const JSCallbackInfo& info)
240 {
241 auto controller = controllerWeak_.Upgrade();
242 if (controller) {
243 JSRef<JSObject> caretObj = JSRef<JSObject>::New();
244 NG::OffsetF caretOffset = controller->GetCaretPosition();
245 caretObj->SetProperty<int32_t>("index", controller->GetCaretIndex());
246 caretObj->SetProperty<float>("x", caretOffset.GetX());
247 caretObj->SetProperty<float>("y", caretOffset.GetY());
248 JSRef<JSVal> ret = JSRef<JSObject>::Cast(caretObj);
249 info.SetReturnValue(ret);
250 } else {
251 TAG_LOGW(AceLogTag::ACE_TEXT_FIELD, "GetCaretOffset: The JSTextEditableController is NULL");
252 }
253 }
254
ClearPreviewText(const JSCallbackInfo & info)255 void JSTextEditableController::ClearPreviewText(const JSCallbackInfo& info)
256 {
257 auto controller = controllerWeak_.Upgrade();
258 if (controller) {
259 controller->ClearPreviewText();
260 } else {
261 TAG_LOGW(AceLogTag::ACE_TEXT_FIELD, "ClearPreviewText: The JSTextEditableController is NULL");
262 }
263 }
264
GetText(const JSCallbackInfo & info)265 void JSTextEditableController::GetText(const JSCallbackInfo& info)
266 {
267 auto controller = controllerWeak_.Upgrade();
268 if (controller) {
269 std::u16string content = controller->GetText();
270 const auto& textRange = info[0];
271 int32_t startIndex = 0;
272 int32_t endIndex = static_cast<int32_t>(content.length());
273 if (textRange->IsObject()) {
274 JSRef<JSObject> rangeObj = JSRef<JSObject>::Cast(textRange);
275 JSRef<JSVal> start = rangeObj->GetProperty("start");
276 if (start->IsNumber()) {
277 startIndex = start->ToNumber<int32_t>();
278 startIndex = startIndex < 0 ? 0 : startIndex;
279 startIndex = std::clamp(startIndex, 0, static_cast<int32_t>(content.length()));
280 }
281 JSRef<JSVal> end = rangeObj->GetProperty("end");
282 if (end->IsNumber()) {
283 endIndex = end->ToNumber<int32_t>();
284 endIndex = endIndex < 0 ? static_cast<int32_t>(content.length()) : endIndex;
285 endIndex = std::clamp(endIndex, 0, static_cast<int32_t>(content.length()));
286 }
287 if (startIndex > endIndex) {
288 std::swap(startIndex, endIndex);
289 }
290 }
291 std::u16string result = content.substr(startIndex, endIndex - startIndex);
292 auto returnValue = JSVal(ToJSValue(result));
293 info.SetReturnValue(JSRef<JSVal>::Make(returnValue));
294 } else {
295 TAG_LOGW(AceLogTag::ACE_TEXT_FIELD, "GetText: The JSTextEditableController is NULL");
296 }
297 }
298
StopEditing()299 void JSTextEditableController::StopEditing()
300 {
301 auto controller = controllerWeak_.Upgrade();
302 if (controller) {
303 controller->StopEditing();
304 } else {
305 TAG_LOGW(AceLogTag::ACE_TEXT_FIELD, "StopEditing: The JSTextEditableController is NULL");
306 }
307 }
308 } // namespace OHOS::Ace::Framework
309