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 "core/common/stylus/stylus_detector_mgr.h"
17
18 #include "base/geometry/dimension.h"
19 #include "base/geometry/ng/offset_t.h"
20 #include "base/geometry/ng/rect_t.h"
21 #include "base/geometry/ng/size_t.h"
22 #include "base/geometry/point.h"
23 #include "base/utils/time_util.h"
24 #include "base/utils/utils.h"
25 #include "core/common/container.h"
26 #include "core/common/stylus/stylus_detector_default.h"
27 #include "core/common/stylus/stylus_detector_loader.h"
28 #include "core/components_ng/base/frame_node.h"
29 #include "core/components_ng/base/view_stack_processor.h"
30 #include "core/components_ng/gestures/recognizers/gesture_recognizer.h"
31 #include "core/components_ng/pattern/rich_editor/rich_editor_pattern.h"
32 #include "core/components_ng/pattern/search/search_text_field.h"
33 #include "core/components_ng/pattern/text/text_base.h"
34 #include "core/components_ng/pattern/text_field/text_field_pattern.h"
35 #include "core/components_v2/inspector/inspector_constants.h"
36 #include "core/pipeline_ng/pipeline_context.h"
37 #include "frameworks/base/log/log_wrapper.h"
38
39 namespace OHOS::Ace {
40 const static std::unordered_set<std::string> TEXT_FIELD_COMPONENT_TAGS = {
41 V2::TEXTINPUT_ETS_TAG,
42 V2::TEXTAREA_ETS_TAG,
43 V2::RICH_EDITOR_ETS_TAG,
44 V2::SEARCH_Field_ETS_TAG,
45 };
46
47 namespace {
48 constexpr int32_t INDEX_S = 1;
49 constexpr int32_t INDEX_E = 2;
50 } // namespace
51
RequestFocus(int32_t nodeId, RefPtr<TaskExecutor> taskScheduler)52 int32_t StylusDetectorMgr::StylusDetectorCallBack::RequestFocus(int32_t nodeId, RefPtr<TaskExecutor> taskScheduler)
53 {
54 int32_t resultCode = -1;
55 taskScheduler->PostSyncTask(
56 [&resultCode, nodeId]() {
57 auto UiNode = ElementRegister::GetInstance()->GetUINodeById(nodeId);
58 auto frameNode = AceType::DynamicCast<NG::FrameNode>(UiNode);
59 CHECK_NULL_VOID(frameNode);
60 auto focusHub = frameNode->GetFocusHub();
61 CHECK_NULL_VOID(focusHub);
62 if (frameNode->GetTag() == V2::SEARCH_Field_ETS_TAG) {
63 auto searchTextFieldPattern = frameNode->GetPattern<NG::SearchTextFieldPattern>();
64 CHECK_NULL_VOID(searchTextFieldPattern);
65 focusHub = searchTextFieldPattern->GetFocusHub();
66 CHECK_NULL_VOID(focusHub);
67 }
68 if (!focusHub->IsCurrentFocus()) {
69 focusHub->RequestFocusImmediately();
70 }
71 if (frameNode->GetTag() == V2::RICH_EDITOR_ETS_TAG) {
72 resultCode = 0;
73 return;
74 }
75 auto pattern = frameNode->GetPattern<NG::TextFieldPattern>();
76 CHECK_NULL_VOID(pattern);
77 bool needToRequestKeyBoardOnFocus = pattern->NeedToRequestKeyboardOnFocus();
78 if (!needToRequestKeyBoardOnFocus) {
79 pattern->RequestKeyboardNotByFocusSwitch(NG::RequestKeyboardReason::STYLUS_DETECTOR);
80 }
81 resultCode = 0;
82 },
83 TaskExecutor::TaskType::UI, "ArkUIDetectorSyncStylusAction");
84 return resultCode;
85 }
86
SetText(int32_t nodeId, void* data, RefPtr<TaskExecutor> taskScheduler, std::shared_ptr<IAceStylusCallback> callback)87 int32_t StylusDetectorMgr::StylusDetectorCallBack::SetText(int32_t nodeId, void* data,
88 RefPtr<TaskExecutor> taskScheduler, std::shared_ptr<IAceStylusCallback> callback)
89 {
90 std::string text = *static_cast<std::string*>(data);
91 taskScheduler->PostTask(
92 [text, callback, nodeId]() {
93 ResultData res;
94 auto UiNode = ElementRegister::GetInstance()->GetUINodeById(nodeId);
95 auto frameNode = AceType::DynamicCast<NG::FrameNode>(UiNode);
96 CHECK_NULL_VOID(frameNode);
97 if (frameNode->GetTag() == V2::RICH_EDITOR_ETS_TAG) {
98 return;
99 }
100 auto pattern = frameNode->GetPattern<NG::TextFieldPattern>();
101 CHECK_NULL_VOID(pattern);
102 if (!text.empty()) {
103 pattern->UpdateEditingValue(text, text.size());
104 frameNode->MarkDirtyNode(NG::PROPERTY_UPDATE_MEASURE_SELF);
105 }
106 if (callback) {
107 callback->Callback(res);
108 }
109 },
110 TaskExecutor::TaskType::UI, "ArkUIDetectorStylusAction");
111 return 0;
112 }
113
GetText(int32_t nodeId, RefPtr<TaskExecutor> taskScheduler, std::shared_ptr<IAceStylusCallback> callback)114 int32_t StylusDetectorMgr::StylusDetectorCallBack::GetText(int32_t nodeId, RefPtr<TaskExecutor> taskScheduler,
115 std::shared_ptr<IAceStylusCallback> callback)
116 {
117 taskScheduler->PostTask(
118 [callback, nodeId]() {
119 CHECK_NULL_VOID(callback);
120 ResultData res;
121 res.resultData = "";
122 auto UiNode = ElementRegister::GetInstance()->GetUINodeById(nodeId);
123 auto frameNode = AceType::DynamicCast<NG::FrameNode>(UiNode);
124 if (!frameNode) {
125 callback->Callback(res);
126 return;
127 }
128 if (frameNode->GetTag() == V2::RICH_EDITOR_ETS_TAG) {
129 callback->Callback(res);
130 return;
131 }
132 auto pattern = frameNode->GetPattern<NG::TextFieldPattern>();
133 if (!pattern) {
134 callback->Callback(res);
135 return;
136 }
137 res.resultData = pattern->GetTextValue();
138 callback->Callback(res);
139 },
140 TaskExecutor::TaskType::UI, "ArkUIDetectorStylusAction");
141 return 0;
142 }
143
Redo(int32_t nodeId, RefPtr<TaskExecutor> taskScheduler)144 int32_t StylusDetectorMgr::StylusDetectorCallBack::Redo(int32_t nodeId, RefPtr<TaskExecutor> taskScheduler)
145 {
146 taskScheduler->PostTask(
147 [nodeId]() {
148 auto UiNode = ElementRegister::GetInstance()->GetUINodeById(nodeId);
149 auto frameNode = AceType::DynamicCast<NG::FrameNode>(UiNode);
150 CHECK_NULL_VOID(frameNode);
151 CHECK_EQUAL_VOID(frameNode->GetTag(), V2::RICH_EDITOR_ETS_TAG);
152 auto pattern = frameNode->GetPattern<NG::TextFieldPattern>();
153 CHECK_NULL_VOID(pattern);
154 pattern->HandleOnRedoAction();
155 frameNode->MarkDirtyNode(NG::PROPERTY_UPDATE_MEASURE_SELF);
156 },
157 TaskExecutor::TaskType::UI, "ArkUIDetectorStylusAction");
158 return 0;
159 }
160
Undo(int32_t nodeId, RefPtr<TaskExecutor> taskScheduler)161 int32_t StylusDetectorMgr::StylusDetectorCallBack::Undo(int32_t nodeId, RefPtr<TaskExecutor> taskScheduler)
162 {
163 taskScheduler->PostTask(
164 [nodeId]() {
165 auto UiNode = ElementRegister::GetInstance()->GetUINodeById(nodeId);
166 auto frameNode = AceType::DynamicCast<NG::FrameNode>(UiNode);
167 CHECK_NULL_VOID(frameNode);
168 CHECK_EQUAL_VOID(frameNode->GetTag(), V2::RICH_EDITOR_ETS_TAG);
169 auto pattern = frameNode->GetPattern<NG::TextFieldPattern>();
170 CHECK_NULL_VOID(pattern);
171 pattern->HandleOnUndoAction();
172 frameNode->MarkDirtyNode(NG::PROPERTY_UPDATE_MEASURE_SELF);
173 },
174 TaskExecutor::TaskType::UI, "ArkUIDetectorStylusAction");
175 return 0;
176 }
177
DeleteText(int32_t nodeId, void* data, RefPtr<TaskExecutor> taskScheduler)178 int32_t StylusDetectorMgr::StylusDetectorCallBack::DeleteText(int32_t nodeId, void* data,
179 RefPtr<TaskExecutor> taskScheduler)
180 {
181 int32_t resultCode = -1;
182 StylusGestureRect *rectPtr = static_cast<StylusGestureRect*>(data);
183 CHECK_NULL_RETURN(rectPtr, resultCode);
184 auto rect = *rectPtr;
185 taskScheduler->PostSyncTask(
186 [&resultCode, nodeId, rect]() {
187 TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture rect:%{public}f, %{public}f, %{public}f, %{public}f",
188 rect.Left, rect.Top, rect.Width, rect.Height);
189 auto UiNode = ElementRegister::GetInstance()->GetUINodeById(nodeId);
190 auto frameNode = AceType::DynamicCast<NG::FrameNode>(UiNode);
191 CHECK_NULL_VOID(frameNode);
192 ContainerScope scope(frameNode->GetInstanceId());
193 auto pipeline = frameNode->GetContextRefPtr();
194 CHECK_NULL_VOID(pipeline);
195 auto windowRect = pipeline->GetCurrentWindowRect();
196 TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture windowRect:%{public}f, %{public}f",
197 windowRect.Left(), windowRect.Top());
198 Offset startCenterGlobalOffset = Offset(rect.Left - windowRect.Left(),
199 rect.Top - windowRect.Top() + rect.Height / 2);
200 Offset endCenterGlobalOffset = Offset(rect.Left - windowRect.Left() + rect.Width,
201 rect.Top - windowRect.Top() + rect.Height / 2);
202 auto sInd = GetGlyphPositionByGlobalOffset(frameNode, startCenterGlobalOffset);
203 auto eInd = GetGlyphPositionByGlobalOffset(frameNode, endCenterGlobalOffset);
204 auto textBase = frameNode->GetPattern<NG::TextBase>();
205 CHECK_NULL_VOID(textBase);
206 auto wtextLength = textBase->GetContentWideTextLength();
207 TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture wtextLength:%{public}d", wtextLength);
208 auto ret = CalculateIntersectedRegion(sInd, eInd, wtextLength);
209 if (std::get<0>(ret) == 0) {
210 auto textInputClient = frameNode->GetPattern<TextInputClient>();
211 CHECK_NULL_VOID(textInputClient);
212 textInputClient->SetCaretOffset(std::get<INDEX_S>(ret));
213 textInputClient->DeleteForward(std::get<INDEX_E>(ret) - std::get<INDEX_S>(ret));
214 resultCode = 0;
215 }
216 },
217 TaskExecutor::TaskType::UI, "ArkUIDetectorSyncStylusAction");
218 return resultCode;
219 }
220
ChoiceText(int32_t nodeId, void* data, RefPtr<TaskExecutor> taskScheduler)221 int32_t StylusDetectorMgr::StylusDetectorCallBack::ChoiceText(int32_t nodeId, void* data,
222 RefPtr<TaskExecutor> taskScheduler)
223 {
224 int32_t resultCode = -1;
225 ChoiceTextOption *optionPtr = static_cast<ChoiceTextOption*>(data);
226 CHECK_NULL_RETURN(optionPtr, resultCode);
227 auto choiceTextOption = *optionPtr;
228 taskScheduler->PostSyncTask(
229 [&resultCode, nodeId, choiceTextOption]() {
230 auto rect = choiceTextOption.rect;
231 auto UiNode = ElementRegister::GetInstance()->GetUINodeById(nodeId);
232 auto frameNode = AceType::DynamicCast<NG::FrameNode>(UiNode);
233 CHECK_NULL_VOID(frameNode);
234 ContainerScope scope(frameNode->GetInstanceId());
235 auto pipeline = frameNode->GetContextRefPtr();
236 CHECK_NULL_VOID(pipeline);
237 auto windowRect = pipeline->GetCurrentWindowRect();
238 TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture windowRect:%{public}f, %{public}f",
239 windowRect.Left(), windowRect.Top());
240 Offset startCenterGlobalOffset = Offset(rect.Left - windowRect.Left(),
241 rect.Top - windowRect.Top() + rect.Height / 2);
242 Offset endCenterGlobalOffset = Offset(rect.Left - windowRect.Left() + rect.Width,
243 rect.Top - windowRect.Top() + rect.Height / 2);
244 auto sInd = GetGlyphPositionByGlobalOffset(frameNode, startCenterGlobalOffset);
245 auto eInd = GetGlyphPositionByGlobalOffset(frameNode, endCenterGlobalOffset);
246 auto textBase = frameNode->GetPattern<NG::TextBase>();
247 CHECK_NULL_VOID(textBase);
248 auto wtextLength = textBase->GetContentWideTextLength();
249 TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture wtextLength:%{public}d", wtextLength);
250 if (StylusDetectorMgr::GetInstance()->sInd_ == static_cast<int32_t>(sInd.position_) &&
251 StylusDetectorMgr::GetInstance()->eInd_ == static_cast<int32_t>(eInd.position_) &&
252 StylusDetectorMgr::GetInstance()->showMenu_ == choiceTextOption.showMenu) {
253 return;
254 }
255 StylusDetectorMgr::GetInstance()->sInd_ = static_cast<int32_t>(sInd.position_);
256 StylusDetectorMgr::GetInstance()->eInd_ = static_cast<int32_t>(eInd.position_);
257 StylusDetectorMgr::GetInstance()->showMenu_ = choiceTextOption.showMenu;
258 auto ret = CalculateIntersectedRegion(sInd, eInd, wtextLength);
259 if (std::get<0>(ret) == 0) {
260 auto textInputClient = frameNode->GetPattern<TextInputClient>();
261 CHECK_NULL_VOID(textInputClient);
262 SelectionOptions option = { .menuPolicy = MenuPolicy::HIDE };
263 if (choiceTextOption.showMenu) {
264 option = { .menuPolicy = MenuPolicy::SHOW };
265 }
266 textInputClient->SetSelection(std::get<INDEX_S>(ret), std::get<INDEX_E>(ret), option);
267 resultCode = 0;
268 }
269 },
270 TaskExecutor::TaskType::UI, "ArkUIDetectorSyncStylusAction");
271 return resultCode;
272 }
273
InsertSpace(int32_t nodeId, void* data, RefPtr<TaskExecutor> taskScheduler)274 int32_t StylusDetectorMgr::StylusDetectorCallBack::InsertSpace(int32_t nodeId, void* data,
275 RefPtr<TaskExecutor> taskScheduler)
276 {
277 int32_t resultCode = -1;
278 StylusGestureRect *rectPtr = static_cast<StylusGestureRect*>(data);
279 CHECK_NULL_RETURN(rectPtr, resultCode);
280 auto rect = *rectPtr;
281 taskScheduler->PostSyncTask(
282 [&resultCode, nodeId, rect]() {
283 TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture global rect:%{public}f, %{public}f, %{public}f, %{public}f",
284 rect.Left, rect.Top, rect.Width, rect.Height);
285 auto UiNode = ElementRegister::GetInstance()->GetUINodeById(nodeId);
286 auto frameNode = AceType::DynamicCast<NG::FrameNode>(UiNode);
287 CHECK_NULL_VOID(frameNode);
288 ContainerScope scope(frameNode->GetInstanceId());
289 auto pipeline = frameNode->GetContextRefPtr();
290 CHECK_NULL_VOID(pipeline);
291 auto windowRect = pipeline->GetCurrentWindowRect();
292 TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture windowRect:%{public}f, %{public}f",
293 windowRect.Left(), windowRect.Top());
294 Offset centerGlobalOffset = Offset(rect.Left - windowRect.Left() + rect.Width / 2,
295 rect.Top - windowRect.Top() + rect.Height / 2);
296 auto sInd = GetGlyphPositionByGlobalOffset(frameNode, centerGlobalOffset);
297 auto textInputClient = frameNode->GetPattern<TextInputClient>();
298 CHECK_NULL_VOID(textInputClient);
299 auto start = static_cast<int32_t>(sInd.position_);
300 auto result = textInputClient->InsertOrDeleteSpace(start);
301 if (result) {
302 resultCode = 0;
303 }
304 },
305 TaskExecutor::TaskType::UI, "ArkUIDetectorSyncStylusAction");
306 return resultCode;
307 }
308
MoveCursor(int32_t nodeId, void* data, RefPtr<TaskExecutor> taskScheduler)309 int32_t StylusDetectorMgr::StylusDetectorCallBack::MoveCursor(int32_t nodeId, void* data,
310 RefPtr<TaskExecutor> taskScheduler)
311 {
312 int32_t resultCode = -1;
313 MoveCursorOption *pointPtr = static_cast<MoveCursorOption*>(data);
314 CHECK_NULL_RETURN(pointPtr, resultCode);
315 auto point = *pointPtr;
316 taskScheduler->PostSyncTask(
317 [&resultCode, nodeId, point]() {
318 TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture point:%{public}d, %{public}d", point.x, point.y);
319 auto UiNode = ElementRegister::GetInstance()->GetUINodeById(nodeId);
320 auto frameNode = AceType::DynamicCast<NG::FrameNode>(UiNode);
321 CHECK_NULL_VOID(frameNode);
322 ContainerScope scope(frameNode->GetInstanceId());
323 auto pipeline = frameNode->GetContextRefPtr();
324 CHECK_NULL_VOID(pipeline);
325 auto windowRect = pipeline->GetCurrentWindowRect();
326 TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture windowRect:%{public}f, %{public}f",
327 windowRect.Left(), windowRect.Top());
328 Offset centerGlobalOffset = Offset(point.x - windowRect.Left(), point.y - windowRect.Top());
329 auto sInd = GetGlyphPositionByGlobalOffset(frameNode, centerGlobalOffset);
330 resultCode = HandleMoveCursor(frameNode, sInd, point.showHandle);
331 },
332 TaskExecutor::TaskType::UI, "ArkUIDetectorSyncStylusAction");
333 return resultCode;
334 }
335
HandleMoveCursor(const RefPtr<NG::FrameNode>& frameNode, NG::PositionWithAffinity sInd, bool showHandle)336 int32_t StylusDetectorMgr::StylusDetectorCallBack::HandleMoveCursor(const RefPtr<NG::FrameNode>& frameNode,
337 NG::PositionWithAffinity sInd, bool showHandle)
338 {
339 auto textBase = frameNode->GetPattern<NG::TextBase>();
340 CHECK_NULL_RETURN(textBase, -1);
341 auto wtextLength = textBase->GetContentWideTextLength();
342 TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture wtextLength:%{public}d", wtextLength);
343 auto start = static_cast<int32_t>(sInd.position_);
344 if (start >= 0 && start <= wtextLength) {
345 auto textInputClient = frameNode->GetPattern<TextInputClient>();
346 CHECK_NULL_RETURN(textInputClient, -1);
347 textInputClient->SetCaretOffset(start);
348 frameNode->MarkDirtyNode(NG::PROPERTY_UPDATE_MEASURE_SELF);
349 return 0;
350 }
351 TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture do not cross");
352 return -1;
353 }
354
GetGlyphPositionByGlobalOffset( const RefPtr<NG::FrameNode>& frameNode, const Offset& offset)355 NG::PositionWithAffinity StylusDetectorMgr::StylusDetectorCallBack::GetGlyphPositionByGlobalOffset(
356 const RefPtr<NG::FrameNode>& frameNode, const Offset& offset)
357 {
358 TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture offset:%{public}f, %{public}f", offset.GetX(), offset.GetY());
359 NG::PositionWithAffinity finalResult(-1, TextAffinity::UPSTREAM);
360 // transform point coords from global to local
361 auto parentGlobalOffset_ = GetPaintRectGlobalOffset(frameNode);
362 auto localOffset = offset - Offset(parentGlobalOffset_.GetX(), parentGlobalOffset_.GetY());
363 if (NG::TextBase::HasRenderTransform(frameNode)) {
364 TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture pattern has select-overlay render transform");
365 auto localOffsetF = NG::OffsetF(offset.GetX(), offset.GetY());
366 NG::TextBase::RevertLocalPointWithTransform(frameNode, localOffsetF);
367 localOffset.SetX(localOffsetF.GetX());
368 localOffset.SetY(localOffsetF.GetY());
369 }
370 TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture localOffset:%{public}f, %{public}f", localOffset.GetX(),
371 localOffset.GetY());
372 auto textDragBase = frameNode->GetPattern<NG::TextDragBase>();
373 CHECK_NULL_RETURN(textDragBase, finalResult);
374 auto textRect = textDragBase->GetTextRect();
375 if (localOffset.GetY() < textRect.GetY() || localOffset.GetY() > textRect.GetY() + textRect.Height()) {
376 TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture point outside the area");
377 return finalResult;
378 }
379 // calculate the start and end indexes of the intersecting region.
380 auto layoutInfo = StylusDetectorMgr::GetInstance()->layoutInfo_.Upgrade();
381 CHECK_NULL_RETURN(layoutInfo, finalResult);
382 return layoutInfo->GetGlyphPositionAtCoordinate(localOffset.GetX(), localOffset.GetY());
383 }
384
CalculateIntersectedRegion( NG::PositionWithAffinity sInd, NG::PositionWithAffinity eInd, int32_t wtextLength)385 std::tuple<int32_t, int32_t, int32_t> StylusDetectorMgr::StylusDetectorCallBack::CalculateIntersectedRegion(
386 NG::PositionWithAffinity sInd, NG::PositionWithAffinity eInd, int32_t wtextLength)
387 {
388 TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture wtextLength:%{public}d", wtextLength);
389 int32_t startPos = static_cast<int32_t>(std::min(eInd.position_, sInd.position_));
390 int32_t endPos = static_cast<int32_t>(std::max(eInd.position_, sInd.position_));
391 if (endPos < 1 || startPos >= wtextLength) {
392 TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture rect do not cross");
393 return std::make_tuple(-1, 0, 0);
394 }
395 // calculate the cross length
396 int32_t start = std::max(startPos, 0);
397 int32_t end = std::min(endPos, wtextLength);
398 if (start < end) {
399 return std::make_tuple(0, start, end);
400 } else {
401 TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture rect do not cross");
402 return std::make_tuple(-1, 0, 0);
403 }
404 }
405
GetPaintRectGlobalOffset( const RefPtr<NG::FrameNode>& frameNode)406 NG::OffsetF StylusDetectorMgr::StylusDetectorCallBack::GetPaintRectGlobalOffset(
407 const RefPtr<NG::FrameNode>& frameNode)
408 {
409 CHECK_NULL_RETURN(frameNode, NG::OffsetF(0.0f, 0.0f));
410 auto pipeline = frameNode->GetContextRefPtr();
411 CHECK_NULL_RETURN(pipeline, NG::OffsetF(0.0f, 0.0f));
412 auto rootOffset = pipeline->GetRootRect().GetOffset();
413 auto textPaintOffset = frameNode->GetPaintRectOffset();
414 return textPaintOffset - rootOffset;
415 }
416
OnDetector( const CommandType& command, void* data, std::shared_ptr<IAceStylusCallback> callback)417 int32_t StylusDetectorMgr::StylusDetectorCallBack::OnDetector(
418 const CommandType& command, void* data, std::shared_ptr<IAceStylusCallback> callback)
419 {
420 auto nodeId = StylusDetectorMgr::GetInstance()->GetDefaultNodeId();
421 if (nodeId == 0) {
422 return -1;
423 }
424 auto container = Container::CurrentSafely();
425 CHECK_NULL_RETURN(container, -1);
426 auto pipelineContext = container->GetPipelineContext();
427 CHECK_NULL_RETURN(pipelineContext, -1);
428 auto taskScheduler = pipelineContext->GetTaskExecutor();
429 CHECK_NULL_RETURN(taskScheduler, -1);
430
431 int32_t resultCode = -1;
432 ResultData res;
433 switch (command) {
434 case COMMAND_REQUEST_FOCUS:
435 return StylusDetectorMgr::StylusDetectorCallBack::RequestFocus(nodeId, taskScheduler);
436 case COMMAND_MOVE_CURSOR:
437 return StylusDetectorMgr::StylusDetectorCallBack::MoveCursor(nodeId, data, taskScheduler);
438 case COMMAND_DELETE_TEXT:
439 return StylusDetectorMgr::StylusDetectorCallBack::DeleteText(nodeId, data, taskScheduler);
440 case COMMAND_CHOICE_TEXT:
441 return StylusDetectorMgr::StylusDetectorCallBack::ChoiceText(nodeId, data, taskScheduler);
442 case COMMAND_INSERT_SPACE:
443 return StylusDetectorMgr::StylusDetectorCallBack::InsertSpace(nodeId, data, taskScheduler);
444 case COMMAND_CLEAR_HIT:
445 return resultCode;
446 case COMMAND_SET_TEXT:
447 return StylusDetectorMgr::StylusDetectorCallBack::SetText(nodeId, data, taskScheduler, callback);
448 case COMMAND_GET_TEXT:
449 return StylusDetectorMgr::StylusDetectorCallBack::GetText(nodeId, taskScheduler, callback);
450 case COMMAND_UNDO:
451 return StylusDetectorMgr::StylusDetectorCallBack::Undo(nodeId, taskScheduler);
452 case COMMAND_REDO:
453 return StylusDetectorMgr::StylusDetectorCallBack::Redo(nodeId, taskScheduler);
454 case COMMAND_INVALID:
455 TAG_LOGE(AceLogTag::ACE_STYLUS, "StylusDetector received error command.");
456 return resultCode;
457 default:
458 return resultCode;
459 }
460 }
461
OnDetectorSync(const CommandType& command)462 bool StylusDetectorMgr::StylusDetectorCallBack::OnDetectorSync(const CommandType& command)
463 {
464 bool result = false;
465 auto nodeId = StylusDetectorMgr::GetInstance()->GetDefaultNodeId();
466 CHECK_EQUAL_RETURN(nodeId, 0, result);
467 auto container = Container::CurrentSafely();
468 CHECK_NULL_RETURN(container, result);
469 auto pipelineContext = container->GetPipelineContext();
470 CHECK_NULL_RETURN(pipelineContext, result);
471 auto taskScheduler = pipelineContext->GetTaskExecutor();
472 CHECK_NULL_RETURN(taskScheduler, result);
473
474 TAG_LOGI(AceLogTag::ACE_STYLUS, "Stylus received commandType:%{public}d", static_cast<int32_t>(command));
475 taskScheduler->PostSyncTask(
476 [nodeId, command, &result]() {
477 auto UiNode = ElementRegister::GetInstance()->GetUINodeById(nodeId);
478 auto frameNode = AceType::DynamicCast<NG::FrameNode>(UiNode);
479 CHECK_NULL_VOID(frameNode);
480 CHECK_EQUAL_VOID(frameNode->GetTag(), V2::RICH_EDITOR_ETS_TAG);
481 auto pattern = frameNode->GetPattern<NG::TextFieldPattern>();
482 CHECK_NULL_VOID(pattern);
483
484 switch (command) {
485 case COMMAND_CANUNDO:
486 result = pattern->CanUndo();
487 break;
488 case COMMAND_CANREDO:
489 result = pattern->CanRedo();
490 break;
491 default:
492 break;
493 }
494 },
495 TaskExecutor::TaskType::UI, "ArkUIDetectorSyncStylusAction");
496 return result;
497 }
498
GetInstance()499 StylusDetectorMgr* StylusDetectorMgr::GetInstance()
500 {
501 static StylusDetectorMgr instance;
502 return &instance;
503 }
504
IsEnable()505 bool StylusDetectorMgr::IsEnable()
506 {
507 CHECK_NULL_RETURN(engine_, false);
508 auto isEnable = engine_->IsEnable();
509 CHECK_NULL_RETURN(isEnable, false);
510 return isEnable;
511 }
RegisterStylusInteractionListener( const std::string& bundleName, const std::shared_ptr<IStylusDetectorCallback>& callback)512 bool StylusDetectorMgr::RegisterStylusInteractionListener(
513 const std::string& bundleName, const std::shared_ptr<IStylusDetectorCallback>& callback)
514 {
515 CHECK_NULL_RETURN(engine_, false);
516 return engine_->RegisterStylusInteractionListener(bundleName, callback);
517 }
UnRegisterStylusInteractionListener(const std::string& bundleName)518 void StylusDetectorMgr::UnRegisterStylusInteractionListener(const std::string& bundleName)
519 {
520 CHECK_NULL_VOID(engine_);
521 return engine_->UnRegisterStylusInteractionListener(bundleName);
522 }
Notify(const NotifyInfo& notifyInfo)523 bool StylusDetectorMgr::Notify(const NotifyInfo& notifyInfo)
524 {
525 CHECK_NULL_RETURN(engine_, false);
526 return engine_->Notify(notifyInfo);
527 }
528
FindHitFrameNode( const TouchEvent& touchEvent, const TouchTestResult& touchTestResult)529 RefPtr<NG::FrameNode> StylusDetectorMgr::FindHitFrameNode(
530 const TouchEvent& touchEvent, const TouchTestResult& touchTestResult)
531 {
532 RefPtr<NG::FrameNode> frameNode;
533 // TextField, textInput, search and richEditor has default touchEventTarget.
534 for (const auto& entry : touchTestResult) {
535 auto recognizer = AceType::DynamicCast<NG::NGGestureRecognizer>(entry);
536 if (recognizer) {
537 continue;
538 }
539 auto nodeId = entry->GetNodeId();
540 auto UiNode = ElementRegister::GetInstance()->GetUINodeById(nodeId);
541 frameNode = AceType::DynamicCast<NG::FrameNode>(UiNode);
542 if (frameNode) {
543 break;
544 }
545 }
546 CHECK_NULL_RETURN(frameNode, nullptr);
547
548 auto pipeline = frameNode->GetContextRefPtr();
549 if (!pipeline) {
550 TAG_LOGI(AceLogTag::ACE_STYLUS, "Can't find pipeline for find hit node.");
551 return nullptr;
552 }
553 auto nanoTimestamp = pipeline->GetVsyncTime();
554 auto textBasePattern = frameNode->GetPattern<NG::TextBase>();
555 CHECK_NULL_RETURN(textBasePattern, nullptr);
556 if (!textBasePattern->IsTextEditableForStylus() ||
557 IsHitCleanNodeResponseArea({ touchEvent.x, touchEvent.y }, frameNode, nanoTimestamp)) {
558 return nullptr;
559 }
560 return frameNode;
561 }
562
IsNeedInterceptedTouchEvent( const TouchEvent& touchEvent, std::unordered_map<size_t, TouchTestResult> touchTestResults)563 bool StylusDetectorMgr::IsNeedInterceptedTouchEvent(
564 const TouchEvent& touchEvent, std::unordered_map<size_t, TouchTestResult> touchTestResults)
565 {
566 if (!IsStylusTouchEvent(touchEvent)) {
567 return false;
568 }
569
570 const auto iter = touchTestResults.find(touchEvent.id);
571 if (iter == touchTestResults.end() || iter->second.empty()) {
572 TAG_LOGI(AceLogTag::ACE_STYLUS, "TouchTestResult is empty");
573 return false;
574 }
575
576 auto frameNode = FindHitFrameNode(touchEvent, iter->second);
577 if (!frameNode) {
578 TAG_LOGI(AceLogTag::ACE_STYLUS, "Stylus hit position is (%{public}f, %{public}f). TargetNode is None",
579 touchEvent.x, touchEvent.y);
580 return false;
581 }
582
583 TAG_LOGI(AceLogTag::ACE_STYLUS,
584 "Stylus hit position is (%{public}f, %{public}f). TargetNode is %{public}s, id is %{public}s", touchEvent.x,
585 touchEvent.y, frameNode->GetTag().c_str(), frameNode->GetInspectorId()->c_str());
586
587 if (!IsEnable()) {
588 TAG_LOGI(AceLogTag::ACE_STYLUS, "Stylus service is not enable");
589 return false;
590 }
591
592 auto container = Container::Current();
593 CHECK_NULL_RETURN(container, false);
594 auto bundleName = container->GetBundleName();
595 NotifyInfo info;
596 info.componentId = frameNode->GetId();
597 nodeId_ = info.componentId;
598 const auto layoutIter = textFieldLayoutInfos_.find(nodeId_);
599 if (layoutIter != textFieldLayoutInfos_.end()) {
600 layoutInfo_ = layoutIter->second;
601 }
602 info.x = touchEvent.screenX;
603 info.y = touchEvent.screenY;
604 info.bundleName = bundleName;
605 auto stylusDetectorCallback = std::make_shared<StylusDetectorCallBack>();
606 isRegistered_ = RegisterStylusInteractionListener(bundleName, stylusDetectorCallback);
607 sInd_ = -1;
608 eInd_ = -1;
609 showMenu_ = false;
610 return Notify(info);
611 }
612
AddTextFieldFrameNode(const RefPtr<NG::FrameNode>& frameNode, const WeakPtr<NG::LayoutInfoInterface>& layoutInfo)613 void StylusDetectorMgr::AddTextFieldFrameNode(const RefPtr<NG::FrameNode>& frameNode,
614 const WeakPtr<NG::LayoutInfoInterface>& layoutInfo)
615 {
616 CHECK_NULL_VOID(frameNode);
617 auto tag = frameNode->GetTag();
618 auto iter = TEXT_FIELD_COMPONENT_TAGS.find(tag);
619 if (iter == TEXT_FIELD_COMPONENT_TAGS.end()) {
620 return;
621 }
622 auto id = frameNode->GetId();
623 auto destructor = [id]() { StylusDetectorMgr::GetInstance()->RemoveTextFieldFrameNode(id); };
624 frameNode->PushDestroyCallbackWithTag(std::move(destructor), "DestroyCallbackForStylus");
625 textFieldNodes_[id] = AceType::WeakClaim(AceType::RawPtr(frameNode));
626 textFieldLayoutInfos_[id] = layoutInfo;
627 }
628
RemoveTextFieldFrameNode(const int32_t id)629 void StylusDetectorMgr::RemoveTextFieldFrameNode(const int32_t id)
630 {
631 textFieldNodes_.erase(id);
632 textFieldLayoutInfos_.erase(id);
633 if (textFieldNodes_.empty()) {
634 auto container = Container::Current();
635 CHECK_NULL_VOID(container);
636 auto bundleName = container->GetBundleName();
637 isRegistered_ = false;
638 UnRegisterStylusInteractionListener(bundleName);
639 }
640 }
641
StylusDetectorMgr()642 StylusDetectorMgr::StylusDetectorMgr() : engine_(nullptr), isRegistered_(false)
643 {
644 auto lib = StylusDetectorLoader::Load();
645 if (!lib || !(engine_ = lib->CreateStylusDetector())) {
646 engine_ = StylusDetectorInstance(StylusDetectorDefault::GetInstance(), [](StylusDetectorInterface* e) {});
647 }
648 }
649
IsStylusTouchEvent(const TouchEvent& touchEvent) const650 bool StylusDetectorMgr::IsStylusTouchEvent(const TouchEvent& touchEvent) const
651 {
652 return touchEvent.sourceTool == SourceTool::PEN && touchEvent.type == TouchType::DOWN;
653 }
654
IsHitCleanNodeResponseArea( const NG::PointF& point, const RefPtr<NG::FrameNode>& frameNode, uint64_t nanoTimestamp)655 bool StylusDetectorMgr::IsHitCleanNodeResponseArea(
656 const NG::PointF& point, const RefPtr<NG::FrameNode>& frameNode, uint64_t nanoTimestamp)
657 {
658 CHECK_NULL_RETURN(frameNode, false);
659 if (frameNode->GetTag() != V2::TEXTINPUT_ETS_TAG) {
660 return false;
661 }
662
663 auto textFieldPattern = frameNode->GetPattern<NG::TextFieldPattern>();
664 CHECK_NULL_RETURN(textFieldPattern, false);
665 auto responseArea = textFieldPattern->GetCleanNodeResponseArea();
666 CHECK_NULL_RETURN(responseArea, false);
667 auto cleanNodeResponseArea = AceType::DynamicCast<NG::CleanNodeResponseArea>(responseArea);
668 if (!cleanNodeResponseArea->IsShow()) {
669 return false;
670 }
671
672 auto cleanNodeFrameNode = cleanNodeResponseArea->GetFrameNode();
673 CHECK_NULL_RETURN(cleanNodeFrameNode, false);
674 auto cleanNodeGeometryNode = cleanNodeFrameNode->GetGeometryNode();
675 CHECK_NULL_RETURN(cleanNodeGeometryNode, false);
676 auto globalFrameRect = cleanNodeGeometryNode->GetFrameRect();
677 globalFrameRect.SetOffset(cleanNodeFrameNode->CalculateCachedTransformRelativeOffset(nanoTimestamp));
678 return globalFrameRect.IsInRegion(point);
679 }
680 } // namespace OHOS::Ace