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