• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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 #include "core/components_ng/pattern/rich_editor/rich_editor_pattern.h"
16 
17 #include <algorithm>
18 #include <chrono>
19 #include <cstddef>
20 #include <cstdint>
21 #include <functional>
22 #include <iterator>
23 #include <sstream>
24 #include <string>
25 #include <utility>
26 
27 #include "base/geometry/dimension.h"
28 #include "base/geometry/ng/offset_t.h"
29 #include "base/geometry/ng/rect_t.h"
30 #include "base/geometry/offset.h"
31 #include "base/log/dump_log.h"
32 #include "base/log/log_wrapper.h"
33 #include "base/memory/ace_type.h"
34 #include "base/utils/string_utils.h"
35 #include "base/utils/utils.h"
36 #include "core/common/ai/data_detector_mgr.h"
37 #include "core/common/clipboard/paste_data.h"
38 #include "core/common/container.h"
39 #include "core/common/container_scope.h"
40 #include "core/common/ime/text_input_client.h"
41 #include "core/components/common/layout/constants.h"
42 #include "core/components_ng/base/view_stack_processor.h"
43 #include "core/components_ng/event/event_hub.h"
44 #include "core/components_ng/event/gesture_event_hub.h"
45 #include "core/components_ng/event/long_press_event.h"
46 #include "core/components_ng/pattern/image/image_pattern.h"
47 #include "core/components_ng/pattern/rich_editor/rich_editor_event_hub.h"
48 #include "core/components_ng/pattern/rich_editor/rich_editor_layout_property.h"
49 #include "core/components_ng/pattern/rich_editor/rich_editor_model.h"
50 #include "core/components_ng/pattern/rich_editor/rich_editor_overlay_modifier.h"
51 #include "core/components_ng/pattern/rich_editor/rich_editor_theme.h"
52 #include "core/components_ng/pattern/rich_editor/selection_info.h"
53 #include "core/components_ng/pattern/rich_editor_drag/rich_editor_drag_pattern.h"
54 #include "core/components_ng/pattern/text/span_node.h"
55 #include "core/components_ng/pattern/text/text_base.h"
56 #include "core/components_ng/pattern/text/typed_text.h"
57 #include "core/components_ng/pattern/text_field/text_field_manager.h"
58 #include "core/components_ng/pattern/text_field/text_input_ai_checker.h"
59 #include "core/components_ng/property/property.h"
60 #include "core/components_v2/inspector/inspector_constants.h"
61 #include "core/gestures/gesture_info.h"
62 #include "core/pipeline/base/element_register.h"
63 
64 #if not defined(ACE_UNITTEST)
65 #if defined(ENABLE_STANDARD_INPUT)
66 #include "commonlibrary/c_utils/base/include/refbase.h"
67 
68 #include "core/components_ng/pattern/text_field/on_text_changed_listener_impl.h"
69 #endif
70 #endif
71 
72 #include "core/common/ace_engine_ext.h"
73 #include "core/common/udmf/udmf_client.h"
74 
75 #ifdef WINDOW_SCENE_SUPPORTED
76 #include "core/components_ng/pattern/window_scene/helper/window_scene_helper.h"
77 #endif
78 
79 namespace OHOS::Ace::NG {
80 namespace {
81 #if defined(ENABLE_STANDARD_INPUT)
82 // should be moved to theme
83 constexpr float CARET_WIDTH = 1.5f;
84 constexpr float DEFAULT_CARET_HEIGHT = 18.5f;
85 constexpr Dimension KEYBOARD_AVOID_OFFSET = 24.0_vp;
86 #endif
87 constexpr int32_t IMAGE_SPAN_LENGTH = 1;
88 constexpr int32_t SYMBOL_SPAN_LENGTH = 2;
89 constexpr int32_t RICH_EDITOR_TWINKLING_INTERVAL_MS = 500;
90 constexpr float DEFAULT_TEXT_SIZE = 16.0f;
91 constexpr int32_t AUTO_SCROLL_INTERVAL = 15;
92 constexpr Dimension AUTO_SCROLL_EDGE_DISTANCE = 15.0_vp;
93 constexpr Dimension AUTO_SCROLL_DRAG_EDGE_DISTANCE = 58.0_vp;
94 constexpr float MAX_DRAG_SCROLL_SPEED = 2400.0f;
95 constexpr float TIME_UNIT = 1000.0f;
96 constexpr float DOUBLE_CLICK_INTERVAL_MS = 300.0f;
97 constexpr float BOX_EPSILON = 0.5f;
98 constexpr uint32_t RECORD_MAX_LENGTH = 20;
99 
100 const std::wstring lineSeparator = L"\n";
101 // hen do ai anaylsis, we should limit the left an right limit of the string
102 constexpr static int32_t AI_TEXT_RANGE_LEFT = 50;
103 constexpr static int32_t AI_TEXT_RANGE_RIGHT = 50;
104 } // namespace
RichEditorPattern()105 RichEditorPattern::RichEditorPattern() {}
106 
~RichEditorPattern()107 RichEditorPattern::~RichEditorPattern()
108 {
109     if (isCustomKeyboardAttached_) {
110         CloseCustomKeyboard();
111     }
112 }
113 
OnModifyDone()114 void RichEditorPattern::OnModifyDone()
115 {
116     auto host = GetHost();
117     CHECK_NULL_VOID(host);
118     auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
119     copyOption_ = layoutProperty->GetCopyOption().value_or(CopyOptions::Distributed);
120     auto context = PipelineContext::GetCurrentContext();
121     CHECK_NULL_VOID(context);
122     context->AddOnAreaChangeNode(host->GetId());
123     if (!clipboard_ && context) {
124         clipboard_ = ClipboardProxy::GetInstance()->GetClipboard(context->GetTaskExecutor());
125     }
126     instanceId_ = context->GetInstanceId();
127     InitMouseEvent();
128     auto focusHub = host->GetOrCreateFocusHub();
129     CHECK_NULL_VOID(focusHub);
130     InitFocusEvent(focusHub);
131     auto gestureEventHub = host->GetOrCreateGestureEventHub();
132     InitClickEvent(gestureEventHub);
133     InitLongPressEvent(gestureEventHub);
134     InitTouchEvent();
135     HandleEnabled();
136     ProcessInnerPadding();
137     InitScrollablePattern();
138     if (CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
139         dataDetectorAdapter_->StartAITask();
140     }
141     if (host->IsDraggable() && copyOption_ != CopyOptions::None) {
142         InitDragDropEvent();
143         AddDragFrameNodeToManager(host);
144     } else {
145         ClearDragDropEvent();
146         RemoveDragFrameNodeFromManager(host);
147     }
148     Register2DragDropManager();
149     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
150 
151     auto eventHub = host->GetEventHub<EventHub>();
152     CHECK_NULL_VOID(eventHub);
153     bool enabledCache = eventHub->IsEnabled();
154     if (textDetectEnable_ && enabledCache != enabled_) {
155         enabled_ = enabledCache;
156         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
157     }
158 }
159 
HandleEnabled()160 void RichEditorPattern::HandleEnabled()
161 {
162     auto host = GetHost();
163     CHECK_NULL_VOID(host);
164     auto renderContext = host->GetRenderContext();
165     CHECK_NULL_VOID(renderContext);
166     if (IsDisabled()) {
167         auto pipeline = PipelineContext::GetCurrentContext();
168         CHECK_NULL_VOID(pipeline);
169         auto richEditorTheme = pipeline->GetTheme<RichEditorTheme>();
170         CHECK_NULL_VOID(richEditorTheme);
171         auto disabledAlpha = richEditorTheme->GetDisabledAlpha();
172         renderContext->OnOpacityUpdate(disabledAlpha);
173     } else {
174         auto opacity = renderContext->GetOpacity().value_or(1.0);
175         renderContext->OnOpacityUpdate(opacity);
176     }
177 }
178 
BeforeCreateLayoutWrapper()179 void RichEditorPattern::BeforeCreateLayoutWrapper()
180 {
181     TextPattern::PreCreateLayoutWrapper();
182 }
183 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)184 bool RichEditorPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
185 {
186     if (config.skipMeasure || dirty->SkipMeasureContent()) {
187         return false;
188     }
189     frameRect_ = dirty->GetGeometryNode()->GetFrameRect();
190     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
191     CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
192     auto richEditorLayoutAlgorithm =
193         DynamicCast<RichEditorLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
194     CHECK_NULL_RETURN(richEditorLayoutAlgorithm, false);
195     parentGlobalOffset_ = richEditorLayoutAlgorithm->GetParentGlobalOffset();
196     richTextRect_ = richEditorLayoutAlgorithm->GetTextRect();
197     UpdateTextFieldManager(Offset(parentGlobalOffset_.GetX(), parentGlobalOffset_.GetY()), frameRect_.Height());
198     auto restoreSelectOverlayProxy = selectOverlayProxy_;
199     selectOverlayProxy_.Reset(); // skip show selectoverlay in the TextPattern.
200     bool ret = TextPattern::OnDirtyLayoutWrapperSwap(dirty, config);
201     selectOverlayProxy_ = restoreSelectOverlayProxy;
202     UpdateScrollStateAfterLayout(config.frameSizeChange);
203     if (!isRichEditorInit_) {
204         auto eventHub = GetEventHub<RichEditorEventHub>();
205         CHECK_NULL_RETURN(eventHub, ret);
206         eventHub->FireOnReady();
207         ClearOperationRecords();
208         isFirstCallOnReady_ = true;
209         isRichEditorInit_ = true;
210     }
211     MoveCaretOnLayoutSwap();
212     if (textSelector_.IsValid() && SelectOverlayIsOn() && isShowMenu_) {
213         CalculateHandleOffsetAndShowOverlay();
214         ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle);
215     }
216     isShowMenu_ = true;
217     UpdateCaretInfoToController();
218     auto host = GetHost();
219     CHECK_NULL_RETURN(host, ret);
220     auto context = host->GetRenderContext();
221     CHECK_NULL_RETURN(context, ret);
222     if (context->GetClipEdge().has_value()) {
223         auto geometryNode = host->GetGeometryNode();
224         auto frameOffset = geometryNode->GetFrameOffset();
225         auto frameSize = geometryNode->GetFrameSize();
226         auto height = static_cast<float>(paragraphs_.GetHeight() + std::fabs(baselineOffset_));
227         if (!context->GetClipEdge().value() && LessNotEqual(frameSize.Height(), height)) {
228             RectF boundsRect(frameOffset.GetX(), frameOffset.GetY(), frameSize.Width(), height);
229             CHECK_NULL_RETURN(overlayMod_, ret);
230             overlayMod_->SetBoundsRect(boundsRect);
231         }
232     }
233     caretUpdateType_ = CaretUpdateType::NONE;
234     return ret;
235 }
236 
MoveCaretOnLayoutSwap()237 void RichEditorPattern::MoveCaretOnLayoutSwap()
238 {
239     MoveCaretAfterTextChange();
240     if (HasFocus()) {
241         MoveCaretToContentRect();
242     }
243 }
244 
CreateImageSourceInfo(const ImageSpanOptions & options)245 std::function<ImageSourceInfo()> RichEditorPattern::CreateImageSourceInfo(const ImageSpanOptions& options)
246 {
247     std::string src;
248     RefPtr<PixelMap> pixMap = nullptr;
249     std::string bundleName;
250     std::string moduleName;
251     if (options.image.has_value()) {
252         src = options.image.value();
253     }
254     if (options.imagePixelMap.has_value()) {
255         pixMap = options.imagePixelMap.value();
256     }
257     if (options.bundleName.has_value()) {
258         bundleName = options.bundleName.value();
259     }
260     if (options.moduleName.has_value()) {
261         moduleName = options.moduleName.value();
262     }
263     auto createSourceInfoFunc = [src, noPixMap = !options.imagePixelMap.has_value(), pixMap, bundleName,
264                                     moduleName]() -> ImageSourceInfo {
265 #if defined(PIXEL_MAP_SUPPORTED)
266         if (noPixMap) {
267             return { src, bundleName, moduleName };
268         }
269         return ImageSourceInfo(pixMap);
270 #else
271         return { src, bundleName, moduleName };
272 #endif
273     };
274     return std::move(createSourceInfoFunc);
275 }
276 
GetTextContentLength()277 int32_t RichEditorPattern::GetTextContentLength()
278 {
279     if (!spans_.empty()) {
280         auto it = spans_.rbegin();
281         return (*it)->position;
282     }
283     return 0;
284 }
AddImageSpan(const ImageSpanOptions & options,bool isPaste,int32_t index)285 int32_t RichEditorPattern::AddImageSpan(const ImageSpanOptions& options, bool isPaste, int32_t index)
286 {
287     auto host = GetHost();
288     CHECK_NULL_RETURN(host, -1);
289 
290     auto imageNode = ImageSpanNode::GetOrCreateSpanNode(V2::IMAGE_ETS_TAG,
291         ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<ImagePattern>(); });
292     auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
293 
294     // Disable the image itself event
295     imageNode->SetDraggable(false);
296     auto gesture = imageNode->GetOrCreateGestureEventHub();
297     CHECK_NULL_RETURN(gesture, -1);
298 
299     OperationRecord record;
300     record.beforeCaretPosition = options.offset.value_or(static_cast<int32_t>(GetTextContentLength()));
301     record.addText = " ";
302     ClearRedoOperationRecords();
303     record.afterCaretPosition = record.beforeCaretPosition + 1;
304     AddOperationRecord(record);
305 
306     // Masked the default drag behavior of node image
307     gesture->SetDragEvent(nullptr, { PanDirection::DOWN }, 0, Dimension(0));
308 
309     int32_t spanIndex = 0;
310     int32_t offset = -1;
311     if (options.offset.has_value()) {
312         offset = TextSpanSplit(options.offset.value());
313         if (offset == -1) {
314             spanIndex = static_cast<int32_t>(host->GetChildren().size());
315         } else {
316             spanIndex = offset;
317         }
318         imageNode->MountToParent(host, offset);
319     } else if (index != -1) {
320         imageNode->MountToParent(host, index);
321         spanIndex = index;
322     } else {
323         spanIndex = static_cast<int32_t>(host->GetChildren().size());
324         imageNode->MountToParent(host);
325     }
326     std::function<ImageSourceInfo()> createSourceInfoFunc = CreateImageSourceInfo(options);
327     imageLayoutProperty->UpdateImageSourceInfo(createSourceInfoFunc());
328     if (options.imageAttribute.has_value()) {
329         auto imgAttr = options.imageAttribute.value();
330         if (imgAttr.size.has_value()) {
331             imageLayoutProperty->UpdateUserDefinedIdealSize(
332                 CalcSize(CalcLength(imgAttr.size.value().width), CalcLength(imgAttr.size.value().height)));
333         }
334         if (imgAttr.verticalAlign.has_value()) {
335             imageLayoutProperty->UpdateVerticalAlign(imgAttr.verticalAlign.value());
336         }
337         if (imgAttr.objectFit.has_value()) {
338             imageLayoutProperty->UpdateImageFit(imgAttr.objectFit.value());
339         }
340         if (imgAttr.marginProp.has_value()) {
341             imageLayoutProperty->UpdateMargin(imgAttr.marginProp.value());
342         }
343         if (imgAttr.borderRadius.has_value()) {
344             auto imageRenderCtx = imageNode->GetRenderContext();
345             imageRenderCtx->UpdateBorderRadius(imgAttr.borderRadius.value());
346             imageRenderCtx->SetClipToBounds(true);
347         }
348     }
349     if (isPaste) {
350         isTextChange_ = true;
351         moveDirection_ = MoveDirection::FORWARD;
352         moveLength_ += 1;
353         MoveCaretAfterTextChange();
354     }
355     imageNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
356     imageNode->MarkModifyDone();
357     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
358     host->MarkModifyDone();
359     auto spanItem = imageNode->GetSpanItem();
360     // The length of the imageSpan defaults to the length of a character to calculate the position
361     spanItem->content = " ";
362     AddSpanItem(spanItem, offset);
363     if (options.userGestureOption.onClick) {
364         auto tmpClickFunc = options.userGestureOption.onClick;
365         spanItem->SetOnClickEvent(std::move(tmpClickFunc));
366     }
367     if (options.userGestureOption.onLongPress) {
368         auto tmpLongPressFunc = options.userGestureOption.onLongPress;
369         spanItem->SetLongPressEvent(std::move(tmpLongPressFunc));
370     }
371     if (options.offset.has_value() && options.offset.value() <= GetCaretPosition()) {
372         SetCaretPosition(options.offset.value() + 1 + moveLength_);
373         moveLength_ = 0;
374     } else {
375         placeholderCount_++;
376         SetCaretPosition(GetTextContentLength());
377     }
378     if (!isPaste && textSelector_.IsValid()) {
379         CloseSelectOverlay();
380         ResetSelection();
381     }
382     return spanIndex;
383 }
384 
AddSpanItem(const RefPtr<SpanItem> & item,int32_t offset)385 void RichEditorPattern::AddSpanItem(const RefPtr<SpanItem>& item, int32_t offset)
386 {
387     auto host = GetHost();
388     CHECK_NULL_VOID(host);
389     if (offset == -1) {
390         offset = host->GetChildren().size();
391     }
392     offset = std::clamp(offset, 0, static_cast<int32_t>(host->GetChildren().size()) - 1);
393     auto it = spans_.begin();
394     std::advance(it, offset);
395     spans_.insert(it, item);
396     UpdateSpanPosition();
397 }
398 
AddPlaceholderSpan(const RefPtr<UINode> & customNode,const SpanOptionBase & options)399 int32_t RichEditorPattern::AddPlaceholderSpan(const RefPtr<UINode>& customNode, const SpanOptionBase& options)
400 {
401     CHECK_NULL_RETURN(customNode, 0);
402     auto host = GetHost();
403     CHECK_NULL_RETURN(host, 0);
404     auto placeholderSpanNode = PlaceholderSpanNode::GetOrCreateSpanNode(V2::PLACEHOLDER_SPAN_ETS_TAG,
405         ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<PlaceholderSpanPattern>(); });
406     CHECK_NULL_RETURN(placeholderSpanNode, 0);
407     customNode->MountToParent(placeholderSpanNode);
408     SetSelfAndChildDraggableFalse(customNode);
409     auto focusHub = placeholderSpanNode->GetOrCreateFocusHub();
410     focusHub->SetFocusable(false);
411     int32_t spanIndex = 0;
412     int32_t offset = -1;
413     auto optionalPosition = options.offset.value_or(-1);
414     if (optionalPosition >= 0) {
415         offset = TextSpanSplit(options.offset.value());
416         if (offset == -1) {
417             spanIndex = static_cast<int32_t>(host->GetChildren().size());
418         } else {
419             spanIndex = offset;
420         }
421         placeholderSpanNode->MountToParent(host, offset);
422     } else {
423         spanIndex = static_cast<int32_t>(host->GetChildren().size());
424         placeholderSpanNode->MountToParent(host);
425     }
426     auto spanItem = placeholderSpanNode->GetSpanItem();
427     spanItem->content = " ";
428     AddSpanItem(spanItem, offset);
429     if (options.offset.has_value() && options.offset.value() <= GetCaretPosition()) {
430         SetCaretPosition(options.offset.value() + 1 + moveLength_);
431         moveLength_ = 0;
432     } else {
433         placeholderCount_++;
434         SetCaretPosition(GetTextContentLength());
435     }
436     if (textSelector_.IsValid()) {
437         CloseSelectOverlay();
438         ResetSelection();
439     }
440     placeholderSpanNode->MarkModifyDone();
441     placeholderSpanNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
442     host->MarkModifyDone();
443     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
444     return spanIndex;
445 }
446 
SetSelfAndChildDraggableFalse(const RefPtr<UINode> & customNode)447 void RichEditorPattern::SetSelfAndChildDraggableFalse(const RefPtr<UINode>& customNode)
448 {
449     CHECK_NULL_VOID(customNode);
450     auto frameNode = DynamicCast<FrameNode>(customNode);
451     if (frameNode) {
452         frameNode->SetDraggable(false);
453     }
454     for (const auto& child : customNode->GetChildren()) {
455         SetSelfAndChildDraggableFalse(child);
456     }
457 }
458 
AddTextSpan(const TextSpanOptions & options,bool isPaste,int32_t index)459 int32_t RichEditorPattern::AddTextSpan(const TextSpanOptions& options, bool isPaste, int32_t index)
460 {
461     OperationRecord record;
462     record.beforeCaretPosition = options.offset.value_or(static_cast<int32_t>(GetTextContentLength()));
463     record.addText = options.value;
464     ClearRedoOperationRecords();
465     record.afterCaretPosition =
466         record.beforeCaretPosition + static_cast<int32_t>(StringUtils::ToWstring(options.value).length());
467     AddOperationRecord(record);
468     return AddTextSpanOperation(options, isPaste, index, false, false);
469 }
470 
AddTextSpanOperation(const TextSpanOptions & options,bool isPaste,int32_t index,bool needLeadingMargin,bool updateCaretOPosition)471 int32_t RichEditorPattern::AddTextSpanOperation(
472     const TextSpanOptions& options, bool isPaste, int32_t index, bool needLeadingMargin, bool updateCaretOPosition)
473 {
474     auto host = GetHost();
475     CHECK_NULL_RETURN(host, -1);
476 
477     auto* stack = ViewStackProcessor::GetInstance();
478     auto nodeId = stack->ClaimNodeId();
479     auto spanNode = SpanNode::GetOrCreateSpanNode(nodeId);
480 
481     int32_t spanIndex = 0;
482     int32_t offset = -1;
483     if (options.offset.has_value()) {
484         offset = TextSpanSplit(options.offset.value(), needLeadingMargin);
485         if (offset == -1) {
486             spanIndex = static_cast<int32_t>(host->GetChildren().size());
487         } else {
488             spanIndex = offset;
489         }
490         spanNode->MountToParent(host, offset);
491     } else if (index != -1) {
492         spanNode->MountToParent(host, index);
493         spanIndex = index;
494     } else {
495         spanIndex = static_cast<int32_t>(host->GetChildren().size());
496         spanNode->MountToParent(host);
497     }
498     spanNode->UpdateContent(options.value);
499     spanNode->AddPropertyInfo(PropertyInfo::NONE);
500     if (options.style.has_value()) {
501         spanNode->UpdateTextColor(options.style.value().GetTextColor());
502         spanNode->AddPropertyInfo(PropertyInfo::FONTCOLOR);
503         spanNode->UpdateFontSize(options.style.value().GetFontSize());
504         spanNode->AddPropertyInfo(PropertyInfo::FONTSIZE);
505         spanNode->UpdateItalicFontStyle(options.style.value().GetFontStyle());
506         spanNode->AddPropertyInfo(PropertyInfo::FONTSTYLE);
507         spanNode->UpdateFontWeight(options.style.value().GetFontWeight());
508         spanNode->AddPropertyInfo(PropertyInfo::FONTWEIGHT);
509         spanNode->UpdateFontFamily(options.style.value().GetFontFamilies());
510         spanNode->AddPropertyInfo(PropertyInfo::FONTFAMILY);
511         spanNode->UpdateTextDecoration(options.style.value().GetTextDecoration());
512         spanNode->AddPropertyInfo(PropertyInfo::TEXTDECORATION);
513         spanNode->UpdateTextDecorationColor(options.style.value().GetTextDecorationColor());
514         spanNode->AddPropertyInfo(PropertyInfo::NONE);
515         spanNode->UpdateTextShadow(options.style.value().GetTextShadows());
516         spanNode->AddPropertyInfo(PropertyInfo::TEXTSHADOW);
517     }
518     auto spanItem = spanNode->GetSpanItem();
519     spanItem->content = options.value;
520     spanItem->SetTextStyle(options.style);
521     spanItem->hasResourceFontColor = options.hasResourceFontColor;
522     spanItem->hasResourceDecorationColor = options.hasResourceDecorationColor;
523     AddSpanItem(spanItem, offset);
524     if (options.paraStyle) {
525         int32_t start = 0;
526         int32_t end = 0;
527         spanItem->GetIndex(start, end);
528         UpdateParagraphStyle(start, end, *options.paraStyle);
529     }
530     if (options.userGestureOption.onClick) {
531         auto tmpClickFunc = options.userGestureOption.onClick;
532         spanItem->SetOnClickEvent(std::move(tmpClickFunc));
533     }
534     if (options.userGestureOption.onLongPress) {
535         auto tmpLongPressFunc = options.userGestureOption.onLongPress;
536         spanItem->SetLongPressEvent(std::move(tmpLongPressFunc));
537     }
538     if (updateCaretOPosition) {
539         if (options.offset.has_value() && options.offset.value() <= GetCaretPosition()) {
540             SetCaretPosition(options.offset.value() + 1 + moveLength_);
541             moveLength_ = 0;
542         } else {
543             SetCaretPosition(GetTextContentLength());
544         }
545     }
546     if (!isPaste && textSelector_.IsValid()) {
547         CloseSelectOverlay();
548         ResetSelection();
549     }
550     SpanNodeFission(spanNode);
551     return spanIndex;
552 }
553 
AddSymbolSpan(const SymbolSpanOptions & options,bool isPaste,int32_t index)554 int32_t RichEditorPattern::AddSymbolSpan(const SymbolSpanOptions& options, bool isPaste, int32_t index)
555 {
556     OperationRecord record;
557     record.beforeCaretPosition = options.offset.value_or(static_cast<int32_t>(GetTextContentLength()));
558     record.addText = " ";
559     ClearRedoOperationRecords();
560     record.afterCaretPosition = record.beforeCaretPosition + 1;
561     AddOperationRecord(record);
562     return AddSymbolSpanOperation(options, isPaste, index);
563 }
564 
AddSymbolSpanOperation(const SymbolSpanOptions & options,bool isPaste,int32_t index)565 int32_t RichEditorPattern::AddSymbolSpanOperation(const SymbolSpanOptions& options, bool isPaste, int32_t index)
566 {
567     auto host = GetHost();
568     CHECK_NULL_RETURN(host, -1);
569 
570     auto* stack = ViewStackProcessor::GetInstance();
571     auto nodeId = stack->ClaimNodeId();
572     auto spanNode = SpanNode::GetOrCreateSpanNode(V2::SYMBOL_SPAN_ETS_TAG, nodeId);
573 
574     int32_t spanIndex = 0;
575     int32_t offset = -1;
576     if (options.offset.has_value()) {
577         offset = TextSpanSplit(options.offset.value());
578         if (offset == -1) {
579             spanIndex = static_cast<int32_t>(host->GetChildren().size());
580         } else {
581             spanIndex = offset;
582         }
583         spanNode->MountToParent(host, offset);
584     } else if (index != -1) {
585         spanNode->MountToParent(host, index);
586         spanIndex = index;
587     } else {
588         spanIndex = static_cast<int32_t>(host->GetChildren().size());
589         spanNode->MountToParent(host);
590     }
591     spanNode->UpdateContent(options.symbolId);
592     spanNode->AddPropertyInfo(PropertyInfo::NONE);
593     if (options.style.has_value()) {
594         spanNode->UpdateFontSize(options.style.value().GetFontSize());
595         spanNode->AddPropertyInfo(PropertyInfo::FONTSIZE);
596         spanNode->UpdateFontWeight(options.style.value().GetFontWeight());
597         spanNode->AddPropertyInfo(PropertyInfo::FONTWEIGHT);
598         spanNode->UpdateSymbolColorList(options.style.value().GetSymbolColorList());
599         spanNode->AddPropertyInfo(PropertyInfo::SYMBOL_COLOR);
600         spanNode->UpdateSymbolRenderingStrategy(options.style.value().GetRenderStrategy());
601         spanNode->AddPropertyInfo(PropertyInfo::SYMBOL_RENDERING_STRATEGY);
602         spanNode->UpdateSymbolEffectStrategy(options.style.value().GetEffectStrategy());
603         spanNode->AddPropertyInfo(PropertyInfo::SYMBOL_EFFECT_STRATEGY);
604     }
605     auto spanItem = spanNode->GetSpanItem();
606     spanItem->content = "  ";
607     spanItem->SetTextStyle(options.style);
608     spanItem->SetResourceObject(options.resourceObject);
609     AddSpanItem(spanItem, offset);
610     if (options.offset.has_value() && options.offset.value() <= GetCaretPosition()) {
611         SetCaretPosition(options.offset.value() + SYMBOL_SPAN_LENGTH + moveLength_);
612         moveLength_ = 0;
613     } else {
614         SetCaretPosition(GetTextContentLength());
615     }
616     SpanNodeFission(spanNode);
617     return spanIndex;
618 }
619 
SpanNodeFission(RefPtr<SpanNode> & spanNode)620 void RichEditorPattern::SpanNodeFission(RefPtr<SpanNode>& spanNode)
621 {
622     auto spanItem = spanNode->GetSpanItem();
623     auto content = StringUtils::ToWstring(spanItem->content);
624     auto contentLen = content.length();
625     auto spanStart = spanItem->position - contentLen;
626     for (size_t i = 0; i < content.length(); i++) {
627         auto character = content[i];
628         if (character == '\n') {
629             auto charPosition = spanStart + i;
630             TextSpanSplit(static_cast<int32_t>(charPosition + 1));
631         }
632     }
633 }
634 
DeleteSpans(const RangeOptions & options)635 void RichEditorPattern::DeleteSpans(const RangeOptions& options)
636 {
637     int32_t start = 0;
638     int32_t end = 0;
639     auto length = GetTextContentLength();
640     start = (!options.start.has_value()) ? 0 : options.start.value();
641     end = (!options.end.has_value()) ? length : options.end.value();
642     if (start > end) {
643         auto value = start;
644         start = end;
645         end = value;
646     }
647     start = std::max(0, start);
648     end = std::min(length, end);
649     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "index range=[%{public}d, %{public}d]", start, end);
650     if (start > length || end < 0 || start == end) {
651         return;
652     }
653 
654     OperationRecord record;
655     record.beforeCaretPosition = start;
656     std::wstringstream wss;
657     for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
658         wss << StringUtils::ToWstring((*iter)->content);
659     }
660     std::wstring deleteText = wss.str().substr(start, end - start);
661     record.deleteText = StringUtils::ToString(deleteText);
662     ClearRedoOperationRecords();
663     record.afterCaretPosition = start;
664     AddOperationRecord(record);
665 
666     auto startInfo = GetSpanPositionInfo(start);
667     auto endInfo = GetSpanPositionInfo(end - 1);
668     if (startInfo.spanIndex_ == endInfo.spanIndex_) {
669         DeleteSpanByRange(start, end, startInfo);
670     } else {
671         DeleteSpansByRange(start, end, startInfo, endInfo);
672     }
673     if (textSelector_.IsValid()) {
674         SetCaretPosition(textSelector_.GetTextStart());
675         CloseSelectOverlay();
676         ResetSelection();
677     }
678     SetCaretOffset(start);
679     auto host = GetHost();
680     CHECK_NULL_VOID(host);
681     auto childrens = host->GetChildren();
682     if (childrens.empty() || GetTextContentLength() == 0) {
683         SetCaretPosition(0);
684     }
685     UpdateSpanPosition();
686 }
687 
DeleteSpanByRange(int32_t start,int32_t end,SpanPositionInfo info)688 void RichEditorPattern::DeleteSpanByRange(int32_t start, int32_t end, SpanPositionInfo info)
689 {
690     auto host = GetHost();
691     CHECK_NULL_VOID(host);
692     auto childrens = host->GetChildren();
693     auto it = childrens.begin();
694     std::advance(it, info.spanIndex_);
695     if (start == info.spanStart_ && end == info.spanEnd_) {
696         ClearContent(*it);
697         host->RemoveChild(*it);
698     } else {
699         auto spanNode = DynamicCast<SpanNode>(*it);
700         CHECK_NULL_VOID(spanNode);
701         auto spanItem = spanNode->GetSpanItem();
702         auto beforStr = StringUtils::ToWstring(spanItem->content).substr(0, start - info.spanStart_);
703         auto endStr = StringUtils::ToWstring(spanItem->content).substr(end - info.spanStart_);
704         std::wstring result = beforStr + endStr;
705         auto str = StringUtils::ToString(result);
706         spanNode->UpdateContent(str);
707     }
708     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
709     host->MarkModifyDone();
710 }
711 
DeleteSpansByRange(int32_t start,int32_t end,SpanPositionInfo startInfo,SpanPositionInfo endInfo)712 void RichEditorPattern::DeleteSpansByRange(
713     int32_t start, int32_t end, SpanPositionInfo startInfo, SpanPositionInfo endInfo)
714 {
715     auto host = GetHost();
716     CHECK_NULL_VOID(host);
717     auto childrens = host->GetChildren();
718     if (childrens.empty()) {
719         return;
720     }
721 
722     auto itStart = childrens.begin();
723     if (startInfo.spanIndex_ >= static_cast<int32_t>(childrens.size())) {
724         std::advance(itStart, childrens.size() - 1);
725         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "startInfo.spanIndex_ is larger than childrens size");
726     } else {
727         std::advance(itStart, startInfo.spanIndex_);
728     }
729     auto saveStartSpan = (start == startInfo.spanStart_) ? 0 : 1;
730     if (saveStartSpan) {
731         auto spanNodeStart = DynamicCast<SpanNode>(*itStart);
732         CHECK_NULL_VOID(spanNodeStart);
733         auto spanItemStart = spanNodeStart->GetSpanItem();
734         auto beforStr = StringUtils::ToWstring(spanItemStart->content).substr(0, start - startInfo.spanStart_);
735         auto strStart = StringUtils::ToString(beforStr);
736         spanNodeStart->UpdateContent(strStart);
737     }
738     auto itEnd = childrens.begin();
739     std::advance(itEnd, endInfo.spanIndex_);
740     auto delEndSpan = (end == endInfo.spanEnd_) ? 1 : 0;
741     if (!delEndSpan) {
742         auto spanNodeEnd = DynamicCast<SpanNode>(*itEnd);
743         CHECK_NULL_VOID(spanNodeEnd);
744         auto spanItemEnd = spanNodeEnd->GetSpanItem();
745         auto endStr =
746             StringUtils::ToWstring(spanItemEnd->content).substr(end - endInfo.spanStart_, endInfo.spanEnd_ - end);
747         auto strEnd = StringUtils::ToString(endStr);
748         spanNodeEnd->UpdateContent(strEnd);
749     }
750     auto startIter = childrens.begin();
751     std::advance(startIter, startInfo.spanIndex_ + saveStartSpan);
752     auto endIter = childrens.begin();
753     std::advance(endIter, endInfo.spanIndex_);
754     for (auto iter = startIter; iter != endIter; ++iter) {
755         ClearContent(*iter);
756         host->RemoveChild(*iter);
757     }
758     if (endIter != childrens.end() && delEndSpan) {
759         ClearContent(*endIter);
760         host->RemoveChild(*endIter);
761     }
762     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
763     host->MarkModifyDone();
764 }
765 
GetLeftTextOfCursor(int32_t number)766 std::u16string RichEditorPattern::GetLeftTextOfCursor(int32_t number)
767 {
768     if (number > caretPosition_) {
769         number = caretPosition_;
770     }
771     auto start = caretPosition_;
772     if (IsSelected()) {
773         start = std::min(textSelector_.GetStart(), textSelector_.GetEnd());
774     }
775     auto stringText = GetSelectedText(start - number, start);
776     return StringUtils::Str8ToStr16(stringText);
777 }
778 
GetRightTextOfCursor(int32_t number)779 std::u16string RichEditorPattern::GetRightTextOfCursor(int32_t number)
780 {
781     auto end = caretPosition_;
782     if (IsSelected()) {
783         end = std::max(textSelector_.GetStart(), textSelector_.GetEnd());
784     }
785     auto stringText = GetSelectedText(end, end + number);
786     return StringUtils::Str8ToStr16(stringText);
787 }
788 
GetTextIndexAtCursor()789 int32_t RichEditorPattern::GetTextIndexAtCursor()
790 {
791     return caretPosition_;
792 }
793 
ClearContent(const RefPtr<UINode> & child)794 void RichEditorPattern::ClearContent(const RefPtr<UINode>& child)
795 {
796     CHECK_NULL_VOID(child);
797     if (child->GetTag() == V2::SPAN_ETS_TAG) {
798         auto spanNode = DynamicCast<SpanNode>(child);
799         if (spanNode) {
800             spanNode->UpdateContent("");
801             spanNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
802         }
803     }
804 }
805 
GetSpanPositionInfo(int32_t position)806 SpanPositionInfo RichEditorPattern::GetSpanPositionInfo(int32_t position)
807 {
808     SpanPositionInfo spanPositionInfo(-1, -1, -1, -1);
809     CHECK_NULL_RETURN(!spans_.empty(), spanPositionInfo);
810     position = std::clamp(position, 0, GetTextContentLength());
811     // find the spanItem where the position is
812     auto it = std::find_if(spans_.begin(), spans_.end(), [position](const RefPtr<SpanItem>& spanItem) {
813         return (spanItem->position - static_cast<int32_t>(StringUtils::ToWstring(spanItem->content).length()) <=
814                    position) &&
815                (position < spanItem->position);
816     });
817     if (it != spans_.end() && (*it)->unicode != 0 && (*it)->position - caretPosition_ + moveLength_ == 1) {
818         it++;
819         moveLength_++;
820         position++;
821     }
822 
823     // the position is at the end
824     if (it == spans_.end()) {
825         return spanPositionInfo;
826     }
827 
828     spanPositionInfo.spanIndex_ = std::distance(spans_.begin(), it);
829     auto contentLen = StringUtils::ToWstring((*it)->content).length();
830     spanPositionInfo.spanStart_ = (*it)->position - contentLen;
831     spanPositionInfo.spanEnd_ = (*it)->position;
832     spanPositionInfo.spanOffset_ = position - spanPositionInfo.spanStart_;
833     return spanPositionInfo;
834 }
835 
CopyTextSpanStyle(RefPtr<SpanNode> & source,RefPtr<SpanNode> & target,bool needLeadingMargin)836 void RichEditorPattern::CopyTextSpanStyle(RefPtr<SpanNode>& source, RefPtr<SpanNode>& target, bool needLeadingMargin)
837 {
838     CHECK_NULL_VOID(source);
839     CHECK_NULL_VOID(target);
840 
841     CopyTextSpanFontStyle(source, target);
842     CopyTextSpanLineStyle(source, target, needLeadingMargin);
843 }
844 
CopyTextSpanFontStyle(RefPtr<SpanNode> & source,RefPtr<SpanNode> & target)845 void RichEditorPattern::CopyTextSpanFontStyle(RefPtr<SpanNode>& source, RefPtr<SpanNode>& target)
846 {
847     if (source->HasFontSize()) {
848         target->UpdateFontSize(source->GetFontSizeValue(Dimension()));
849         target->AddPropertyInfo(PropertyInfo::FONTSIZE);
850     }
851 
852     if (source->HasTextColor()) {
853         target->UpdateTextColor(source->GetTextColorValue(Color::BLACK));
854         target->AddPropertyInfo(PropertyInfo::FONTCOLOR);
855     }
856 
857     if (source->HasItalicFontStyle()) {
858         target->UpdateItalicFontStyle(source->GetItalicFontStyleValue(OHOS::Ace::FontStyle::NORMAL));
859         target->AddPropertyInfo(PropertyInfo::FONTSTYLE);
860     }
861 
862     if (source->HasFontWeight()) {
863         target->UpdateFontWeight(source->GetFontWeightValue(FontWeight::NORMAL));
864         target->AddPropertyInfo(PropertyInfo::FONTWEIGHT);
865     }
866 
867     if (source->HasFontFamily()) {
868         target->UpdateFontFamily(source->GetFontFamilyValue({ "HarmonyOS Sans" }));
869         target->AddPropertyInfo(PropertyInfo::FONTFAMILY);
870     }
871 
872     if (source->HasTextDecoration()) {
873         target->UpdateTextDecoration(source->GetTextDecorationValue(TextDecoration::NONE));
874         target->AddPropertyInfo(PropertyInfo::TEXTDECORATION);
875     }
876 
877     if (source->HasTextDecorationColor()) {
878         target->UpdateTextDecorationColor(source->GetTextDecorationColorValue(Color::BLACK));
879         target->AddPropertyInfo(PropertyInfo::NONE);
880     }
881 
882     if (source->HasTextCase()) {
883         target->UpdateTextCase(source->GetTextCaseValue(TextCase::NORMAL));
884         target->AddPropertyInfo(PropertyInfo::TEXTCASE);
885     }
886 
887     if (source->HasLetterSpacing()) {
888         target->UpdateLetterSpacing(source->GetLetterSpacingValue(Dimension()));
889         target->AddPropertyInfo(PropertyInfo::LETTERSPACE);
890     }
891 }
892 
CopyTextSpanLineStyle(RefPtr<SpanNode> & source,RefPtr<SpanNode> & target,bool needLeadingMargin)893 void RichEditorPattern::CopyTextSpanLineStyle(
894     RefPtr<SpanNode>& source, RefPtr<SpanNode>& target, bool needLeadingMargin)
895 {
896     if (source->HasLineHeight()) {
897         target->UpdateLineHeight(source->GetLineHeightValue(Dimension()));
898         target->AddPropertyInfo(PropertyInfo::LINEHEIGHT);
899     }
900 
901     if (source->HasTextShadow()) {
902         target->UpdateTextShadow(source->GetTextShadowValue({Shadow()}));
903         target->AddPropertyInfo(PropertyInfo::TEXTSHADOW);
904     }
905 
906     if (needLeadingMargin && source->HasLeadingMargin()) {
907         target->UpdateLeadingMargin(source->GetLeadingMarginValue({}));
908         target->AddPropertyInfo(PropertyInfo::LEADING_MARGIN);
909     }
910 
911     if (source->HasTextAlign()) {
912         target->UpdateTextAlign(source->GetTextAlignValue(TextAlign::LEFT));
913         target->AddPropertyInfo(PropertyInfo::TEXT_ALIGN);
914     }
915 }
916 
TextSpanSplit(int32_t position,bool needLeadingMargin)917 int32_t RichEditorPattern::TextSpanSplit(int32_t position, bool needLeadingMargin)
918 {
919     if (spans_.empty()) {
920         return -1;
921     }
922 
923     auto positionInfo = GetSpanPositionInfo(position);
924     int32_t spanIndex = positionInfo.spanIndex_;
925     int32_t spanStart = positionInfo.spanStart_;
926     int32_t offsetInSpan = positionInfo.spanOffset_;
927     TAG_LOGD(AceLogTag::ACE_RICH_TEXT,
928         "position=%{public}d, spanIndex=%{public}d, spanStart=%{public}d, offsetInSpan=%{public}d",
929         position, spanIndex, spanStart, offsetInSpan);
930 
931     if (offsetInSpan <= 0) {
932         return spanIndex;
933     }
934 
935     auto host = GetHost();
936     CHECK_NULL_RETURN(host, -1);
937     auto it = host->GetChildren().begin();
938     std::advance(it, spanIndex);
939 
940     auto spanNode = DynamicCast<SpanNode>(*it);
941     CHECK_NULL_RETURN(spanNode, -1);
942     auto spanItem = spanNode->GetSpanItem();
943     auto spanItemContent = StringUtils::ToWstring(spanItem->content);
944     if (offsetInSpan > static_cast<int32_t>(spanItemContent.length())) {
945         offsetInSpan = static_cast<int32_t>(spanItemContent.length());
946     }
947     auto newContent = spanItemContent.substr(offsetInSpan);
948     auto deleteContent = spanItemContent.substr(0, offsetInSpan);
949 
950     auto* stack = ViewStackProcessor::GetInstance();
951     CHECK_NULL_RETURN(stack, -1);
952     auto nodeId = stack->ClaimNodeId();
953     auto newSpanNode = SpanNode::GetOrCreateSpanNode(nodeId);
954     CHECK_NULL_RETURN(newSpanNode, -1);
955 
956     auto newSpanItem = newSpanNode->GetSpanItem();
957     newSpanItem->position = spanStart + offsetInSpan;
958     auto spanIter = spans_.begin();
959     std::advance(spanIter, spanIndex);
960     spans_.insert(spanIter, newSpanItem);
961 
962     spanNode->UpdateContent(StringUtils::ToString(newContent));
963     newSpanNode->UpdateContent(StringUtils::ToString(deleteContent));
964 
965     CopyTextSpanStyle(spanNode, newSpanNode, needLeadingMargin);
966     newSpanNode->MountToParent(host, spanIndex);
967 
968     return spanIndex + 1;
969 }
970 
GetCaretPosition()971 int32_t RichEditorPattern::GetCaretPosition()
972 {
973     return caretPosition_;
974 }
975 
SetCaretOffset(int32_t caretPosition)976 bool RichEditorPattern::SetCaretOffset(int32_t caretPosition)
977 {
978     bool success = false;
979     success = SetCaretPosition(caretPosition);
980     auto host = GetHost();
981     CHECK_NULL_RETURN(host, false);
982     auto focusHub = host->GetOrCreateFocusHub();
983     CHECK_NULL_RETURN(focusHub, false);
984     if (focusHub->IsCurrentFocus()) {
985         StartTwinkling();
986     }
987     CloseSelectOverlay();
988     ResetSelection();
989     return success;
990 }
991 
CalcCursorOffsetByPosition(int32_t position,float & selectLineHeight,bool downStreamFirst,bool needLineHighest)992 OffsetF RichEditorPattern::CalcCursorOffsetByPosition(
993     int32_t position, float& selectLineHeight, bool downStreamFirst, bool needLineHighest)
994 {
995     selectLineHeight = 0.0f;
996     auto host = GetHost();
997     CHECK_NULL_RETURN(host, OffsetF(0, 0));
998     auto pipeline = PipelineContext::GetCurrentContext();
999     CHECK_NULL_RETURN(pipeline, OffsetF(0, 0));
1000     auto rootOffset = pipeline->GetRootRect().GetOffset();
1001     auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
1002     auto startOffset = paragraphs_.ComputeCursorOffset(position, selectLineHeight, downStreamFirst, needLineHighest);
1003     auto children = host->GetChildren();
1004     if (NearZero(selectLineHeight)) {
1005         if (children.empty() || GetTextContentLength() == 0) {
1006             return textPaintOffset - rootOffset;
1007         }
1008         if (std::all_of(children.begin(), children.end(), [](RefPtr<UINode>& node) {
1009                 CHECK_NULL_RETURN(node, false);
1010                 return (node->GetTag() == V2::IMAGE_ETS_TAG || node->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG);
1011             })) {
1012             bool isTail = false;
1013             auto it = children.begin();
1014             if (position >= static_cast<int32_t>(children.size())) {
1015                 std::advance(it, (static_cast<int32_t>(children.size()) - 1));
1016                 isTail = true;
1017             } else {
1018                 std::advance(it, position);
1019             }
1020             if (it == children.end()) {
1021                 return startOffset;
1022             }
1023             auto imageNode = DynamicCast<FrameNode>(*it);
1024             if (imageNode) {
1025                 auto geometryNode = imageNode->GetGeometryNode();
1026                 CHECK_NULL_RETURN(geometryNode, OffsetF(0.0f, 0.0f));
1027                 startOffset = geometryNode->GetMarginFrameOffset();
1028                 selectLineHeight = geometryNode->GetMarginFrameSize().Height();
1029                 startOffset += isTail ? OffsetF(geometryNode->GetMarginFrameSize().Width(), 0.0f) : OffsetF(0.0f, 0.0f);
1030             }
1031             return startOffset;
1032         }
1033     }
1034     auto caretOffset = startOffset + textPaintOffset + rootOffset;
1035     auto geometryNode = host->GetGeometryNode();
1036     CHECK_NULL_RETURN(geometryNode, caretOffset);
1037     auto frameSize = geometryNode->GetFrameRect().GetSize();
1038     CHECK_NULL_RETURN(overlayMod_, caretOffset);
1039     float caretWidth = DynamicCast<RichEditorOverlayModifier>(overlayMod_)->GetCaretWidth();
1040     caretOffset.SetX(std::clamp(caretOffset.GetX(), 0.0f, static_cast<float>(frameSize.Width()) - caretWidth));
1041     return caretOffset;
1042 }
1043 
SetCaretPosition(int32_t pos)1044 bool RichEditorPattern::SetCaretPosition(int32_t pos)
1045 {
1046     auto correctPos = std::clamp(pos, 0, GetTextContentLength());
1047     ResetLastClickOffset();
1048     UpdateCaretInfoToController();
1049     if (pos == correctPos) {
1050         FireOnSelectionChange(correctPos);
1051         caretPosition_ = correctPos;
1052         return true;
1053     }
1054     return false;
1055 }
1056 
FireOnSelectionChange(const int32_t caretPosition)1057 void RichEditorPattern::FireOnSelectionChange(const int32_t caretPosition)
1058 {
1059     if (!textSelector_.SelectNothing() || caretPosition == caretPosition_) {
1060         return;
1061     }
1062     FireOnSelectionChange(caretPosition, caretPosition);
1063 }
1064 
FireOnSelectionChange(const TextSelector & selector)1065 void RichEditorPattern::FireOnSelectionChange(const TextSelector& selector)
1066 {
1067     if (selector.SelectNothing()) {
1068         return;
1069     }
1070     FireOnSelectionChange(selector.GetStart(), selector.GetEnd());
1071 }
1072 
FireOnSelectionChange(int32_t start,int32_t end)1073 void RichEditorPattern::FireOnSelectionChange(int32_t start, int32_t end)
1074 {
1075     auto host = GetHost();
1076     CHECK_NULL_VOID(host);
1077     auto eventHub = host->GetEventHub<RichEditorEventHub>();
1078     CHECK_NULL_VOID(eventHub);
1079     CHECK_NULL_VOID(HasFocus());
1080     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "range=[%{public}d,%{public}d], caretTwinkling_=%{public}d",
1081         start, end, caretTwinkling_);
1082     if (start < 0 || end < 0) {
1083         return;
1084     }
1085     if (start == end && !caretTwinkling_) {
1086         return;
1087     }
1088     if (start > end) {
1089         std::swap(start, end);
1090     }
1091     auto rangeInfo = SelectionRangeInfo(start, end);
1092     eventHub->FireOnSelectionChange(&rangeInfo);
1093 }
1094 
GetCaretVisible() const1095 bool RichEditorPattern::GetCaretVisible() const
1096 {
1097     return caretVisible_;
1098 }
1099 
SetUpdateSpanStyle(struct UpdateSpanStyle updateSpanStyle)1100 void RichEditorPattern::SetUpdateSpanStyle(struct UpdateSpanStyle updateSpanStyle)
1101 {
1102     updateSpanStyle_ = updateSpanStyle;
1103 }
1104 
SetTypingStyle(struct UpdateSpanStyle typingStyle,TextStyle textStyle)1105 void RichEditorPattern::SetTypingStyle(struct UpdateSpanStyle typingStyle, TextStyle textStyle)
1106 {
1107     typingStyle_ = typingStyle;
1108     typingTextStyle_ = textStyle;
1109 }
1110 
UpdateTextStyle(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle updateSpanStyle,TextStyle textStyle)1111 void RichEditorPattern::UpdateTextStyle(
1112     RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle updateSpanStyle, TextStyle textStyle)
1113 {
1114     if (spanNode->GetTag() != V2::SPAN_ETS_TAG || updateSpanStyle_.isSymbolStyle) {
1115         return;
1116     }
1117     auto host = GetHost();
1118     CHECK_NULL_VOID(host);
1119     if (updateSpanStyle.updateTextColor.has_value()) {
1120         spanNode->UpdateTextColor(textStyle.GetTextColor());
1121         spanNode->AddPropertyInfo(PropertyInfo::FONTCOLOR);
1122     }
1123     if (updateSpanStyle.updateFontSize.has_value()) {
1124         spanNode->UpdateFontSize(textStyle.GetFontSize());
1125         spanNode->AddPropertyInfo(PropertyInfo::FONTSIZE);
1126     }
1127     if (updateSpanStyle.updateItalicFontStyle.has_value()) {
1128         spanNode->UpdateItalicFontStyle(textStyle.GetFontStyle());
1129         spanNode->AddPropertyInfo(PropertyInfo::FONTSTYLE);
1130     }
1131     if (updateSpanStyle.updateFontWeight.has_value()) {
1132         spanNode->UpdateFontWeight(textStyle.GetFontWeight());
1133         spanNode->AddPropertyInfo(PropertyInfo::FONTWEIGHT);
1134     }
1135     if (updateSpanStyle.updateFontFamily.has_value()) {
1136         spanNode->UpdateFontFamily(textStyle.GetFontFamilies());
1137         spanNode->AddPropertyInfo(PropertyInfo::FONTFAMILY);
1138     }
1139     if (updateSpanStyle.updateTextDecoration.has_value()) {
1140         spanNode->UpdateTextDecoration(textStyle.GetTextDecoration());
1141         spanNode->AddPropertyInfo(PropertyInfo::TEXTDECORATION);
1142     }
1143     if (updateSpanStyle.updateTextDecorationColor.has_value()) {
1144         spanNode->UpdateTextDecorationColor(textStyle.GetTextDecorationColor());
1145         spanNode->AddPropertyInfo(PropertyInfo::NONE);
1146     }
1147     if (updateSpanStyle.updateTextShadows.has_value()) {
1148         spanNode->UpdateTextShadow(textStyle.GetTextShadows());
1149         spanNode->AddPropertyInfo(PropertyInfo::TEXTSHADOW);
1150     }
1151     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1152     host->MarkModifyDone();
1153 }
1154 
UpdateSymbolStyle(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle updateSpanStyle,TextStyle textStyle)1155 void RichEditorPattern::UpdateSymbolStyle(
1156     RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle updateSpanStyle, TextStyle textStyle)
1157 {
1158     if (spanNode->GetTag() != V2::SYMBOL_SPAN_ETS_TAG || !updateSpanStyle_.isSymbolStyle) {
1159         return;
1160     }
1161     auto host = GetHost();
1162     CHECK_NULL_VOID(host);
1163     if (updateSpanStyle.updateFontSize.has_value()) {
1164         spanNode->UpdateFontSize(textStyle.GetFontSize());
1165         spanNode->AddPropertyInfo(PropertyInfo::FONTSIZE);
1166     }
1167     if (updateSpanStyle.updateFontWeight.has_value()) {
1168         spanNode->UpdateFontWeight(textStyle.GetFontWeight());
1169         spanNode->AddPropertyInfo(PropertyInfo::FONTWEIGHT);
1170     }
1171     if (updateSpanStyle.updateSymbolColor.has_value()) {
1172         spanNode->UpdateSymbolColorList(textStyle.GetSymbolColorList());
1173         spanNode->AddPropertyInfo(PropertyInfo::SYMBOL_COLOR);
1174     }
1175     if (updateSpanStyle.updateSymbolRenderingStrategy.has_value()) {
1176         spanNode->UpdateSymbolRenderingStrategy(textStyle.GetRenderStrategy());
1177         spanNode->AddPropertyInfo(PropertyInfo::SYMBOL_RENDERING_STRATEGY);
1178     }
1179     if (updateSpanStyle.updateSymbolEffectStrategy.has_value()) {
1180         spanNode->UpdateSymbolEffectStrategy(textStyle.GetEffectStrategy());
1181         spanNode->AddPropertyInfo(PropertyInfo::SYMBOL_EFFECT_STRATEGY);
1182     }
1183     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1184     host->MarkModifyDone();
1185 }
1186 
HasSameTypingStyle(const RefPtr<SpanNode> & spanNode)1187 bool RichEditorPattern::HasSameTypingStyle(const RefPtr<SpanNode>& spanNode)
1188 {
1189     auto spanItem = spanNode->GetSpanItem();
1190     CHECK_NULL_RETURN(spanItem, false);
1191     auto spanTextStyle = spanItem->GetTextStyle();
1192     if (spanTextStyle.has_value() && typingTextStyle_.has_value()) {
1193         return spanTextStyle.value() == typingTextStyle_.value();
1194     } else {
1195         return !(spanTextStyle.has_value() || typingTextStyle_.has_value());
1196     }
1197 }
1198 
UpdateImageStyle(RefPtr<FrameNode> & imageNode,const ImageSpanAttribute & imageStyle)1199 void RichEditorPattern::UpdateImageStyle(RefPtr<FrameNode>& imageNode, const ImageSpanAttribute& imageStyle)
1200 {
1201     auto host = GetHost();
1202     CHECK_NULL_VOID(host);
1203     auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
1204     if (updateSpanStyle_.updateImageWidth.has_value() || updateSpanStyle_.updateImageHeight.has_value()) {
1205         imageLayoutProperty->UpdateUserDefinedIdealSize(
1206             CalcSize(CalcLength(imageStyle.size.value().width), CalcLength(imageStyle.size.value().height)));
1207     }
1208     if (updateSpanStyle_.updateImageFit.has_value()) {
1209         imageLayoutProperty->UpdateImageFit(imageStyle.objectFit.value());
1210     }
1211     if (updateSpanStyle_.updateImageVerticalAlign.has_value()) {
1212         imageLayoutProperty->UpdateVerticalAlign(imageStyle.verticalAlign.value());
1213     }
1214     if (updateSpanStyle_.borderRadius.has_value()) {
1215         auto imageRenderCtx = imageNode->GetRenderContext();
1216         imageRenderCtx->UpdateBorderRadius(imageStyle.borderRadius.value());
1217         imageRenderCtx->SetClipToBounds(true);
1218     }
1219     if (updateSpanStyle_.marginProp.has_value()) {
1220         imageLayoutProperty->UpdateMargin(imageStyle.marginProp.value());
1221     }
1222 
1223     imageNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1224     imageNode->MarkModifyDone();
1225     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1226     host->MarkModifyDone();
1227 }
1228 
SymbolSpanUpdateStyle(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle updateSpanStyle,TextStyle textStyle)1229 bool RichEditorPattern::SymbolSpanUpdateStyle(
1230     RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle updateSpanStyle, TextStyle textStyle)
1231 {
1232     if (spanNode->GetTag() == V2::SYMBOL_SPAN_ETS_TAG) {
1233         UpdateSymbolStyle(spanNode, updateSpanStyle_, textStyle);
1234         return true;
1235     }
1236     return false;
1237 }
1238 
UpdateSpanStyle(int32_t start,int32_t end,const TextStyle & textStyle,const ImageSpanAttribute & imageStyle)1239 void RichEditorPattern::UpdateSpanStyle(
1240     int32_t start, int32_t end, const TextStyle& textStyle, const ImageSpanAttribute& imageStyle)
1241 {
1242     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "range=[%{public}d, %{public}d]", start, end);
1243     auto host = GetHost();
1244     CHECK_NULL_VOID(host);
1245     int32_t spanStart = 0;
1246     int32_t spanEnd = 0;
1247     for (auto it = host->GetChildren().begin(); it != host->GetChildren().end(); ++it) {
1248         auto spanNode = DynamicCast<SpanNode>(*it);
1249         auto imageNode = DynamicCast<FrameNode>(*it);
1250         if (!spanNode) {
1251             if (spanEnd != 0) {
1252                 spanStart = spanEnd;
1253             }
1254             spanEnd = spanStart + 1;
1255         } else {
1256             spanNode->GetSpanItem()->GetIndex(spanStart, spanEnd);
1257         }
1258         if (spanEnd < start) {
1259             continue;
1260         }
1261 
1262         if (spanStart >= start && spanEnd <= end) {
1263             if (spanNode) {
1264                 UpdateSymbolStyle(spanNode, updateSpanStyle_, textStyle);
1265                 UpdateTextStyle(spanNode, updateSpanStyle_, textStyle);
1266             } else {
1267                 UpdateImageStyle(imageNode, imageStyle);
1268             }
1269             if (spanEnd == end) {
1270                 break;
1271             }
1272         } else if (spanStart < start && start < spanEnd) {
1273             if (SymbolSpanUpdateStyle(spanNode, updateSpanStyle_, textStyle)) {
1274                 continue;
1275             }
1276             TextSpanSplit(start, true);
1277             --it;
1278         } else if (spanStart < end && end < spanEnd) {
1279             if (SymbolSpanUpdateStyle(spanNode, updateSpanStyle_, textStyle)) {
1280                 continue;
1281             }
1282             TextSpanSplit(end, true);
1283             --(--it);
1284         } else if (spanStart >= end) {
1285             break;
1286         }
1287     }
1288 
1289     // Custom menus do not actively close.
1290     if (!SelectOverlayIsOn() || selectOverlayProxy_->GetSelectOverlayMangerInfo().menuInfo.menuBuilder == nullptr) {
1291         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "CloseSelectOverlay");
1292         CloseSelectOverlay();
1293     }
1294 }
1295 
GetParagraphInfo(int32_t start,int32_t end)1296 std::vector<ParagraphInfo> RichEditorPattern::GetParagraphInfo(int32_t start, int32_t end)
1297 {
1298     std::vector<ParagraphInfo> res;
1299     auto spanNodes = GetParagraphNodes(start, end);
1300     CHECK_NULL_RETURN(!spanNodes.empty(), {});
1301 
1302     auto&& firstSpan = spanNodes.front()->GetSpanItem();
1303     auto paraStart = firstSpan->position - StringUtils::ToWstring(firstSpan->content).length();
1304 
1305     for (auto it = spanNodes.begin(); it != spanNodes.end(); ++it) {
1306         if (it == std::prev(spanNodes.end()) || StringUtils::ToWstring((*it)->GetSpanItem()->content).back() == L'\n') {
1307             ParagraphInfo info;
1308             auto lm = (*it)->GetLeadingMarginValue({});
1309 
1310             res.emplace_back(ParagraphInfo {
1311                 .leadingMarginPixmap = lm.pixmap,
1312                 .leadingMarginSize = { Dimension(lm.size.Width()).ConvertToVp(),
1313                     Dimension(lm.size.Height()).ConvertToVp() },
1314                 .textAlign = static_cast<int32_t>((*it)->GetTextAlignValue(TextAlign::START)),
1315                 .range = { paraStart, (*it)->GetSpanItem()->position },
1316             });
1317             paraStart = (*it)->GetSpanItem()->position;
1318         }
1319     }
1320 
1321     return res;
1322 }
1323 
GetParagraphLength(const std::list<RefPtr<UINode>> & spans) const1324 int32_t RichEditorPattern::GetParagraphLength(const std::list<RefPtr<UINode>>& spans) const
1325 {
1326     if (spans.empty()) {
1327         return 0;
1328     }
1329     int32_t imageSpanCnt = 0;
1330     for (auto it = spans.rbegin(); it != spans.rend(); ++it) {
1331         auto spanNode = DynamicCast<SpanNode>(*it);
1332         if (spanNode) {
1333             return spanNode->GetSpanItem()->position + imageSpanCnt;
1334         }
1335         ++imageSpanCnt;
1336     }
1337     return imageSpanCnt;
1338 }
1339 
GetParagraphNodes(int32_t start,int32_t end) const1340 std::vector<RefPtr<SpanNode>> RichEditorPattern::GetParagraphNodes(int32_t start, int32_t end) const
1341 {
1342     CHECK_NULL_RETURN(start != end, {});
1343     auto host = GetHost();
1344     CHECK_NULL_RETURN(host, {});
1345     CHECK_NULL_RETURN(!host->GetChildren().empty(), {});
1346 
1347     const auto& spans = host->GetChildren();
1348     int32_t length = GetParagraphLength(spans);
1349     std::vector<RefPtr<SpanNode>> res;
1350 
1351     if (start >= length) {
1352         return res;
1353     }
1354 
1355     auto headIt = spans.begin();
1356     auto flagNode = headIt;
1357     bool isEnd = false;
1358     int32_t spanEnd = -1;
1359     while (flagNode != spans.end()) {
1360         auto spanNode = DynamicCast<SpanNode>(*flagNode);
1361         if (spanNode) {
1362             auto&& info = spanNode->GetSpanItem();
1363             spanEnd = info->position;
1364             isEnd = StringUtils::ToWstring(info->content).back() == '\n';
1365         } else {
1366             ++spanEnd;
1367             isEnd = false;
1368         }
1369         flagNode++;
1370         if (spanEnd > start) {
1371             break;
1372         }
1373         if (isEnd) {
1374             headIt = flagNode;
1375         }
1376     }
1377     while (headIt != flagNode) {
1378         auto spanNode = DynamicCast<SpanNode>(*headIt);
1379         if (spanNode) {
1380             res.emplace_back(spanNode);
1381         }
1382         headIt++;
1383     }
1384     while (flagNode != spans.end() && (spanEnd < end || !isEnd)) {
1385         auto spanNode = DynamicCast<SpanNode>(*flagNode);
1386         if (spanNode) {
1387             res.emplace_back(spanNode);
1388             auto&& info = spanNode->GetSpanItem();
1389             spanEnd = info->position;
1390             isEnd = StringUtils::ToWstring(info->content).back() == '\n';
1391         } else {
1392             ++spanEnd;
1393             isEnd = false;
1394         }
1395         flagNode++;
1396     }
1397 
1398     return res;
1399 }
1400 
UpdateParagraphStyle(int32_t start,int32_t end,const struct UpdateParagraphStyle & style)1401 void RichEditorPattern::UpdateParagraphStyle(int32_t start, int32_t end, const struct UpdateParagraphStyle& style)
1402 {
1403     auto spanNodes = GetParagraphNodes(start, end);
1404     for (const auto& spanNode : spanNodes) {
1405         if (style.textAlign.has_value()) {
1406             spanNode->UpdateTextAlign(*style.textAlign);
1407         } else {
1408             spanNode->UpdateTextAlign(TextAlign::START);
1409         }
1410         if (style.leadingMargin.has_value()) {
1411             spanNode->GetSpanItem()->leadingMargin = *style.leadingMargin;
1412             spanNode->UpdateLeadingMargin(*style.leadingMargin);
1413         }
1414     }
1415 }
1416 
ScheduleCaretTwinkling()1417 void RichEditorPattern::ScheduleCaretTwinkling()
1418 {
1419     auto context = PipelineContext::GetCurrentContext();
1420     CHECK_NULL_VOID(context);
1421 
1422     if (!context->GetTaskExecutor()) {
1423         return;
1424     }
1425 
1426     if (isCursorAlwaysDisplayed_) {
1427         return;
1428     }
1429 
1430     auto weak = WeakClaim(this);
1431     caretTwinklingTask_.Reset([weak] {
1432         auto client = weak.Upgrade();
1433         CHECK_NULL_VOID(client);
1434         client->OnCaretTwinkling();
1435     });
1436     auto taskExecutor = context->GetTaskExecutor();
1437     CHECK_NULL_VOID(taskExecutor);
1438     taskExecutor->PostDelayedTask(caretTwinklingTask_, TaskExecutor::TaskType::UI, RICH_EDITOR_TWINKLING_INTERVAL_MS);
1439 }
1440 
StartTwinkling()1441 void RichEditorPattern::StartTwinkling()
1442 {
1443     caretTwinklingTask_.Cancel();
1444     // Fire on selecion change when caret invisible -> visible
1445     if (!caretTwinkling_) {
1446         caretTwinkling_ = true;
1447         FireOnSelectionChange(caretPosition_, caretPosition_);
1448     }
1449     caretVisible_ = true;
1450     auto tmpHost = GetHost();
1451     CHECK_NULL_VOID(tmpHost);
1452     tmpHost->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1453     ScheduleCaretTwinkling();
1454 }
1455 
OnCaretTwinkling()1456 void RichEditorPattern::OnCaretTwinkling()
1457 {
1458     caretTwinklingTask_.Cancel();
1459     caretVisible_ = !caretVisible_;
1460     GetHost()->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1461     ScheduleCaretTwinkling();
1462 }
1463 
StopTwinkling()1464 void RichEditorPattern::StopTwinkling()
1465 {
1466     caretTwinkling_ = false;
1467     caretTwinklingTask_.Cancel();
1468     if (caretVisible_) {
1469         caretVisible_ = false;
1470         GetHost()->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1471     }
1472 }
1473 
HandleClickEvent(GestureEvent & info)1474 void RichEditorPattern::HandleClickEvent(GestureEvent& info)
1475 {
1476     if (dataDetectorAdapter_->hasClickedAISpan_) {
1477         dataDetectorAdapter_->hasClickedAISpan_ = false;
1478     } else if (hasClicked_) {
1479         hasClicked_ = false;
1480         TimeStamp clickTimeStamp = info.GetTimeStamp();
1481         std::chrono::duration<float, std::ratio<1, InputAIChecker::SECONDS_TO_MILLISECONDS>> timeout =
1482             clickTimeStamp - lastClickTimeStamp_;
1483         lastClickTimeStamp_ = info.GetTimeStamp();
1484         if (timeout.count() < DOUBLE_CLICK_INTERVAL_MS) {
1485             HandleDoubleClickEvent(info);
1486             return;
1487         }
1488     }
1489     HandleSingleClickEvent(info);
1490 }
1491 
HandleSingleClickEvent(OHOS::Ace::GestureEvent & info)1492 void RichEditorPattern::HandleSingleClickEvent(OHOS::Ace::GestureEvent& info)
1493 {
1494     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "in handleSingleClickEvent");
1495     hasClicked_ = true;
1496     lastClickTimeStamp_ = info.GetTimeStamp();
1497     if (info.GetSourceDevice() != SourceType::MOUSE && SelectOverlayIsOn() &&
1498         BetweenSelectedPosition(info.GetGlobalLocation())) {
1499         selectOverlayProxy_->ShowOrHiddenMenu(false);
1500         return;
1501     }
1502 
1503     auto textRect = GetTextRect();
1504     textRect.SetTop(textRect.GetY() - std::min(baselineOffset_, 0.0f));
1505     textRect.SetHeight(textRect.Height() - std::max(baselineOffset_, 0.0f));
1506     Offset textOffset = { info.GetLocalLocation().GetX() - textRect.GetX(),
1507         info.GetLocalLocation().GetY() - textRect.GetY() };
1508     HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY()));
1509     if (dataDetectorAdapter_->hasClickedAISpan_) {
1510         if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
1511             selectOverlayProxy_->DisableMenu(true);
1512         }
1513         return;
1514     }
1515 
1516     HandleUserClickEvent(info);
1517     if (textSelector_.IsValid() && !isMouseSelect_) {
1518         CloseSelectOverlay();
1519         ResetSelection();
1520     }
1521 
1522     caretUpdateType_ = CaretUpdateType::PRESSED;
1523     UseHostToUpdateTextFieldManager();
1524 
1525     auto position = paragraphs_.GetIndex(textOffset);
1526     AdjustCursorPosition(position);
1527 
1528     auto focusHub = GetHost()->GetOrCreateFocusHub();
1529     if (focusHub) {
1530         SetCaretPosition(position);
1531         if (!dataDetectorAdapter_->hasClickedAISpan_ && focusHub->RequestFocusImmediately()) {
1532             float caretHeight = 0.0f;
1533             OffsetF caretOffset = CalcCursorOffsetByPosition(GetCaretPosition(), caretHeight, false, false);
1534             CHECK_NULL_VOID(overlayMod_);
1535             DynamicCast<RichEditorOverlayModifier>(overlayMod_)->SetCaretOffsetAndHeight(caretOffset, caretHeight);
1536             StartTwinkling();
1537             if (overlayMod_) {
1538                 RequestKeyboard(false, true, true);
1539             }
1540         }
1541     }
1542     UseHostToUpdateTextFieldManager();
1543     CalcCaretInfoByClick(info);
1544 }
1545 
HandleDoubleClickEvent(OHOS::Ace::GestureEvent & info)1546 void RichEditorPattern::HandleDoubleClickEvent(OHOS::Ace::GestureEvent& info)
1547 {
1548     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "in double HandleDoubleClickEvent,use mouse:%{public}d", info.GetSourceDevice());
1549     caretUpdateType_ = CaretUpdateType::DOUBLE_CLICK;
1550     HandleDoubleClickOrLongPress(info);
1551     caretUpdateType_ = CaretUpdateType::NONE;
1552 }
1553 
HandleUserGestureEvent(GestureEvent & info,std::function<bool (RefPtr<SpanItem> item,GestureEvent & info)> && gestureFunc)1554 bool RichEditorPattern::HandleUserGestureEvent(
1555     GestureEvent& info, std::function<bool(RefPtr<SpanItem> item, GestureEvent& info)>&& gestureFunc)
1556 {
1557     RectF textContentRect = contentRect_;
1558     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
1559     textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
1560     if (!textContentRect.IsInRegion(PointF(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY())) ||
1561         spans_.empty()) {
1562         return false;
1563     }
1564     PointF textOffset = { info.GetLocalLocation().GetX() - GetTextRect().GetX(),
1565         info.GetLocalLocation().GetY() - GetTextRect().GetY() };
1566     int32_t start = 0;
1567     bool isParagraphHead = true;
1568     Offset paragraphOffset(0, 0);
1569     for (const auto& item : spans_) {
1570         if (!item) {
1571             continue;
1572         }
1573         std::vector<RectF> selectedRects = paragraphs_.GetRects(start, item->position);
1574         start = item->position;
1575         if (isParagraphHead && !selectedRects.empty()) {
1576             if (item->leadingMargin.has_value()) {
1577                 auto addWidth = item->leadingMargin.value().size.Width();
1578                 selectedRects[0].SetLeft(selectedRects[0].GetX() - addWidth);
1579                 selectedRects[0].SetWidth(selectedRects[0].GetSize().Width() + addWidth);
1580             }
1581             paragraphOffset.SetX(selectedRects[0].GetOffset().GetX());
1582             paragraphOffset.SetY(selectedRects[0].GetOffset().GetY());
1583             isParagraphHead = false;
1584         }
1585         if (!isParagraphHead && item->content.back() == '\n') {
1586             isParagraphHead = true;
1587         }
1588         for (auto&& rect : selectedRects) {
1589             if (!rect.IsInRegion(textOffset)) {
1590                 continue;
1591             }
1592             info = info.SetScreenLocation(
1593                 Offset(textOffset.GetX() - paragraphOffset.GetX(), textOffset.GetY() - paragraphOffset.GetY()));
1594             return gestureFunc(item, info);
1595         }
1596     }
1597     return false;
1598 }
1599 
ClickAISpan(const PointF & textOffset,const AISpan & aiSpan)1600 bool RichEditorPattern::ClickAISpan(const PointF& textOffset, const AISpan& aiSpan)
1601 {
1602     auto calculateHandleFunc = [weak = WeakClaim(this)]() {
1603         auto pattern = weak.Upgrade();
1604         CHECK_NULL_VOID(pattern);
1605         pattern->CalculateHandleOffsetAndShowOverlay();
1606     };
1607     auto showSelectOverlayFunc = [weak = WeakClaim(this)](const RectF& firstHandle, const RectF& secondHandle) {
1608         auto pattern = weak.Upgrade();
1609         CHECK_NULL_VOID(pattern);
1610         pattern->ShowSelectOverlay(firstHandle, secondHandle);
1611     };
1612 
1613     std::vector<RectF> aiRects = paragraphs_.GetRects(aiSpan.start, aiSpan.end);
1614     for (auto&& rect : aiRects) {
1615         if (rect.IsInRegion(textOffset)) {
1616             dataDetectorAdapter_->hasClickedAISpan_ = true;
1617             ShowUIExtensionMenu(aiSpan, calculateHandleFunc, showSelectOverlayFunc);
1618             return true;
1619         }
1620     }
1621     return false;
1622 }
1623 
HandleUserClickEvent(GestureEvent & info)1624 bool RichEditorPattern::HandleUserClickEvent(GestureEvent& info)
1625 {
1626     auto clickFunc = [](RefPtr<SpanItem> item, GestureEvent& info) -> bool {
1627         if (item && item->onClick) {
1628             item->onClick(info);
1629             return true;
1630         }
1631         return false;
1632     };
1633     return HandleUserGestureEvent(info, std::move(clickFunc));
1634 }
1635 
CalcCaretInfoByClick(GestureEvent & info)1636 void RichEditorPattern::CalcCaretInfoByClick(GestureEvent& info)
1637 {
1638     auto textRect = GetTextRect();
1639     auto touchOffset = info.GetLocalLocation();
1640     textRect.SetTop(textRect.GetY() - std::min(baselineOffset_, 0.0f));
1641     textRect.SetHeight(textRect.Height() - std::max(baselineOffset_, 0.0f));
1642     Offset textOffset = { touchOffset.GetX() - textRect.GetX(), touchOffset.GetY() - textRect.GetY() };
1643     // get the caret position
1644     auto position = paragraphs_.GetIndex(textOffset);
1645     // get the caret offset when click
1646     float selectLineHeight = 0.0f;
1647     auto lastClickOffset = paragraphs_.ComputeCursorInfoByClick(position, selectLineHeight,
1648         OffsetF(static_cast<float>(textOffset.GetX()), static_cast<float>(textOffset.GetY())));
1649 
1650     lastClickOffset.AddX(textRect.GetX());
1651     lastClickOffset.AddY(textRect.GetY());
1652 
1653     CHECK_NULL_VOID(overlayMod_);
1654     DynamicCast<RichEditorOverlayModifier>(overlayMod_)->SetCaretOffsetAndHeight(lastClickOffset, selectLineHeight);
1655     SetLastClickOffset(lastClickOffset);
1656 
1657     MoveCaretToContentRect(lastClickOffset, selectLineHeight);
1658 }
1659 
InitClickEvent(const RefPtr<GestureEventHub> & gestureHub)1660 void RichEditorPattern::InitClickEvent(const RefPtr<GestureEventHub>& gestureHub)
1661 {
1662     CHECK_NULL_VOID(!clickEventInitialized_);
1663     auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
1664         auto pattern = weak.Upgrade();
1665         CHECK_NULL_VOID(pattern);
1666         pattern->HandleClickEvent(info);
1667     };
1668     auto clickListener = MakeRefPtr<ClickEvent>(std::move(clickCallback));
1669     gestureHub->AddClickEvent(clickListener);
1670     clickEventInitialized_ = true;
1671 }
1672 
InitFocusEvent(const RefPtr<FocusHub> & focusHub)1673 void RichEditorPattern::InitFocusEvent(const RefPtr<FocusHub>& focusHub)
1674 {
1675     CHECK_NULL_VOID(!focusEventInitialized_);
1676     auto focusTask = [weak = WeakClaim(this)]() {
1677         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "rich editor in focus");
1678         auto pattern = weak.Upgrade();
1679         CHECK_NULL_VOID(pattern);
1680         pattern->HandleFocusEvent();
1681     };
1682     focusHub->SetOnFocusInternal(focusTask);
1683     auto blurTask = [weak = WeakClaim(this)]() {
1684         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "rich editor in blur");
1685         auto pattern = weak.Upgrade();
1686         CHECK_NULL_VOID(pattern);
1687         pattern->HandleBlurEvent();
1688     };
1689     focusHub->SetOnBlurInternal(blurTask);
1690     focusEventInitialized_ = true;
1691     auto keyTask = [weak = WeakClaim(this)](const KeyEvent& keyEvent) -> bool {
1692         auto pattern = weak.Upgrade();
1693         CHECK_NULL_RETURN(pattern, false);
1694         return pattern->OnKeyEvent(keyEvent);
1695     };
1696     focusHub->SetOnKeyEventInternal(std::move(keyTask));
1697 }
1698 
CheckBlurReason()1699 bool RichEditorPattern::CheckBlurReason()
1700 {
1701     auto host = GetHost();
1702     CHECK_NULL_RETURN(host, false);
1703     auto curFocusHub = host->GetFocusHub();
1704     CHECK_NULL_RETURN(curFocusHub, false);
1705     auto curBlurReason = curFocusHub->GetBlurReason();
1706     if (curBlurReason == BlurReason::FRAME_DESTROY) {
1707         TAG_LOGI(AceLogTag::ACE_KEYBOARD, "TextFieldPattern CheckBlurReason, Close Keyboard.");
1708         return true;
1709     }
1710     return false;
1711 }
1712 
HandleBlurEvent()1713 void RichEditorPattern::HandleBlurEvent()
1714 {
1715     if (textDetectEnable_) {
1716         isLongPress_ = false;
1717         if (CanStartAITask()) {
1718             dataDetectorAdapter_->StartAITask();
1719         }
1720     }
1721     StopTwinkling();
1722     // The pattern handles blurevent, Need to close the softkeyboard first.
1723     if ((customKeyboardBuilder_ && isCustomKeyboardAttached_) || CheckBlurReason()) {
1724         TAG_LOGI(AceLogTag::ACE_KEYBOARD, "RichEditor Blur, Close Keyboard.");
1725         CloseKeyboard(true);
1726     }
1727 
1728     if (textSelector_.IsValid()) {
1729         CloseSelectOverlay();
1730         ResetSelection();
1731     }
1732 }
1733 
HandleFocusEvent()1734 void RichEditorPattern::HandleFocusEvent()
1735 {
1736     auto host = GetHost();
1737     if (host && textDetectEnable_ && !dataDetectorAdapter_->aiSpanMap_.empty()) {
1738         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1739     }
1740     dataDetectorAdapter_->CancelAITask();
1741     UseHostToUpdateTextFieldManager();
1742     StartTwinkling();
1743     if (!usingMouseRightButton_ && !isLongPress_ && !isDragging_) {
1744         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Handle Focus Event, Request keyboard.");
1745         RequestKeyboard(false, true, true);
1746     }
1747 }
1748 
UseHostToUpdateTextFieldManager()1749 void RichEditorPattern::UseHostToUpdateTextFieldManager()
1750 {
1751     auto host = GetHost();
1752     CHECK_NULL_VOID(host);
1753     auto context = PipelineContext::GetCurrentContext();
1754     CHECK_NULL_VOID(context);
1755     auto globalOffset = host->GetPaintRectOffset() - context->GetRootRect().GetOffset();
1756     UpdateTextFieldManager(Offset(globalOffset.GetX(), globalOffset.GetY()), frameRect_.Height());
1757 }
1758 
OnVisibleChange(bool isVisible)1759 void RichEditorPattern::OnVisibleChange(bool isVisible)
1760 {
1761     TextPattern::OnVisibleChange(isVisible);
1762     StopTwinkling();
1763     CloseKeyboard(true);
1764 }
1765 
CloseKeyboard(bool forceClose)1766 bool RichEditorPattern::CloseKeyboard(bool forceClose)
1767 {
1768     CloseSelectOverlay();
1769     ResetSelection();
1770     if (forceClose) {
1771         if (customKeyboardBuilder_ && isCustomKeyboardAttached_) {
1772             return CloseCustomKeyboard();
1773         }
1774         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Request close soft keyboard.");
1775 #if defined(ENABLE_STANDARD_INPUT)
1776 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
1777         if (!imeAttached_) {
1778             return false;
1779         }
1780 #endif
1781         auto inputMethod = MiscServices::InputMethodController::GetInstance();
1782         CHECK_NULL_RETURN(inputMethod, false);
1783         inputMethod->HideTextInput();
1784         inputMethod->Close();
1785 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
1786         imeAttached_ = false;
1787 #endif
1788 #else
1789         if (HasConnection()) {
1790             connection_->Close(GetInstanceId());
1791             connection_ = nullptr;
1792         }
1793 #endif
1794         return true;
1795     }
1796     return false;
1797 }
1798 
JudgeDraggable(GestureEvent & info)1799 bool RichEditorPattern::JudgeDraggable(GestureEvent& info)
1800 {
1801     auto host = GetHost();
1802     CHECK_NULL_RETURN(host, false);
1803     if (copyOption_ == CopyOptions::None) {
1804         return false;
1805     }
1806     auto hub = host->GetEventHub<EventHub>();
1807     CHECK_NULL_RETURN(hub, false);
1808     auto gestureHub = hub->GetOrCreateGestureEventHub();
1809     if (BetweenSelectedPosition(info.GetGlobalLocation())) {
1810         dragBoxes_ = GetTextBoxes();
1811         // prevent long press event from being triggered when dragging
1812         auto selectStart = textSelector_.GetTextStart();
1813         auto selectEnd = textSelector_.GetTextEnd();
1814         auto textSelectInfo = GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT);
1815         auto resultObjects = textSelectInfo.GetSelection().resultObjects;
1816         auto iter =
1817             std::find_if(resultObjects.begin(), resultObjects.end(), [](ResultObject& obj) { return obj.isDraggable; });
1818         gestureHub->SetIsTextDraggable(iter != resultObjects.end());
1819         return true;
1820     }
1821     gestureHub->SetIsTextDraggable(false);
1822     return false;
1823 }
1824 
HandleLongPress(GestureEvent & info)1825 void RichEditorPattern::HandleLongPress(GestureEvent& info)
1826 {
1827     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "handle long press!");
1828     caretUpdateType_ = CaretUpdateType::LONG_PRESSED;
1829     HandleDoubleClickOrLongPress(info);
1830     caretUpdateType_ = CaretUpdateType::NONE;
1831 }
1832 
HandleDoubleClickOrLongPress(GestureEvent & info)1833 void RichEditorPattern::HandleDoubleClickOrLongPress(GestureEvent& info)
1834 {
1835     if (caretUpdateType_ == CaretUpdateType::LONG_PRESSED) {
1836         HandleUserLongPressEvent(info);
1837     }
1838     if (JudgeDraggable(info) || isMousePressed_) {
1839         return;
1840     }
1841     auto host = GetHost();
1842     CHECK_NULL_VOID(host);
1843     auto focusHub = host->GetOrCreateFocusHub();
1844     CHECK_NULL_VOID(focusHub);
1845     isLongPress_ = true;
1846     auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
1847     Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
1848         info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
1849     InitSelection(textOffset);
1850     auto selectEnd = std::max(textSelector_.baseOffset, textSelector_.destinationOffset);
1851     auto selectStart = std::min(textSelector_.baseOffset, textSelector_.destinationOffset);
1852 
1853     auto textSelectInfo = GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT);
1854     UpdateSelectionType(textSelectInfo);
1855     CalculateHandleOffsetAndShowOverlay();
1856     if (IsShowSelectMenuUsingMouse()) {
1857         CloseSelectOverlay();
1858     }
1859     selectionMenuOffset_ = info.GetGlobalLocation();
1860     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1861     auto eventHub = host->GetEventHub<RichEditorEventHub>();
1862     CHECK_NULL_VOID(eventHub);
1863     if (!textSelectInfo.GetSelection().resultObjects.empty()) {
1864         eventHub->FireOnSelect(&textSelectInfo);
1865     }
1866     SetCaretPosition(std::min(selectEnd, GetTextContentLength()));
1867     focusHub->RequestFocusImmediately();
1868     if (overlayMod_) {
1869         RequestKeyboard(false, true, true);
1870     }
1871     if (info.GetSourceDevice() != SourceType::MOUSE || caretUpdateType_ != CaretUpdateType::DOUBLE_CLICK) {
1872         ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle);
1873         StopTwinkling();
1874     } else if (selectStart == selectEnd) {
1875         StartTwinkling();
1876     } else {
1877         StopTwinkling();
1878     }
1879 }
1880 
HandleUserLongPressEvent(GestureEvent & info)1881 bool RichEditorPattern::HandleUserLongPressEvent(GestureEvent& info)
1882 {
1883     auto longPressFunc = [](RefPtr<SpanItem> item, GestureEvent& info) -> bool {
1884         if (item && item->onLongPress) {
1885             item->onLongPress(info);
1886             return true;
1887         }
1888         return false;
1889     };
1890     return HandleUserGestureEvent(info, std::move(longPressFunc));
1891 }
1892 
HandleMenuCallbackOnSelectAll()1893 void RichEditorPattern::HandleMenuCallbackOnSelectAll()
1894 {
1895     auto textSize = static_cast<int32_t>(GetWideText().length()) + placeholderCount_;
1896     textSelector_.Update(0, textSize);
1897     CalculateHandleOffsetAndShowOverlay();
1898     if (IsShowSelectMenuUsingMouse()) {
1899         CloseSelectOverlay();
1900     }
1901     auto responseType = selectOverlayProxy_
1902                             ? static_cast<TextResponseType>(
1903                                   selectOverlayProxy_->GetSelectOverlayMangerInfo().menuInfo.responseType.value_or(0))
1904                             : TextResponseType::LONG_PRESS;
1905     selectMenuInfo_.showCopyAll = false;
1906     selectOverlayProxy_->UpdateSelectMenuInfo(selectMenuInfo_);
1907     auto host = GetHost();
1908     CHECK_NULL_VOID(host);
1909     FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
1910     selectOverlayProxy_.Reset();
1911     ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true, responseType);
1912     SetCaretPosition(textSize);
1913     MoveCaretToContentRect();
1914     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1915 }
1916 
InitLongPressEvent(const RefPtr<GestureEventHub> & gestureHub)1917 void RichEditorPattern::InitLongPressEvent(const RefPtr<GestureEventHub>& gestureHub)
1918 {
1919     CHECK_NULL_VOID(!longPressEvent_);
1920     auto longPressCallback = [weak = WeakClaim(this)](GestureEvent& info) {
1921         auto pattern = weak.Upgrade();
1922         CHECK_NULL_VOID(pattern);
1923         pattern->HandleLongPress(info);
1924     };
1925     longPressEvent_ = MakeRefPtr<LongPressEvent>(std::move(longPressCallback));
1926     gestureHub->SetLongPressEvent(longPressEvent_);
1927 
1928     auto onTextSelectorChange = [weak = WeakClaim(this), &selector = textSelector_]() {
1929         auto pattern = weak.Upgrade();
1930         CHECK_NULL_VOID(pattern);
1931         auto frameNode = pattern->GetHost();
1932         CHECK_NULL_VOID(frameNode);
1933         frameNode->OnAccessibilityEvent(AccessibilityEventType::TEXT_SELECTION_UPDATE);
1934         pattern->FireOnSelectionChange(selector);
1935     };
1936     textSelector_.SetOnAccessibility(std::move(onTextSelectorChange));
1937 }
1938 
InitDragDropEvent()1939 void RichEditorPattern::InitDragDropEvent()
1940 {
1941     auto host = GetHost();
1942     CHECK_NULL_VOID(host);
1943     auto gestureHub = host->GetOrCreateGestureEventHub();
1944     CHECK_NULL_VOID(gestureHub);
1945     gestureHub->InitDragDropEvent();
1946     gestureHub->SetThumbnailCallback(GetThumbnailCallback());
1947     auto eventHub = host->GetEventHub<EventHub>();
1948     CHECK_NULL_VOID(eventHub);
1949     auto onDragStart = [weakPtr = WeakClaim(this)](const RefPtr<OHOS::Ace::DragEvent>& event,
1950                            const std::string& extraParams) -> NG::DragDropInfo {
1951         NG::DragDropInfo itemInfo;
1952         auto pattern = weakPtr.Upgrade();
1953         CHECK_NULL_RETURN(pattern, itemInfo);
1954         pattern->timestamp_ = std::chrono::system_clock::now().time_since_epoch().count();
1955         auto eventHub = pattern->GetEventHub<RichEditorEventHub>();
1956         eventHub->SetTimestamp(pattern->GetTimestamp());
1957         CHECK_NULL_RETURN(eventHub, itemInfo);
1958         pattern->showSelect_ = false;
1959         return pattern->OnDragStart(event, extraParams);
1960     };
1961     eventHub->SetOnDragStart(std::move(onDragStart));
1962     auto onDragMove = [weakPtr = WeakClaim(this)](
1963                           const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& extraParams) {
1964         auto pattern = weakPtr.Upgrade();
1965         CHECK_NULL_VOID(pattern);
1966         pattern->showSelect_ = false;
1967         pattern->OnDragMove(event);
1968     };
1969     eventHub->SetOnDragMove(std::move(onDragMove));
1970     auto onDragEnd = [weakPtr = WeakClaim(this), scopeId = Container::CurrentId()](
1971                          const RefPtr<OHOS::Ace::DragEvent>& event) {
1972         ContainerScope scope(scopeId);
1973         auto pattern = weakPtr.Upgrade();
1974         CHECK_NULL_VOID(pattern);
1975         pattern->showSelect_ = true;
1976         pattern->StopAutoScroll();
1977         pattern->ClearRedoOperationRecords();
1978         pattern->OnDragEnd(event);
1979     };
1980     eventHub->SetOnDragEnd(std::move(onDragEnd));
1981     onDragDropAndLeave();
1982 }
1983 
onDragDropAndLeave()1984 void RichEditorPattern::onDragDropAndLeave()
1985 {
1986     auto host = GetHost();
1987     CHECK_NULL_VOID(host);
1988     auto eventHub = host->GetEventHub<EventHub>();
1989     CHECK_NULL_VOID(eventHub);
1990     auto onDragDrop = [weakPtr = WeakClaim(this), scopeId = Container::CurrentId()](
1991                           const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& value) {};
1992     eventHub->SetOnDrop(std::move(onDragDrop));
1993     auto onDragDragLeave = [weakPtr = WeakClaim(this), scopeId = Container::CurrentId()](
1994                                const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& value) {
1995         ContainerScope scope(scopeId);
1996         auto pattern = weakPtr.Upgrade();
1997         CHECK_NULL_VOID(pattern);
1998         pattern->StopAutoScroll();
1999     };
2000     eventHub->SetOnDragLeave(onDragDragLeave);
2001 }
2002 
ClearDragDropEvent()2003 void RichEditorPattern::ClearDragDropEvent()
2004 {
2005     auto host = GetHost();
2006     CHECK_NULL_VOID(host);
2007     auto gestureHub = host->GetOrCreateGestureEventHub();
2008     CHECK_NULL_VOID(gestureHub);
2009     gestureHub->SetIsTextDraggable(false);
2010     auto eventHub = host->GetEventHub<EventHub>();
2011     CHECK_NULL_VOID(eventHub);
2012     eventHub->SetOnDragStart(nullptr);
2013     eventHub->SetOnDragEnter(nullptr);
2014     eventHub->SetOnDragMove(nullptr);
2015     eventHub->SetOnDragLeave(nullptr);
2016     eventHub->SetOnDragEnd(nullptr);
2017     eventHub->SetOnDrop(nullptr);
2018 }
2019 
OnDragMove(const RefPtr<OHOS::Ace::DragEvent> & event)2020 void RichEditorPattern::OnDragMove(const RefPtr<OHOS::Ace::DragEvent>& event)
2021 {
2022     auto pipeline = PipelineBase::GetCurrentContext();
2023     CHECK_NULL_VOID(pipeline);
2024     auto theme = pipeline->GetTheme<RichEditorTheme>();
2025     CHECK_NULL_VOID(theme);
2026     auto touchX = event->GetX();
2027     auto touchY = event->GetY();
2028     auto textRect = GetTextRect();
2029     textRect.SetTop(textRect.GetY() - std::min(baselineOffset_, 0.0f));
2030     Offset textOffset = { touchX - textRect.GetX() - GetParentGlobalOffset().GetX(),
2031         touchY - textRect.GetY() - GetParentGlobalOffset().GetY() - theme->GetInsertCursorOffset().ConvertToPx() };
2032     auto position = paragraphs_.GetIndex(textOffset);
2033     float caretHeight = 0.0f;
2034     SetCaretPosition(position);
2035     OffsetF caretOffset = CalcCursorOffsetByPosition(GetCaretPosition(), caretHeight);
2036     CHECK_NULL_VOID(overlayMod_);
2037     DynamicCast<RichEditorOverlayModifier>(overlayMod_)->SetCaretOffsetAndHeight(caretOffset, caretHeight);
2038 
2039     AutoScrollParam param = { .autoScrollEvent = AutoScrollEvent::DRAG, .showScrollbar = true };
2040     auto localOffset = OffsetF(touchX, touchY) - parentGlobalOffset_;
2041     AutoScrollByEdgeDetection(param, localOffset, EdgeDetectionStrategy::IN_BOUNDARY);
2042 }
2043 
OnDragEnd(const RefPtr<Ace::DragEvent> & event)2044 void RichEditorPattern::OnDragEnd(const RefPtr<Ace::DragEvent>& event)
2045 {
2046     ResetDragRecordSize(-1);
2047     auto host = GetHost();
2048     CHECK_NULL_VOID(host);
2049     if (status_ == Status::DRAGGING) {
2050         status_ = Status::NONE;
2051     }
2052     if (recoverDragResultObjects_.empty()) {
2053         return;
2054     }
2055     UpdateSpanItemDragStatus(recoverDragResultObjects_, false);
2056     recoverDragResultObjects_.clear();
2057     if (event && event->GetResult() != DragRet::DRAG_SUCCESS) {
2058         HandleSelectionChange(recoverStart_, recoverEnd_);
2059         showSelect_ = true;
2060         isShowMenu_ = false;
2061         CalculateHandleOffsetAndShowOverlay();
2062         if (!IsUsingMouse()) {
2063             ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle);
2064         }
2065     }
2066     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
2067 }
2068 
SelectOverlayIsOn()2069 bool RichEditorPattern::SelectOverlayIsOn()
2070 {
2071     auto pipeline = PipelineContext::GetCurrentContext();
2072     CHECK_NULL_RETURN(pipeline, false);
2073     CHECK_NULL_RETURN(selectOverlayProxy_, false);
2074     auto overlayId = selectOverlayProxy_->GetSelectOverlayId();
2075     return pipeline->GetSelectOverlayManager()->HasSelectOverlay(overlayId);
2076 }
2077 
UpdateEditingValue(const std::shared_ptr<TextEditingValue> & value,bool needFireChangeEvent)2078 void RichEditorPattern::UpdateEditingValue(const std::shared_ptr<TextEditingValue>& value, bool needFireChangeEvent)
2079 {
2080     InsertValue(value->text);
2081 }
2082 
PerformAction(TextInputAction action,bool forceCloseKeyboard)2083 void RichEditorPattern::PerformAction(TextInputAction action, bool forceCloseKeyboard)
2084 {
2085     InsertValue("\n");
2086 }
2087 
InitMouseEvent()2088 void RichEditorPattern::InitMouseEvent()
2089 {
2090     CHECK_NULL_VOID(!mouseEventInitialized_);
2091     auto host = GetHost();
2092     CHECK_NULL_VOID(host);
2093     auto eventHub = host->GetEventHub<EventHub>();
2094     CHECK_NULL_VOID(eventHub);
2095     auto inputHub = eventHub->GetOrCreateInputEventHub();
2096     CHECK_NULL_VOID(inputHub);
2097 
2098     auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
2099         auto pattern = weak.Upgrade();
2100         CHECK_NULL_VOID(pattern);
2101         pattern->HandleMouseEvent(info);
2102     };
2103     auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
2104     inputHub->AddOnMouseEvent(mouseEvent);
2105     auto hoverTask = [weak = WeakClaim(this)](bool isHover) {
2106         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "on hover event isHover=%{public}d", isHover);
2107         auto pattern = weak.Upgrade();
2108         if (pattern) {
2109             pattern->OnHover(isHover);
2110         }
2111     };
2112     auto hoverEvent = MakeRefPtr<InputEvent>(std::move(hoverTask));
2113     inputHub->AddOnHoverEvent(hoverEvent);
2114     mouseEventInitialized_ = true;
2115 }
2116 
OnHover(bool isHover)2117 void RichEditorPattern::OnHover(bool isHover)
2118 {
2119     auto frame = GetHost();
2120     CHECK_NULL_VOID(frame);
2121     auto frameId = frame->GetId();
2122     auto pipeline = PipelineContext::GetCurrentContext();
2123     CHECK_NULL_VOID(pipeline);
2124     auto scrollBar = GetScrollBar();
2125     if (isHover && !scrollBar->IsPressed()) {
2126         pipeline->SetMouseStyleHoldNode(frameId);
2127         pipeline->ChangeMouseStyle(frameId, MouseFormat::TEXT_CURSOR);
2128     } else {
2129         pipeline->ChangeMouseStyle(frameId, MouseFormat::DEFAULT);
2130         pipeline->FreeMouseStyleHoldNode(frameId);
2131     }
2132 }
2133 
RequestKeyboard(bool isFocusViewChanged,bool needStartTwinkling,bool needShowSoftKeyboard)2134 bool RichEditorPattern::RequestKeyboard(bool isFocusViewChanged, bool needStartTwinkling, bool needShowSoftKeyboard)
2135 {
2136     auto context = PipelineContext::GetCurrentContext();
2137     CHECK_NULL_RETURN(context, false);
2138     CHECK_NULL_RETURN(needShowSoftKeyboard, false);
2139     if (needShowSoftKeyboard && customKeyboardBuilder_) {
2140         return RequestCustomKeyboard();
2141     }
2142 #if defined(ENABLE_STANDARD_INPUT)
2143     if (!EnableStandardInput(needShowSoftKeyboard)) {
2144         return false;
2145     }
2146 #else
2147     if (!UnableStandardInput(isFocusViewChanged)) {
2148         return false;
2149     }
2150 #endif
2151     return true;
2152 }
2153 
2154 #if defined(ENABLE_STANDARD_INPUT)
2155 #ifdef WINDOW_SCENE_SUPPORTED
GetSCBSystemWindowId()2156 uint32_t RichEditorPattern::GetSCBSystemWindowId()
2157 {
2158     RefPtr<FrameNode> frameNode = GetHost();
2159     CHECK_NULL_RETURN(frameNode, {});
2160     auto focusSystemWindowId = WindowSceneHelper::GetFocusSystemWindowId(frameNode);
2161     return focusSystemWindowId;
2162 }
2163 #endif
2164 
EnableStandardInput(bool needShowSoftKeyboard)2165 bool RichEditorPattern::EnableStandardInput(bool needShowSoftKeyboard)
2166 {
2167     auto context = PipelineContext::GetCurrentContext();
2168     CHECK_NULL_RETURN(context, false);
2169     MiscServices::Configuration configuration;
2170     configuration.SetEnterKeyType(static_cast<MiscServices::EnterKeyType>(static_cast<int32_t>(TextInputAction::DONE)));
2171     configuration.SetTextInputType(
2172         static_cast<MiscServices::TextInputType>(static_cast<int32_t>(TextInputType::UNSPECIFIED)));
2173     MiscServices::InputMethodController::GetInstance()->OnConfigurationChange(configuration);
2174     if (richEditTextChangeListener_ == nullptr) {
2175         richEditTextChangeListener_ = new OnTextChangedListenerImpl(WeakClaim(this));
2176     }
2177     auto inputMethod = MiscServices::InputMethodController::GetInstance();
2178     CHECK_NULL_RETURN(inputMethod, false);
2179     auto miscTextConfig = GetMiscTextConfig();
2180     CHECK_NULL_RETURN(miscTextConfig.has_value(), false);
2181     TAG_LOGI(
2182         AceLogTag::ACE_RICH_TEXT, "RequestKeyboard set calling window id is : %{public}u", miscTextConfig->windowId);
2183     MiscServices::TextConfig textconfig = miscTextConfig.value();
2184 #ifdef WINDOW_SCENE_SUPPORTED
2185     auto systemWindowId = GetSCBSystemWindowId();
2186     if (systemWindowId) {
2187         miscTextConfig->windowId = systemWindowId;
2188     }
2189 #endif
2190     inputMethod->Attach(richEditTextChangeListener_, needShowSoftKeyboard, textconfig);
2191     UpdateKeyboardOffset(textconfig.positionY, textconfig.height);
2192     if (context) {
2193         inputMethod->SetCallingWindow(context->GetWindowId());
2194     }
2195 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
2196     imeAttached_ = true;
2197 #endif
2198     return true;
2199 }
2200 
GetMiscTextConfig()2201 std::optional<MiscServices::TextConfig> RichEditorPattern::GetMiscTextConfig()
2202 {
2203     auto tmpHost = GetHost();
2204     CHECK_NULL_RETURN(tmpHost, {});
2205     auto pipeline = tmpHost->GetContext();
2206     CHECK_NULL_RETURN(pipeline, {});
2207     auto windowRect = pipeline->GetCurrentWindowRect();
2208     double positionY = (tmpHost->GetPaintRectOffset() - pipeline->GetRootRect().GetOffset()).GetY() + windowRect.Top();
2209     double height = frameRect_.Height();
2210 
2211     float caretHeight = 0.0f;
2212     OffsetF caretOffset = CalcCursorOffsetByPosition(GetCaretPosition(), caretHeight);
2213     if (NearZero(caretHeight)) {
2214         auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
2215         caretHeight = overlayModifier ? overlayModifier->GetCaretHeight() : DEFAULT_CARET_HEIGHT;
2216     }
2217     auto offset = KEYBOARD_AVOID_OFFSET.ConvertToPx();
2218     auto caretTop = caretOffset.GetY() + windowRect.Top() + parentGlobalOffset_.GetY();
2219     height = caretTop + caretHeight + offset - positionY;
2220 
2221     MiscServices::CursorInfo cursorInfo { .left = caretOffset.GetX() + windowRect.Left() + parentGlobalOffset_.GetX(),
2222         .top = caretTop,
2223         .width = CARET_WIDTH,
2224         .height = caretHeight };
2225     MiscServices::InputAttribute inputAttribute = { .inputPattern = (int32_t)TextInputType::UNSPECIFIED,
2226         .enterKeyType = (int32_t)TextInputAction::DONE };
2227     MiscServices::TextConfig textConfig = { .inputAttribute = inputAttribute,
2228         .cursorInfo = cursorInfo,
2229         .range = { .start = textSelector_.GetStart(), .end = textSelector_.GetEnd() },
2230         .windowId = pipeline->GetFocusWindowId(),
2231         .positionY = positionY,
2232         .height = height };
2233     return textConfig;
2234 }
2235 #else
UnableStandardInput(bool isFocusViewChanged)2236 bool RichEditorPattern::UnableStandardInput(bool isFocusViewChanged)
2237 {
2238     auto context = PipelineContext::GetCurrentContext();
2239     CHECK_NULL_RETURN(context, false);
2240     if (HasConnection()) {
2241         connection_->Show(isFocusViewChanged, GetInstanceId());
2242         return true;
2243     }
2244     TextInputConfiguration config;
2245     config.type = TextInputType::UNSPECIFIED;
2246     config.action = TextInputAction::DONE;
2247     config.obscureText = false;
2248     connection_ =
2249         TextInputProxy::GetInstance().Attach(WeakClaim(this), config, context->GetTaskExecutor(), GetInstanceId());
2250     if (!HasConnection()) {
2251         return false;
2252     }
2253     TextEditingValue value;
2254     if (spans_.empty()) {
2255         value.text = textForDisplay_;
2256     } else {
2257         for (auto it = spans_.begin(); it != spans_.end(); it++) {
2258             if ((*it)->placeholderIndex < 0) {
2259                 value.text.append((*it)->content);
2260             } else {
2261                 value.text.append(" ");
2262             }
2263         }
2264     }
2265     value.selection.Update(caretPosition_, caretPosition_);
2266     connection_->SetEditingState(value, GetInstanceId());
2267     connection_->Show(isFocusViewChanged, GetInstanceId());
2268     return true;
2269 }
2270 #endif
2271 
OnColorConfigurationUpdate()2272 void RichEditorPattern::OnColorConfigurationUpdate()
2273 {
2274     auto host = GetHost();
2275     CHECK_NULL_VOID(host);
2276     const auto& spans = host->GetChildren();
2277     auto context = PipelineContext::GetCurrentContext();
2278     CHECK_NULL_VOID(context);
2279     auto theme = context->GetTheme<TextTheme>();
2280     CHECK_NULL_VOID(theme);
2281     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
2282     CHECK_NULL_VOID(textLayoutProperty);
2283     textLayoutProperty->UpdateTextColor(theme->GetTextStyle().GetTextColor());
2284     textLayoutProperty->UpdateTextDecorationColor(theme->GetTextStyle().GetTextColor());
2285     for (auto span : spans) {
2286         auto spanNode = DynamicCast<SpanNode>(span);
2287         if (!spanNode) {
2288             continue;
2289         }
2290         auto spanItem = spanNode->GetSpanItem();
2291         if (!spanItem) {
2292             continue;
2293         }
2294         if (spanItem->hasResourceFontColor) {
2295             spanNode->UpdateTextColor(theme->GetTextStyle().GetTextColor());
2296         }
2297         if (spanItem->hasResourceDecorationColor) {
2298             spanNode->UpdateTextDecorationColor(theme->GetTextStyle().GetTextColor());
2299         }
2300     }
2301 }
2302 
UpdateCaretInfoToController()2303 void RichEditorPattern::UpdateCaretInfoToController()
2304 {
2305     CHECK_NULL_VOID(HasFocus());
2306     auto selectionResult = GetSpansInfo(0, GetTextContentLength(), GetSpansMethod::ONSELECT);
2307     auto resultObjects = selectionResult.GetSelection().resultObjects;
2308     std::string text = "";
2309     if (!resultObjects.empty()) {
2310         for (const auto& resultObj : resultObjects) {
2311             if (resultObj.type == SelectSpanType::TYPESPAN) {
2312                 text += resultObj.valueString;
2313             }
2314         }
2315     }
2316 #if defined(ENABLE_STANDARD_INPUT)
2317     auto miscTextConfig = GetMiscTextConfig();
2318     CHECK_NULL_VOID(miscTextConfig.has_value());
2319     MiscServices::CursorInfo cursorInfo = miscTextConfig.value().cursorInfo;
2320     MiscServices::InputMethodController::GetInstance()->OnCursorUpdate(cursorInfo);
2321     MiscServices::InputMethodController::GetInstance()->OnSelectionChange(
2322         StringUtils::Str8ToStr16(text), textSelector_.GetStart(), textSelector_.GetEnd());
2323     TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
2324         "Caret position update, left %{public}f, top %{public}f, width %{public}f, height %{public}f; textSelector_ "
2325         "Start "
2326         "%{public}d, end %{public}d",
2327         cursorInfo.left, cursorInfo.top, cursorInfo.width, cursorInfo.height, textSelector_.GetStart(),
2328         textSelector_.GetEnd());
2329 #else
2330     if (HasConnection()) {
2331         TextEditingValue editingValue;
2332         editingValue.text = text;
2333         editingValue.hint = "";
2334         editingValue.selection.Update(textSelector_.baseOffset, textSelector_.destinationOffset);
2335         connection_->SetEditingState(editingValue, GetInstanceId());
2336     }
2337 #endif
2338 }
2339 
HasConnection() const2340 bool RichEditorPattern::HasConnection() const
2341 {
2342 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
2343     return imeAttached_;
2344 #else
2345     return connection_ != nullptr;
2346 #endif
2347 }
2348 
RequestCustomKeyboard()2349 bool RichEditorPattern::RequestCustomKeyboard()
2350 {
2351 #if defined(ENABLE_STANDARD_INPUT)
2352     auto inputMethod = MiscServices::InputMethodController::GetInstance();
2353     if (inputMethod) {
2354         TAG_LOGI(AceLogTag::ACE_KEYBOARD, "RequestCKeyboard,close softkeyboard.");
2355         inputMethod->RequestHideInput();
2356         inputMethod->Close();
2357     }
2358 #else
2359     if (HasConnection()) {
2360         connection_->Close(GetInstanceId());
2361         connection_ = nullptr;
2362     }
2363 #endif
2364 
2365     if (isCustomKeyboardAttached_) {
2366         return true;
2367     }
2368     CHECK_NULL_RETURN(customKeyboardBuilder_, false);
2369     auto frameNode = GetHost();
2370     CHECK_NULL_RETURN(frameNode, false);
2371     auto pipeline = PipelineContext::GetCurrentContext();
2372     CHECK_NULL_RETURN(pipeline, false);
2373     auto overlayManager = pipeline->GetOverlayManager();
2374     CHECK_NULL_RETURN(overlayManager, false);
2375     overlayManager->BindKeyboard(customKeyboardBuilder_, frameNode->GetId());
2376     isCustomKeyboardAttached_ = true;
2377     keyboardOverlay_ = overlayManager;
2378     return true;
2379 }
2380 
CloseCustomKeyboard()2381 bool RichEditorPattern::CloseCustomKeyboard()
2382 {
2383     auto frameNode = GetHost();
2384     CHECK_NULL_RETURN(frameNode, false);
2385     CHECK_NULL_RETURN(keyboardOverlay_, false);
2386     keyboardOverlay_->CloseKeyboard(frameNode->GetId());
2387     isCustomKeyboardAttached_ = false;
2388     return true;
2389 }
2390 
InsertValue(const std::string & insertValue)2391 void RichEditorPattern::InsertValue(const std::string& insertValue)
2392 {
2393     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "insertValue=[%{public}s]", StringUtils::RestoreEscape(insertValue).c_str());
2394     OperationRecord record;
2395     record.beforeCaretPosition = caretPosition_ + moveLength_;
2396     if (textSelector_.IsValid()) {
2397         record.beforeCaretPosition = textSelector_.GetTextStart();
2398     }
2399     record.addText = insertValue;
2400     ClearRedoOperationRecords();
2401     InsertValueOperation(insertValue, &record);
2402     record.afterCaretPosition = caretPosition_ + moveLength_;
2403     AddOperationRecord(record);
2404 }
2405 
InsertValueOperation(const std::string & insertValue,OperationRecord * const record)2406 void RichEditorPattern::InsertValueOperation(const std::string& insertValue, OperationRecord* const record)
2407 {
2408     bool isSelector = textSelector_.IsValid();
2409     if (isSelector) {
2410         SetCaretPosition(textSelector_.GetTextStart());
2411     }
2412 
2413     std::string insertValueTemp = insertValue;
2414     bool isLineSeparator = insertValueTemp == std::string("\n");
2415 
2416     auto isInsert = BeforeIMEInsertValue(insertValueTemp);
2417     CHECK_NULL_VOID(isInsert);
2418     TextInsertValueInfo info;
2419     CalcInsertValueObj(info);
2420     if (isSelector) {
2421         std::wstring deleteText = DeleteForwardOperation(textSelector_.GetTextEnd() - textSelector_.GetTextStart());
2422         if (record && deleteText.length() != 0) {
2423             record->deleteText = StringUtils::ToString(deleteText);
2424         }
2425         CloseSelectOverlay();
2426         ResetSelection();
2427     }
2428     if (!caretVisible_) {
2429         StartTwinkling();
2430     }
2431     auto host = GetHost();
2432     CHECK_NULL_VOID(host);
2433     RefPtr<SpanNode> spanNode = DynamicCast<SpanNode>(host->GetChildAtIndex(info.GetSpanIndex()));
2434     if (spanNode == nullptr || spanNode->GetSpanItem()->unicode != 0) {
2435         InsertValueWithoutSpan(spanNode, info, insertValueTemp);
2436         return;
2437     }
2438     if (info.GetOffsetInSpan() == 0) {
2439         auto spanNodeBefore = DynamicCast<SpanNode>(host->GetChildAtIndex(info.GetSpanIndex() - 1));
2440         if (spanNodeBefore != nullptr && !IsLineSeparatorInLast(spanNodeBefore) &&
2441             spanNodeBefore->GetTag() == V2::SPAN_ETS_TAG) {
2442             InsertValueAfterBeforeSpan(spanNodeBefore, spanNode, info, insertValueTemp);
2443             return;
2444         }
2445     }
2446     if (typingStyle_.has_value() && !HasSameTypingStyle(spanNode)) {
2447         InsertDiffStyleValueInSpan(spanNode, info, insertValueTemp);
2448         return;
2449     }
2450     if (!isLineSeparator) {
2451         InsertValueToSpanNode(spanNode, insertValueTemp, info);
2452     } else {
2453         SpanNodeFission(spanNode, insertValueTemp, info);
2454     }
2455     AfterIMEInsertValue(spanNode, static_cast<int32_t>(StringUtils::ToWstring(insertValueTemp).length()), false);
2456 }
2457 
InsertValueWithoutSpan(RefPtr<SpanNode> & spanNode,const TextInsertValueInfo & info,const std::string & insertValue)2458 void RichEditorPattern::InsertValueWithoutSpan(
2459     RefPtr<SpanNode>& spanNode, const TextInsertValueInfo& info, const std::string& insertValue)
2460 {
2461     auto host = GetHost();
2462     CHECK_NULL_VOID(host);
2463     if (info.GetSpanIndex() == 0) {
2464         CreateTextSpanNode(spanNode, info, insertValue);
2465         return;
2466     }
2467     auto spanNodeBefore = DynamicCast<SpanNode>(host->GetChildAtIndex(info.GetSpanIndex() - 1));
2468     if (spanNodeBefore == nullptr || spanNodeBefore->GetSpanItem()->unicode != 0) {
2469         CreateTextSpanNode(spanNode, info, insertValue);
2470         return;
2471     }
2472     InsertValueAfterBeforeSpan(spanNodeBefore, spanNode, info, insertValue);
2473 }
2474 
InsertValueAfterBeforeSpan(RefPtr<SpanNode> & spanNodeBefore,RefPtr<SpanNode> & spanNode,const TextInsertValueInfo & info,const std::string & insertValue)2475 void RichEditorPattern::InsertValueAfterBeforeSpan(RefPtr<SpanNode>& spanNodeBefore, RefPtr<SpanNode>& spanNode,
2476     const TextInsertValueInfo& info, const std::string& insertValue)
2477 {
2478     if (typingStyle_.has_value() && !HasSameTypingStyle(spanNodeBefore)) {
2479         CreateTextSpanNode(spanNode, info, insertValue);
2480         CopyTextSpanLineStyle(spanNodeBefore, spanNode, true);
2481         return;
2482     }
2483     auto spanNodeGet = InsertValueToBeforeSpan(spanNodeBefore, insertValue);
2484     bool isCreate = spanNodeBefore->GetId() != spanNodeGet->GetId();
2485     AfterIMEInsertValue(
2486         spanNodeGet, static_cast<int32_t>(StringUtils::ToWstring(insertValue).length()), isCreate);
2487 }
2488 
InsertDiffStyleValueInSpan(RefPtr<SpanNode> & spanNode,const TextInsertValueInfo & info,const std::string & insertValue)2489 void RichEditorPattern::InsertDiffStyleValueInSpan(
2490     RefPtr<SpanNode>& spanNode, const TextInsertValueInfo& info, const std::string& insertValue)
2491 {
2492     auto host = GetHost();
2493     CHECK_NULL_VOID(host);
2494     TextSpanOptions options;
2495     options.value = insertValue;
2496     options.offset = caretPosition_;
2497     options.style = typingTextStyle_;
2498     auto newSpanIndex = AddTextSpanOperation(options, false, -1,  true);
2499     auto newSpanNode = DynamicCast<SpanNode>(host->GetChildAtIndex(newSpanIndex));
2500     CopyTextSpanLineStyle(spanNode, newSpanNode, true);
2501     AfterIMEInsertValue(spanNode, static_cast<int32_t>(StringUtils::ToWstring(insertValue).length()), true);
2502 }
2503 
IsLineSeparatorInLast(RefPtr<SpanNode> & spanNode)2504 bool RichEditorPattern::IsLineSeparatorInLast(RefPtr<SpanNode>& spanNode)
2505 {
2506     auto spanItem = spanNode->GetSpanItem();
2507     auto text = spanItem->content;
2508     std::wstring textTemp = StringUtils::ToWstring(text);
2509     auto index = textTemp.find(lineSeparator);
2510     if (index != std::wstring::npos) {
2511         auto textBefore = textTemp.substr(0, index + 1);
2512         auto textAfter = textTemp.substr(index + 1);
2513         if (textAfter.empty()) {
2514             return true;
2515         }
2516     }
2517     return false;
2518 }
2519 
InsertValueToSpanNode(RefPtr<SpanNode> & spanNode,const std::string & insertValue,const TextInsertValueInfo & info)2520 void RichEditorPattern::InsertValueToSpanNode(
2521     RefPtr<SpanNode>& spanNode, const std::string& insertValue, const TextInsertValueInfo& info)
2522 {
2523     auto spanItem = spanNode->GetSpanItem();
2524     CHECK_NULL_VOID(spanItem);
2525     auto text = spanItem->content;
2526     std::wstring textTemp = StringUtils::ToWstring(text);
2527     auto textTempSize = static_cast<int32_t>(textTemp.size());
2528     if (textTempSize < info.GetOffsetInSpan()) {
2529         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "InsertValue error, offsetInSpan is greater than the size of spanItem, "
2530             "spanItemSize = %{public}d, offsetInSpan = %{public}d", textTempSize, info.GetOffsetInSpan());
2531         return;
2532     }
2533     std::wstring insertValueTemp = StringUtils::ToWstring(insertValue);
2534     textTemp.insert(info.GetOffsetInSpan(), insertValueTemp);
2535     text = StringUtils::ToString(textTemp);
2536     spanNode->UpdateContent(text);
2537     spanItem->position += static_cast<int32_t>(StringUtils::ToWstring(insertValue).length());
2538 }
2539 
SpanNodeFission(RefPtr<SpanNode> & spanNode,const std::string & insertValue,const TextInsertValueInfo & info)2540 void RichEditorPattern::SpanNodeFission(
2541     RefPtr<SpanNode>& spanNode, const std::string& insertValue, const TextInsertValueInfo& info)
2542 {
2543     auto spanItem = spanNode->GetSpanItem();
2544     CHECK_NULL_VOID(spanItem);
2545     auto text = spanItem->content;
2546     std::wstring textTemp = StringUtils::ToWstring(text);
2547     std::wstring insertValueTemp = StringUtils::ToWstring(insertValue);
2548     InsertValueInSpanOffset(info, textTemp, insertValueTemp);
2549     auto index = textTemp.find(lineSeparator);
2550     if (index != std::wstring::npos) {
2551         auto textBefore = textTemp.substr(0, index + 1);
2552         auto textAfter = textTemp.substr(index + 1);
2553         text = StringUtils::ToString(textBefore);
2554         spanNode->UpdateContent(text);
2555         spanItem->position += 1 - static_cast<int32_t>(textAfter.length());
2556         if (!textAfter.empty()) {
2557             TextInsertValueInfo infoAfter;
2558             infoAfter.SetSpanIndex(info.GetSpanIndex() + 1);
2559             infoAfter.SetOffsetInSpan(0);
2560             auto host = GetHost();
2561             CHECK_NULL_VOID(host);
2562             auto nodeId = ViewStackProcessor::GetInstance()->ClaimNodeId();
2563             RefPtr<SpanNode> spanNodeAfter = SpanNode::GetOrCreateSpanNode(nodeId);
2564             spanNodeAfter->MountToParent(host, infoAfter.GetSpanIndex());
2565             spanNodeAfter->UpdateContent(StringUtils::ToString(textAfter));
2566             CopyTextSpanStyle(spanNode, spanNodeAfter);
2567         }
2568     } else {
2569         text = StringUtils::ToString(textTemp);
2570         spanNode->UpdateContent(text);
2571         spanItem->position += static_cast<int32_t>(StringUtils::ToWstring(insertValue).length());
2572     }
2573 }
2574 
InsertValueInSpanOffset(const TextInsertValueInfo & info,std::wstring & text,const std::wstring & insertValue)2575 void RichEditorPattern::InsertValueInSpanOffset(
2576     const TextInsertValueInfo& info, std::wstring& text, const std::wstring& insertValue)
2577 {
2578     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "insert value info: %{public}s", info.ToString().c_str());
2579     auto offsetInSpan = std::clamp(info.GetOffsetInSpan(), 0, static_cast<int32_t>(text.length()));
2580     text.insert(offsetInSpan, insertValue);
2581 }
2582 
InsertValueToBeforeSpan(RefPtr<SpanNode> & spanNodeBefore,const std::string & insertValue)2583 RefPtr<SpanNode> RichEditorPattern::InsertValueToBeforeSpan(
2584     RefPtr<SpanNode>& spanNodeBefore, const std::string& insertValue)
2585 {
2586     auto spanItem = spanNodeBefore->GetSpanItem();
2587     CHECK_NULL_RETURN(spanItem, spanNodeBefore);
2588     auto text = spanItem->content;
2589     std::wstring textTemp = StringUtils::ToWstring(text);
2590     std::wstring insertValueTemp = StringUtils::ToWstring(insertValue);
2591     textTemp.append(insertValueTemp);
2592 
2593     auto index = textTemp.find(lineSeparator);
2594     if (index != std::wstring::npos) {
2595         auto textBefore = textTemp.substr(0, index + 1);
2596         auto textAfter = textTemp.substr(index + 1);
2597         text = StringUtils::ToString(textBefore);
2598         spanNodeBefore->UpdateContent(text);
2599         spanItem->position += static_cast<int32_t>(insertValueTemp.length()) - static_cast<int32_t>(textAfter.length());
2600         if (!textAfter.empty()) {
2601             auto host = GetHost();
2602             CHECK_NULL_RETURN(spanItem, spanNodeBefore);
2603             TextInsertValueInfo infoAfter;
2604             infoAfter.SetSpanIndex(host->GetChildIndex(spanNodeBefore) + 1);
2605             infoAfter.SetOffsetInSpan(0);
2606             auto nodeId = ViewStackProcessor::GetInstance()->ClaimNodeId();
2607             RefPtr<SpanNode> spanNodeAfter = SpanNode::GetOrCreateSpanNode(nodeId);
2608             spanNodeAfter->MountToParent(host, infoAfter.GetSpanIndex());
2609             spanNodeAfter->UpdateContent(StringUtils::ToString(textAfter));
2610             CopyTextSpanStyle(spanNodeBefore, spanNodeAfter);
2611             auto spanItemAfter = spanNodeAfter->GetSpanItem();
2612             spanItemAfter->position = static_cast<int32_t>(textTemp.length());
2613             spanItemAfter->hasResourceFontColor = spanItem->hasResourceFontColor;
2614             spanItemAfter->hasResourceDecorationColor = spanItem->hasResourceDecorationColor;
2615             AddSpanItem(spanItemAfter, host->GetChildIndex(spanNodeBefore) + 1);
2616             SpanNodeFission(spanNodeAfter);
2617             return spanNodeAfter;
2618         }
2619     } else {
2620         text = StringUtils::ToString(textTemp);
2621         spanNodeBefore->UpdateContent(text);
2622         spanItem->position += static_cast<int32_t>(StringUtils::ToWstring(insertValue).length());
2623     }
2624     return spanNodeBefore;
2625 }
2626 
CreateTextSpanNode(RefPtr<SpanNode> & spanNode,const TextInsertValueInfo & info,const std::string & insertValue,bool isIME)2627 void RichEditorPattern::CreateTextSpanNode(
2628     RefPtr<SpanNode>& spanNode, const TextInsertValueInfo& info, const std::string& insertValue, bool isIME)
2629 {
2630     auto host = GetHost();
2631     CHECK_NULL_VOID(host);
2632     auto nodeId = ViewStackProcessor::GetInstance()->ClaimNodeId();
2633     spanNode = SpanNode::GetOrCreateSpanNode(nodeId);
2634     spanNode->MountToParent(host, info.GetSpanIndex());
2635     auto spanItem = spanNode->GetSpanItem();
2636     spanItem->hasResourceFontColor = true;
2637     spanItem->hasResourceDecorationColor = true;
2638     spanNode->UpdateContent(insertValue);
2639     AddSpanItem(spanItem, info.GetSpanIndex());
2640     if (typingStyle_.has_value() && typingTextStyle_.has_value()) {
2641         UpdateTextStyle(spanNode, typingStyle_.value(), typingTextStyle_.value());
2642         auto spanItem = spanNode->GetSpanItem();
2643         spanItem->SetTextStyle(typingTextStyle_);
2644     } else {
2645         spanNode->UpdateFontSize(Dimension(DEFAULT_TEXT_SIZE, DimensionUnit::FP));
2646         spanNode->AddPropertyInfo(PropertyInfo::FONTSIZE);
2647     }
2648     if (isIME) {
2649         AfterIMEInsertValue(spanNode, static_cast<int32_t>(StringUtils::ToWstring(insertValue).length()), true);
2650     }
2651 }
2652 
BeforeIMEInsertValue(const std::string & insertValue)2653 bool RichEditorPattern::BeforeIMEInsertValue(const std::string& insertValue)
2654 {
2655     auto eventHub = GetEventHub<RichEditorEventHub>();
2656     CHECK_NULL_RETURN(eventHub, true);
2657     RichEditorInsertValue insertValueInfo;
2658     insertValueInfo.SetInsertOffset(caretPosition_);
2659     insertValueInfo.SetInsertValue(insertValue);
2660     return eventHub->FireAboutToIMEInput(insertValueInfo);
2661 }
2662 
AfterIMEInsertValue(const RefPtr<SpanNode> & spanNode,int32_t insertValueLength,bool isCreate)2663 void RichEditorPattern::AfterIMEInsertValue(const RefPtr<SpanNode>& spanNode, int32_t insertValueLength, bool isCreate)
2664 {
2665     RichEditorAbstractSpanResult retInfo;
2666     isTextChange_ = true;
2667     moveDirection_ = MoveDirection::FORWARD;
2668     moveLength_ += insertValueLength;
2669     auto eventHub = GetEventHub<RichEditorEventHub>();
2670     CHECK_NULL_VOID(eventHub);
2671     auto host = GetHost();
2672     CHECK_NULL_VOID(host);
2673     retInfo.SetSpanIndex(host->GetChildIndex(spanNode));
2674     retInfo.SetEraseLength(insertValueLength);
2675     retInfo.SetValue(spanNode->GetSpanItem()->content);
2676     auto contentLength = StringUtils::ToWstring(spanNode->GetSpanItem()->content).length();
2677     if (isCreate) {
2678         auto spanStart = 0;
2679         auto spanEnd = static_cast<int32_t>(contentLength);
2680         retInfo.SetSpanRangeStart(spanStart);
2681         retInfo.SetSpanRangeEnd(spanEnd);
2682         retInfo.SetOffsetInSpan(0);
2683     } else {
2684         auto spanEnd = spanNode->GetSpanItem()->position;
2685         auto spanStart = spanEnd - static_cast<int32_t>(contentLength);
2686         retInfo.SetSpanRangeStart(spanStart);
2687         retInfo.SetSpanRangeEnd(spanEnd);
2688         retInfo.SetOffsetInSpan(GetCaretPosition() - retInfo.GetSpanRangeStart());
2689     }
2690     retInfo.SetFontColor(spanNode->GetTextColorValue(Color::BLACK).ColorToString());
2691     retInfo.SetFontSize(spanNode->GetFontSizeValue(Dimension(16.0f, DimensionUnit::VP)).ConvertToVp());
2692     retInfo.SetFontStyle(spanNode->GetItalicFontStyleValue(OHOS::Ace::FontStyle::NORMAL));
2693     retInfo.SetFontWeight(static_cast<int32_t>(spanNode->GetFontWeightValue(FontWeight::NORMAL)));
2694     std::string fontFamilyValue;
2695     auto fontFamily = spanNode->GetFontFamilyValue({ "HarmonyOS Sans" });
2696     for (const auto& str : fontFamily) {
2697         fontFamilyValue += str;
2698     }
2699     retInfo.SetFontFamily(fontFamilyValue);
2700     retInfo.SetTextDecoration(spanNode->GetTextDecorationValue(TextDecoration::NONE));
2701     retInfo.SetColor(spanNode->GetTextDecorationColorValue(Color::BLACK).ColorToString());
2702     UpdateSpanPosition();
2703     MoveCaretAfterTextChange();
2704     eventHub->FireOnIMEInputComplete(retInfo);
2705 }
2706 
ResetFirstNodeStyle()2707 void RichEditorPattern::ResetFirstNodeStyle()
2708 {
2709     auto tmpHost = GetHost();
2710     CHECK_NULL_VOID(tmpHost);
2711     auto spans = tmpHost->GetChildren();
2712     if (!spans.empty()) {
2713         auto&& firstNode = DynamicCast<SpanNode>(*(spans.begin()));
2714         if (firstNode) {
2715             firstNode->ResetTextAlign();
2716             firstNode->ResetLeadingMargin();
2717             tmpHost->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2718         }
2719     }
2720 }
2721 
FireOnDeleteComplete(const RichEditorDeleteValue & info)2722 void RichEditorPattern::FireOnDeleteComplete(const RichEditorDeleteValue& info)
2723 {
2724     auto eventHub = GetEventHub<RichEditorEventHub>();
2725     CHECK_NULL_VOID(eventHub);
2726     auto isDelete = eventHub->FireAboutToDelete(info);
2727     if (isDelete) {
2728         DeleteByDeleteValueInfo(info);
2729         eventHub->FireOnDeleteComplete();
2730     }
2731 }
2732 
EraseEmoji(const RefPtr<SpanItem> & spanItem)2733 bool RichEditorPattern::EraseEmoji(const RefPtr<SpanItem>& spanItem)
2734 {
2735     CHECK_NULL_RETURN(spanItem, false);
2736     auto& content = spanItem->content;
2737     bool eraseSuccess = false;
2738     uint32_t start = 0;
2739     uint32_t end = 0;
2740     while (start < content.length()) {
2741         auto unicode = TypedText::GetUTF8Next(content.c_str(), start, end);
2742         if (TypedText::IsEmoji(unicode)) {
2743             content.erase(start, end - start);
2744             eraseSuccess = true;
2745             continue;
2746         }
2747         start = end;
2748     }
2749     return eraseSuccess;
2750 }
2751 
EraseEmoji()2752 bool RichEditorPattern::EraseEmoji()
2753 {
2754     auto host = GetHost();
2755     CHECK_NULL_RETURN(host, false);
2756     std::set<int32_t, std::greater<int32_t>> deleteNodes;
2757     bool eraseSuccess = false;
2758     for (auto i = 0; i < static_cast<int32_t>(host->GetChildren().size()); ++i) {
2759         RefPtr<UINode> uiNode = host->GetChildAtIndex(i);
2760         auto spanNode = DynamicCast<SpanNode>(uiNode);
2761         if (!spanNode || spanNode->GetTag() != V2::SPAN_ETS_TAG) {
2762             continue;
2763         }
2764         const auto& spanItem = spanNode->GetSpanItem();
2765         eraseSuccess |= EraseEmoji(spanItem);
2766         if (spanItem && spanItem->content.empty()) {
2767             deleteNodes.emplace(i);
2768         }
2769     }
2770     for (auto index : deleteNodes) {
2771         host->RemoveChildAtIndex(index);
2772     }
2773     if (eraseSuccess) {
2774         UpdateSpanPosition();
2775         SetCaretPosition(std::clamp(caretPosition_, 0, static_cast<int32_t>(GetTextContentLength())));
2776         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2777         OnModifyDone();
2778     }
2779     return eraseSuccess;
2780 }
2781 
HandleOnDelete(bool backward)2782 void RichEditorPattern::HandleOnDelete(bool backward)
2783 {
2784     backward ? DeleteBackward(1) : DeleteForward(1);
2785 }
2786 
DeleteBackward(int32_t length)2787 void RichEditorPattern::DeleteBackward(int32_t length)
2788 {
2789     if (EraseEmoji()) {
2790         return;
2791     }
2792     OperationRecord record;
2793     record.beforeCaretPosition = caretPosition_;
2794     std::wstring deleteText = DeleteBackwardOperation(length);
2795     if (deleteText.length() != 0) {
2796         ClearRedoOperationRecords();
2797         record.deleteText = StringUtils::ToString(deleteText);
2798         record.afterCaretPosition = caretPosition_;
2799         AddOperationRecord(record);
2800     }
2801 }
2802 
DeleteBackwardOperation(int32_t length)2803 std::wstring RichEditorPattern::DeleteBackwardOperation(int32_t length)
2804 {
2805     if (textSelector_.IsValid()) {
2806         length = textSelector_.GetTextEnd() - textSelector_.GetTextStart();
2807         SetCaretPosition(textSelector_.GetTextEnd());
2808         CloseSelectOverlay();
2809         ResetSelection();
2810     }
2811     std::wstringstream wss;
2812     for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
2813         wss << StringUtils::ToWstring((*iter)->content);
2814     }
2815     auto textContent = wss.str();
2816     if (static_cast<int32_t>(textContent.length()) != GetTextContentLength()) {
2817         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "textContent length mismatch, %{public}d vs. %{public}d",
2818             static_cast<int32_t>(textContent.length()), GetTextContentLength());
2819     }
2820     auto start = std::clamp(caretPosition_ - length, 0, static_cast<int32_t>(textContent.length()));
2821     std::wstring deleteText =
2822         textContent.substr(static_cast<uint32_t>(start), static_cast<uint32_t>(caretPosition_ - start));
2823     RichEditorDeleteValue info;
2824     info.SetRichEditorDeleteDirection(RichEditorDeleteDirection::BACKWARD);
2825     if (caretPosition_ == 0) {
2826         info.SetLength(0);
2827         ResetFirstNodeStyle();
2828         FireOnDeleteComplete(info);
2829         return deleteText;
2830     }
2831     if (length == spans_.back()->position) {
2832         ResetFirstNodeStyle();
2833         textForDisplay_.clear();
2834     }
2835     info.SetOffset(caretPosition_ - 1);
2836     info.SetLength(length);
2837     int32_t currentPosition = std::clamp((caretPosition_ - length), 0, static_cast<int32_t>(GetTextContentLength()));
2838     if (!spans_.empty()) {
2839         CalcDeleteValueObj(currentPosition, length, info);
2840         FireOnDeleteComplete(info);
2841     }
2842     if (!caretVisible_) {
2843         StartTwinkling();
2844     }
2845     return deleteText;
2846 }
2847 
DeleteForward(int32_t length)2848 void RichEditorPattern::DeleteForward(int32_t length)
2849 {
2850     if (EraseEmoji()) {
2851         return;
2852     }
2853     OperationRecord record;
2854     record.beforeCaretPosition = caretPosition_;
2855     std::wstring deleteText = DeleteForwardOperation(length);
2856     if (deleteText.length() != 0) {
2857         ClearRedoOperationRecords();
2858         record.deleteText = StringUtils::ToString(deleteText);
2859         record.afterCaretPosition = caretPosition_;
2860         AddOperationRecord(record);
2861     }
2862 }
2863 
DeleteForwardOperation(int32_t length)2864 std::wstring RichEditorPattern::DeleteForwardOperation(int32_t length)
2865 {
2866     if (textSelector_.IsValid()) {
2867         length = textSelector_.GetTextEnd() - textSelector_.GetTextStart();
2868         SetCaretPosition(textSelector_.GetTextStart());
2869         CloseSelectOverlay();
2870         ResetSelection();
2871     }
2872     std::wstringstream wss;
2873     for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
2874         wss << StringUtils::ToWstring((*iter)->content);
2875     }
2876     auto textContent = wss.str();
2877     if (static_cast<int32_t>(textContent.length()) != GetTextContentLength()) {
2878         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "textContent length mismatch, %{public}d vs. %{public}d",
2879             static_cast<int32_t>(textContent.length()), GetTextContentLength());
2880     }
2881     auto end = std::clamp(caretPosition_ + length, 0, static_cast<int32_t>(textContent.length()));
2882     std::wstring deleteText = textContent.substr(
2883         static_cast<uint32_t>(std::clamp(caretPosition_, 0, static_cast<int32_t>(textContent.length()))),
2884         static_cast<uint32_t>(end - caretPosition_));
2885     if (caretPosition_ == GetTextContentLength()) {
2886         return deleteText;
2887     }
2888     RichEditorDeleteValue info;
2889     info.SetOffset(caretPosition_);
2890     info.SetRichEditorDeleteDirection(RichEditorDeleteDirection::FORWARD);
2891     info.SetLength(length);
2892     int32_t currentPosition = caretPosition_;
2893     if (!spans_.empty()) {
2894         CalcDeleteValueObj(currentPosition, length, info);
2895         auto eventHub = GetEventHub<RichEditorEventHub>();
2896         CHECK_NULL_RETURN(eventHub, deleteText);
2897         auto isDelete = eventHub->FireAboutToDelete(info);
2898         if (isDelete) {
2899             DeleteByDeleteValueInfo(info);
2900             eventHub->FireOnDeleteComplete();
2901         }
2902     }
2903     if (!caretVisible_) {
2904         StartTwinkling();
2905     }
2906     return deleteText;
2907 }
2908 
OnBackPressed()2909 bool RichEditorPattern::OnBackPressed()
2910 {
2911     auto tmpHost = GetHost();
2912     CHECK_NULL_RETURN(tmpHost, false);
2913     TAG_LOGI(AceLogTag::ACE_TEXT_FIELD, "RichEditor %{public}d receives back press event", tmpHost->GetId());
2914     if (SelectOverlayIsOn()) {
2915         CloseSelectOverlay();
2916         textSelector_.Update(textSelector_.destinationOffset);
2917         StartTwinkling();
2918         return true;
2919     }
2920 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
2921     if (!imeShown_ && !isCustomKeyboardAttached_) {
2922 #else
2923     if (!isCustomKeyboardAttached_) {
2924 #endif
2925         return false;
2926     }
2927     tmpHost->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2928     CloseKeyboard(true);
2929     FocusHub::LostFocusToViewRoot();
2930 #if defined(ANDROID_PLATFORM)
2931     return false;
2932 #else
2933     return true;
2934 #endif
2935 }
2936 
2937 void RichEditorPattern::SetInputMethodStatus(bool keyboardShown)
2938 {
2939 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
2940     imeShown_ = keyboardShown;
2941 #endif
2942 }
2943 
2944 bool RichEditorPattern::CursorMoveLeft()
2945 {
2946     CloseSelectOverlay();
2947     ResetSelection();
2948     auto caretPosition = std::clamp((caretPosition_ - 1), 0, static_cast<int32_t>(GetTextContentLength()));
2949     if (caretPosition_ == caretPosition) {
2950         return false;
2951     }
2952     SetCaretPosition(caretPosition);
2953     MoveCaretToContentRect();
2954     StartTwinkling();
2955     auto host = GetHost();
2956     CHECK_NULL_RETURN(host, false);
2957     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2958     return true;
2959 }
2960 
2961 bool RichEditorPattern::CursorMoveRight()
2962 {
2963     CloseSelectOverlay();
2964     ResetSelection();
2965     auto caretPosition = std::clamp((caretPosition_ + 1), 0, static_cast<int32_t>(GetTextContentLength()));
2966     if (caretPosition_ == caretPosition) {
2967         return false;
2968     }
2969     SetCaretPosition(caretPosition);
2970     MoveCaretToContentRect();
2971     StartTwinkling();
2972     auto host = GetHost();
2973     CHECK_NULL_RETURN(host, false);
2974     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2975     return true;
2976 }
2977 
2978 bool RichEditorPattern::CursorMoveUp()
2979 {
2980     CloseSelectOverlay();
2981     ResetSelection();
2982     if (static_cast<int32_t>(GetTextContentLength()) > 1) {
2983         float caretHeight = 0.0f;
2984         OffsetF caretOffset = CalcCursorOffsetByPosition(GetCaretPosition(), caretHeight);
2985         auto minDet = paragraphs_.minParagraphFontSize.value_or(0.0f) / 2.0;
2986         int32_t caretPosition = paragraphs_.GetIndex(
2987             Offset(caretOffset.GetX() - GetTextRect().GetX(), caretOffset.GetY() - GetTextRect().GetY() - minDet));
2988         caretPosition = std::clamp(caretPosition, 0, static_cast<int32_t>(GetTextContentLength()));
2989         if (caretPosition_ == caretPosition) {
2990             return false;
2991         }
2992         SetCaretPosition(caretPosition);
2993         MoveCaretToContentRect();
2994     }
2995     StartTwinkling();
2996     auto host = GetHost();
2997     CHECK_NULL_RETURN(host, false);
2998     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2999     return true;
3000 }
3001 
3002 bool RichEditorPattern::CursorMoveDown()
3003 {
3004     CloseSelectOverlay();
3005     ResetSelection();
3006     if (static_cast<int32_t>(GetTextContentLength()) > 1) {
3007         float caretHeight = 0.0f;
3008         OffsetF caretOffset = CalcCursorOffsetByPosition(GetCaretPosition(), caretHeight);
3009         auto minDet = paragraphs_.minParagraphFontSize.value() / 2.0;
3010         int32_t caretPosition = paragraphs_.GetIndex(Offset(caretOffset.GetX() - GetTextRect().GetX(),
3011             caretOffset.GetY() - GetTextRect().GetY() + caretHeight + minDet / 2.0));
3012         caretPosition = std::clamp(caretPosition, 0, static_cast<int32_t>(GetTextContentLength()));
3013         if (caretPosition_ == caretPosition) {
3014             return false;
3015         }
3016         SetCaretPosition(caretPosition);
3017         MoveCaretToContentRect();
3018     }
3019     StartTwinkling();
3020     auto host = GetHost();
3021     CHECK_NULL_RETURN(host, false);
3022     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3023     return true;
3024 }
3025 
3026 bool RichEditorPattern::CursorMoveLeftWord()
3027 {
3028     CloseSelectOverlay();
3029     ResetSelection();
3030     auto newPos = GetLeftWordPosition(caretPosition_);
3031     if (newPos == caretPosition_) {
3032         return false;
3033     }
3034     SetCaretPosition(newPos);
3035     MoveCaretToContentRect();
3036     StartTwinkling();
3037     auto host = GetHost();
3038     CHECK_NULL_RETURN(host, false);
3039     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3040     return true;
3041 }
3042 
3043 bool RichEditorPattern::CursorMoveRightWord()
3044 {
3045     CloseSelectOverlay();
3046     ResetSelection();
3047     auto newPos = GetRightWordPosition(caretPosition_);
3048     if (newPos == caretPosition_) {
3049         return false;
3050     }
3051     SetCaretPosition(newPos);
3052     MoveCaretToContentRect();
3053     StartTwinkling();
3054     auto host = GetHost();
3055     CHECK_NULL_RETURN(host, false);
3056     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3057     return true;
3058 }
3059 
3060 bool RichEditorPattern::CursorMoveToParagraphBegin()
3061 {
3062     CloseSelectOverlay();
3063     ResetSelection();
3064     auto newPos = GetParagraphBeginPosition(caretPosition_);
3065     if (newPos == caretPosition_ && caretPosition_ > 0) {
3066         newPos = GetParagraphBeginPosition(caretPosition_ - 1);
3067     }
3068     if (newPos == caretPosition_) {
3069         return false;
3070     }
3071     SetCaretPosition(newPos);
3072     MoveCaretToContentRect();
3073     StartTwinkling();
3074     auto host = GetHost();
3075     CHECK_NULL_RETURN(host, false);
3076     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3077     return true;
3078 }
3079 
3080 bool RichEditorPattern::CursorMoveToParagraphEnd()
3081 {
3082     CloseSelectOverlay();
3083     ResetSelection();
3084     auto newPos = GetParagraphEndPosition(caretPosition_);
3085     if (newPos == caretPosition_ && caretPosition_ < static_cast<int32_t>(GetTextContentLength())) {
3086         newPos = GetParagraphEndPosition(caretPosition_ + 1);
3087     }
3088     if (newPos == caretPosition_) {
3089         return false;
3090     }
3091     SetCaretPosition(newPos);
3092     MoveCaretToContentRect();
3093     StartTwinkling();
3094     auto host = GetHost();
3095     CHECK_NULL_RETURN(host, false);
3096     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3097     return true;
3098 }
3099 
3100 bool RichEditorPattern::CursorMoveHome()
3101 {
3102     CloseSelectOverlay();
3103     ResetSelection();
3104     if (0 == caretPosition_) {
3105         return false;
3106     }
3107     SetCaretPosition(0);
3108     MoveCaretToContentRect();
3109     StartTwinkling();
3110     auto host = GetHost();
3111     CHECK_NULL_RETURN(host, false);
3112     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3113     return true;
3114 }
3115 
3116 bool RichEditorPattern::CursorMoveEnd()
3117 {
3118     CloseSelectOverlay();
3119     ResetSelection();
3120     auto newPos = GetTextContentLength();
3121     if (newPos == caretPosition_) {
3122         return false;
3123     }
3124     SetCaretPosition(newPos);
3125     MoveCaretToContentRect();
3126     StartTwinkling();
3127     auto host = GetHost();
3128     CHECK_NULL_RETURN(host, false);
3129     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3130     return true;
3131 }
3132 
3133 int32_t RichEditorPattern::GetLeftWordPosition(int32_t caretPosition)
3134 {
3135     int32_t offset = 0;
3136     bool jumpSpace = true;
3137     for (auto iter = spans_.rbegin(); iter != spans_.rend(); iter++) {
3138         auto span = *iter;
3139         auto content = StringUtils::ToWstring(span->content);
3140         if (caretPosition <= span->position - static_cast<int32_t>(content.length())) {
3141             continue;
3142         }
3143         int32_t position = span->position;
3144         for (auto iterContent = content.rbegin(); iterContent != content.rend(); iterContent++) {
3145             if (position-- > caretPosition) {
3146                 continue;
3147             }
3148             if (*iterContent != L' ' || span->placeholderIndex >= 0) {
3149                 jumpSpace = false;
3150             }
3151             if (position + 1 == caretPosition) {
3152                 if (!(StringUtils::IsLetterOrNumberForWchar(*iterContent) ||
3153                         (*iterContent == L' ' && span->placeholderIndex < 0))) {
3154                     return std::clamp(caretPosition - 1, 0, static_cast<int32_t>(GetTextContentLength()));
3155                 }
3156             }
3157             if (!jumpSpace) {
3158                 if (!StringUtils::IsLetterOrNumberForWchar(*iterContent)) {
3159                     return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
3160                 }
3161             } else {
3162                 if (*iterContent == L' ' && span->placeholderIndex >= 0) {
3163                     return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
3164                 }
3165             }
3166             offset++;
3167         }
3168     }
3169     return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
3170 }
3171 
3172 int32_t RichEditorPattern::GetRightWordPosition(int32_t caretPosition)
3173 {
3174     int32_t offset = 0;
3175     bool jumpSpace = false;
3176     for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
3177         auto span = *iter;
3178         auto content = StringUtils::ToWstring(span->content);
3179         if (caretPosition > span->position) {
3180             continue;
3181         }
3182         int32_t position = span->position - static_cast<int32_t>(content.length());
3183         for (auto iterContent = content.cbegin(); iterContent != content.cend(); iterContent++) {
3184             if (position++ < caretPosition) {
3185                 continue;
3186             }
3187             if (*iterContent == L' ' && span->placeholderIndex < 0) {
3188                 jumpSpace = true;
3189                 offset++;
3190                 continue;
3191             }
3192             if (position - 1 == caretPosition) {
3193                 if (!StringUtils::IsLetterOrNumberForWchar(*iterContent)) {
3194                     return std::clamp(caretPosition + 1, 0, static_cast<int32_t>(GetTextContentLength()));
3195                 }
3196             }
3197             if (jumpSpace) {
3198                 if (*iterContent != L' ' || span->placeholderIndex >= 0) {
3199                     return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
3200                 }
3201             } else {
3202                 if (!(StringUtils::IsLetterOrNumberForWchar(*iterContent) ||
3203                         (*iterContent == L' ' && span->placeholderIndex < 0))) {
3204                     return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
3205                 }
3206             }
3207             offset++;
3208         }
3209     }
3210     return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
3211 }
3212 
3213 int32_t RichEditorPattern::GetParagraphBeginPosition(int32_t caretPosition)
3214 {
3215     int32_t offset = 0;
3216     for (auto iter = spans_.rbegin(); iter != spans_.rend(); iter++) {
3217         auto span = *iter;
3218         auto content = StringUtils::ToWstring(span->content);
3219         if (caretPosition <= span->position - static_cast<int32_t>(content.length())) {
3220             continue;
3221         }
3222         int32_t position = span->position;
3223         for (auto iterContent = content.rbegin(); iterContent != content.rend(); iterContent++) {
3224             if (position-- > caretPosition) {
3225                 continue;
3226             }
3227             if (*iterContent == L'\n') {
3228                 return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
3229             }
3230             offset++;
3231         }
3232     }
3233     return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
3234 }
3235 
3236 int32_t RichEditorPattern::GetParagraphEndPosition(int32_t caretPosition)
3237 {
3238     int32_t offset = 0;
3239     for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
3240         auto span = *iter;
3241         auto content = StringUtils::ToWstring(span->content);
3242         if (caretPosition > span->position) {
3243             continue;
3244         }
3245         int32_t position = span->position - static_cast<int32_t>(content.length());
3246         for (auto iterContent = content.cbegin(); iterContent != content.cend(); iterContent++) {
3247             if (position++ < caretPosition) {
3248                 continue;
3249             }
3250             if (*iterContent == L'\n') {
3251                 return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
3252             }
3253             offset++;
3254         }
3255     }
3256     return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
3257 }
3258 
3259 void RichEditorPattern::HandleOnSelectAll()
3260 {
3261     CloseSelectOverlay();
3262     auto host = GetHost();
3263     CHECK_NULL_VOID(host);
3264     int32_t newPos = static_cast<int32_t>(GetTextContentLength());
3265     textSelector_.Update(0, newPos);
3266     FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
3267     SetCaretPosition(newPos);
3268     MoveCaretToContentRect();
3269     StartTwinkling();
3270     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3271 }
3272 
3273 void RichEditorPattern::HandleSelect(CaretMoveIntent direction)
3274 {
3275     CloseSelectOverlay();
3276     auto host = GetHost();
3277     CHECK_NULL_VOID(host);
3278     int32_t newPos, fixedPos = caretPosition_;
3279     if (IsSelected()) {
3280         fixedPos = (caretPosition_ == textSelector_.GetTextStart() ? textSelector_.GetTextEnd()
3281                                                                    : textSelector_.GetTextStart());
3282     }
3283     switch (direction) {
3284         case CaretMoveIntent::Left:
3285             newPos = caretPosition_ - 1;
3286             break;
3287         case CaretMoveIntent::Right:
3288             newPos = caretPosition_ + 1;
3289             break;
3290         case CaretMoveIntent::Up: {
3291             float caretHeight = 0.0f;
3292             OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight);
3293             auto minDet = paragraphs_.minParagraphFontSize.value() / 2.0;
3294             newPos = paragraphs_.GetIndex(Offset(caretOffset.GetX() - GetTextRect().GetX(),
3295                 caretOffset.GetY() - GetTextRect().GetY() - minDet), true);
3296             break;
3297         }
3298         case CaretMoveIntent::Down: {
3299             float caretHeight = 0.0f;
3300             OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight);
3301             auto minDet = paragraphs_.minParagraphFontSize.value() / 2.0;
3302             newPos = paragraphs_.GetIndex(Offset(caretOffset.GetX() - GetTextRect().GetX(),
3303                 caretOffset.GetY() - GetTextRect().GetY() + caretHeight + minDet / 2.0), true);
3304             break;
3305         }
3306         default:
3307             LOGW("Unsupported select operation for rich editor");
3308             return;
3309     }
3310     newPos = std::clamp(newPos, 0, static_cast<int32_t>(GetTextContentLength()));
3311     if (newPos == caretPosition_) {
3312         return;
3313     }
3314     textSelector_.Update(newPos, fixedPos);
3315     FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
3316     SetCaretPosition(newPos);
3317     MoveCaretToContentRect();
3318     StartTwinkling();
3319     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3320 }
3321 
3322 void RichEditorPattern::ClearOperationRecords()
3323 {
3324     ClearRedoOperationRecords();
3325     if (operationRecords_.empty()) {
3326         return;
3327     }
3328     operationRecords_.clear();
3329 }
3330 
3331 void RichEditorPattern::ClearRedoOperationRecords()
3332 {
3333     if (redoOperationRecords_.empty()) {
3334         return;
3335     }
3336     redoOperationRecords_.clear();
3337 }
3338 
3339 void RichEditorPattern::AddOperationRecord(const OperationRecord& record)
3340 {
3341     if (operationRecords_.size() >= RECORD_MAX_LENGTH) {
3342         // case of max length is 0
3343         if (operationRecords_.empty()) {
3344             return;
3345         }
3346         operationRecords_.erase(operationRecords_.begin());
3347     }
3348     operationRecords_.emplace_back(record);
3349 }
3350 
3351 bool RichEditorPattern::HandleOnEscape()
3352 {
3353     CloseSelectOverlay();
3354     return false;
3355 }
3356 
3357 void RichEditorPattern::HandleOnUndoAction()
3358 {
3359     if (operationRecords_.empty()) {
3360         return;
3361     }
3362     auto value = operationRecords_.back();
3363     operationRecords_.pop_back();
3364     if (redoOperationRecords_.size() >= RECORD_MAX_LENGTH && !(redoOperationRecords_.empty())) {
3365         redoOperationRecords_.erase(redoOperationRecords_.begin());
3366     }
3367     redoOperationRecords_.push_back(value);
3368     CloseSelectOverlay();
3369     ResetSelection();
3370     if (value.addText.has_value() && value.deleteText.has_value()) {
3371         SetCaretPosition(value.afterCaretPosition);
3372         DeleteBackwardOperation(StringUtils::ToWstring(value.addText.value_or("")).length());
3373         InsertValueOperation(value.deleteText.value_or(""));
3374         return;
3375     }
3376     if (value.addText.has_value()) {
3377         SetCaretPosition(value.afterCaretPosition);
3378         DeleteBackwardOperation(StringUtils::ToWstring(value.addText.value_or("")).length());
3379     }
3380     if (value.deleteText.has_value()) {
3381         SetCaretPosition(value.afterCaretPosition);
3382         InsertValueOperation(value.deleteText.value_or(""));
3383     }
3384 }
3385 
3386 void RichEditorPattern::HandleOnRedoAction()
3387 {
3388     if (redoOperationRecords_.empty()) {
3389         return;
3390     }
3391     auto value = redoOperationRecords_.back();
3392     redoOperationRecords_.pop_back();
3393     if (value.addText.has_value() && value.deleteText.has_value()) {
3394         SetCaretPosition(value.beforeCaretPosition);
3395         DeleteForwardOperation(StringUtils::ToWstring(value.deleteText.value_or("")).length());
3396         InsertValueOperation(value.addText.value_or(""));
3397         operationRecords_.push_back(value);
3398         return;
3399     }
3400     if (value.deleteText.has_value()) {
3401         SetCaretPosition(value.beforeCaretPosition);
3402         if (value.beforeCaretPosition != value.afterCaretPosition) {
3403             DeleteBackwardOperation(StringUtils::ToWstring(value.deleteText.value_or("")).length());
3404         } else {
3405             DeleteForwardOperation(StringUtils::ToWstring(value.deleteText.value_or("")).length());
3406         }
3407     }
3408     if (value.addText.has_value()) {
3409         SetCaretPosition(value.beforeCaretPosition);
3410         InsertValueOperation(value.addText.value_or(""));
3411     }
3412     operationRecords_.push_back(value);
3413 }
3414 
3415 void RichEditorPattern::CalcInsertValueObj(TextInsertValueInfo& info)
3416 {
3417     if (spans_.empty()) {
3418         info.SetSpanIndex(0);
3419         info.SetOffsetInSpan(0);
3420         return;
3421     }
3422     auto it = std::find_if(
3423         spans_.begin(), spans_.end(), [caretPosition = caretPosition_ + moveLength_](const RefPtr<SpanItem>& spanItem) {
3424             auto spanLength = static_cast<int32_t>(StringUtils::ToWstring(spanItem->content).length());
3425             if (spanLength == 0) {
3426                 return spanItem->position == caretPosition;
3427             }
3428             return (spanItem->position - spanLength <= caretPosition) && (caretPosition < spanItem->position);
3429         });
3430     if (it != spans_.end() && (*it)->unicode != 0 && (*it)->position - caretPosition_ + moveLength_ == 1) {
3431         it++;
3432         moveLength_++;
3433     }
3434     info.SetSpanIndex(std::distance(spans_.begin(), it));
3435     if (it == spans_.end()) {
3436         info.SetOffsetInSpan(0);
3437         return;
3438     }
3439     info.SetOffsetInSpan(
3440         caretPosition_ + moveLength_ - ((*it)->position - StringUtils::ToWstring((*it)->content).length()));
3441 }
3442 
3443 void RichEditorPattern::CalcDeleteValueObj(int32_t currentPosition, int32_t length, RichEditorDeleteValue& info)
3444 {
3445     auto it =
3446         std::find_if(spans_.begin(), spans_.end(), [caretPosition = currentPosition](const RefPtr<SpanItem>& spanItem) {
3447             return (spanItem->position - static_cast<int32_t>(StringUtils::ToWstring(spanItem->content).length()) <=
3448                        caretPosition) &&
3449                    (caretPosition < spanItem->position);
3450         });
3451     while (it != spans_.end() && length > 0) {
3452         if ((*it)->placeholderIndex >= 0 || (*it)->unicode != 0) {
3453             RichEditorAbstractSpanResult spanResult;
3454             spanResult.SetSpanIndex(std::distance(spans_.begin(), it));
3455             int32_t eraseLength = 0;
3456             if ((*it)->unicode != 0) {
3457                 eraseLength = DeleteValueSetSymbolSpan(*it, spanResult);
3458             } else if (AceType::InstanceOf<ImageSpanItem>(*it)) {
3459                 eraseLength = DeleteValueSetImageSpan(*it, spanResult);
3460             } else {
3461                 eraseLength = DeleteValueSetBuilderSpan(*it, spanResult);
3462             }
3463             currentPosition += eraseLength;
3464             length -= eraseLength;
3465             info.SetRichEditorDeleteSpans(spanResult);
3466         } else {
3467             RichEditorAbstractSpanResult spanResult;
3468             spanResult.SetSpanIndex(std::distance(spans_.begin(), it));
3469             auto eraseLength = DeleteValueSetTextSpan(*it, currentPosition, length, spanResult);
3470             length -= eraseLength;
3471             currentPosition += eraseLength;
3472             info.SetRichEditorDeleteSpans(spanResult);
3473         }
3474         std::advance(it, 1);
3475     }
3476 }
3477 
3478 int32_t RichEditorPattern::DeleteValueSetSymbolSpan(
3479     const RefPtr<SpanItem>& spanItem, RichEditorAbstractSpanResult& spanResult)
3480 {
3481     spanResult.SetSpanType(SpanResultType::SYMBOL);
3482     spanResult.SetSpanRangeEnd(spanItem->position);
3483     spanResult.SetSpanRangeStart(spanItem->position - SYMBOL_SPAN_LENGTH);
3484     if (GetCaretPosition() < spanItem->position) {
3485         spanResult.SetEraseLength(1);
3486     } else {
3487         spanResult.SetEraseLength(SYMBOL_SPAN_LENGTH);
3488     }
3489     return SYMBOL_SPAN_LENGTH;
3490 }
3491 
3492 int32_t RichEditorPattern::DeleteValueSetImageSpan(
3493     const RefPtr<SpanItem>& spanItem, RichEditorAbstractSpanResult& spanResult)
3494 {
3495     spanResult.SetSpanType(SpanResultType::IMAGE);
3496     spanResult.SetSpanRangeEnd(spanItem->position);
3497     spanResult.SetSpanRangeStart(spanItem->position - 1);
3498     spanResult.SetEraseLength(1);
3499     auto host = GetHost();
3500     CHECK_NULL_RETURN(host, IMAGE_SPAN_LENGTH);
3501     auto uiNode = host->GetChildAtIndex(spanResult.GetSpanIndex());
3502     CHECK_NULL_RETURN(uiNode, IMAGE_SPAN_LENGTH);
3503     auto imageNode = AceType::DynamicCast<FrameNode>(uiNode);
3504     CHECK_NULL_RETURN(imageNode, IMAGE_SPAN_LENGTH);
3505     auto geometryNode = imageNode->GetGeometryNode();
3506     CHECK_NULL_RETURN(geometryNode, IMAGE_SPAN_LENGTH);
3507     auto imageLayoutProperty = DynamicCast<ImageLayoutProperty>(imageNode->GetLayoutProperty());
3508     CHECK_NULL_RETURN(imageLayoutProperty, IMAGE_SPAN_LENGTH);
3509     spanResult.SetSizeWidth(geometryNode->GetMarginFrameSize().Width());
3510     spanResult.SetSizeHeight(geometryNode->GetMarginFrameSize().Height());
3511     if (!imageLayoutProperty->GetImageSourceInfo()->GetPixmap()) {
3512         spanResult.SetValueResourceStr(imageLayoutProperty->GetImageSourceInfo()->GetSrc());
3513     } else {
3514         spanResult.SetValuePixelMap(imageLayoutProperty->GetImageSourceInfo()->GetPixmap());
3515     }
3516     if (imageLayoutProperty->HasImageFit()) {
3517         spanResult.SetImageFit(imageLayoutProperty->GetImageFitValue());
3518     }
3519     if (imageLayoutProperty->HasVerticalAlign()) {
3520         spanResult.SetVerticalAlign(imageLayoutProperty->GetVerticalAlignValue());
3521     }
3522     return IMAGE_SPAN_LENGTH;
3523 }
3524 
3525 int32_t RichEditorPattern::DeleteValueSetBuilderSpan(
3526     const RefPtr<SpanItem>& spanItem, RichEditorAbstractSpanResult& spanResult)
3527 {
3528     spanResult.SetSpanType(SpanResultType::IMAGE);
3529     spanResult.SetSpanRangeEnd(spanItem->position);
3530     spanResult.SetSpanRangeStart(spanItem->position - 1);
3531     spanResult.SetEraseLength(1);
3532     auto host = GetHost();
3533     CHECK_NULL_RETURN(host, 1);
3534     auto uiNode = host->GetChildAtIndex(spanResult.GetSpanIndex());
3535     CHECK_NULL_RETURN(uiNode, 1);
3536     auto builderNode = AceType::DynamicCast<FrameNode>(uiNode);
3537     CHECK_NULL_RETURN(builderNode, 1);
3538     auto geometryNode = builderNode->GetGeometryNode();
3539     CHECK_NULL_RETURN(geometryNode, 1);
3540     spanResult.SetSizeWidth(geometryNode->GetMarginFrameSize().Width());
3541     spanResult.SetSizeHeight(geometryNode->GetMarginFrameSize().Height());
3542     return 1;
3543 }
3544 
3545 int32_t RichEditorPattern::DeleteValueSetTextSpan(
3546     const RefPtr<SpanItem>& spanItem, int32_t currentPosition, int32_t length, RichEditorAbstractSpanResult& spanResult)
3547 {
3548     spanResult.SetSpanType(SpanResultType::TEXT);
3549     auto contentStartPosition = spanItem->position - StringUtils::ToWstring(spanItem->content).length();
3550     spanResult.SetSpanRangeStart(contentStartPosition);
3551     int32_t eraseLength = 0;
3552     if (spanItem->position - currentPosition >= length) {
3553         eraseLength = length;
3554     } else {
3555         eraseLength = spanItem->position - currentPosition;
3556     }
3557     spanResult.SetSpanRangeEnd(spanItem->position);
3558     spanResult.SetValue(spanItem->content);
3559     spanResult.SetOffsetInSpan(currentPosition - contentStartPosition);
3560     spanResult.SetEraseLength(eraseLength);
3561     spanResult.SetFontColor(spanItem->GetTextStyle()->GetTextColor().ColorToString());
3562     spanResult.SetFontSize(spanItem->GetTextStyle()->GetFontSize().Value());
3563     spanResult.SetFontStyle(spanItem->GetTextStyle()->GetFontStyle());
3564     spanResult.SetFontWeight((int32_t)(spanItem->GetTextStyle()->GetFontWeight()));
3565     if (!spanItem->GetTextStyle()->GetFontFamilies().empty()) {
3566         spanResult.SetFontFamily(spanItem->GetTextStyle()->GetFontFamilies().at(0));
3567     }
3568     spanResult.SetColor(spanItem->GetTextStyle()->GetTextDecorationColor().ColorToString());
3569     spanResult.SetTextDecoration(spanItem->GetTextStyle()->GetTextDecoration());
3570     return eraseLength;
3571 }
3572 
3573 void RichEditorPattern::DeleteByDeleteValueInfo(const RichEditorDeleteValue& info)
3574 {
3575     auto deleteSpans = info.GetRichEditorDeleteSpans();
3576     auto host = GetHost();
3577     CHECK_NULL_VOID(host);
3578     std::list<RefPtr<UINode>> deleteNode;
3579     std::set<int32_t, std::greater<int32_t>> deleteNodes;
3580     auto caretMoveLength = 0;
3581     for (const auto& it : deleteSpans) {
3582         caretMoveLength += it.GetEraseLength();
3583         switch (it.GetType()) {
3584             case SpanResultType::TEXT: {
3585                 auto ui_node = host->GetChildAtIndex(it.GetSpanIndex());
3586                 CHECK_NULL_VOID(ui_node);
3587                 auto spanNode = DynamicCast<SpanNode>(ui_node);
3588                 CHECK_NULL_VOID(spanNode);
3589                 auto spanItem = spanNode->GetSpanItem();
3590                 CHECK_NULL_VOID(spanItem);
3591                 auto text = spanItem->content;
3592                 std::wstring textTemp = StringUtils::ToWstring(text);
3593                 textTemp.erase(it.OffsetInSpan(), it.GetEraseLength());
3594                 if (textTemp.size() == 0) {
3595                     deleteNodes.emplace(it.GetSpanIndex());
3596                 }
3597                 text = StringUtils::ToString(textTemp);
3598                 spanNode->UpdateContent(text);
3599                 spanItem->position -= it.GetEraseLength();
3600                 break;
3601             }
3602             case SpanResultType::IMAGE:
3603                 deleteNodes.emplace(it.GetSpanIndex());
3604                 break;
3605             case SpanResultType::SYMBOL:
3606                 deleteNodes.emplace(it.GetSpanIndex());
3607                 break;
3608             default:
3609                 break;
3610         }
3611     }
3612     for (auto index : deleteNodes) {
3613         host->RemoveChildAtIndex(index);
3614     }
3615     if (info.GetRichEditorDeleteDirection() == RichEditorDeleteDirection::BACKWARD) {
3616         SetCaretPosition(std::clamp(caretPosition_ - caretMoveLength, 0, static_cast<int32_t>(GetTextContentLength())));
3617     }
3618     UpdateSpanPosition();
3619     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
3620     OnModifyDone();
3621 }
3622 
3623 bool RichEditorPattern::OnKeyEvent(const KeyEvent& keyEvent)
3624 {
3625     return TextInputClient::HandleKeyEvent(keyEvent);
3626 }
3627 
3628 void RichEditorPattern::CursorMove(CaretMoveIntent direction)
3629 {
3630     switch (direction) {
3631         case CaretMoveIntent::Left:
3632             CursorMoveLeft();
3633             break;
3634         case CaretMoveIntent::Right:
3635             CursorMoveRight();
3636             break;
3637         case CaretMoveIntent::Up:
3638             CursorMoveUp();
3639             break;
3640         case CaretMoveIntent::Down:
3641             CursorMoveDown();
3642             break;
3643         case CaretMoveIntent::LeftWord:
3644             CursorMoveLeftWord();
3645             break;
3646         case CaretMoveIntent::RightWord:
3647             CursorMoveRightWord();
3648             break;
3649         case CaretMoveIntent::ParagraghBegin:
3650             CursorMoveToParagraphBegin();
3651             break;
3652         case CaretMoveIntent::ParagraghEnd:
3653             CursorMoveToParagraphEnd();
3654             break;
3655         case CaretMoveIntent::Home:
3656             CursorMoveHome();
3657             break;
3658         case CaretMoveIntent::End:
3659             CursorMoveEnd();
3660             break;
3661         default:
3662             LOGW("Unsupported cursor move operation for rich editor");
3663     }
3664 }
3665 
3666 void RichEditorPattern::MoveCaretAfterTextChange()
3667 {
3668     CHECK_NULL_VOID(isTextChange_);
3669     isTextChange_ = false;
3670     switch (moveDirection_) {
3671         case MoveDirection::BACKWARD:
3672             SetCaretPosition(
3673                 std::clamp((caretPosition_ - moveLength_), 0, static_cast<int32_t>(GetTextContentLength())));
3674             break;
3675         case MoveDirection::FORWARD:
3676             SetCaretPosition(
3677                 std::clamp((caretPosition_ + moveLength_), 0, static_cast<int32_t>(GetTextContentLength())));
3678             break;
3679         default:
3680             break;
3681     }
3682     moveLength_ = 0;
3683 }
3684 
3685 void RichEditorPattern::InitTouchEvent()
3686 {
3687     CHECK_NULL_VOID(!touchListener_);
3688     auto host = GetHost();
3689     CHECK_NULL_VOID(host);
3690 
3691     auto gesture = host->GetOrCreateGestureEventHub();
3692     CHECK_NULL_VOID(gesture);
3693     auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
3694         auto pattern = weak.Upgrade();
3695         CHECK_NULL_VOID(pattern);
3696         pattern->HandleTouchEvent(info);
3697     };
3698     touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
3699     gesture->AddTouchEvent(touchListener_);
3700 }
3701 
3702 void RichEditorPattern::HandleTouchEvent(const TouchEventInfo& info)
3703 {
3704     if (SelectOverlayIsOn()) {
3705         return;
3706     }
3707     auto touchType = info.GetTouches().front().GetTouchType();
3708     if (touchType == TouchType::DOWN) {
3709     } else if (touchType == TouchType::UP) {
3710         isMousePressed_ = false;
3711 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
3712         if (isLongPress_) {
3713             isLongPress_ = false;
3714         }
3715 #endif
3716     }
3717 }
3718 
3719 bool RichEditorPattern::IsScrollBarPressed(const MouseInfo& info)
3720 {
3721     auto scrollBar = GetScrollBar();
3722     Point point(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY());
3723     return scrollBar->InBarTouchRegion(point);
3724 }
3725 
3726 void RichEditorPattern::HandleMouseLeftButtonMove(const MouseInfo& info)
3727 {
3728     if (blockPress_ || !leftMousePress_) {
3729         return;
3730     }
3731     auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
3732     Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
3733         info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
3734 
3735     mouseStatus_ = MouseStatus::MOVE;
3736     if (isFirstMouseSelect_) {
3737         int32_t extend = paragraphs_.GetIndex(textOffset);
3738         textSelector_.Update(textSelector_.baseOffset, extend);
3739         isFirstMouseSelect_ = false;
3740     } else {
3741         int32_t extend = paragraphs_.GetIndex(textOffset);
3742         textSelector_.Update(textSelector_.baseOffset, extend);
3743         auto position = paragraphs_.GetIndex(textOffset);
3744         AdjustCursorPosition(position);
3745         SetCaretPosition(position);
3746         AutoScrollParam param = {
3747             .autoScrollEvent = AutoScrollEvent::MOUSE, .showScrollbar = true, .eventOffset = info.GetLocalLocation()
3748         };
3749         AutoScrollByEdgeDetection(param, OffsetF(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY()),
3750             EdgeDetectionStrategy::OUT_BOUNDARY);
3751     }
3752 
3753     isMouseSelect_ = true;
3754     auto host = GetHost();
3755     CHECK_NULL_VOID(host);
3756     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3757 }
3758 
3759 void RichEditorPattern::HandleMouseLeftButtonPress(const MouseInfo& info)
3760 {
3761     isMousePressed_ = true;
3762     if (IsScrollBarPressed(info) || BetweenSelectedPosition(info.GetGlobalLocation())) {
3763         blockPress_ = true;
3764         return;
3765     }
3766     auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
3767     Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
3768         info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
3769     int32_t extend = paragraphs_.GetIndex(textOffset);
3770     textSelector_.Update(extend);
3771     leftMousePress_ = true;
3772     mouseStatus_ = MouseStatus::PRESSED;
3773     blockPress_ = false;
3774     caretUpdateType_ = CaretUpdateType::PRESSED;
3775     UseHostToUpdateTextFieldManager();
3776 
3777     auto position = paragraphs_.GetIndex(textOffset);
3778     AdjustCursorPosition(position);
3779     auto focusHub = GetHost()->GetOrCreateFocusHub();
3780     if (focusHub && focusHub->RequestFocusImmediately()) {
3781         float caretHeight = 0.0f;
3782         SetCaretPosition(position);
3783         OffsetF caretOffset = CalcCursorOffsetByPosition(GetCaretPosition(), caretHeight, false, false);
3784         MoveCaretToContentRect();
3785         CHECK_NULL_VOID(overlayMod_);
3786         DynamicCast<RichEditorOverlayModifier>(overlayMod_)->SetCaretOffsetAndHeight(caretOffset, caretHeight);
3787         StartTwinkling();
3788         if (overlayMod_) {
3789             RequestKeyboard(false, true, true);
3790         }
3791     }
3792     UseHostToUpdateTextFieldManager();
3793 }
3794 
3795 void RichEditorPattern::HandleMouseLeftButtonRelease(const MouseInfo& info)
3796 {
3797     blockPress_ = false;
3798     leftMousePress_ = false;
3799     auto oldMouseStatus = mouseStatus_;
3800     mouseStatus_ = MouseStatus::RELEASED;
3801     isMouseSelect_ = false;
3802     isMousePressed_ = false;
3803     isFirstMouseSelect_ = true;
3804     auto selectStart = std::min(textSelector_.baseOffset, textSelector_.destinationOffset);
3805     auto selectEnd = std::max(textSelector_.baseOffset, textSelector_.destinationOffset);
3806     if (selectStart != selectEnd) {
3807         FireOnSelect(selectStart, selectEnd);
3808     }
3809     StopAutoScroll();
3810     if (textSelector_.IsValid() && !textSelector_.StartEqualToDest() && IsSelectedBindSelectionMenu() &&
3811         oldMouseStatus == MouseStatus::MOVE) {
3812         selectionMenuOffsetByMouse_ = OffsetF(static_cast<float>(info.GetGlobalLocation().GetX()),
3813             static_cast<float>(info.GetGlobalLocation().GetY()));
3814         ShowSelectOverlay(RectF(), RectF(), false, TextResponseType::SELECTED_BY_MOUSE);
3815     }
3816 }
3817 
3818 void RichEditorPattern::HandleMouseLeftButton(const MouseInfo& info)
3819 {
3820     if (info.GetAction() == MouseAction::MOVE) {
3821         HandleMouseLeftButtonMove(info);
3822     } else if (info.GetAction() == MouseAction::PRESS) {
3823         HandleMouseLeftButtonPress(info);
3824     } else if (info.GetAction() == MouseAction::RELEASE) {
3825         HandleMouseLeftButtonRelease(info);
3826     }
3827 }
3828 
3829 void RichEditorPattern::HandleMouseRightButton(const MouseInfo& info)
3830 {
3831     if (info.GetAction() == MouseAction::PRESS) {
3832         isMousePressed_ = true;
3833         usingMouseRightButton_ = true;
3834         CloseSelectionMenu();
3835     } else if (info.GetAction() == MouseAction::RELEASE) {
3836         selectionMenuOffsetByMouse_ = OffsetF(
3837             static_cast<float>(info.GetGlobalLocation().GetX()), static_cast<float>(info.GetGlobalLocation().GetY()));
3838         if (textSelector_.IsValid() && BetweenSelectedPosition(info.GetGlobalLocation())) {
3839             ShowSelectOverlay(RectF(), RectF(), IsSelectAll(), TextResponseType::RIGHT_CLICK);
3840             isMousePressed_ = false;
3841             usingMouseRightButton_ = false;
3842             return;
3843         }
3844         if (textSelector_.IsValid()) {
3845             CloseSelectOverlay();
3846             ResetSelection();
3847         }
3848         MouseRightFocus(info);
3849         ShowSelectOverlay(RectF(), RectF(), IsSelectAll(), TextResponseType::RIGHT_CLICK);
3850         isMousePressed_ = false;
3851         usingMouseRightButton_ = false;
3852     }
3853 }
3854 
3855 void RichEditorPattern::MouseRightFocus(const MouseInfo& info)
3856 {
3857     auto textRect = GetTextRect();
3858     textRect.SetTop(textRect.GetY() - std::min(baselineOffset_, 0.0f));
3859     textRect.SetHeight(textRect.Height() - std::max(baselineOffset_, 0.0f));
3860     Offset textOffset = { info.GetLocalLocation().GetX() - textRect.GetX(),
3861         info.GetLocalLocation().GetY() - textRect.GetY() };
3862     InitSelection(textOffset);
3863     auto selectStart = std::min(textSelector_.baseOffset, textSelector_.destinationOffset);
3864     auto selectEnd = std::max(textSelector_.baseOffset, textSelector_.destinationOffset);
3865     auto host = GetHost();
3866     CHECK_NULL_VOID(host);
3867     auto focusHub = host->GetOrCreateFocusHub();
3868     CHECK_NULL_VOID(focusHub);
3869     focusHub->RequestFocusImmediately();
3870     SetCaretPosition(selectEnd);
3871 
3872     TextInsertValueInfo spanInfo;
3873     CalcInsertValueObj(spanInfo);
3874     auto spanNode = DynamicCast<FrameNode>(GetChildByIndex(spanInfo.GetSpanIndex() - 1));
3875     if (spanNode && spanNode->GetTag() == V2::IMAGE_ETS_TAG && spanInfo.GetOffsetInSpan() == 0 &&
3876         selectEnd == selectStart + 1 && BetweenSelectedPosition(info.GetGlobalLocation())) {
3877         selectedType_ = TextSpanType::IMAGE;
3878         FireOnSelect(selectStart, selectEnd);
3879         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3880         return;
3881     }
3882     if (textSelector_.IsValid()) {
3883         ResetSelection();
3884     }
3885     auto position = paragraphs_.GetIndex(textOffset);
3886     float caretHeight = 0.0f;
3887     OffsetF caretOffset = CalcCursorOffsetByPosition(GetCaretPosition(), caretHeight);
3888     SetCaretPosition(position);
3889     selectedType_ = TextSpanType::TEXT;
3890     CHECK_NULL_VOID(overlayMod_);
3891     DynamicCast<RichEditorOverlayModifier>(overlayMod_)->SetCaretOffsetAndHeight(caretOffset, caretHeight);
3892     StartTwinkling();
3893 }
3894 
3895 void RichEditorPattern::FireOnSelect(int32_t selectStart, int32_t selectEnd)
3896 {
3897     auto host = GetHost();
3898     CHECK_NULL_VOID(host);
3899     auto eventHub = host->GetEventHub<RichEditorEventHub>();
3900     CHECK_NULL_VOID(eventHub);
3901     auto textSelectInfo = GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT);
3902     if (!textSelectInfo.GetSelection().resultObjects.empty()) {
3903         eventHub->FireOnSelect(&textSelectInfo);
3904     }
3905     UpdateSelectionType(textSelectInfo);
3906 }
3907 
3908 void RichEditorPattern::HandleMouseEvent(const MouseInfo& info)
3909 {
3910     auto tmpHost = GetHost();
3911     CHECK_NULL_VOID(tmpHost);
3912     auto frameId = tmpHost->GetId();
3913     auto pipeline = PipelineContext::GetCurrentContext();
3914     CHECK_NULL_VOID(pipeline);
3915     auto scrollBar = GetScrollBar();
3916     if (scrollBar && (scrollBar->IsHover() || scrollBar->IsPressed())) {
3917         pipeline->SetMouseStyleHoldNode(frameId);
3918         pipeline->ChangeMouseStyle(frameId, MouseFormat::DEFAULT);
3919         return;
3920     }
3921 
3922     caretUpdateType_ = CaretUpdateType::NONE;
3923     if (info.GetButton() == MouseButton::LEFT_BUTTON) {
3924         HandleMouseLeftButton(info);
3925     } else if (info.GetButton() == MouseButton::RIGHT_BUTTON) {
3926         HandleMouseRightButton(info);
3927     }
3928 }
3929 
3930 void RichEditorPattern::OnHandleMoveDone(const RectF& handleRect, bool isFirstHandle)
3931 {
3932     auto host = GetHost();
3933     CHECK_NULL_VOID(host);
3934     auto eventHub = host->GetEventHub<RichEditorEventHub>();
3935     CHECK_NULL_VOID(eventHub);
3936     auto selectStart = std::min(textSelector_.baseOffset, textSelector_.destinationOffset);
3937     auto selectEnd = std::max(textSelector_.baseOffset, textSelector_.destinationOffset);
3938     auto textSelectInfo = GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT);
3939     if (!textSelectInfo.GetSelection().resultObjects.empty()) {
3940         eventHub->FireOnSelect(&textSelectInfo);
3941     }
3942     UpdateSelectionType(textSelectInfo);
3943     SetCaretPosition(selectEnd);
3944     CalculateHandleOffsetAndShowOverlay();
3945     StopAutoScroll();
3946     if (selectOverlayProxy_) {
3947         SelectHandleInfo handleInfo;
3948         if (!selectOverlayProxy_->IsSingleHandle() && textSelector_.firstHandle == textSelector_.secondHandle) {
3949             CloseSelectOverlay();
3950             StartTwinkling();
3951             return;
3952         }
3953         if (isFirstHandle) {
3954             handleInfo.paintRect = textSelector_.firstHandle;
3955             CheckHandles(handleInfo);
3956             selectOverlayProxy_->UpdateFirstSelectHandleInfo(handleInfo);
3957         } else {
3958             handleInfo.paintRect = textSelector_.secondHandle;
3959             CheckHandles(handleInfo);
3960             selectOverlayProxy_->UpdateSecondSelectHandleInfo(handleInfo);
3961         }
3962 
3963         if (IsSelectAll() && selectMenuInfo_.showCopyAll == true) {
3964             selectMenuInfo_.showCopyAll = false;
3965             selectOverlayProxy_->UpdateSelectMenuInfo(selectMenuInfo_);
3966         } else if (!IsSelectAll() && selectMenuInfo_.showCopyAll == false) {
3967             selectMenuInfo_.showCopyAll = true;
3968             selectOverlayProxy_->UpdateSelectMenuInfo(selectMenuInfo_);
3969         }
3970         auto handleReverse = selectOverlayProxy_->IsHandleReverse();
3971         selectOverlayProxy_.Reset();
3972         ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, IsSelectAll(),
3973             TextResponseType::LONG_PRESS, handleReverse);
3974         return;
3975     }
3976     ShowSelectOverlay(
3977         textSelector_.firstHandle, textSelector_.secondHandle, IsSelectAll(), TextResponseType::LONG_PRESS);
3978     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3979 }
3980 
3981 void RichEditorPattern::CopySelectionMenuParams(SelectOverlayInfo& selectInfo, TextResponseType responseType)
3982 {
3983     auto selectType = selectedType_.value_or(TextSpanType::NONE);
3984     std::shared_ptr<SelectionMenuParams> menuParams = nullptr;
3985     menuParams = GetMenuParams(selectType, responseType);
3986     if (menuParams == nullptr) {
3987         return;
3988     }
3989 
3990     // long pressing on the image needs to set the position of the pop-up menu following the long pressing position
3991     if (selectType == TextSpanType::IMAGE && !selectInfo.isUsingMouse) {
3992         selectInfo.menuInfo.menuOffset = OffsetF(selectionMenuOffset_.GetX(), selectionMenuOffset_.GetY());
3993     }
3994 
3995     CopyBindSelectionMenuParams(selectInfo, menuParams);
3996 }
3997 
3998 void RichEditorPattern::ShowSelectOverlay(const RectF& firstHandle, const RectF& secondHandle, bool isCopyAll,
3999     TextResponseType responseType, bool handleReverse)
4000 {
4001     auto pipeline = PipelineContext::GetCurrentContext();
4002     CHECK_NULL_VOID(pipeline);
4003     showSelect_ = true;
4004     auto hasDataCallback = [weak = WeakClaim(this), handleReverse, pipeline, firstHandle, secondHandle, isCopyAll,
4005                                responseType](bool hasData) mutable {
4006         auto pattern = weak.Upgrade();
4007         SelectOverlayInfo selectInfo;
4008         selectInfo.handleReverse = handleReverse;
4009         bool usingMouse = pattern->IsUsingMouse();
4010         if (!pattern->IsUsingMouse() && responseType == TextResponseType::LONG_PRESS) {
4011             selectInfo.firstHandle.paintRect = firstHandle;
4012             selectInfo.secondHandle.paintRect = secondHandle;
4013         } else {
4014             if (responseType == TextResponseType::LONG_PRESS) {
4015                 responseType = TextResponseType::RIGHT_CLICK;
4016             }
4017             selectInfo.isUsingMouse = true;
4018             selectInfo.rightClickOffset = pattern->GetSelectionMenuOffset();
4019             pattern->ResetIsMousePressed();
4020         }
4021         selectInfo.menuInfo.responseType = static_cast<int32_t>(responseType);
4022         selectInfo.menuInfo.editorType = static_cast<int32_t>(pattern->GetEditorType());
4023         selectInfo.onHandleMove = [weak](const RectF& handleRect, bool isFirst) {
4024             auto pattern = weak.Upgrade();
4025             CHECK_NULL_VOID(pattern);
4026             pattern->OnHandleMove(handleRect, isFirst);
4027         };
4028         selectInfo.onHandleMoveDone = [weak](const RectF& handleRect, bool isFirst) {
4029             auto pattern = weak.Upgrade();
4030             CHECK_NULL_VOID(pattern);
4031             pattern->OnHandleMoveDone(handleRect, isFirst);
4032         };
4033 
4034         auto host = pattern->GetHost();
4035         CHECK_NULL_VOID(host);
4036 
4037         selectInfo.menuCallback.onCopy = [weak, usingMouse]() {
4038             auto pattern = weak.Upgrade();
4039             CHECK_NULL_VOID(pattern);
4040             pattern->HandleOnCopy();
4041             pattern->CloseSelectOverlay();
4042             if (!usingMouse) {
4043                 pattern->ResetSelection();
4044             }
4045         };
4046 
4047         selectInfo.menuCallback.onCut = [weak]() {
4048             auto pattern = weak.Upgrade();
4049             CHECK_NULL_VOID(pattern);
4050             pattern->HandleOnCut();
4051         };
4052 
4053         selectInfo.menuCallback.onPaste = [weak]() {
4054             auto pattern = weak.Upgrade();
4055             CHECK_NULL_VOID(pattern);
4056             pattern->HandleOnPaste();
4057             pattern->CloseSelectOverlay();
4058         };
4059         selectInfo.menuCallback.onSelectAll = [weak, usingMouse]() {
4060             auto pattern = weak.Upgrade();
4061             CHECK_NULL_VOID(pattern);
4062             pattern->isMousePressed_ = usingMouse;
4063             pattern->HandleMenuCallbackOnSelectAll();
4064         };
4065 
4066         selectInfo.menuCallback.onCameraInput = [weak, usingMouse]() {
4067             auto pattern = weak.Upgrade();
4068             CHECK_NULL_VOID(pattern);
4069             pattern->HandleOnCameraInput();
4070         };
4071 
4072         if (pattern->GetTextDetectEnable() && !pattern->HasFocus()) {
4073             selectInfo.onClose = [weak](bool closedByGlobalEvent) {
4074                 auto pattern = weak.Upgrade();
4075                 CHECK_NULL_VOID(pattern);
4076                 if (closedByGlobalEvent) {
4077                     pattern->ResetSelection();
4078                 }
4079             };
4080         }
4081         selectInfo.callerFrameNode = host;
4082         selectInfo.isNewAvoid = true;
4083         selectInfo.selectArea = pattern->GetSelectArea();
4084         selectInfo.checkIsTouchInHostArea = [weak](const PointF& touchPoint) -> bool {
4085             auto pattern = weak.Upgrade();
4086             CHECK_NULL_RETURN(pattern, false);
4087             return pattern->IsTouchInFrameArea(touchPoint);
4088         };
4089         pattern->CopySelectionMenuParams(selectInfo, responseType);
4090         pattern->UpdateSelectMenuInfo(hasData, selectInfo, isCopyAll);
4091         pattern->CheckEditorTypeChange();
4092         pattern->UpdateSelectOverlayOrCreate(selectInfo);
4093     };
4094     CHECK_NULL_VOID(clipboard_);
4095     clipboard_->HasData(hasDataCallback);
4096 }
4097 
4098 void RichEditorPattern::UpdateSelectOverlayOrCreate(SelectOverlayInfo& selectInfo, bool animation)
4099 {
4100     bool isOriginMenuShow = true;
4101     if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
4102         isOriginMenuShow = GetOriginIsMenuShow();
4103     }
4104     TextPattern::UpdateSelectOverlayOrCreate(selectInfo, animation);
4105     CHECK_NULL_VOID(selectOverlayProxy_);
4106     selectOverlayProxy_->ShowOrHiddenMenu(!isOriginMenuShow || !isShowMenu_);
4107 }
4108 
4109 void RichEditorPattern::CheckEditorTypeChange()
4110 {
4111     CHECK_NULL_VOID(selectOverlayProxy_);
4112     CHECK_NULL_VOID(!selectOverlayProxy_->IsClosed());
4113     if (selectOverlayProxy_->GetSelectOverlayMangerInfo().menuInfo.editorType.value_or(static_cast<int32_t>(
4114             TextSpanType::NONE)) != static_cast<int32_t>(selectedType_.value_or(TextSpanType::NONE))) {
4115         CloseSelectionMenu();
4116     }
4117 }
4118 
4119 void RichEditorPattern::HandleOnCopy(bool isUsingExternalKeyboard)
4120 {
4121     CHECK_NULL_VOID(clipboard_);
4122     if (copyOption_ == CopyOptions::None) {
4123         return;
4124     }
4125     auto selectStart = textSelector_.GetTextStart();
4126     auto selectEnd = textSelector_.GetTextEnd();
4127     auto textSelectInfo = GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT);
4128     auto copyResultObjects = textSelectInfo.GetSelection().resultObjects;
4129     caretUpdateType_ = CaretUpdateType::NONE;
4130     if (copyResultObjects.empty()) {
4131         return;
4132     }
4133     RefPtr<PasteDataMix> pasteData = clipboard_->CreatePasteDataMix();
4134     auto resultProcessor = [weak = WeakClaim(this), pasteData, selectStart, selectEnd, clipboard = clipboard_](
4135                                const ResultObject& result) {
4136         auto pattern = weak.Upgrade();
4137         CHECK_NULL_VOID(pattern);
4138         if (result.type == SelectSpanType::TYPESPAN) {
4139             auto data = pattern->GetSelectedSpanText(StringUtils::ToWstring(result.valueString),
4140                 result.offsetInSpan[RichEditorSpanRange::RANGESTART],
4141                 result.offsetInSpan[RichEditorSpanRange::RANGEEND]);
4142             clipboard->AddTextRecord(pasteData, data);
4143             return;
4144         }
4145         if (result.type == SelectSpanType::TYPEIMAGE) {
4146             if (result.valuePixelMap) {
4147                 clipboard->AddPixelMapRecord(pasteData, result.valuePixelMap);
4148             } else {
4149                 clipboard->AddImageRecord(pasteData, result.valueString);
4150             }
4151         }
4152     };
4153     for (auto resultObj = copyResultObjects.rbegin(); resultObj != copyResultObjects.rend(); ++resultObj) {
4154         resultProcessor(*resultObj);
4155     }
4156     clipboard_->SetData(pasteData, copyOption_);
4157     if (!textDetectEnable_) {
4158         StartTwinkling();
4159     }
4160 }
4161 
4162 void RichEditorPattern::ResetAfterPaste()
4163 {
4164     OperationRecord record;
4165     record.beforeCaretPosition = caretPosition_ + moveLength_;
4166     auto pasteStr = GetPasteStr();
4167     record.addText = pasteStr;
4168     SetCaretSpanIndex(-1);
4169     StartTwinkling();
4170     if (textSelector_.IsValid()) {
4171         SetCaretPosition(textSelector_.GetTextStart());
4172         record.beforeCaretPosition = caretPosition_;
4173         auto length = textSelector_.GetTextEnd() - textSelector_.GetTextStart();
4174         textSelector_.Update(-1, -1);
4175         record.deleteText = StringUtils::ToString(DeleteForwardOperation(length));
4176         ResetSelection();
4177     }
4178     InsertValueByPaste(pasteStr);
4179     ClearPasteStr();
4180     record.afterCaretPosition = caretPosition_ + moveLength_;
4181     ClearRedoOperationRecords();
4182     AddOperationRecord(record);
4183 }
4184 
4185 void RichEditorPattern::HandleOnPaste()
4186 {
4187     auto host = GetHost();
4188     CHECK_NULL_VOID(host);
4189     auto eventHub = host->GetEventHub<RichEditorEventHub>();
4190     CHECK_NULL_VOID(eventHub);
4191     TextCommonEvent event;
4192     eventHub->FireOnPaste(event);
4193     if (event.IsPreventDefault()) {
4194         CloseSelectOverlay();
4195         ResetSelection();
4196         StartTwinkling();
4197         return;
4198     }
4199     CHECK_NULL_VOID(clipboard_);
4200     auto pasteCallback = [weak = WeakClaim(this)](const std::string& data) {
4201         auto richEditor = weak.Upgrade();
4202         CHECK_NULL_VOID(richEditor);
4203         if (data.empty()) {
4204             richEditor->ResetSelection();
4205             richEditor->StartTwinkling();
4206             return;
4207         }
4208         richEditor->AddPasteStr(data);
4209         richEditor->ResetAfterPaste();
4210     };
4211     clipboard_->GetData(pasteCallback);
4212 }
4213 
4214 void RichEditorPattern::InsertValueByPaste(const std::string& insertValue)
4215 {
4216     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "insertValue=[%{public}s]", StringUtils::RestoreEscape(insertValue).c_str());
4217     RefPtr<UINode> child;
4218     TextInsertValueInfo info;
4219     CalcInsertValueObj(info);
4220     TextSpanOptions options;
4221     options.value = insertValue;
4222     if (typingStyle_.has_value() && typingTextStyle_.has_value()) {
4223         options.style = typingTextStyle_.value();
4224     }
4225     auto newSpanOffset = caretPosition_ + moveLength_;
4226     isTextChange_ = true;
4227     moveDirection_ = MoveDirection::FORWARD;
4228     moveLength_ += static_cast<int32_t>(StringUtils::ToWstring(insertValue).length());
4229     if (caretSpanIndex_ == -1) {
4230         child = GetChildByIndex(info.GetSpanIndex());
4231         if (child && child->GetTag() == V2::SPAN_ETS_TAG) {
4232             auto spanNode = DynamicCast<SpanNode>(child);
4233             CHECK_NULL_VOID(spanNode);
4234             if (typingStyle_.has_value() && !HasSameTypingStyle(spanNode)) {
4235                 options.offset = newSpanOffset;
4236                 caretSpanIndex_ = AddTextSpanOperation(options, true);
4237             } else {
4238                 InsertValueToSpanNode(spanNode, insertValue, info);
4239             }
4240             return;
4241         } else if (!child) {
4242             auto spanNodeBefore = DynamicCast<SpanNode>(GetChildByIndex(info.GetSpanIndex() - 1));
4243             if (spanNodeBefore == nullptr) {
4244                 caretSpanIndex_ = AddTextSpanOperation(options, true);
4245             } else if ((typingStyle_.has_value() && !HasSameTypingStyle(spanNodeBefore)) ||
4246                 spanNodeBefore->GetTag() != V2::SPAN_ETS_TAG) {
4247                 auto spanNode = DynamicCast<SpanNode>(child);
4248                 CreateTextSpanNode(spanNode, info, insertValue, false);
4249                 caretSpanIndex_ = info.GetSpanIndex();
4250             } else {
4251                 InsertValueToBeforeSpan(spanNodeBefore, insertValue);
4252                 caretSpanIndex_ = info.GetSpanIndex() - 1;
4253             }
4254             return;
4255         }
4256     } else {
4257         child = GetChildByIndex(caretSpanIndex_);
4258         if (child && child->GetTag() == V2::SPAN_ETS_TAG) {
4259             auto spanNode = DynamicCast<SpanNode>(child);
4260             CHECK_NULL_VOID(spanNode);
4261             if (typingStyle_.has_value() && !HasSameTypingStyle(spanNode)) {
4262                 options.offset = newSpanOffset;
4263                 caretSpanIndex_ = AddTextSpanOperation(options, true);
4264             } else {
4265                 InsertValueToBeforeSpan(spanNode, insertValue);
4266             }
4267             return;
4268         }
4269     }
4270     if (child && child->GetTag() == V2::IMAGE_ETS_TAG) {
4271         auto spanNodeBefore = DynamicCast<SpanNode>(GetChildByIndex(info.GetSpanIndex() - 1));
4272         if (spanNodeBefore != nullptr && caretSpanIndex_ == -1) {
4273             if (typingStyle_.has_value() && !HasSameTypingStyle(spanNodeBefore)) {
4274                 options.offset = newSpanOffset;
4275                 caretSpanIndex_ = AddTextSpanOperation(options, true);
4276             } else {
4277                 InsertValueToBeforeSpan(spanNodeBefore, insertValue);
4278                 caretSpanIndex_ = info.GetSpanIndex() - 1;
4279             }
4280         } else {
4281             auto imageNode = DynamicCast<FrameNode>(child);
4282             if (imageNode && caretSpanIndex_ == -1) {
4283                 caretSpanIndex_ = AddTextSpanOperation(options, true, info.GetSpanIndex(), false, false);
4284             } else {
4285                 caretSpanIndex_ = AddTextSpanOperation(options, true, caretSpanIndex_ + 1);
4286             }
4287         }
4288     } else {
4289         caretSpanIndex_ = AddTextSpanOperation(options, true);
4290     }
4291 }
4292 
4293 void RichEditorPattern::SetCaretSpanIndex(int32_t index)
4294 {
4295     caretSpanIndex_ = index;
4296 }
4297 
4298 void RichEditorPattern::HandleOnCut()
4299 {
4300     if (copyOption_ == CopyOptions::None) {
4301         return;
4302     }
4303     if (!textSelector_.IsValid()) {
4304         return;
4305     }
4306     caretUpdateType_ = CaretUpdateType::NONE;
4307     HandleOnCopy();
4308     DeleteBackward();
4309 }
4310 
4311 void RichEditorPattern::OnHandleMove(const RectF& handleRect, bool isFirstHandle)
4312 {
4313     CHECK_NULL_VOID(HasFocus());
4314     TextPattern::OnHandleMove(handleRect, isFirstHandle);
4315     if (!isFirstHandle) {
4316         SetCaretPosition(textSelector_.destinationOffset);
4317     }
4318     auto localOffset = handleRect.GetOffset() - parentGlobalOffset_;
4319     AutoScrollParam param = { .autoScrollEvent = AutoScrollEvent::HANDLE,
4320         .handleRect = handleRect,
4321         .isFirstHandle = isFirstHandle,
4322         .showScrollbar = true };
4323     AutoScrollByEdgeDetection(param, localOffset, EdgeDetectionStrategy::OUT_BOUNDARY);
4324 }
4325 
4326 std::function<void(Offset)> RichEditorPattern::GetThumbnailCallback()
4327 {
4328     return [wk = WeakClaim(this)](const Offset& point) {
4329         auto pattern = wk.Upgrade();
4330         CHECK_NULL_VOID(pattern);
4331         if (pattern->BetweenSelectedPosition(point)) {
4332             auto host = pattern->GetHost();
4333             auto children = host->GetChildren();
4334             std::list<RefPtr<FrameNode>> imageChildren;
4335             for (const auto& child : children) {
4336                 auto node = DynamicCast<FrameNode>(child);
4337                 if (!node) {
4338                     continue;
4339                 }
4340                 auto image = node->GetPattern<ImagePattern>();
4341                 if (image) {
4342                     imageChildren.emplace_back(node);
4343                 }
4344             }
4345             pattern->dragNode_ = RichEditorDragPattern::CreateDragNode(host, imageChildren);
4346             FrameNode::ProcessOffscreenNode(pattern->dragNode_);
4347         }
4348     };
4349 }
4350 
4351 void RichEditorPattern::CreateHandles()
4352 {
4353     if (IsDragging()) {
4354         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "do not show handles when dragging");
4355         return;
4356     }
4357     auto host = GetHost();
4358     CHECK_NULL_VOID(host);
4359     float startSelectHeight = 0.0f;
4360     float endSelectHeight = 0.0f;
4361     textSelector_.ReverseTextSelector();
4362     auto firstHandlePosition = CalcCursorOffsetByPosition(textSelector_.GetStart(), startSelectHeight, true, false);
4363     OffsetF firstHandleOffset(firstHandlePosition.GetX() + parentGlobalOffset_.GetX(),
4364         firstHandlePosition.GetY() + parentGlobalOffset_.GetY());
4365     textSelector_.firstHandleOffset_ = firstHandleOffset;
4366     auto secondHandlePosition = CalcCursorOffsetByPosition(textSelector_.GetEnd(), endSelectHeight, false, false);
4367     OffsetF secondHandleOffset(secondHandlePosition.GetX() + parentGlobalOffset_.GetX(),
4368         secondHandlePosition.GetY() + parentGlobalOffset_.GetY());
4369     textSelector_.secondHandleOffset_ = secondHandleOffset;
4370     SizeF firstHandlePaintSize = { SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), startSelectHeight };
4371     SizeF secondHandlePaintSize = { SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), endSelectHeight };
4372     RectF firstHandle = RectF(firstHandleOffset, firstHandlePaintSize);
4373     textSelector_.firstHandle = firstHandle;
4374     RectF secondHandle = RectF(secondHandleOffset, secondHandlePaintSize);
4375     textSelector_.secondHandle = secondHandle;
4376     ShowSelectOverlay(firstHandle, secondHandle, IsSelectAll(), TextResponseType::LONG_PRESS);
4377 }
4378 
4379 void RichEditorPattern::OnAreaChangedInner()
4380 {
4381     float selectLineHeight = 0.0f;
4382     auto host = GetHost();
4383     CHECK_NULL_VOID(host);
4384     auto context = PipelineContext::GetCurrentContext();
4385     CHECK_NULL_VOID(context);
4386     auto parentGlobalOffset = host->GetPaintRectOffset() - context->GetRootRect().GetOffset();
4387     if (parentGlobalOffset != parentGlobalOffset_) {
4388         parentGlobalOffset_ = parentGlobalOffset;
4389         UpdateTextFieldManager(Offset(parentGlobalOffset_.GetX(), parentGlobalOffset_.GetY()), frameRect_.Height());
4390         CHECK_NULL_VOID(SelectOverlayIsOn());
4391         textSelector_.selectionBaseOffset.SetX(
4392             CalcCursorOffsetByPosition(textSelector_.GetStart(), selectLineHeight).GetX());
4393         textSelector_.selectionDestinationOffset.SetX(
4394             CalcCursorOffsetByPosition(textSelector_.GetEnd(), selectLineHeight).GetX());
4395         CreateHandles();
4396         selectOverlayProxy_->ShowOrHiddenMenu(true);
4397     }
4398 }
4399 
4400 void RichEditorPattern::CloseSelectionMenu()
4401 {
4402     CloseSelectOverlay();
4403 }
4404 
4405 void RichEditorPattern::CloseSelectOverlay()
4406 {
4407     TextPattern::CloseSelectOverlay(true);
4408 }
4409 
4410 void RichEditorPattern::CalculateHandleOffsetAndShowOverlay(bool isUsingMouse)
4411 {
4412     auto host = GetHost();
4413     CHECK_NULL_VOID(host);
4414     auto pipeline = PipelineContext::GetCurrentContext();
4415     CHECK_NULL_VOID(pipeline);
4416     auto rootOffset = pipeline->GetRootRect().GetOffset();
4417     auto offset = host->GetPaintRectOffset();
4418     auto textPaintOffset = offset - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
4419     float startSelectHeight = 0.0f;
4420     float endSelectHeight = 0.0f;
4421     textSelector_.ReverseTextSelector();
4422     int32_t baseOffset = std::min(textSelector_.baseOffset, GetTextContentLength());
4423     int32_t destinationOffset = std::min(textSelector_.destinationOffset, GetTextContentLength());
4424     auto startOffset = CalcCursorOffsetByPosition(baseOffset, startSelectHeight, true, false);
4425     auto endOffset =
4426         CalcCursorOffsetByPosition(destinationOffset, endSelectHeight, false, false);
4427     if (baseOffset == destinationOffset && NearEqual(startOffset.GetX(), endOffset.GetX())) {
4428         endOffset = CalcCursorOffsetByPosition(destinationOffset, endSelectHeight, true, false);
4429     }
4430     SizeF firstHandlePaintSize = { SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), startSelectHeight };
4431     SizeF secondHandlePaintSize = { SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), endSelectHeight };
4432     OffsetF firstHandleOffset = startOffset + textPaintOffset - rootOffset;
4433     OffsetF secondHandleOffset = endOffset + textPaintOffset - rootOffset;
4434     AdjustHandleRect(firstHandleOffset, secondHandleOffset, firstHandlePaintSize, secondHandlePaintSize);
4435     textSelector_.selectionBaseOffset = firstHandleOffset;
4436     textSelector_.selectionDestinationOffset = secondHandleOffset;
4437     RectF firstHandle;
4438     firstHandle.SetOffset(firstHandleOffset);
4439     firstHandle.SetSize(firstHandlePaintSize);
4440     textSelector_.firstHandle = firstHandle;
4441     RectF secondHandle;
4442     secondHandle.SetOffset(secondHandleOffset);
4443     secondHandle.SetSize(secondHandlePaintSize);
4444     textSelector_.secondHandle = secondHandle;
4445 }
4446 
4447 void RichEditorPattern::AdjustHandleRect(
4448     OffsetF& firstHandleOffset, OffsetF& secondHandleOffset, SizeF& firstHandlePaintSize, SizeF& secondHandlePaintSize)
4449 {
4450     if (GetTextContentLength() == 0) {
4451         float caretHeight = DynamicCast<RichEditorOverlayModifier>(overlayMod_)->GetCaretHeight();
4452         secondHandlePaintSize = { SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), caretHeight / 2 };
4453         secondHandleOffset = OffsetF(secondHandleOffset.GetX(), secondHandleOffset.GetY() + caretHeight / 2);
4454         // only show the second handle.
4455         firstHandlePaintSize = SizeF{};
4456         firstHandleOffset = OffsetF{};
4457     }
4458 }
4459 
4460 void RichEditorPattern::ResetSelection()
4461 {
4462     bool selectNothing = textSelector_.SelectNothing();
4463     textSelector_.Update(-1, -1);
4464     if (!selectNothing) {
4465         textSelector_.Update(-1, -1);
4466         auto host = GetHost();
4467         CHECK_NULL_VOID(host);
4468         auto eventHub = host->GetEventHub<RichEditorEventHub>();
4469         CHECK_NULL_VOID(eventHub);
4470         auto textSelectInfo = GetSpansInfo(-1, -1, GetSpansMethod::ONSELECT);
4471         eventHub->FireOnSelect(&textSelectInfo);
4472         UpdateSelectionType(textSelectInfo);
4473         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
4474     }
4475 }
4476 
4477 bool RichEditorPattern::BetweenSelectedPosition(const Offset& globalOffset)
4478 {
4479     auto host = GetHost();
4480     CHECK_NULL_RETURN(host, false);
4481     auto offset = host->GetPaintRectOffset();
4482     auto localOffset = globalOffset - Offset(offset.GetX(), offset.GetY());
4483     auto eventHub = host->GetEventHub<EventHub>();
4484     if (GreatNotEqual(textSelector_.GetTextEnd(), textSelector_.GetTextStart())) {
4485         // Determine if the pan location is in the selected area
4486         auto selectedRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
4487         auto panOffset = OffsetF(localOffset.GetX(), localOffset.GetY()) - GetTextRect().GetOffset() +
4488                          OffsetF(0.0, std::min(baselineOffset_, 0.0f));
4489         for (const auto& selectedRect : selectedRects) {
4490             if (selectedRect.IsInRegion(PointF(panOffset.GetX(), panOffset.GetY()))) {
4491                 return true;
4492             }
4493         }
4494     }
4495     return false;
4496 }
4497 
4498 void RichEditorPattern::HandleSurfaceChanged(int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight)
4499 {
4500     if (newWidth != prevWidth || newHeight != prevHeight) {
4501         TextPattern::HandleSurfaceChanged(newWidth, newHeight, prevWidth, prevHeight);
4502         UpdateOriginIsMenuShow(false);
4503     }
4504     UpdateCaretInfoToController();
4505 }
4506 
4507 void RichEditorPattern::HandleSurfacePositionChanged(int32_t posX, int32_t posY)
4508 {
4509     UpdateCaretInfoToController();
4510 }
4511 
4512 void RichEditorPattern::DumpInfo()
4513 {
4514     if (customKeyboardBuilder_) {
4515         DumpLog::GetInstance().AddDesc(std::string("CustomKeyboard: true")
4516                                            .append(", Attached: ")
4517                                            .append(std::to_string(isCustomKeyboardAttached_)));
4518     }
4519     auto context = GetHost()->GetContext();
4520     CHECK_NULL_VOID(context);
4521     auto richEditorTheme = context->GetTheme<RichEditorTheme>();
4522     CHECK_NULL_VOID(richEditorTheme);
4523     DumpLog::GetInstance().AddDesc(std::string("caret offset: ").append(GetCaretRect().GetOffset().ToString()));
4524     DumpLog::GetInstance().AddDesc(
4525         std::string("caret height: ")
4526             .append(std::to_string(NearZero(GetCaretRect().Height())
4527                                        ? richEditorTheme->GetDefaultCaretHeight().ConvertToPx()
4528                                        : GetCaretRect().Height())));
4529 }
4530 
4531 bool RichEditorPattern::HasFocus() const
4532 {
4533     auto focusHub = GetHost()->GetOrCreateFocusHub();
4534     CHECK_NULL_RETURN(focusHub, false);
4535     return focusHub->IsCurrentFocus();
4536 }
4537 
4538 void RichEditorPattern::UpdateTextFieldManager(const Offset& offset, float height)
4539 {
4540     if (!HasFocus()) {
4541         return;
4542     }
4543     auto context = GetHost()->GetContext();
4544     CHECK_NULL_VOID(context);
4545     auto richEditorTheme = context->GetTheme<RichEditorTheme>();
4546     CHECK_NULL_VOID(richEditorTheme);
4547     auto textFieldManager = DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
4548     CHECK_NULL_VOID(textFieldManager);
4549     textFieldManager->SetClickPosition(
4550         { offset.GetX() + GetCaretRect().GetX(), offset.GetY() + GetCaretRect().GetY() });
4551     textFieldManager->SetHeight(NearZero(GetCaretRect().Height())
4552                                     ? richEditorTheme->GetDefaultCaretHeight().ConvertToPx()
4553                                     : GetCaretRect().Height());
4554     textFieldManager->SetOnFocusTextField(WeakClaim(this));
4555 
4556     if (!isTextChange_) {
4557         return;
4558     }
4559     auto taskExecutor = context->GetTaskExecutor();
4560     CHECK_NULL_VOID(taskExecutor);
4561     taskExecutor->PostTask(
4562         [weak = WeakClaim(this)] {
4563             auto pattern = weak.Upgrade();
4564             CHECK_NULL_VOID(pattern);
4565             pattern->ScrollToSafeArea();
4566         },
4567         TaskExecutor::TaskType::UI);
4568 }
4569 
4570 bool RichEditorPattern::IsDisabled() const
4571 {
4572     auto eventHub = GetHost()->GetEventHub<RichEditorEventHub>();
4573     CHECK_NULL_RETURN(eventHub, true);
4574     return !eventHub->IsEnabled();
4575 }
4576 
4577 void RichEditorPattern::InitSelection(const Offset& pos)
4578 {
4579     int32_t currentPosition = paragraphs_.GetIndex(pos);
4580     currentPosition = std::min(currentPosition, GetTextContentLength());
4581     int32_t nextPosition = currentPosition + GetGraphemeClusterLength(GetWideText(), currentPosition);
4582     nextPosition = std::min(nextPosition, GetTextContentLength());
4583     AdjustPlaceholderSelection(currentPosition, nextPosition, pos);
4584     adjusted_ = AdjustWordSelection(currentPosition, nextPosition);
4585     textSelector_.Update(currentPosition, nextPosition);
4586     auto selectedRects = paragraphs_.GetRects(currentPosition, nextPosition);
4587     if (selectedRects.empty() || (selectedRects.size() == 1 && NearZero((selectedRects[0].Width())))) {
4588         textSelector_.Update(currentPosition, currentPosition);
4589     }
4590     if (adjusted_) {
4591         return;
4592     }
4593 
4594     bool selectedSingle =
4595         selectedRects.size() == 1 && (pos.GetX() < selectedRects[0].Left() || pos.GetY() < selectedRects[0].Top());
4596     bool selectedLast = selectedRects.empty() && currentPosition == GetTextContentLength();
4597     if (selectedSingle || selectedLast) {
4598         if (selectedLast) {
4599             nextPosition = currentPosition + 1;
4600         }
4601         auto selectedNextRects = paragraphs_.GetRects(currentPosition - 1, nextPosition - 1);
4602         if (selectedNextRects.size() == 1) {
4603             bool isInRange = pos.GetX() >= selectedNextRects[0].Left() && pos.GetX() <= selectedNextRects[0].Right() &&
4604                              pos.GetY() >= selectedNextRects[0].Top() && pos.GetY() <= selectedNextRects[0].Bottom();
4605             if (isInRange || (!selectedLast && selectedRects[0].Top() != selectedNextRects[0].Top())) {
4606                 textSelector_.Update(currentPosition - 1, nextPosition - 1);
4607             }
4608         }
4609     }
4610 }
4611 
4612 void RichEditorPattern::SetSelection(int32_t start, int32_t end)
4613 {
4614     CHECK_NULL_VOID(HasFocus());
4615     bool changeSelected = false;
4616     if (start > end) {
4617         changeSelected = textSelector_.IsValid();
4618         ResetSelection();
4619     } else {
4620         if (start == -1 && end == -1) {
4621             start = 0;
4622             end = GetTextContentLength();
4623         } else {
4624             start = std::min(std::max(0, start), GetTextContentLength());
4625             end = std::min(std::max(0, end), GetTextContentLength());
4626         }
4627         changeSelected = start != textSelector_.GetTextStart() || end != textSelector_.GetTextEnd();
4628         textSelector_.Update(start, end);
4629     }
4630 
4631     auto oldSelectedType = selectedType_;
4632     if (textSelector_.IsValid() && !textSelector_.StartEqualToDest()) {
4633         StopTwinkling();
4634         if (changeSelected) {
4635             FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
4636         }
4637     }
4638     if (SelectOverlayIsOn()) {
4639         isMousePressed_ = selectOverlayProxy_->GetSelectOverlayMangerInfo().isUsingMouse;
4640         auto selectedTypeChange = (oldSelectedType.has_value() && selectedType_.has_value() &&
4641                                       oldSelectedType.value() != selectedType_.value()) ||
4642                                   (!oldSelectedType.has_value() && selectedType_.has_value());
4643         if (!isMousePressed_ || selectedTypeChange) {
4644             CalculateHandleOffsetAndShowOverlay();
4645             CloseSelectOverlay();
4646             auto responseType = static_cast<TextResponseType>(
4647                 selectOverlayProxy_->GetSelectOverlayMangerInfo().menuInfo.responseType.value_or(0));
4648             ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, IsSelectAll(), responseType);
4649         }
4650     }
4651     SetCaretPosition(textSelector_.GetTextEnd());
4652     MoveCaretToContentRect();
4653     auto host = GetHost();
4654     CHECK_NULL_VOID(host);
4655     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
4656 }
4657 
4658 void RichEditorPattern::BindSelectionMenu(TextResponseType type, TextSpanType richEditorType,
4659     std::function<void()>& menuBuilder, std::function<void(int32_t, int32_t)>& onAppear,
4660     std::function<void()>& onDisappear)
4661 {
4662     TextPattern::BindSelectionMenu(richEditorType, type, menuBuilder, onAppear, onDisappear);
4663 }
4664 
4665 RefPtr<NodePaintMethod> RichEditorPattern::CreateNodePaintMethod()
4666 {
4667     if (!contentMod_) {
4668         contentMod_ = MakeRefPtr<RichEditorContentModifier>(textStyle_, &paragraphs_, WeakClaim(this));
4669     }
4670     if (!overlayMod_) {
4671         auto scrollBar = GetScrollBar();
4672         if (scrollBar) {
4673             auto scrollBarModifier = AceType::MakeRefPtr<ScrollBarOverlayModifier>();
4674             scrollBarModifier->SetRect(scrollBar->GetActiveRect());
4675             scrollBarModifier->SetPositionMode(scrollBar->GetPositionMode());
4676             SetScrollBarOverlayModifier(scrollBarModifier);
4677         }
4678         SetEdgeEffect(EdgeEffect::FADE, GetAlwaysEnabled());
4679         SetEdgeEffect();
4680         overlayMod_ = AceType::MakeRefPtr<RichEditorOverlayModifier>(
4681             WeakClaim(this), GetScrollBarOverlayModifier(), GetScrollEdgeEffect());
4682     }
4683 
4684     if (GetIsCustomFont()) {
4685         contentMod_->SetIsCustomFont(true);
4686     }
4687     return MakeRefPtr<RichEditorPaintMethod>(WeakClaim(this), &paragraphs_, baselineOffset_, contentMod_, overlayMod_);
4688 }
4689 
4690 int32_t RichEditorPattern::GetHandleIndex(const Offset& offset) const
4691 {
4692     return paragraphs_.GetIndex(Offset(offset.GetX() + contentRect_.GetX() - richTextRect_.GetX(),
4693         offset.GetY() + contentRect_.GetY() - richTextRect_.GetY()));
4694 }
4695 
4696 std::vector<RectF> RichEditorPattern::GetTextBoxes()
4697 {
4698     auto selectedRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
4699     std::vector<RectF> res;
4700     res.reserve(selectedRects.size());
4701     for (auto&& rect : selectedRects) {
4702         res.emplace_back(rect);
4703     }
4704     if (!res.empty() && paragraphs_.IsSelectLineHeadAndUseLeadingMargin(textSelector_.GetTextStart())) {
4705         // To make drag screenshot include LeadingMarginPlaceholder.
4706         res.front().SetLeft(0.0f);
4707     }
4708     return res;
4709 }
4710 
4711 float RichEditorPattern::GetLineHeight() const
4712 {
4713     auto selectedRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
4714     CHECK_NULL_RETURN(selectedRects.size(), 0.0f);
4715     return selectedRects.front().Height();
4716 }
4717 
4718 void RichEditorPattern::UpdateSelectMenuInfo(bool hasData, SelectOverlayInfo& selectInfo, bool isCopyAll)
4719 {
4720     auto hasValue = (static_cast<int32_t>(GetWideText().length()) + placeholderCount_) > 0;
4721     bool isShowItem = copyOption_ != CopyOptions::None;
4722     selectInfo.menuInfo.showCopy = isShowItem && hasValue && textSelector_.IsValid() &&
4723                                     !textSelector_.StartEqualToDest();
4724     selectInfo.menuInfo.showCut = isShowItem && hasValue && textSelector_.IsValid() &&
4725                                     !textSelector_.StartEqualToDest();
4726     selectInfo.menuInfo.showCopyAll = !isCopyAll && hasValue;
4727     selectInfo.menuInfo.showPaste = hasData;
4728     bool isSupportCameraInput = false;
4729 #if defined(ENABLE_STANDARD_INPUT)
4730     auto inputMethod = MiscServices::InputMethodController::GetInstance();
4731     isSupportCameraInput =
4732         inputMethod && inputMethod->IsInputTypeSupported(MiscServices::InputType::CAMERA_INPUT);
4733 #endif
4734     selectInfo.menuInfo.showCameraInput = !IsSelected() && isSupportCameraInput && !customKeyboardBuilder_;
4735     selectInfo.menuInfo.menuIsShow = isShowMenu_ && (hasValue || hasData || selectInfo.menuInfo.showCameraInput);
4736     selectMenuInfo_ = selectInfo.menuInfo;
4737 }
4738 
4739 RectF RichEditorPattern::GetCaretRect() const
4740 {
4741     RectF rect;
4742     CHECK_NULL_RETURN(overlayMod_, rect);
4743     auto richEditorOverlay = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
4744     CHECK_NULL_RETURN(richEditorOverlay, rect);
4745     rect.SetOffset(richEditorOverlay->GetCaretOffset());
4746     rect.SetHeight(richEditorOverlay->GetCaretHeight());
4747     return rect;
4748 }
4749 
4750 void RichEditorPattern::ScrollToSafeArea() const
4751 {
4752     auto pipeline = PipelineContext::GetCurrentContext();
4753     CHECK_NULL_VOID(pipeline);
4754     auto textFieldManager = DynamicCast<TextFieldManagerNG>(pipeline->GetTextFieldManager());
4755     CHECK_NULL_VOID(textFieldManager);
4756     textFieldManager->ScrollTextFieldToSafeArea();
4757 }
4758 
4759 void RichEditorPattern::InitScrollablePattern()
4760 {
4761     if (GetScrollableEvent()) {
4762         return;
4763     }
4764     SetAxis(Axis::VERTICAL);
4765     AddScrollEvent();
4766     SetScrollBar(DisplayMode::AUTO);
4767     auto scrollBar = GetScrollBar();
4768     if (scrollBar) {
4769         auto pipeline = PipelineContext::GetCurrentContext();
4770         CHECK_NULL_VOID(pipeline);
4771         auto richEditorTheme = pipeline->GetTheme<RichEditorTheme>();
4772         CHECK_NULL_VOID(richEditorTheme);
4773         scrollBar->SetMinHeight(richEditorTheme->GetScrollbarMinHeight());
4774     }
4775     if (overlayMod_) {
4776         UpdateScrollBarOffset();
4777     }
4778     auto layoutProperty = GetLayoutProperty<RichEditorLayoutProperty>();
4779     CHECK_NULL_VOID(layoutProperty);
4780     auto& paddingProperty = layoutProperty->GetPaddingProperty();
4781     if (paddingProperty) {
4782         auto offsetY = paddingProperty->top.has_value() ? paddingProperty->top->GetDimension().ConvertToPx() : 0.0f;
4783         auto offsetX = paddingProperty->left.has_value() ? paddingProperty->left->GetDimension().ConvertToPx() : 0.0f;
4784         richTextRect_.SetOffset(OffsetF(offsetX, offsetY));
4785     }
4786 }
4787 
4788 void RichEditorPattern::ProcessInnerPadding()
4789 {
4790     auto context = PipelineBase::GetCurrentContext();
4791     CHECK_NULL_VOID(context);
4792     auto theme = context->GetTheme<RichEditorTheme>();
4793     CHECK_NULL_VOID(theme);
4794     auto host = GetHost();
4795     CHECK_NULL_VOID(host);
4796     auto layoutProperty = host->GetLayoutProperty<RichEditorLayoutProperty>();
4797     CHECK_NULL_VOID(layoutProperty);
4798     auto themePadding = theme->GetPadding();
4799     auto& paddingProp = layoutProperty->GetPaddingProperty();
4800     auto left = !paddingProp ? CalcLength(themePadding.Left()).GetDimension()
4801                              : paddingProp->left.value_or(CalcLength(themePadding.Left())).GetDimension();
4802     auto top = !paddingProp ? CalcLength(themePadding.Top()).GetDimension()
4803                             : paddingProp->top.value_or(CalcLength(themePadding.Top())).GetDimension();
4804     auto bottom = !paddingProp ? CalcLength(themePadding.Bottom()).GetDimension()
4805                                : paddingProp->bottom.value_or(CalcLength(themePadding.Bottom())).GetDimension();
4806     auto right = !paddingProp ? CalcLength(themePadding.Right()).GetDimension()
4807                               : paddingProp->right.value_or(CalcLength(themePadding.Right())).GetDimension();
4808     PaddingProperty paddings;
4809     paddings.top = NG::CalcLength(top);
4810     paddings.bottom = NG::CalcLength(bottom);
4811     paddings.left = NG::CalcLength(left);
4812     paddings.right = NG::CalcLength(right);
4813     layoutProperty->UpdatePadding(paddings);
4814 }
4815 
4816 void RichEditorPattern::UpdateScrollStateAfterLayout(bool shouldDisappear)
4817 {
4818     bool hasTextOffsetChanged = false;
4819     if (GreatNotEqual(richTextRect_.GetY(), contentRect_.GetY())) {
4820         auto offset = richTextRect_.GetOffset();
4821         offset.AddY(contentRect_.GetY() - richTextRect_.GetY());
4822         richTextRect_.SetOffset(offset);
4823         hasTextOffsetChanged = true;
4824     }
4825     if (GreatNotEqual(richTextRect_.Height(), contentRect_.Height()) &&
4826         LessNotEqual(richTextRect_.Bottom(), contentRect_.Bottom())) {
4827         auto offset = richTextRect_.GetOffset();
4828         offset.AddY(contentRect_.Bottom() - richTextRect_.Bottom());
4829         richTextRect_.SetOffset(offset);
4830         hasTextOffsetChanged = true;
4831     }
4832     if (LessOrEqual(richTextRect_.Height(), contentRect_.Height()) &&
4833         LessNotEqual(richTextRect_.GetY(), contentRect_.GetY())) {
4834         richTextRect_.SetOffset(contentRect_.GetOffset());
4835         hasTextOffsetChanged = true;
4836     }
4837     if (hasTextOffsetChanged) {
4838         UpdateChildrenOffset();
4839     }
4840     StopScrollable();
4841     CheckScrollable();
4842     if (overlayMod_) {
4843         UpdateScrollBarOffset();
4844     }
4845     if (!GetScrollBar()) {
4846         return;
4847     }
4848     if (isFirstCallOnReady_) {
4849         isFirstCallOnReady_ = false;
4850         GetScrollBar()->ScheduleDisappearDelayTask();
4851         return;
4852     }
4853     if (shouldDisappear) {
4854         GetScrollBar()->ScheduleDisappearDelayTask();
4855     }
4856 }
4857 
4858 bool RichEditorPattern::OnScrollCallback(float offset, int32_t source)
4859 {
4860     if (source == SCROLL_FROM_START) {
4861         auto scrollBar = GetScrollBar();
4862         if (scrollBar) {
4863             scrollBar->PlayScrollBarAppearAnimation();
4864         }
4865         if (SelectOverlayIsOn()) {
4866             selectOverlayProxy_->ShowOrHiddenMenu(true);
4867         }
4868         return true;
4869     }
4870     if (IsReachedBoundary(offset)) {
4871         return false;
4872     }
4873     auto newOffset = MoveTextRect(offset);
4874     MoveFirstHandle(newOffset);
4875     MoveSecondHandle(newOffset);
4876     return true;
4877 }
4878 
4879 float RichEditorPattern::MoveTextRect(float offset)
4880 {
4881     if (GreatNotEqual(richTextRect_.Height(), contentRect_.Height())) {
4882         if (GreatNotEqual(richTextRect_.GetY() + offset, contentRect_.GetY())) {
4883             offset = contentRect_.GetY() - richTextRect_.GetY();
4884         } else if (LessNotEqual(richTextRect_.Bottom() + offset, contentRect_.Bottom())) {
4885             offset = contentRect_.Bottom() - richTextRect_.Bottom();
4886         }
4887     } else if (!NearEqual(richTextRect_.GetY(), contentRect_.GetY())) {
4888         offset = contentRect_.GetY() - richTextRect_.GetY();
4889     } else {
4890         return 0.0f;
4891     }
4892     if (NearEqual(offset, 0.0f)) {
4893         return offset;
4894     }
4895     scrollOffset_ = richTextRect_.GetY() + offset;
4896     richTextRect_.SetOffset(OffsetF(richTextRect_.GetX(), scrollOffset_));
4897     UpdateScrollBarOffset();
4898     UpdateChildrenOffset();
4899     return offset;
4900 }
4901 
4902 void RichEditorPattern::MoveFirstHandle(float offset)
4903 {
4904     if (SelectOverlayIsOn() && !NearEqual(offset, 0.0f)) {
4905         textSelector_.selectionBaseOffset.AddY(offset);
4906         auto firstHandleOffset = textSelector_.firstHandle.GetOffset();
4907         firstHandleOffset.AddY(offset);
4908         textSelector_.firstHandle.SetOffset(firstHandleOffset);
4909         SelectHandleInfo firstHandleInfo;
4910         firstHandleInfo.paintRect = textSelector_.firstHandle;
4911         firstHandleInfo.needLayout = true;
4912         CheckHandles(firstHandleInfo);
4913         selectOverlayProxy_->UpdateFirstSelectHandleInfo(firstHandleInfo);
4914     }
4915 }
4916 
4917 void RichEditorPattern::MoveSecondHandle(float offset)
4918 {
4919     if (SelectOverlayIsOn() && !NearEqual(offset, 0.0f)) {
4920         textSelector_.selectionDestinationOffset.AddY(offset);
4921         auto secondHandleOffset = textSelector_.secondHandle.GetOffset();
4922         secondHandleOffset.AddY(offset);
4923         textSelector_.secondHandle.SetOffset(secondHandleOffset);
4924         SelectHandleInfo secondHandleInfo;
4925         secondHandleInfo.paintRect = textSelector_.secondHandle;
4926         secondHandleInfo.needLayout = true;
4927         CheckHandles(secondHandleInfo);
4928         selectOverlayProxy_->UpdateSecondSelectHandleInfo(secondHandleInfo);
4929     }
4930 }
4931 
4932 void RichEditorPattern::MoveCaretToContentRect()
4933 {
4934     float caretHeight = 0.0f;
4935     auto caretOffset = CalcCursorOffsetByPosition(GetCaretPosition(), caretHeight, true);
4936     MoveCaretToContentRect(caretOffset, caretHeight);
4937 }
4938 
4939 void RichEditorPattern::MoveCaretToContentRect(const OffsetF& caretOffset, float caretHeight)
4940 {
4941     auto contentRect = GetTextContentRect();
4942     auto textRect = GetTextRect();
4943     if (LessOrEqual(textRect.Height(), contentRect.Height())) {
4944         return;
4945     }
4946     if (LessNotEqual(contentRect.GetSize().Height(), caretHeight) &&
4947         !NearEqual(caretOffset.GetY() + caretHeight, contentRect.Bottom())) {
4948         OnScrollCallback(contentRect.Bottom() - caretOffset.GetY() - caretHeight, SCROLL_FROM_NONE);
4949     }
4950     if (LessNotEqual(contentRect.GetSize().Height(), caretHeight)) {
4951         return;
4952     }
4953     if (LessNotEqual(caretOffset.GetY(), contentRect.GetY())) {
4954         if (LessOrEqual(caretOffset.GetX(), GetTextRect().GetX())) {
4955             OnScrollCallback(contentRect.GetY() - caretOffset.GetY() + caretHeight, SCROLL_FROM_NONE);
4956         } else {
4957             OnScrollCallback(contentRect.GetY() - caretOffset.GetY(), SCROLL_FROM_NONE);
4958         }
4959     } else if (GreatNotEqual(caretOffset.GetY() + caretHeight, contentRect.Bottom())) {
4960         OnScrollCallback(contentRect.Bottom() - caretOffset.GetY() - caretHeight, SCROLL_FROM_NONE);
4961     }
4962 }
4963 
4964 void RichEditorPattern::UpdateScrollBarOffset()
4965 {
4966     if (!GetScrollBar() && !GetScrollBarProxy()) {
4967         return;
4968     }
4969     Size size(frameRect_.Width(), frameRect_.Height());
4970     auto verticalGap = frameRect_.Height() - contentRect_.Height();
4971     UpdateScrollBarRegion(
4972         contentRect_.GetY() - richTextRect_.GetY(), richTextRect_.Height() + verticalGap, size, Offset(0.0, 0.0));
4973     auto tmpHost = GetHost();
4974     CHECK_NULL_VOID(tmpHost);
4975     tmpHost->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
4976 }
4977 
4978 void RichEditorPattern::OnScrollEndCallback()
4979 {
4980     auto scrollBar = GetScrollBar();
4981     if (scrollBar) {
4982         scrollBar->ScheduleDisappearDelayTask();
4983     }
4984     UpdateOverlaySelectArea();
4985 }
4986 
4987 bool RichEditorPattern::IsReachedBoundary(float offset)
4988 {
4989     return (NearEqual(richTextRect_.GetY(), contentRect_.GetY()) && GreatNotEqual(offset, 0.0f)) ||
4990            (NearEqual(richTextRect_.GetY() + richTextRect_.Height(), contentRect_.GetY() + contentRect_.Height()) &&
4991                LessNotEqual(offset, 0.0f));
4992 }
4993 
4994 void RichEditorPattern::CheckScrollable()
4995 {
4996     auto host = GetHost();
4997     CHECK_NULL_VOID(host);
4998     auto hub = host->GetEventHub<EventHub>();
4999     CHECK_NULL_VOID(hub);
5000     auto gestureHub = hub->GetOrCreateGestureEventHub();
5001     CHECK_NULL_VOID(gestureHub);
5002     scrollable_ = GetTextContentLength() > 0 && GreatNotEqual(richTextRect_.Height(), contentRect_.Height());
5003     SetScrollEnable(scrollable_);
5004 }
5005 
5006 void RichEditorPattern::UpdateChildrenOffset()
5007 {
5008     auto host = GetHost();
5009     CHECK_NULL_VOID(host);
5010     std::vector<int32_t> placeholderIndex;
5011     for (const auto& child : spans_) {
5012         if (!child) {
5013             continue;
5014         }
5015         if (AceType::InstanceOf<ImageSpanItem>(child) || AceType::InstanceOf<PlaceholderSpanItem>(child)) {
5016             placeholderIndex.emplace_back(child->placeholderIndex);
5017         }
5018     }
5019     if (spans_.empty() || placeholderIndex.empty()) {
5020         return;
5021     }
5022     size_t index = 0;
5023     std::vector<RectF> rectsForPlaceholders = paragraphs_.GetPlaceholderRects();
5024     auto childrenNodes = host->GetChildren();
5025     auto textOffset = GetTextRect().GetOffset();
5026     for (const auto& child : childrenNodes) {
5027         auto childNode = AceType::DynamicCast<FrameNode>(child);
5028         if (!childNode) {
5029             continue;
5030         }
5031         if (!(childNode->GetPattern<ImagePattern>() || childNode->GetPattern<PlaceholderSpanPattern>())) {
5032             continue;
5033         }
5034         if (index >= rectsForPlaceholders.size()) {
5035             break;
5036         }
5037         auto rect = rectsForPlaceholders.at(index);
5038         auto geometryNode = childNode->GetGeometryNode();
5039         if (geometryNode) {
5040             geometryNode->SetMarginFrameOffset(textOffset + OffsetF(rect.Left(), rect.Top()));
5041             childNode->ForceSyncGeometryNode();
5042             childNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
5043         }
5044         ++index;
5045     }
5046 }
5047 
5048 void RichEditorPattern::AutoScrollByEdgeDetection(AutoScrollParam param, OffsetF offset, EdgeDetectionStrategy strategy)
5049 {
5050     if (NearEqual(prevAutoScrollOffset_.GetY(), offset.GetY())) {
5051         return;
5052     }
5053     prevAutoScrollOffset_ = offset;
5054     auto contentRect = GetTextContentRect();
5055     auto isDragging = param.autoScrollEvent == AutoScrollEvent::DRAG;
5056     float edgeThreshold = isDragging ? AUTO_SCROLL_DRAG_EDGE_DISTANCE.ConvertToPx()
5057                                      : AUTO_SCROLL_EDGE_DISTANCE.ConvertToPx();
5058     auto maxHeight = isDragging ? frameRect_.Height() : contentRect.Height();
5059     if (GreatNotEqual(edgeThreshold * 2, maxHeight)) {
5060         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AutoScrollByEdgeDetection: hot area height is great than max height.");
5061         return;
5062     }
5063     float topEdgeThreshold = isDragging ? edgeThreshold : edgeThreshold + contentRect.GetY();
5064     float bottomThreshold = isDragging ? frameRect_.Height() - edgeThreshold : contentRect.Bottom() - edgeThreshold;
5065     if (param.autoScrollEvent == AutoScrollEvent::HANDLE) {
5066         auto handleTopOffset = offset;
5067         auto handleBottomOffset = OffsetF(offset.GetX(), offset.GetY() + param.handleRect.Height());
5068         if (GreatNotEqual(handleBottomOffset.GetY(), bottomThreshold)) {
5069             param.offset = bottomThreshold - handleBottomOffset.GetY();
5070             ScheduleAutoScroll(param);
5071         } else if (LessNotEqual(handleTopOffset.GetY(), topEdgeThreshold)) {
5072             param.offset = topEdgeThreshold - handleTopOffset.GetY();
5073             ScheduleAutoScroll(param);
5074         } else {
5075             StopAutoScroll();
5076         }
5077         return;
5078     }
5079     // drag and mouse
5080     if (GreatNotEqual(offset.GetY(), bottomThreshold)) {
5081         param.offset = isDragging ? -CalcDragSpeed(bottomThreshold, frameRect_.Height(), offset.GetY())
5082                                   : bottomThreshold - offset.GetY();
5083         ScheduleAutoScroll(param);
5084     } else if (LessNotEqual(offset.GetY(), topEdgeThreshold)) {
5085         param.offset = isDragging ? CalcDragSpeed(topEdgeThreshold, 0, offset.GetY())
5086                                   : topEdgeThreshold - offset.GetY();
5087         ScheduleAutoScroll(param);
5088     } else {
5089         StopAutoScroll();
5090     }
5091 }
5092 
5093 float RichEditorPattern::CalcDragSpeed(float hotAreaStart, float hotAreaEnd, float point)
5094 {
5095     auto distanceRatio = (point - hotAreaStart) / (hotAreaEnd - hotAreaStart);
5096     auto speedFactor = Curves::SHARP->MoveInternal(distanceRatio);
5097     return ((MAX_DRAG_SCROLL_SPEED * speedFactor) / TIME_UNIT) * AUTO_SCROLL_INTERVAL;
5098 }
5099 
5100 void RichEditorPattern::ScheduleAutoScroll(AutoScrollParam param)
5101 {
5102     if (GreatNotEqual(param.offset, 0.0f) && IsReachTop()) {
5103         return;
5104     }
5105     if (LessNotEqual(param.offset, 0.0f) && IsReachBottom()) {
5106         return;
5107     }
5108     auto context = PipelineContext::GetCurrentContext();
5109     CHECK_NULL_VOID(context);
5110     auto taskExecutor = context->GetTaskExecutor();
5111     CHECK_NULL_VOID(taskExecutor);
5112     if (param.isFirstRun_) {
5113         param.isFirstRun_ = false;
5114         currentScrollParam_ = param;
5115         if (isAutoScrollRunning_) {
5116             return;
5117         }
5118     }
5119     autoScrollTask_.Reset([weak = WeakClaim(this)]() {
5120         auto client = weak.Upgrade();
5121         CHECK_NULL_VOID(client);
5122         client->OnAutoScroll(client->currentScrollParam_);
5123         if (client->IsReachTop() || client->IsReachBottom()) {
5124             client->StopAutoScroll();
5125         }
5126     });
5127     isAutoScrollRunning_ = true;
5128     taskExecutor->PostDelayedTask(autoScrollTask_, TaskExecutor::TaskType::UI, AUTO_SCROLL_INTERVAL);
5129 }
5130 
5131 void RichEditorPattern::OnAutoScroll(AutoScrollParam param)
5132 {
5133     if (param.showScrollbar) {
5134         auto scrollBar = GetScrollBar();
5135         if (scrollBar) {
5136             scrollBar->PlayScrollBarAppearAnimation();
5137         }
5138         param.showScrollbar = false;
5139     }
5140 
5141     if (param.autoScrollEvent == AutoScrollEvent::HANDLE) {
5142         auto newOffset = MoveTextRect(param.offset);
5143         if (param.isFirstHandle) {
5144             MoveSecondHandle(newOffset);
5145         } else {
5146             MoveFirstHandle(newOffset);
5147         }
5148         TextPattern::OnHandleMove(param.handleRect, param.isFirstHandle);
5149         if (NearEqual(newOffset, 0.0f)) {
5150             return;
5151         }
5152         ScheduleAutoScroll(param);
5153         return;
5154     }
5155 
5156     if (param.autoScrollEvent == AutoScrollEvent::DRAG) {
5157         auto newOffset = MoveTextRect(param.offset);
5158         if (NearEqual(newOffset, 0.0f)) {
5159             return;
5160         }
5161         ScheduleAutoScroll(param);
5162         return;
5163     }
5164 
5165     if (param.autoScrollEvent == AutoScrollEvent::MOUSE) {
5166         auto newOffset = MoveTextRect(param.offset);
5167         auto textOffset =
5168             Offset(param.eventOffset.GetX() - GetTextRect().GetX(), param.eventOffset.GetY() - GetTextRect().GetY());
5169         int32_t extend = paragraphs_.GetIndex(textOffset);
5170         textSelector_.Update(textSelector_.baseOffset, extend);
5171         SetCaretPosition(std::max(textSelector_.baseOffset, extend));
5172         if (NearEqual(newOffset, 0.0f)) {
5173             return;
5174         }
5175         ScheduleAutoScroll(param);
5176     }
5177 }
5178 
5179 void RichEditorPattern::StopAutoScroll()
5180 {
5181     isAutoScrollRunning_ = false;
5182     autoScrollTask_.Cancel();
5183     prevAutoScrollOffset_ = OffsetF(0.0f, 0.0f);
5184     auto scrollBar = GetScrollBar();
5185     if (scrollBar) {
5186         scrollBar->PlayScrollBarDisappearAnimation();
5187     }
5188 }
5189 
5190 bool RichEditorPattern::NeedAiAnalysis(
5191     const CaretUpdateType targeType, const int32_t pos, const int32_t& spanStart, const std::string& content)
5192 {
5193     if (spanStart < 0) {
5194         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "NeedAiAnalysis -spanStart%{public}d,return!", spanStart);
5195         return false;
5196     }
5197 
5198     if (!InputAIChecker::NeedAIAnalysis(content, targeType, lastClickTimeStamp_ - lastAiPosTimeStamp_)) {
5199         return false;
5200     }
5201 
5202     if (pos == static_cast<int32_t>(content.length())) {
5203         return false;
5204     }
5205 
5206     if (IsClickBoundary(pos) && targeType == CaretUpdateType::PRESSED) {
5207         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "NeedAiAnalysis IsClickBoundary,return!");
5208         return false;
5209     }
5210     return true;
5211 }
5212 
5213 void RichEditorPattern::AdjustCursorPosition(int32_t& pos)
5214 {
5215     // the rich text has some spans, the pos is belong to the whole richtext content, should use (pos - spanStarint)
5216     int32_t spanStart = -1;
5217     // get the span text by the position, maybe text is empty
5218     std::string content = GetPositionSpansText(pos, spanStart);
5219 
5220     if (NeedAiAnalysis(CaretUpdateType::PRESSED, pos, spanStart, content)) {
5221         int32_t aiPos = pos - spanStart;
5222         DataDetectorMgr::GetInstance().AdjustCursorPosition(aiPos, content, lastAiPosTimeStamp_, lastClickTimeStamp_);
5223         if (aiPos < 0) {
5224             return;
5225         }
5226         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "get ai pos:%{public}d--spanStart%{public}d", aiPos, spanStart);
5227         pos = aiPos + spanStart;
5228     }
5229 }
5230 
5231 bool RichEditorPattern::AdjustWordSelection(int32_t& start, int32_t& end)
5232 {
5233     // the rich text has some spans, the pos is belong to the whole richtext content, should use (pos - spanStarint)
5234     int32_t spanStart = -1;
5235     // get the span text by the position, maybe text is empty
5236     std::string content = GetPositionSpansText(start, spanStart);
5237     if (NeedAiAnalysis(CaretUpdateType::DOUBLE_CLICK, start, spanStart, content)) {
5238         int32_t aiPosStart = start - spanStart;
5239         int32_t aiPosEnd = end - spanStart;
5240         DataDetectorMgr::GetInstance().AdjustWordSelection(aiPosStart, content, aiPosStart, aiPosEnd);
5241         if (aiPosStart < 0 || aiPosEnd < 0) {
5242             return false;
5243         }
5244 
5245         start = std::min(aiPosStart + spanStart, GetTextContentLength());
5246         end = std::min(aiPosEnd + spanStart, GetTextContentLength());
5247         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "get ai selector [%{public}d--%{public}d", start, end);
5248         return true;
5249     }
5250     return false;
5251 }
5252 
5253 void RichEditorPattern::AdjustPlaceholderSelection(int32_t& start, int32_t& end, const Offset& touchPos)
5254 {
5255     CHECK_NULL_VOID(!spans_.empty());
5256     float selectLineHeight = 0.0f;
5257     auto clickPositionOffset = paragraphs_.ComputeCursorInfoByClick(start, selectLineHeight,
5258         OffsetF(static_cast<float>(touchPos.GetX()), static_cast<float>(touchPos.GetY())));
5259     if (touchPos.GetX() > clickPositionOffset.GetX()) {
5260         return;
5261     }
5262     auto it = std::find_if(spans_.begin(), spans_.end(), [start](const RefPtr<SpanItem>& spanItem) {
5263         return spanItem->position == start;
5264     });
5265     if (it != spans_.end()) {
5266         // adjust selection if touch right of image or placeholder
5267         auto spanIndex = std::distance(spans_.begin(), it);
5268         auto spanNodeBefore = DynamicCast<FrameNode>(GetChildByIndex(spanIndex));
5269         if (spanNodeBefore && (spanNodeBefore->GetTag() == V2::IMAGE_ETS_TAG ||
5270             spanNodeBefore->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG)) {
5271             end = start;
5272             --start;
5273             TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "get placeholder selector [%{public}d--%{public}d", start, end);
5274         }
5275     }
5276 }
5277 
5278 bool RichEditorPattern::IsClickBoundary(const int32_t position)
5279 {
5280     if (InputAIChecker::IsSingleClickAtBoundary(position, GetTextContentLength())) {
5281         return true;
5282     }
5283 
5284     float height = 0;
5285     auto handleOffset = CalcCursorOffsetByPosition(position, height);
5286     if (InputAIChecker::IsMultiClickAtBoundary(handleOffset, TextPattern::GetTextRect())) {
5287         return true;
5288     }
5289     return false;
5290 }
5291 
5292 std::string RichEditorPattern::GetPositionSpansText(int32_t position, int32_t& startSpan)
5293 {
5294     int32_t start = position - AI_TEXT_RANGE_LEFT;
5295     int32_t end = position + AI_TEXT_RANGE_RIGHT;
5296 
5297     start = std::clamp(start, 0, GetTextContentLength());
5298     end = std::clamp(end, 0, GetTextContentLength());
5299 
5300     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "get span pos:%{public}d-start:%{public}d-end:%{public}d", position, start, end);
5301 
5302     // get all the spans between start and end, then filter the valid text
5303     auto infos = GetSpansInfo(start, end, GetSpansMethod::ONSELECT);
5304     if (infos.GetSelection().resultObjects.empty()) {
5305         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "get spans text is null pos:%{public}d,return", position);
5306         return "";
5307     }
5308     auto list = infos.GetSelection().resultObjects;
5309 
5310     std::stringstream sstream;
5311     for (const auto& obj : list) {
5312         if (obj.type == SelectSpanType::TYPEIMAGE) {
5313             if (obj.spanPosition.spanRange[1] <= position) {
5314                 sstream.str("");
5315                 startSpan = -1;
5316             } else {
5317                 break;
5318             }
5319         } else if (obj.type == SelectSpanType::TYPESPAN) {
5320             if (startSpan < 0) {
5321                 startSpan = obj.spanPosition.spanRange[0] + obj.offsetInSpan[0];
5322             }
5323             // we should use the wide string deal to avoid crash
5324             auto wideText = StringUtils::ToWstring(obj.valueString);
5325             sstream << StringUtils::ToString(
5326                 wideText.substr(obj.offsetInSpan[0], obj.offsetInSpan[1] - obj.offsetInSpan[0]));
5327         }
5328     }
5329 
5330     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "get spans text ret spanStart:%{public}d", startSpan);
5331     return sstream.str();
5332 }
5333 
5334 void RichEditorPattern::CheckHandles(SelectHandleInfo& handleInfo)
5335 {
5336     handleInfo.isShow = CheckHandleVisible(handleInfo.paintRect);
5337 }
5338 
5339 bool RichEditorPattern::CheckHandleVisible(const RectF& paintRect)
5340 {
5341     auto host = GetHost();
5342     CHECK_NULL_RETURN(host, false);
5343     // use global offset.
5344     RectF visibleContentRect(contentRect_.GetOffset() + parentGlobalOffset_, contentRect_.GetSize());
5345     auto parent = host->GetAncestorNodeOfFrame();
5346     visibleContentRect = GetVisibleContentRect(parent, visibleContentRect);
5347     PointF bottomPoint = { paintRect.Left(), paintRect.Bottom() - BOX_EPSILON };
5348     PointF topPoint = { paintRect.Left(), paintRect.Top() + BOX_EPSILON };
5349     return visibleContentRect.IsInRegion(bottomPoint) && visibleContentRect.IsInRegion(topPoint);
5350 }
5351 
5352 bool RichEditorPattern::IsShowSelectMenuUsingMouse()
5353 {
5354     auto pipeline = PipelineContext::GetCurrentContext();
5355     CHECK_NULL_RETURN(pipeline, false);
5356     auto selectOverlayManager = pipeline->GetSelectOverlayManager();
5357     CHECK_NULL_RETURN(selectOverlayManager, false);
5358     return selectOverlayManager->GetSelectOverlayInfo().isUsingMouse;
5359 }
5360 
5361 RefPtr<FocusHub> RichEditorPattern::GetFocusHub() const
5362 {
5363     auto host = GetHost();
5364     CHECK_NULL_RETURN(host, nullptr);
5365     auto focusHub = host->GetOrCreateFocusHub();
5366     return focusHub;
5367 }
5368 
5369 void RichEditorPattern::HandleCursorOnDragMoved(const RefPtr<NotifyDragEvent>& notifyDragEvent)
5370 {
5371     auto host = GetHost();
5372     CHECK_NULL_VOID(host);
5373     if (isCursorAlwaysDisplayed_) {
5374         if (SystemProperties::GetDebugEnabled()) {
5375             TAG_LOGI(AceLogTag::ACE_TEXT_FIELD,
5376                 "In OnDragMoved, the cursor has always Displayed in the textField, id:%{public}d", host->GetId());
5377         }
5378         return;
5379     }
5380     TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
5381         "In OnDragMoved, the dragging node is moving in the richEditor, id:%{public}d", host->GetId());
5382     auto focusHub = GetFocusHub();
5383     CHECK_NULL_VOID(focusHub);
5384     focusHub->RequestFocusImmediately();
5385     isCursorAlwaysDisplayed_ = true;
5386     StartTwinkling();
5387 };
5388 
5389 void RichEditorPattern::HandleCursorOnDragLeaved(const RefPtr<NotifyDragEvent>& notifyDragEvent)
5390 {
5391     auto host = GetHost();
5392     CHECK_NULL_VOID(host);
5393     TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
5394         "In OnDragLeaved, the dragging node has left from richEditor, id:%{public}d", host->GetId());
5395     auto focusHub = GetFocusHub();
5396     CHECK_NULL_VOID(focusHub);
5397     focusHub->LostFocus();
5398     isCursorAlwaysDisplayed_ = false;
5399     StopTwinkling();
5400 };
5401 
5402 void RichEditorPattern::HandleCursorOnDragEnded(const RefPtr<NotifyDragEvent>& notifyDragEvent)
5403 {
5404     auto host = GetHost();
5405     CHECK_NULL_VOID(host);
5406     auto focusHub = GetFocusHub();
5407     CHECK_NULL_VOID(focusHub);
5408     StopAutoScroll();
5409     if (!isCursorAlwaysDisplayed_) {
5410         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "In OnDragEnded,"
5411             " the released location is not in the current richEditor, id:%{public}d", host->GetId());
5412         focusHub->LostFocus();
5413         StopTwinkling();
5414         return;
5415     }
5416     TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
5417         "In OnDragEnded, the released location is in the current richEditor, id:%{public}d", host->GetId());
5418     if (HasFocus()) {
5419         RequestKeyboard(false, true, true);
5420     } else {
5421         focusHub->RequestFocusImmediately();
5422     }
5423     isCursorAlwaysDisplayed_ = false;
5424     StartTwinkling();
5425 };
5426 
5427 void RichEditorPattern::HandleOnDragStatusCallback(
5428     const DragEventType& dragEventType, const RefPtr<NotifyDragEvent>& notifyDragEvent)
5429 {
5430     ScrollablePattern::HandleOnDragStatusCallback(dragEventType, notifyDragEvent);
5431     switch (dragEventType) {
5432         case DragEventType::MOVE:
5433             isDragging_ = true;
5434             HandleCursorOnDragMoved(notifyDragEvent);
5435             break;
5436         case DragEventType::LEAVE:
5437             HandleCursorOnDragLeaved(notifyDragEvent);
5438             break;
5439         case DragEventType::DROP:
5440             isDragging_ = false;
5441             HandleCursorOnDragEnded(notifyDragEvent);
5442             break;
5443         default:
5444             break;
5445     }
5446 }
5447 
5448 void RichEditorPattern::HandleOnCameraInput()
5449 {
5450 #if defined(ENABLE_STANDARD_INPUT)
5451     if (richEditTextChangeListener_ == nullptr) {
5452         richEditTextChangeListener_ = new OnTextChangedListenerImpl(WeakClaim(this));
5453     }
5454     auto inputMethod = MiscServices::InputMethodController::GetInstance();
5455     if (!inputMethod) {
5456         return;
5457     }
5458     StartTwinkling();
5459 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
5460     if (imeShown_) {
5461         inputMethod->StartInputType(MiscServices::InputType::CAMERA_INPUT);
5462     } else {
5463         auto optionalTextConfig = GetMiscTextConfig();
5464         CHECK_NULL_VOID(optionalTextConfig.has_value());
5465         MiscServices::TextConfig textConfig = optionalTextConfig.value();
5466         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnCameraInput set calling window id is : %{public}u",
5467             textConfig.windowId);
5468 #ifdef WINDOW_SCENE_SUPPORTED
5469         auto systemWindowId = GetSCBSystemWindowId();
5470         if (systemWindowId) {
5471             TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "windowId From %{public}u to %{public}u.", textConfig.windowId,
5472                 systemWindowId);
5473             textConfig.windowId = systemWindowId;
5474         }
5475 #endif
5476         inputMethod->Attach(richEditTextChangeListener_, false, textConfig);
5477         inputMethod->StartInputType(MiscServices::InputType::CAMERA_INPUT);
5478         inputMethod->ShowTextInput();
5479     }
5480 #endif
5481 #endif
5482 }
5483 
5484 bool RichEditorPattern::CanStartAITask()
5485 {
5486     return TextPattern::CanStartAITask() && !HasFocus();
5487 }
5488 
5489 bool RichEditorPattern::NeedShowAIDetect()
5490 {
5491     return TextPattern::NeedShowAIDetect() && !HasFocus();
5492 }
5493 
5494 void RichEditorPattern::ToJsonValue(std::unique_ptr<JsonValue>& json) const
5495 {
5496     json->Put("enableDataDetector", textDetectEnable_ ? "true" : "false");
5497     auto jsonValue = JsonUtil::Create(true);
5498     jsonValue->Put("types", "");
5499     json->Put("dataDetectorConfig", jsonValue->ToString().c_str());
5500 }
5501 
5502 void RichEditorPattern::GetCaretMetrics(CaretMetricsF& caretCaretMetric)
5503 {
5504     float caretHeight = 0.0f;
5505     OffsetF caretOffset = CalcCursorOffsetByPosition(GetCaretPosition(), caretHeight);
5506     auto host = GetHost();
5507     CHECK_NULL_VOID(host);
5508     auto offset = host->GetPaintRectOffset();
5509     caretOffset += offset;
5510     caretCaretMetric.offset = caretOffset;
5511     caretCaretMetric.height = caretHeight;
5512 }
5513 
5514 void RichEditorPattern::OnVirtualKeyboardAreaChanged()
5515 {
5516     CHECK_NULL_VOID(SelectOverlayIsOn());
5517     float selectLineHeight = 0.0f;
5518     textSelector_.selectionBaseOffset.SetX(
5519         CalcCursorOffsetByPosition(textSelector_.GetStart(), selectLineHeight).GetX());
5520     textSelector_.selectionDestinationOffset.SetX(
5521         CalcCursorOffsetByPosition(textSelector_.GetEnd(), selectLineHeight).GetX());
5522     CreateHandles();
5523 }
5524 
5525 void RichEditorPattern::ResetDragOption()
5526 {
5527     auto host = GetHost();
5528     CHECK_NULL_VOID(host);
5529     auto gestureEventHub = host->GetOrCreateGestureEventHub();
5530     CHECK_NULL_VOID(gestureEventHub);
5531     if (gestureEventHub->GetIsTextDraggable()) {
5532         CloseSelectOverlay();
5533         ResetSelection();
5534     }
5535 }
5536 
5537 RectF RichEditorPattern::GetSelectArea()
5538 {
5539     auto selectRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
5540     if (selectRects.empty()) {
5541         float caretHeight = 0.0f;
5542         auto caretOffset = CalcCursorOffsetByPosition(GetCaretPosition(), caretHeight);
5543         auto caretWidth = Dimension(1.5f, DimensionUnit::VP).ConvertToPx();
5544         return RectF(caretOffset + parentGlobalOffset_, SizeF(caretWidth, caretHeight));
5545     }
5546     auto frontRect = selectRects.front();
5547     auto backRect = selectRects.back();
5548     RectF res;
5549     if (GreatNotEqual(backRect.Bottom(), frontRect.Bottom())) {
5550         res.SetRect(contentRect_.GetX() + parentGlobalOffset_.GetX(),
5551             frontRect.GetY() + richTextRect_.GetY() + parentGlobalOffset_.GetY(), contentRect_.Width(),
5552             backRect.Bottom() - frontRect.Top());
5553     } else {
5554         res.SetRect(frontRect.GetX() + richTextRect_.GetX() + parentGlobalOffset_.GetX(),
5555             frontRect.GetY() + richTextRect_.GetY() + parentGlobalOffset_.GetY(), backRect.Right() - frontRect.Left(),
5556             backRect.Bottom() - frontRect.Top());
5557     }
5558     auto contentRect = contentRect_;
5559     contentRect.SetOffset(contentRect.GetOffset() + parentGlobalOffset_);
5560     auto host = GetHost();
5561     CHECK_NULL_RETURN(host, RectF(0, 0, 0, 0));
5562     auto parent = host->GetAncestorNodeOfFrame();
5563     contentRect = GetVisibleContentRect(parent, contentRect);
5564     return res.IntersectRectT(contentRect);
5565 }
5566 
5567 void RichEditorPattern::UpdateOverlaySelectArea()
5568 {
5569     CHECK_NULL_VOID(selectOverlayProxy_);
5570     selectOverlayProxy_->UpdateSelectArea(GetSelectArea());
5571 }
5572 
5573 bool RichEditorPattern::IsTouchInFrameArea(const PointF& touchPoint)
5574 {
5575     auto host = GetHost();
5576     CHECK_NULL_RETURN(host, false);
5577     auto viewPort = RectF(parentGlobalOffset_, frameRect_.GetSize());
5578     auto parent = host->GetAncestorNodeOfFrame();
5579     viewPort = GetVisibleContentRect(parent, viewPort);
5580     return viewPort.IsInRegion(touchPoint);
5581 }
5582 } // namespace OHOS::Ace::NG
5583