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