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