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