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_callback.h"
17 
18 #include "core/common/stylus/stylus_detector_mgr.h"
19 #include "core/components_ng/base/frame_node.h"
20 #include "core/components_ng/pattern/rich_editor/rich_editor_pattern.h"
21 #include "core/components_ng/pattern/search/search_text_field.h"
22 #include "core/components_ng/pattern/text/text_base.h"
23 #include "core/components_ng/pattern/text_field/text_field_pattern.h"
24 #include "frameworks/base/log/log_wrapper.h"
25 
26 namespace OHOS::Ace {
27 
28 namespace {
29 constexpr int32_t INDEX_S = 1;
30 constexpr int32_t INDEX_E = 2;
31 } // namespace
32 
RequestFocus(int32_t nodeId,const RefPtr<TaskExecutor> & taskScheduler)33 int32_t StylusDetectorCallBack::RequestFocus(int32_t nodeId, const RefPtr<TaskExecutor>& taskScheduler)
34 {
35     int32_t resultCode = -1;
36     taskScheduler->PostSyncTask(
37         [&resultCode, nodeId]() {
38             auto UiNode = ElementRegister::GetInstance()->GetUINodeById(nodeId);
39             auto frameNode = AceType::DynamicCast<NG::FrameNode>(UiNode);
40             CHECK_NULL_VOID(frameNode);
41             auto focusHub = frameNode->GetFocusHub();
42             CHECK_NULL_VOID(focusHub);
43             if (frameNode->GetTag() == V2::SEARCH_Field_ETS_TAG) {
44                 auto searchTextFieldPattern = frameNode->GetPattern<NG::SearchTextFieldPattern>();
45                 CHECK_NULL_VOID(searchTextFieldPattern);
46                 focusHub = searchTextFieldPattern->GetFocusHub();
47                 CHECK_NULL_VOID(focusHub);
48             }
49             if (!focusHub->IsCurrentFocus()) {
50                 focusHub->RequestFocusImmediately();
51             }
52             if (frameNode->GetTag() == V2::RICH_EDITOR_ETS_TAG) {
53                 resultCode = 0;
54                 return;
55             }
56             auto pattern = frameNode->GetPattern<NG::TextFieldPattern>();
57             CHECK_NULL_VOID(pattern);
58             bool needToRequestKeyBoardOnFocus = pattern->NeedToRequestKeyboardOnFocus();
59             if (!needToRequestKeyBoardOnFocus) {
60                 pattern->RequestKeyboardNotByFocusSwitch(NG::RequestKeyboardReason::STYLUS_DETECTOR);
61             }
62             resultCode = 0;
63         },
64         TaskExecutor::TaskType::UI, "ArkUIDetectorSyncStylusAction");
65     return resultCode;
66 }
67 
SetText(int32_t nodeId,void * data,const RefPtr<TaskExecutor> & taskScheduler,std::shared_ptr<IAceStylusCallback> callback)68 int32_t StylusDetectorCallBack::SetText(int32_t nodeId, void* data,
69     const RefPtr<TaskExecutor>& taskScheduler, std::shared_ptr<IAceStylusCallback> callback)
70 {
71     std::string text = *static_cast<std::string*>(data);
72     taskScheduler->PostTask(
73         [text, callback, nodeId]() {
74             ResultData res;
75             auto UiNode = ElementRegister::GetInstance()->GetUINodeById(nodeId);
76             auto frameNode = AceType::DynamicCast<NG::FrameNode>(UiNode);
77             CHECK_NULL_VOID(frameNode);
78             if (frameNode->GetTag() == V2::RICH_EDITOR_ETS_TAG) {
79                 return;
80             }
81             auto pattern = frameNode->GetPattern<NG::TextFieldPattern>();
82             CHECK_NULL_VOID(pattern);
83             if (!text.empty()) {
84                 pattern->UpdateEditingValue(text, text.size());
85                 frameNode->MarkDirtyNode(NG::PROPERTY_UPDATE_MEASURE_SELF);
86             }
87             if (callback) {
88                 callback->Callback(res);
89             }
90         },
91         TaskExecutor::TaskType::UI, "ArkUIDetectorStylusAction");
92     return 0;
93 }
94 
GetText(int32_t nodeId,const RefPtr<TaskExecutor> & taskScheduler,std::shared_ptr<IAceStylusCallback> callback)95 int32_t StylusDetectorCallBack::GetText(int32_t nodeId, const RefPtr<TaskExecutor>& taskScheduler,
96     std::shared_ptr<IAceStylusCallback> callback)
97 {
98     taskScheduler->PostTask(
99         [callback, nodeId]() {
100             CHECK_NULL_VOID(callback);
101             ResultData res;
102             res.resultData = "";
103             auto UiNode = ElementRegister::GetInstance()->GetUINodeById(nodeId);
104             auto frameNode = AceType::DynamicCast<NG::FrameNode>(UiNode);
105             if (!frameNode) {
106                 callback->Callback(res);
107                 return;
108             }
109             if (frameNode->GetTag() == V2::RICH_EDITOR_ETS_TAG) {
110                 callback->Callback(res);
111                 return;
112             }
113             auto pattern = frameNode->GetPattern<NG::TextFieldPattern>();
114             if (!pattern) {
115                 callback->Callback(res);
116                 return;
117             }
118             res.resultData = pattern->GetTextValue();
119             callback->Callback(res);
120         },
121         TaskExecutor::TaskType::UI, "ArkUIDetectorStylusAction");
122     return 0;
123 }
124 
Redo(int32_t nodeId,const RefPtr<TaskExecutor> & taskScheduler)125 int32_t StylusDetectorCallBack::Redo(int32_t nodeId, const RefPtr<TaskExecutor>& taskScheduler)
126 {
127     taskScheduler->PostTask(
128         [nodeId]() {
129             auto UiNode = ElementRegister::GetInstance()->GetUINodeById(nodeId);
130             auto frameNode = AceType::DynamicCast<NG::FrameNode>(UiNode);
131             CHECK_NULL_VOID(frameNode);
132             CHECK_EQUAL_VOID(frameNode->GetTag(), V2::RICH_EDITOR_ETS_TAG);
133             auto pattern = frameNode->GetPattern<NG::TextFieldPattern>();
134             CHECK_NULL_VOID(pattern);
135             pattern->CloseSelectOverlay(true);
136             pattern->HandleOnRedoAction();
137             frameNode->MarkDirtyNode(NG::PROPERTY_UPDATE_MEASURE_SELF);
138         },
139         TaskExecutor::TaskType::UI, "ArkUIDetectorStylusAction");
140     return 0;
141 }
142 
Undo(int32_t nodeId,const RefPtr<TaskExecutor> & taskScheduler)143 int32_t StylusDetectorCallBack::Undo(int32_t nodeId, const RefPtr<TaskExecutor>& taskScheduler)
144 {
145     taskScheduler->PostTask(
146         [nodeId]() {
147             auto UiNode = ElementRegister::GetInstance()->GetUINodeById(nodeId);
148             auto frameNode = AceType::DynamicCast<NG::FrameNode>(UiNode);
149             CHECK_NULL_VOID(frameNode);
150             CHECK_EQUAL_VOID(frameNode->GetTag(), V2::RICH_EDITOR_ETS_TAG);
151             auto pattern = frameNode->GetPattern<NG::TextFieldPattern>();
152             CHECK_NULL_VOID(pattern);
153             pattern->CloseSelectOverlay(true);
154             pattern->HandleOnUndoAction();
155             frameNode->MarkDirtyNode(NG::PROPERTY_UPDATE_MEASURE_SELF);
156         },
157         TaskExecutor::TaskType::UI, "ArkUIDetectorStylusAction");
158     return 0;
159 }
160 
DeleteText(int32_t nodeId,void * data,const RefPtr<TaskExecutor> & taskScheduler)161 int32_t StylusDetectorCallBack::DeleteText(int32_t nodeId, void* data,
162     const RefPtr<TaskExecutor>& taskScheduler)
163 {
164     int32_t resultCode = -1;
165     StylusGestureRect *rectPtr = static_cast<StylusGestureRect*>(data);
166     CHECK_NULL_RETURN(rectPtr, resultCode);
167     auto rect = *rectPtr;
168     taskScheduler->PostSyncTask(
169         [&resultCode, nodeId, rect]() {
170             TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture rect:%{public}f, %{public}f, %{public}f, %{public}f",
171                 rect.Left, rect.Top, rect.Width, rect.Height);
172             auto UiNode = ElementRegister::GetInstance()->GetUINodeById(nodeId);
173             auto frameNode = AceType::DynamicCast<NG::FrameNode>(UiNode);
174             CHECK_NULL_VOID(frameNode);
175             ContainerScope scope(frameNode->GetInstanceId());
176             Offset startCenterGlobalOffset = Offset(rect.Left, rect.Top + rect.Height / 2);
177             Offset endCenterGlobalOffset = Offset(rect.Left + rect.Width, rect.Top + rect.Height / 2);
178             auto sInd = GetGlyphPositionByGlobalOffset(frameNode, startCenterGlobalOffset);
179             auto eInd = GetGlyphPositionByGlobalOffset(frameNode, endCenterGlobalOffset);
180             auto textBase = frameNode->GetPattern<NG::TextBase>();
181             CHECK_NULL_VOID(textBase);
182             auto wtextLength = textBase->GetContentWideTextLength();
183             TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture wtextLength:%{public}d", wtextLength);
184             auto ret = CalculateIntersectedRegion(sInd, eInd, wtextLength);
185             if (std::get<0>(ret) == 0) {
186                 auto textInputClient = frameNode->GetPattern<TextInputClient>();
187                 CHECK_NULL_VOID(textInputClient);
188                 textInputClient->DeleteRange(std::get<INDEX_S>(ret), std::get<INDEX_E>(ret));
189                 resultCode = 0;
190             }
191         },
192         TaskExecutor::TaskType::UI, "ArkUIDetectorSyncStylusAction");
193     return resultCode;
194 }
195 
ChoiceText(int32_t nodeId,void * data,const RefPtr<TaskExecutor> & taskScheduler)196 int32_t StylusDetectorCallBack::ChoiceText(int32_t nodeId, void* data,
197     const RefPtr<TaskExecutor>& taskScheduler)
198 {
199     int32_t resultCode = -1;
200     ChoiceTextOption *optionPtr = static_cast<ChoiceTextOption*>(data);
201     CHECK_NULL_RETURN(optionPtr, resultCode);
202     auto choiceTextOption = *optionPtr;
203     taskScheduler->PostSyncTask(
204         [&resultCode, nodeId, choiceTextOption]() {
205             auto rect = choiceTextOption.rect;
206             auto UiNode = ElementRegister::GetInstance()->GetUINodeById(nodeId);
207             auto frameNode = AceType::DynamicCast<NG::FrameNode>(UiNode);
208             CHECK_NULL_VOID(frameNode);
209             ContainerScope scope(frameNode->GetInstanceId());
210             Offset startCenterGlobalOffset = Offset(rect.Left, rect.Top + rect.Height / 2);
211             Offset endCenterGlobalOffset = Offset(rect.Left + rect.Width, rect.Top + rect.Height / 2);
212             auto sInd = GetGlyphPositionByGlobalOffset(frameNode, startCenterGlobalOffset);
213             auto eInd = GetGlyphPositionByGlobalOffset(frameNode, endCenterGlobalOffset);
214             auto textBase = frameNode->GetPattern<NG::TextBase>();
215             CHECK_NULL_VOID(textBase);
216             auto wtextLength = textBase->GetContentWideTextLength();
217             TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture wtextLength:%{public}d", wtextLength);
218             if (!StylusDetectorMgr::GetInstance()->HasSelectChanged(static_cast<int32_t>(sInd.position_),
219                 static_cast<int32_t>(eInd.position_), choiceTextOption.showMenu)) {
220                 return;
221             }
222             StylusDetectorMgr::GetInstance()->SetSelectState(static_cast<int32_t>(sInd.position_),
223                 static_cast<int32_t>(eInd.position_), choiceTextOption.showMenu);
224             auto ret = CalculateIntersectedRegion(sInd, eInd, wtextLength);
225             if (std::get<0>(ret) == 0) {
226                 auto textInputClient = frameNode->GetPattern<TextInputClient>();
227                 CHECK_NULL_VOID(textInputClient);
228                 SelectionOptions option = { .menuPolicy = MenuPolicy::HIDE };
229                 if (choiceTextOption.showMenu) {
230                     option = { .menuPolicy = MenuPolicy::SHOW };
231                 }
232                 textInputClient->SetSelection(std::get<INDEX_S>(ret), std::get<INDEX_E>(ret), option);
233                 resultCode = 0;
234             }
235         },
236         TaskExecutor::TaskType::UI, "ArkUIDetectorSyncStylusAction");
237     return resultCode;
238 }
239 
InsertSpace(int32_t nodeId,void * data,const RefPtr<TaskExecutor> & taskScheduler)240 int32_t StylusDetectorCallBack::InsertSpace(int32_t nodeId, void* data,
241     const RefPtr<TaskExecutor>& taskScheduler)
242 {
243     int32_t resultCode = -1;
244     StylusGestureRect *rectPtr = static_cast<StylusGestureRect*>(data);
245     CHECK_NULL_RETURN(rectPtr, resultCode);
246     auto rect = *rectPtr;
247     taskScheduler->PostSyncTask(
248         [&resultCode, nodeId, rect]() {
249             TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture global rect:%{public}f, %{public}f, %{public}f, %{public}f",
250                 rect.Left, rect.Top, rect.Width, rect.Height);
251             auto UiNode = ElementRegister::GetInstance()->GetUINodeById(nodeId);
252             auto frameNode = AceType::DynamicCast<NG::FrameNode>(UiNode);
253             CHECK_NULL_VOID(frameNode);
254             ContainerScope scope(frameNode->GetInstanceId());
255             Offset centerGlobalOffset = Offset(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2);
256             auto sInd = GetGlyphPositionByGlobalOffset(frameNode, centerGlobalOffset);
257             auto textInputClient = frameNode->GetPattern<TextInputClient>();
258             CHECK_NULL_VOID(textInputClient);
259             auto start = static_cast<int32_t>(sInd.position_);
260             auto result = textInputClient->InsertOrDeleteSpace(start);
261             if (result) {
262                 resultCode = 0;
263             }
264         },
265         TaskExecutor::TaskType::UI, "ArkUIDetectorSyncStylusAction");
266     return resultCode;
267 }
268 
MoveCursor(int32_t nodeId,void * data,const RefPtr<TaskExecutor> & taskScheduler)269 int32_t StylusDetectorCallBack::MoveCursor(int32_t nodeId, void* data,
270     const RefPtr<TaskExecutor>& taskScheduler)
271 {
272     int32_t resultCode = -1;
273     MoveCursorOption *pointPtr = static_cast<MoveCursorOption*>(data);
274     CHECK_NULL_RETURN(pointPtr, resultCode);
275     auto point = *pointPtr;
276     taskScheduler->PostSyncTask(
277         [&resultCode, nodeId, point]() {
278             TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture point:%{public}d, %{public}d", point.x, point.y);
279             auto UiNode = ElementRegister::GetInstance()->GetUINodeById(nodeId);
280             auto frameNode = AceType::DynamicCast<NG::FrameNode>(UiNode);
281             CHECK_NULL_VOID(frameNode);
282             ContainerScope scope(frameNode->GetInstanceId());
283             Offset centerGlobalOffset = Offset(point.x, point.y);
284             auto sInd = GetGlyphPositionByGlobalOffset(frameNode, centerGlobalOffset);
285             resultCode = HandleMoveCursor(frameNode, sInd, point.showHandle);
286         },
287         TaskExecutor::TaskType::UI, "ArkUIDetectorSyncStylusAction");
288     return resultCode;
289 }
290 
HandleMoveCursor(const RefPtr<NG::FrameNode> & frameNode,NG::PositionWithAffinity sInd,bool showHandle)291 int32_t StylusDetectorCallBack::HandleMoveCursor(const RefPtr<NG::FrameNode>& frameNode,
292     NG::PositionWithAffinity sInd, bool showHandle)
293 {
294     auto textBase = frameNode->GetPattern<NG::TextBase>();
295     CHECK_NULL_RETURN(textBase, -1);
296     auto wtextLength = textBase->GetContentWideTextLength();
297     TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture wtextLength:%{public}d", wtextLength);
298     auto start = static_cast<int32_t>(sInd.position_);
299     if (start >= 0 && start <= wtextLength) {
300         auto textInputClient = frameNode->GetPattern<TextInputClient>();
301         CHECK_NULL_RETURN(textInputClient, -1);
302         textInputClient->SetCaretOffset(start);
303         frameNode->MarkDirtyNode(NG::PROPERTY_UPDATE_MEASURE_SELF);
304         return 0;
305     }
306     TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture do not cross");
307     return -1;
308 }
309 
GetGlyphPositionByGlobalOffset(const RefPtr<NG::FrameNode> & frameNode,const Offset & offset)310 NG::PositionWithAffinity StylusDetectorCallBack::GetGlyphPositionByGlobalOffset(
311     const RefPtr<NG::FrameNode>& frameNode, const Offset& offset)
312 {
313     TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture offset:%{public}f, %{public}f", offset.GetX(), offset.GetY());
314     NG::PositionWithAffinity finalResult(-1, TextAffinity::UPSTREAM);
315     // transform point coords from global to local
316     auto parentGlobalOffset_ = GetPaintRectGlobalOffset(frameNode);
317     auto localOffset = offset - Offset(parentGlobalOffset_.GetX(), parentGlobalOffset_.GetY());
318     if (NG::TextBase::HasRenderTransform(frameNode)) {
319         TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture pattern has select-overlay render transform");
320         auto localOffsetF = NG::OffsetF(offset.GetX(), offset.GetY());
321         NG::TextBase::RevertLocalPointWithTransform(frameNode, localOffsetF);
322         localOffset.SetX(localOffsetF.GetX());
323         localOffset.SetY(localOffsetF.GetY());
324     }
325     TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture localOffset:%{public}f, %{public}f", localOffset.GetX(),
326         localOffset.GetY());
327     auto textDragBase = frameNode->GetPattern<NG::TextDragBase>();
328     CHECK_NULL_RETURN(textDragBase, finalResult);
329     auto textRect = textDragBase->GetTextRect();
330     if (localOffset.GetY() < textRect.GetY() || localOffset.GetY() > textRect.GetY() + textRect.Height()) {
331         TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture point outside the area");
332         return finalResult;
333     }
334     auto textContentRect = textDragBase->GetTextContentRect();
335     localOffset.SetX(std::clamp(localOffset.GetX(), static_cast<double>(textContentRect.Left()),
336         static_cast<double>(textContentRect.Right())));
337     // calculate the start and end indexes of the intersecting region.
338     auto layoutInfo = StylusDetectorMgr::GetInstance()->GetLayoutInfo().Upgrade();
339     CHECK_NULL_RETURN(layoutInfo, finalResult);
340     return layoutInfo->GetGlyphPositionAtCoordinate(localOffset.GetX(), localOffset.GetY());
341 }
342 
CalculateIntersectedRegion(NG::PositionWithAffinity sInd,NG::PositionWithAffinity eInd,int32_t wtextLength)343 std::tuple<int32_t, int32_t, int32_t> StylusDetectorCallBack::CalculateIntersectedRegion(
344     NG::PositionWithAffinity sInd, NG::PositionWithAffinity eInd, int32_t wtextLength)
345 {
346     TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture wtextLength:%{public}d", wtextLength);
347     int32_t startPos = static_cast<int32_t>(std::min(eInd.position_, sInd.position_));
348     int32_t endPos = static_cast<int32_t>(std::max(eInd.position_, sInd.position_));
349     if (endPos < 1 || startPos >= wtextLength) {
350         TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture rect do not cross");
351         return std::make_tuple(-1, 0, 0);
352     }
353     // calculate the cross length
354     int32_t start = std::max(startPos, 0);
355     int32_t end = std::min(endPos, wtextLength);
356     if (start < end) {
357         return std::make_tuple(0, start, end);
358     } else {
359         TAG_LOGI(AceLogTag::ACE_STYLUS, "stylusGesture rect do not cross");
360         return std::make_tuple(-1, 0, 0);
361     }
362 }
363 
GetPaintRectGlobalOffset(const RefPtr<NG::FrameNode> & frameNode)364 NG::OffsetF StylusDetectorCallBack::GetPaintRectGlobalOffset(
365     const RefPtr<NG::FrameNode>& frameNode)
366 {
367     CHECK_NULL_RETURN(frameNode, NG::OffsetF(0.0f, 0.0f));
368     auto pipeline = frameNode->GetContextRefPtr();
369     CHECK_NULL_RETURN(pipeline, NG::OffsetF(0.0f, 0.0f));
370     auto rootOffset = pipeline->GetRootRect().GetOffset();
371     auto textPaintOffset = frameNode->GetPaintRectOffset();
372     return textPaintOffset - rootOffset;
373 }
374 
OnDetector(const CommandType & command,void * data,std::shared_ptr<IAceStylusCallback> callback)375 int32_t StylusDetectorCallBack::OnDetector(
376     const CommandType& command, void* data, std::shared_ptr<IAceStylusCallback> callback)
377 {
378     auto nodeId = StylusDetectorMgr::GetInstance()->GetDefaultNodeId();
379     if (nodeId == 0) {
380         return -1;
381     }
382     auto container = Container::CurrentSafely();
383     CHECK_NULL_RETURN(container, -1);
384     auto pipelineContext = container->GetPipelineContext();
385     CHECK_NULL_RETURN(pipelineContext, -1);
386     auto taskScheduler = pipelineContext->GetTaskExecutor();
387     CHECK_NULL_RETURN(taskScheduler, -1);
388 
389     int32_t resultCode = -1;
390     ResultData res;
391     switch (command) {
392         case COMMAND_REQUEST_FOCUS:
393             return StylusDetectorCallBack::RequestFocus(nodeId, taskScheduler);
394         case COMMAND_MOVE_CURSOR:
395             return StylusDetectorCallBack::MoveCursor(nodeId, data, taskScheduler);
396         case COMMAND_DELETE_TEXT:
397             return StylusDetectorCallBack::DeleteText(nodeId, data, taskScheduler);
398         case COMMAND_CHOICE_TEXT:
399             return StylusDetectorCallBack::ChoiceText(nodeId, data, taskScheduler);
400         case COMMAND_INSERT_SPACE:
401             return StylusDetectorCallBack::InsertSpace(nodeId, data, taskScheduler);
402         case COMMAND_CLEAR_HIT:
403             return resultCode;
404         case COMMAND_SET_TEXT:
405             return StylusDetectorCallBack::SetText(nodeId, data, taskScheduler, callback);
406         case COMMAND_GET_TEXT:
407             return StylusDetectorCallBack::GetText(nodeId, taskScheduler, callback);
408         case COMMAND_UNDO:
409             return StylusDetectorCallBack::Undo(nodeId, taskScheduler);
410         case COMMAND_REDO:
411             return StylusDetectorCallBack::Redo(nodeId, taskScheduler);
412         case COMMAND_INVALID:
413             TAG_LOGE(AceLogTag::ACE_STYLUS, "StylusDetector received error command.");
414             return resultCode;
415         default:
416             return resultCode;
417     }
418 }
419 
OnDetectorSync(const CommandType & command)420 bool StylusDetectorCallBack::OnDetectorSync(const CommandType& command)
421 {
422     bool result = false;
423     auto nodeId = StylusDetectorMgr::GetInstance()->GetDefaultNodeId();
424     CHECK_EQUAL_RETURN(nodeId, 0, result);
425     auto container = Container::CurrentSafely();
426     CHECK_NULL_RETURN(container, result);
427     auto pipelineContext = container->GetPipelineContext();
428     CHECK_NULL_RETURN(pipelineContext, result);
429     auto taskScheduler = pipelineContext->GetTaskExecutor();
430     CHECK_NULL_RETURN(taskScheduler, result);
431 
432     TAG_LOGI(AceLogTag::ACE_STYLUS, "Stylus received commandType:%{public}d", static_cast<int32_t>(command));
433     taskScheduler->PostSyncTask(
434         [nodeId, command, &result]() {
435             auto UiNode = ElementRegister::GetInstance()->GetUINodeById(nodeId);
436             auto frameNode = AceType::DynamicCast<NG::FrameNode>(UiNode);
437             CHECK_NULL_VOID(frameNode);
438             CHECK_EQUAL_VOID(frameNode->GetTag(), V2::RICH_EDITOR_ETS_TAG);
439             auto pattern = frameNode->GetPattern<NG::TextFieldPattern>();
440             CHECK_NULL_VOID(pattern);
441 
442             switch (command) {
443                 case COMMAND_CANUNDO:
444                     result = pattern->CanUndo();
445                     break;
446                 case COMMAND_CANREDO:
447                     result = pattern->CanRedo();
448                     break;
449                 default:
450                     break;
451             }
452         },
453         TaskExecutor::TaskType::UI, "ArkUIDetectorSyncStylusAction");
454     return result;
455 }
456 } // namespace OHOS::Ace