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 <chrono>
18 #include <cstddef>
19 #include <cstdint>
20 #include <functional>
21 #include <iterator>
22 #include <sstream>
23 #include <string>
24 #include <utility>
25 
26 #if !defined(PREVIEW) && defined(OHOS_PLATFORM)
27 #include "interfaces/inner_api/ui_session/ui_session_manager.h"
28 #endif
29 #include "adapter/ohos/capability/clipboard/clipboard_impl.h"
30 #include "base/geometry/dimension.h"
31 #include "base/geometry/ng/offset_t.h"
32 #include "base/geometry/ng/rect_t.h"
33 #include "base/geometry/offset.h"
34 #include "base/i18n/localization.h"
35 #include "base/log/ace_trace.h"
36 #include "base/log/dump_log.h"
37 #include "base/log/log_wrapper.h"
38 #include "base/memory/ace_type.h"
39 #include "base/utils/string_utils.h"
40 #include "base/utils/utils.h"
41 #include "core/common/ai/data_detector_mgr.h"
42 #include "core/common/clipboard/paste_data.h"
43 #include "core/common/container.h"
44 #include "core/common/container_scope.h"
45 #include "core/common/ime/text_input_client.h"
46 #include "core/common/stylus/stylus_detector_mgr.h"
47 #include "core/common/vibrator/vibrator_utils.h"
48 #include "core/components/common/layout/constants.h"
49 #include "core/components/common/properties/text_style_parser.h"
50 #include "core/components_ng/base/inspector_filter.h"
51 #include "core/components_ng/base/observer_handler.h"
52 #include "core/components_ng/base/view_stack_processor.h"
53 #include "core/components_ng/event/event_hub.h"
54 #include "core/components_ng/event/gesture_event_hub.h"
55 #include "core/components_ng/event/long_press_event.h"
56 #include "core/components_ng/pattern/image/image_pattern.h"
57 #include "core/components_ng/pattern/overlay/keyboard_base_pattern.h"
58 #include "core/components_ng/pattern/rich_editor/rich_editor_event_hub.h"
59 #include "core/components_ng/pattern/rich_editor/rich_editor_layout_property.h"
60 #include "core/components_ng/pattern/rich_editor/rich_editor_model.h"
61 #include "core/components_ng/pattern/rich_editor/rich_editor_overlay_modifier.h"
62 #include "core/components_ng/pattern/rich_editor/rich_editor_theme.h"
63 #include "core/components_ng/pattern/rich_editor/selection_info.h"
64 #include "core/components_ng/pattern/rich_editor_drag/rich_editor_drag_info.h"
65 #include "core/components_ng/pattern/rich_editor_drag/rich_editor_drag_pattern.h"
66 #include "core/components_ng/pattern/text/span_node.h"
67 #include "core/components_ng/pattern/text/text_base.h"
68 #include "core/components_ng/pattern/text/typed_text.h"
69 #include "core/components_ng/pattern/text_field/text_field_manager.h"
70 #include "core/components_ng/pattern/text_field/text_field_model.h"
71 #include "core/components_ng/pattern/text_field/text_input_ai_checker.h"
72 #include "core/components_ng/property/property.h"
73 #include "core/components_v2/inspector/inspector_constants.h"
74 #include "core/gestures/gesture_info.h"
75 #include "core/pipeline/base/element_register.h"
76 
77 #ifndef ACE_UNITTEST
78 #ifdef ENABLE_STANDARD_INPUT
79 #include "commonlibrary/c_utils/base/include/refbase.h"
80 
81 #include "core/components_ng/pattern/text_field/on_text_changed_listener_impl.h"
82 #endif
83 #endif
84 
85 #include "core/common/ace_engine_ext.h"
86 #include "core/common/udmf/udmf_client.h"
87 
88 #ifdef WINDOW_SCENE_SUPPORTED
89 #include "core/components_ng/pattern/window_scene/helper/window_scene_helper.h"
90 #endif
91 
92 #ifdef ENABLE_ROSEN_BACKEND
93 #include "core/components/custom_paint/rosen_render_custom_paint.h"
94 #endif
95 
96 namespace OHOS::Ace::NG {
97 namespace {
98 #if defined(ENABLE_STANDARD_INPUT)
99 // should be moved to theme
100 constexpr float CARET_WIDTH = 1.5f;
101 constexpr float DEFAULT_CARET_HEIGHT = 18.5f;
102 constexpr Dimension KEYBOARD_AVOID_OFFSET = 24.0_vp;
103 #endif
104 constexpr int32_t IMAGE_SPAN_LENGTH = 1;
105 constexpr int32_t SYMBOL_SPAN_LENGTH = 2;
106 constexpr int32_t RICH_EDITOR_TWINKLING_INTERVAL_MS = 500;
107 constexpr int32_t AUTO_SCROLL_INTERVAL = 15;
108 constexpr Dimension CARET_BOTTOM_DISTANCE = 16.0_vp;
109 constexpr Dimension AUTO_SCROLL_EDGE_DISTANCE = 15.0_vp;
110 constexpr Dimension AUTO_SCROLL_DRAG_EDGE_DISTANCE = 58.0_vp;
111 constexpr float MAX_DRAG_SCROLL_SPEED = 2400.0f;
112 constexpr float TIME_UNIT = 1000.0f;
113 constexpr uint32_t RECORD_MAX_LENGTH = 20;
114 constexpr float DOUBLE_CLICK_INTERVAL_MS = 300.0f;
115 
116 constexpr float DEFAILT_OPACITY = 0.2f;
117 constexpr int64_t COLOR_OPAQUE = 255;
118 constexpr int32_t MAX_CLICK = 3;
119 
120 constexpr Color SYSTEM_CARET_COLOR = Color(0xff007dff);
121 constexpr Color SYSTEM_SELECT_BACKGROUND_COLOR = Color(0x33007dff);
122 
123 constexpr int32_t ERROR_BAD_PARAMETERS = -1;
124 constexpr char PREVIEW_STYLE_NORMAL[] = "normal";
125 constexpr char PREVIEW_STYLE_UNDERLINE[] = "underline";
126 const std::wstring lineSeparator = L"\n";
127 // hen do ai anaylsis, we should limit the left an right limit of the string
128 constexpr static int32_t AI_TEXT_RANGE_LEFT = 50;
129 constexpr static int32_t AI_TEXT_RANGE_RIGHT = 50;
130 constexpr static int32_t NONE_SELECT_TYPE = -1;
131 constexpr float RICH_DEFAULT_SHADOW_COLOR = 0x33000000;
132 constexpr float RICH_DEFAULT_ELEVATION = 120.0f;
133 constexpr int32_t CUSTOM_CONTENT_LENGTH = 1;
134 constexpr int32_t SYMBOL_CONTENT_LENGTH = 2;
135 constexpr int32_t PLACEHOLDER_LENGTH = 6;
136 const std::wstring PLACEHOLDER_MARK = L"![id";
137 constexpr int32_t RICH_DEFAULT_AI_WORD = 100;
138 } // namespace
139 
RichEditorPattern()140 RichEditorPattern::RichEditorPattern() :
141 #ifndef ACE_UNITTEST
142     isAPI14Plus(AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_FOURTEEN))
143 #else
144     isAPI14Plus(true)
145 #endif
146 {
147     magnifierController_ = MakeRefPtr<MagnifierController>(WeakClaim(this));
148     selectOverlay_ = AceType::MakeRefPtr<RichEditorSelectOverlay>(WeakClaim(this));
149     styledString_ = MakeRefPtr<MutableSpanString>("");
150     styledString_->SetSpanWatcher(WeakClaim(this));
151 }
152 
~RichEditorPattern()153 RichEditorPattern::~RichEditorPattern()
154 {
155     if (isCustomKeyboardAttached_) {
156         CloseCustomKeyboard();
157     }
158 }
159 
SetStyledString(const RefPtr<SpanString> & value)160 void RichEditorPattern::SetStyledString(const RefPtr<SpanString>& value)
161 {
162     CHECK_NULL_VOID(value && styledString_);
163     CloseSelectOverlay();
164     ResetSelection();
165     styledString_->RemoveCustomSpan();
166     auto length = styledString_->GetLength();
167     styledString_->ReplaceSpanString(0, length, value);
168     SetCaretPosition(styledString_->GetLength());
169     MoveCaretToContentRect();
170     auto host = GetHost();
171     CHECK_NULL_VOID(host);
172     styledString_->AddCustomSpan();
173     styledString_->SetFramNode(WeakClaim(host.GetRawPtr()));
174     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
175     ForceTriggerAvoidOnCaretChange();
176 }
177 
UpdateSpanItems(const std::list<RefPtr<NG::SpanItem>> & spanItems)178 void RichEditorPattern::UpdateSpanItems(const std::list<RefPtr<NG::SpanItem>>& spanItems)
179 {
180     SetSpanItemChildren(spanItems);
181     ProcessStyledString();
182 }
183 
ProcessStyledString()184 void RichEditorPattern::ProcessStyledString()
185 {
186     auto host = GetHost();
187     CHECK_NULL_VOID(host);
188     std::string textCache = textForDisplay_;
189     textForDisplay_.clear();
190     dataDetectorAdapter_->textForAI_.clear();
191     host->Clean();
192     RemoveEmptySpanItems();
193     hasUrlSpan_ = false;
194     for (const auto& span : spans_) {
195         if (!span) {
196             continue;
197         }
198         auto imageSpan = DynamicCast<ImageSpanItem>(span);
199         if (imageSpan) {
200             MountImageNode(imageSpan);
201             dataDetectorAdapter_->textForAI_ += '\n';
202         } else {
203             dataDetectorAdapter_->textForAI_ += span->content;
204         }
205         textForDisplay_ += span->content;
206         auto [spanStart, spanEnd] = span->interval;
207         span->rangeStart = spanStart;
208         span->position = spanEnd;
209 
210         if (span->urlOnRelease) {
211             hasUrlSpan_ = true;
212         }
213     }
214     if (textForDisplay_ != textCache) {
215         dataDetectorAdapter_->aiDetectInitialized_ = false;
216     }
217     if (CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
218         dataDetectorAdapter_->StartAITask();
219     }
220 }
221 
MountImageNode(const RefPtr<ImageSpanItem> & imageItem)222 void RichEditorPattern::MountImageNode(const RefPtr<ImageSpanItem>& imageItem)
223 {
224     auto host = GetHost();
225     CHECK_NULL_VOID(host);
226     auto imageNode = ImageSpanNode::GetOrCreateSpanNode(V2::IMAGE_ETS_TAG,
227         ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<ImagePattern>(); });
228     auto pattern = imageNode->GetPattern<ImagePattern>();
229     CHECK_NULL_VOID(pattern);
230     pattern->SetSyncLoad(true);
231     auto index = host->GetChildren().size();
232     imageNode->MountToParent(host, index);
233     CHECK_NULL_VOID(imageItem);
234     auto options = imageItem->options;
235     EnableImageDrag(imageNode, oneStepDragParam_ != nullptr);
236     SetImageLayoutProperty(imageNode, options);
237     imageNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
238     imageNode->MarkModifyDone();
239     imageItem->imageNodeId = imageNode->GetId();
240     imageNode->SetImageItem(imageItem);
241 }
242 
SetImageLayoutProperty(RefPtr<ImageSpanNode> imageNode,const ImageSpanOptions & options)243 void RichEditorPattern::SetImageLayoutProperty(RefPtr<ImageSpanNode> imageNode, const ImageSpanOptions& options)
244 {
245     CHECK_NULL_VOID(imageNode);
246     auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
247     CHECK_NULL_VOID(imageLayoutProperty);
248     std::function<ImageSourceInfo()> createSourceInfoFunc = CreateImageSourceInfo(options);
249     imageLayoutProperty->UpdateImageSourceInfo(createSourceInfoFunc());
250     if (options.imageAttribute.has_value()) {
251         auto imgAttr = options.imageAttribute.value();
252         if (imgAttr.size.has_value()) {
253             imageLayoutProperty->UpdateUserDefinedIdealSize(imgAttr.size->GetSize());
254         }
255         if (imgAttr.verticalAlign.has_value()) {
256             imageLayoutProperty->UpdateVerticalAlign(imgAttr.verticalAlign.value());
257         }
258         if (imgAttr.objectFit.has_value()) {
259             imageLayoutProperty->UpdateImageFit(imgAttr.objectFit.value());
260         }
261         if (imgAttr.marginProp.has_value()) {
262             imageLayoutProperty->UpdateMargin(imgAttr.marginProp.value());
263         }
264         if (imgAttr.paddingProp.has_value()) {
265             imageLayoutProperty->UpdatePadding(imgAttr.paddingProp.value());
266         }
267         if (imgAttr.borderRadius.has_value()) {
268             auto imageRenderCtx = imageNode->GetRenderContext();
269             imageRenderCtx->UpdateBorderRadius(imgAttr.borderRadius.value());
270             imageRenderCtx->SetClipToBounds(true);
271         }
272     }
273     imageNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
274     imageNode->MarkModifyDone();
275     IF_TRUE(oneStepDragParam_, dirtyImageNodes.push(WeakClaim(RawPtr(imageNode))));
276 }
277 
InsertValueInStyledString(const std::string & insertValue)278 void RichEditorPattern::InsertValueInStyledString(const std::string& insertValue)
279 {
280     CHECK_NULL_VOID(styledString_);
281     int32_t changeStart = caretPosition_;
282     int32_t changeLength = 0;
283     if (textSelector_.IsValid()) {
284         changeStart = textSelector_.GetTextStart();
285         changeLength = textSelector_.GetTextEnd() - changeStart;
286     }
287     bool isPreventChange = false;
288     RefPtr<SpanString> insertStyledString = nullptr;
289     if (typingStyle_.has_value() && typingTextStyle_.has_value()) {
290         insertStyledString = CreateStyledStringByTextStyle(insertValue, typingStyle_.value(), typingTextStyle_.value());
291         isPreventChange = !BeforeStyledStringChange(changeStart, changeLength, insertStyledString);
292     } else {
293         isPreventChange = !BeforeStyledStringChange(changeStart, changeLength, insertValue);
294     }
295     CHECK_NULL_VOID(!isPreventChange);
296     if (changeLength > 0) {
297         DeleteForwardInStyledString(changeLength, false);
298     }
299     if (textSelector_.IsValid()) {
300         CloseSelectOverlay();
301         ResetSelection();
302     }
303     if (insertStyledString) {
304         styledString_->InsertSpanString(changeStart, insertStyledString);
305     } else {
306         styledString_->InsertString(changeStart, insertValue);
307     }
308     SetCaretPosition(changeStart + static_cast<int32_t>(StringUtils::ToWstring(insertValue).length()));
309     if (!caretVisible_) {
310         StartTwinkling();
311     }
312     auto host = GetHost();
313     CHECK_NULL_VOID(host);
314     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
315     host->MarkModifyDone();
316     AfterStyledStringChange(changeStart, changeLength, insertValue);
317 }
318 
CreateStyledStringByTextStyle(const std::string & insertValue,const struct UpdateSpanStyle & updateSpanStyle,const TextStyle & textStyle)319 RefPtr<SpanString> RichEditorPattern::CreateStyledStringByTextStyle(
320     const std::string& insertValue, const struct UpdateSpanStyle& updateSpanStyle, const TextStyle& textStyle)
321 {
322     auto styledString = AceType::MakeRefPtr<SpanString>(insertValue);
323     auto length = styledString->GetLength();
324     auto fontSpan = CreateFontSpanByTextStyle(updateSpanStyle, textStyle, length);
325     styledString->AddSpan(fontSpan);
326     auto decorationSpan = CreateDecorationSpanByTextStyle(updateSpanStyle, textStyle, length);
327     styledString->AddSpan(decorationSpan);
328     if (updateSpanStyle.updateTextShadows.has_value()) {
329         auto textShadowSpan = AceType::MakeRefPtr<TextShadowSpan>(textStyle.GetTextShadows(), 0, length);
330         styledString->AddSpan(textShadowSpan);
331     }
332     if (updateSpanStyle.updateLineHeight.has_value()) {
333         auto lineHeightSpan = AceType::MakeRefPtr<LineHeightSpan>(textStyle.GetLineHeight(), 0, length);
334         styledString->AddSpan(lineHeightSpan);
335     }
336     if (updateSpanStyle.updateLetterSpacing.has_value()) {
337         auto letterSpacingSpan = AceType::MakeRefPtr<LetterSpacingSpan>(textStyle.GetLetterSpacing(), 0, length);
338         styledString->AddSpan(letterSpacingSpan);
339     }
340     return styledString;
341 }
342 
CreateFontSpanByTextStyle(const struct UpdateSpanStyle & updateSpanStyle,const TextStyle & textStyle,int32_t length)343 RefPtr<FontSpan> RichEditorPattern::CreateFontSpanByTextStyle(
344     const struct UpdateSpanStyle& updateSpanStyle, const TextStyle& textStyle, int32_t length)
345 {
346     Font font;
347     if (updateSpanStyle.updateFontWeight.has_value()) {
348         font.fontWeight = textStyle.GetFontWeight();
349     }
350     if (updateSpanStyle.updateFontSize.has_value()) {
351         font.fontSize = textStyle.GetFontSize();
352     }
353     if (updateSpanStyle.updateItalicFontStyle.has_value()) {
354         font.fontStyle = textStyle.GetFontStyle();
355     }
356     if (updateSpanStyle.updateFontFamily.has_value()) {
357         font.fontFamilies = textStyle.GetFontFamilies();
358     }
359     if (updateSpanStyle.updateTextColor.has_value()) {
360         font.fontColor = textStyle.GetTextColor();
361     }
362     return AceType::MakeRefPtr<FontSpan>(font, 0, length);
363 }
364 
CreateDecorationSpanByTextStyle(const struct UpdateSpanStyle & updateSpanStyle,const TextStyle & textStyle,int32_t length)365 RefPtr<DecorationSpan> RichEditorPattern::CreateDecorationSpanByTextStyle(
366     const struct UpdateSpanStyle& updateSpanStyle, const TextStyle& textStyle, int32_t length)
367 {
368     TextDecoration type = TextDecoration::NONE;
369     std::optional<Color> colorOption;
370     std::optional<TextDecorationStyle> styleOption;
371     if (updateSpanStyle.updateTextDecoration.has_value()) {
372         type = textStyle.GetTextDecoration();
373     }
374     if (updateSpanStyle.updateTextDecorationColor.has_value()) {
375         colorOption = textStyle.GetTextDecorationColor();
376     }
377     if (updateSpanStyle.updateTextDecorationStyle.has_value()) {
378         styleOption = textStyle.GetTextDecorationStyle();
379     }
380     return AceType::MakeRefPtr<DecorationSpan>(type, colorOption, styleOption, 0, length);
381 }
382 
DeleteBackwardInStyledString(int32_t length)383 void RichEditorPattern::DeleteBackwardInStyledString(int32_t length)
384 {
385     DeleteValueInStyledString(caretPosition_ - length, length);
386 }
387 
DeleteForwardInStyledString(int32_t length,bool isIME)388 void RichEditorPattern::DeleteForwardInStyledString(int32_t length, bool isIME)
389 {
390     DeleteValueInStyledString(caretPosition_, length, isIME);
391 }
392 
DeleteValueInStyledString(int32_t start,int32_t length,bool isIME,bool isUpdateCaret)393 void RichEditorPattern::DeleteValueInStyledString(int32_t start, int32_t length, bool isIME, bool isUpdateCaret)
394 {
395     CHECK_NULL_VOID(styledString_);
396     if (!textSelector_.SelectNothing()) {
397         start = textSelector_.GetTextStart();
398         length = textSelector_.GetTextEnd() - textSelector_.GetTextStart();
399     }
400     auto range = TextEmojiProcessor::CalSubWstringRange(start, length, styledString_->GetWideString(), true);
401     start = range.startIndex;
402     length = range.endIndex - range.startIndex;
403     bool isPreventChange = isIME && !BeforeStyledStringChange(start, length, "");
404     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "start=%{public}d, length=%{public}d, isPreventChange=%{public}d",
405         start, length, isPreventChange);
406     CHECK_NULL_VOID(!isPreventChange);
407     if (textSelector_.IsValid()) {
408         CloseSelectOverlay();
409         ResetSelection();
410     }
411     styledString_->RemoveString(start, length);
412     if (isUpdateCaret) {
413         SetCaretPosition(start);
414     }
415     if (!caretVisible_) {
416         StartTwinkling();
417         if (previewLongPress_ && isIME) {
418             TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "previewLongPress_ is true, before RequestKeyboard");
419             RequestKeyboard(false, true, true);
420             HandleOnEditChanged(true);
421             previewLongPress_ = false;
422         }
423     }
424     if (isIME) {
425         AfterStyledStringChange(start, length, "");
426     }
427     auto host = GetHost();
428     CHECK_NULL_VOID(host);
429     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
430     host->MarkModifyDone();
431 }
432 
BeforeStyledStringChange(int32_t start,int32_t length,const std::string & string)433 bool RichEditorPattern::BeforeStyledStringChange(int32_t start, int32_t length, const std::string& string)
434 {
435     auto eventHub = GetEventHub<RichEditorEventHub>();
436     CHECK_NULL_RETURN(eventHub, true);
437     CHECK_NULL_RETURN(eventHub->HasOnStyledStringWillChange(), true);
438     auto styledString = AceType::MakeRefPtr<SpanString>(string);
439     auto stringLength = styledString->GetLength();
440     auto changeStart = std::clamp(start, 0, GetTextContentLength());
441     if (stringLength != 0) {
442         auto lastStyles = styledString_->GetSpans(changeStart - 1, 1);
443         for (auto && style : lastStyles) {
444             if (!style) {
445                 continue;
446             }
447             auto spanType = style->GetSpanType();
448             if (spanType == SpanType::Image || spanType == SpanType::CustomSpan) {
449                 continue;
450             }
451             auto span = style->GetSubSpan(0, stringLength);
452             styledString->AddSpan(span);
453         }
454     }
455     return BeforeStyledStringChange(changeStart, length, styledString);
456 }
457 
BeforeStyledStringChange(int32_t start,int32_t length,const RefPtr<SpanString> & styledString)458 bool RichEditorPattern::BeforeStyledStringChange(int32_t start, int32_t length, const RefPtr<SpanString>& styledString)
459 {
460     auto eventHub = GetEventHub<RichEditorEventHub>();
461     CHECK_NULL_RETURN(eventHub, true);
462     CHECK_NULL_RETURN(eventHub->HasOnStyledStringWillChange(), true);
463     auto replaceMentString = AceType::MakeRefPtr<MutableSpanString>("");
464     replaceMentString->AppendSpanString(styledString);
465     StyledStringChangeValue changeValue;
466     auto changeStart = std::clamp(start, 0, GetTextContentLength());
467     auto changeEnd = std::clamp(changeStart + length, 0, GetTextContentLength());
468     changeValue.SetRangeBefore({ changeStart, changeEnd });
469     changeValue.SetReplacementString(replaceMentString);
470     return eventHub->FireOnStyledStringWillChange(changeValue);
471 }
472 
AfterStyledStringChange(int32_t start,int32_t length,const std::string & string)473 void RichEditorPattern::AfterStyledStringChange(int32_t start, int32_t length, const std::string& string)
474 {
475     auto eventHub = GetEventHub<RichEditorEventHub>();
476     CHECK_NULL_VOID(eventHub);
477     if (eventHub->HasOnStyledStringDidChange()) {
478         StyledStringChangeValue changeValue;
479         auto changeStart = std::clamp(start, 0, GetTextContentLength());
480         auto changeEnd = changeStart + length;
481         auto stringLength = static_cast<int32_t>(StringUtils::ToWstring(string).length());
482         auto stringEnd = changeStart + stringLength;
483         changeValue.SetRangeBefore({ changeStart, changeEnd });
484         changeValue.SetRangeAfter({ changeStart, stringEnd });
485         eventHub->FireOnStyledStringDidChange(changeValue);
486     }
487     ForceTriggerAvoidOnCaretChange();
488 }
489 
OnModifyDone()490 void RichEditorPattern::OnModifyDone()
491 {
492     auto host = GetHost();
493     CHECK_NULL_VOID(host);
494     auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
495     copyOption_ = layoutProperty->GetCopyOption().value_or(CopyOptions::Distributed);
496     auto context = host->GetContext();
497     CHECK_NULL_VOID(context);
498     ResetKeyboardIfNeed();
499     context->AddOnAreaChangeNode(host->GetId());
500     if (!clipboard_ && context) {
501         clipboard_ = ClipboardProxy::GetInstance()->GetClipboard(context->GetTaskExecutor());
502     }
503     instanceId_ = context->GetInstanceId();
504     InitMouseEvent();
505     auto focusHub = host->GetOrCreateFocusHub();
506     CHECK_NULL_VOID(focusHub);
507     InitFocusEvent(focusHub);
508     auto gestureEventHub = host->GetOrCreateGestureEventHub();
509     InitClickEvent(gestureEventHub);
510     InitLongPressEvent(gestureEventHub);
511     InitTouchEvent();
512     InitPanEvent();
513     HandleEnabled();
514     ProcessInnerPadding();
515     InitScrollablePattern();
516     SetAccessibilityAction();
517     if (host->IsDraggable()) {
518         InitDragDropEvent();
519         AddDragFrameNodeToManager(host);
520     } else {
521         ClearDragDropEvent();
522         RemoveDragFrameNodeFromManager(host);
523     }
524     Register2DragDropManager();
525     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
526 
527     auto eventHub = host->GetEventHub<EventHub>();
528     CHECK_NULL_VOID(eventHub);
529     bool enabledCache = eventHub->IsEnabled();
530     if (textDetectEnable_ && enabledCache != enabled_) {
531         enabled_ = enabledCache;
532         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
533     }
534 }
535 
HandleEnabled()536 void RichEditorPattern::HandleEnabled()
537 {
538     auto host = GetHost();
539     CHECK_NULL_VOID(host);
540     auto renderContext = host->GetRenderContext();
541     CHECK_NULL_VOID(renderContext);
542     if (IsDisabled()) {
543         auto pipeline = host->GetContext();
544         CHECK_NULL_VOID(pipeline);
545         auto richEditorTheme = pipeline->GetTheme<RichEditorTheme>();
546         CHECK_NULL_VOID(richEditorTheme);
547         auto disabledAlpha = richEditorTheme->GetDisabledAlpha();
548         renderContext->OnOpacityUpdate(disabledAlpha);
549     } else {
550         auto opacity = renderContext->GetOpacity().value_or(1.0);
551         renderContext->OnOpacityUpdate(opacity);
552     }
553 }
554 
BeforeCreateLayoutWrapper()555 void RichEditorPattern::BeforeCreateLayoutWrapper()
556 {
557     ACE_SCOPED_TRACE("RichEditorBeforeCreateLayoutWrapper");
558     if (!isSpanStringMode_) {
559         TextPattern::PreCreateLayoutWrapper();
560     } else if (contentMod_) {
561         contentMod_->ContentChange();
562     }
563 }
564 
UpdateMagnifierStateAfterLayout(bool frameSizeChange)565 void RichEditorPattern::UpdateMagnifierStateAfterLayout(bool frameSizeChange)
566 {
567     CHECK_NULL_VOID(!selectOverlay_->GetIsHandleMoving());
568     if (frameSizeChange && magnifierController_ && magnifierController_->GetMagnifierNodeExist()) {
569         previewLongPress_ = false;
570         editingLongPress_ = false;
571         if (moveCaretState_.isMoveCaret) {
572             isCursorAlwaysDisplayed_ = false;
573             StartTwinkling();
574         }
575         moveCaretState_.Reset();
576         magnifierController_->RemoveMagnifierFrameNode();
577     }
578 }
579 
ClearOnFocusTextField()580 void RichEditorPattern::ClearOnFocusTextField()
581 {
582     CHECK_NULL_VOID(AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_FOURTEEN));
583     auto host = GetHost();
584     CHECK_NULL_VOID(host);
585     auto context = host->GetContextRefPtr();
586     CHECK_NULL_VOID(context);
587     auto textFieldManager = DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
588     CHECK_NULL_VOID(textFieldManager);
589     textFieldManager->ClearOnFocusTextField(host->GetId());
590 }
591 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)592 bool RichEditorPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
593 {
594     CHECK_NULL_RETURN(!config.skipMeasure && !dirty->SkipMeasureContent(), false);
595     auto originalFrameRect = frameRect_;
596     frameRect_ = dirty->GetGeometryNode()->GetFrameRect();
597     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
598     CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
599     auto richEditorLayoutAlgorithm =
600         DynamicCast<RichEditorLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
601     CHECK_NULL_RETURN(richEditorLayoutAlgorithm, false);
602     auto parentGlobalOffset = richEditorLayoutAlgorithm->GetParentGlobalOffset();
603     richTextRect_ = richEditorLayoutAlgorithm->GetTextRect();
604     if (parentGlobalOffset != parentGlobalOffset_) {
605         parentGlobalOffset_ = parentGlobalOffset;
606         selectOverlay_->UpdateSelectOverlayOnAreaChanged();
607     }
608     UpdateTextFieldManager(Offset(parentGlobalOffset_.GetX(), parentGlobalOffset_.GetY()), frameRect_.Height());
609     bool ret = TextPattern::OnDirtyLayoutWrapperSwap(dirty, config);
610     UpdateScrollStateAfterLayout(config.frameSizeChange);
611     UpdateMagnifierStateAfterLayout(config.frameSizeChange);
612     IF_TRUE(!isRichEditorInit_, FireOnReady());
613     MoveCaretOnLayoutSwap();
614     HandleTasksOnLayoutSwap();
615     HandleSelectOverlayOnLayoutSwap();
616     IF_TRUE(originalFrameRect.GetSize() != frameRect_.GetSize(), {
617         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "frame size change");
618         TriggerAvoidOnCaretChangeNextFrame();
619     });
620     IF_TRUE(!isModifyingContent_, UpdateCaretInfoToController());
621     auto host = GetHost();
622     CHECK_NULL_RETURN(host, ret);
623     SupplementIdealSizeWidth(host);
624     auto context = host->GetRenderContext();
625     CHECK_NULL_RETURN(context, ret);
626     if (context->GetClipEdge().has_value()) {
627         auto geometryNode = host->GetGeometryNode();
628         auto frameOffset = geometryNode->GetFrameOffset();
629         auto frameSize = geometryNode->GetFrameSize();
630         auto height = static_cast<float>(paragraphs_.GetHeight() + std::fabs(baselineOffset_));
631         if (!context->GetClipEdge().value() && LessNotEqual(frameSize.Height(), height)) {
632             RectF boundsRect(frameOffset.GetX(), frameOffset.GetY(), frameSize.Width(), height);
633             CHECK_NULL_RETURN(overlayMod_, ret);
634             overlayMod_->SetBoundsRect(boundsRect);
635         }
636     }
637     caretUpdateType_ = CaretUpdateType::NONE;
638     UpdateImagePreviewParam();
639     return ret;
640 }
641 
HandleSelectOverlayOnLayoutSwap()642 void RichEditorPattern::HandleSelectOverlayOnLayoutSwap()
643 {
644     bool needToRefreshSelectOverlay = textSelector_.IsValid() && SelectOverlayIsOn() && !IsPreviewTextInputting();
645     CHECK_NULL_VOID(needToRefreshSelectOverlay);
646     auto overlayTask = [weak = WeakClaim(this)]() {
647         auto pattern = weak.Upgrade();
648         CHECK_NULL_VOID(pattern);
649         auto selectOverlay = pattern->selectOverlay_;
650         CHECK_NULL_VOID(selectOverlay);
651         pattern->CalculateHandleOffsetAndShowOverlay();
652         selectOverlay->ProcessOverlay({ .menuIsShow = selectOverlay->IsCurrentMenuVisibile(), .animation = true });
653     };
654     if (AnimationUtils::IsImplicitAnimationOpen()) {
655         auto pipeline = PipelineContext::GetCurrentContextSafely();
656         CHECK_NULL_VOID(pipeline);
657         pipeline->AddAfterRenderTask(overlayTask);
658     } else {
659         overlayTask();
660     }
661 }
662 
FireOnReady()663 void RichEditorPattern::FireOnReady()
664 {
665     auto eventHub = GetEventHub<RichEditorEventHub>();
666     CHECK_NULL_VOID(eventHub);
667     eventHub->FireOnReady();
668     ClearOperationRecords();
669     isFirstCallOnReady_ = true;
670     isRichEditorInit_ = true;
671 }
672 
SupplementIdealSizeWidth(const RefPtr<FrameNode> & frameNode)673 void RichEditorPattern::SupplementIdealSizeWidth(const RefPtr<FrameNode>& frameNode)
674 {
675     auto layoutProperty = frameNode->GetLayoutProperty<RichEditorLayoutProperty>();
676     CHECK_NULL_VOID(layoutProperty);
677     auto&& constraint = layoutProperty->GetCalcLayoutConstraint();
678     if (!constraint || !constraint->selfIdealSize.has_value() || !constraint->selfIdealSize->Width().has_value()) {
679         layoutProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(frameRect_.Width()), std::nullopt));
680     }
681 }
682 
MoveCaretOnLayoutSwap()683 void RichEditorPattern::MoveCaretOnLayoutSwap()
684 {
685     MoveCaretAfterTextChange();
686     if (needMoveCaretToContentRect_ || isEditing_) {
687         MoveCaretToContentRect();
688         needMoveCaretToContentRect_ = false;
689     }
690 }
691 
CreateImageSourceInfo(const ImageSpanOptions & options)692 std::function<ImageSourceInfo()> RichEditorPattern::CreateImageSourceInfo(const ImageSpanOptions& options)
693 {
694     std::string src;
695     RefPtr<PixelMap> pixMap = nullptr;
696     std::string bundleName;
697     std::string moduleName;
698     if (options.image.has_value()) {
699         src = options.image.value();
700     }
701     if (options.imagePixelMap.has_value()) {
702         pixMap = options.imagePixelMap.value();
703     }
704     if (options.bundleName.has_value()) {
705         bundleName = options.bundleName.value();
706     }
707     if (options.moduleName.has_value()) {
708         moduleName = options.moduleName.value();
709     }
710     auto createSourceInfoFunc = [src, noPixMap = !options.imagePixelMap.has_value(), pixMap, bundleName,
711                                     moduleName]() -> ImageSourceInfo {
712 #if defined(PIXEL_MAP_SUPPORTED)
713         if (noPixMap) {
714             return { src, bundleName, moduleName };
715         }
716         return ImageSourceInfo(pixMap);
717 #else
718         return { src, bundleName, moduleName };
719 #endif
720     };
721     return std::move(createSourceInfoFunc);
722 }
723 
GetTextContentLength()724 int32_t RichEditorPattern::GetTextContentLength()
725 {
726     if (isSpanStringMode_ && styledString_) {
727         return styledString_->GetLength();
728     }
729     if (!spans_.empty()) {
730         auto it = spans_.rbegin();
731         return (*it)->position;
732     }
733     return 0;
734 }
735 
SetImagePreviewMenuParam(std::function<void ()> & builder,const SelectMenuParam & menuParam)736 void RichEditorPattern::SetImagePreviewMenuParam(std::function<void()>& builder, const SelectMenuParam& menuParam)
737 {
738     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "SetImagePreviewMenuParam");
739     oneStepDragParam_ = std::make_shared<OneStepDragParam>(builder, menuParam);
740 }
741 
EnableImageDrag(const RefPtr<ImageSpanNode> & imageNode,bool isEnable)742 void RichEditorPattern::EnableImageDrag(const RefPtr<ImageSpanNode>& imageNode, bool isEnable)
743 {
744     CHECK_NULL_VOID(imageNode);
745     if (isEnable) {
746         EnableOneStepDrag(imageNode);
747     } else {
748         DisableDrag(imageNode);
749     }
750 }
751 
DisableDrag(const RefPtr<ImageSpanNode> & imageNode)752 void RichEditorPattern::DisableDrag(const RefPtr<ImageSpanNode>& imageNode)
753 {
754     // Disable the image itself event
755     imageNode->SetDraggable(false);
756     auto gesture = imageNode->GetOrCreateGestureEventHub();
757     CHECK_NULL_VOID(gesture);
758     gesture->SetDragEvent(nullptr, { PanDirection::DOWN }, 0, Dimension(0));
759 }
760 
SetImageSelfResponseEvent(bool isEnable)761 void RichEditorPattern::SetImageSelfResponseEvent(bool isEnable)
762 {
763     CHECK_NULL_VOID(oneStepDragParam_);
764     CHECK_NULL_VOID(isImageSelfResponseEvent_ != isEnable);
765     auto host = GetHost();
766     CHECK_NULL_VOID(host);
767     for (const auto& spanNode : host->GetChildren()) {
768         if (spanNode->GetTag() != V2::IMAGE_ETS_TAG) {
769             continue;
770         }
771         auto imageNode = DynamicCast<ImageSpanNode>(spanNode);
772         if (auto hub = imageNode->GetOrCreateGestureEventHub(); hub) {
773             hub->SetHitTestMode(isEnable ? HitTestMode::HTMDEFAULT : HitTestMode::HTMNONE);
774         }
775     }
776     isImageSelfResponseEvent_ = isEnable;
777 }
778 
EnableOneStepDrag(const RefPtr<ImageSpanNode> & imageNode)779 void RichEditorPattern::EnableOneStepDrag(const RefPtr<ImageSpanNode>& imageNode)
780 {
781     CHECK_NULL_VOID(oneStepDragParam_);
782     imageNode->SetDraggable(true);
783     auto imageGestureHub = imageNode->GetOrCreateGestureEventHub();
784     CHECK_NULL_VOID(imageGestureHub);
785     imageGestureHub->InitDragDropEvent();
786 
787     CopyDragCallback(imageNode);
788 }
789 
OneStepDragParam(const std::function<void ()> & builder,const SelectMenuParam & selectMenuParam)790 RichEditorPattern::OneStepDragParam::OneStepDragParam(const std::function<void()>& builder,
791     const SelectMenuParam& selectMenuParam)
792 {
793     menuBuilder = builder;
794     onAppear = selectMenuParam.onAppear;
795     menuParam.previewMode = MenuPreviewMode::IMAGE;
796     menuParam.type = MenuType::CONTEXT_MENU;
797     menuParam.onDisappear = selectMenuParam.onDisappear;
798     menuParam.previewAnimationOptions.scaleFrom = 1.0f;
799     menuParam.previewBorderRadius = BorderRadiusProperty(Dimension(0, DimensionUnit::VP));
800     menuParam.backgroundBlurStyle = static_cast<int>(BlurStyle::NO_MATERIAL);
801 }
802 
GetMenuParam(const RefPtr<ImageSpanNode> & imageNode)803 MenuParam RichEditorPattern::OneStepDragParam::GetMenuParam(const RefPtr<ImageSpanNode>& imageNode)
804 {
805     CHECK_NULL_RETURN(imageNode, menuParam);
806     auto res = menuParam;
807     res.onAppear = [weak = WeakClaim(RawPtr(imageNode)), onAppear = this->onAppear]() {
808         CHECK_NULL_VOID(onAppear);
809         auto imageNode = weak.Upgrade();
810         CHECK_NULL_VOID(imageNode);
811         auto& imageSpanItem = imageNode->GetSpanItem();
812         onAppear(imageSpanItem->rangeStart, imageSpanItem->position);
813     };
814     auto dispSize = imageNode->GetGeometryNode()->GetMarginFrameSize();
815     CHECK_NULL_RETURN(dispSize.IsPositive(), res);
816     auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
817     CHECK_NULL_RETURN(imageLayoutProperty, res);
818     auto imageSourceInfo = imageLayoutProperty->GetImageSourceInfoValue();
819     CHECK_NULL_RETURN(imageSourceInfo.IsPixmap(), res);
820     auto pixelMap = imageSourceInfo.GetPixmap();
821     CHECK_NULL_RETURN(pixelMap, res);
822 
823     auto realWidth = pixelMap->GetWidth();
824     auto realHeight = pixelMap->GetHeight();
825     float scale = std::max((float) realWidth / dispSize.Width(), (float) realHeight / dispSize.Height());
826     scale = std::max(scale, 1.1f);
827     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "realSize=[%{public}d,%{public}d], scale=%{public}.2f",
828         realWidth, realHeight, scale);
829     res.previewAnimationOptions.scaleTo = scale;
830     return res;
831 }
832 
UpdateImagePreviewParam()833 void RichEditorPattern::UpdateImagePreviewParam()
834 {
835     CHECK_NULL_VOID(oneStepDragParam_);
836     while (!dirtyImageNodes.empty()) {
837         auto weakImageNode = dirtyImageNodes.front();
838         dirtyImageNodes.pop();
839         auto imageNode = weakImageNode.Upgrade();
840         if (!imageNode) {
841             continue;
842         }
843 
844 #ifndef ACE_UNITTEST
845         auto& menuBuilder = oneStepDragParam_->menuBuilder;
846         auto& previewBuilder = oneStepDragParam_->previewBuilder;
847         auto resType = ResponseType::LONG_PRESS;
848         auto menuParam = oneStepDragParam_->GetMenuParam(imageNode);
849         ViewStackProcessor::GetInstance()->Push(imageNode);
850         ViewAbstractModel::GetInstance()->BindContextMenu(resType, menuBuilder, menuParam, previewBuilder);
851         ViewAbstractModel::GetInstance()->BindDragWithContextMenuParams(menuParam);
852         ViewStackProcessor::GetInstance()->Finish();
853 #endif
854     }
855 }
856 
CopyDragCallback(const RefPtr<ImageSpanNode> & imageNode)857 void RichEditorPattern::CopyDragCallback(const RefPtr<ImageSpanNode>& imageNode)
858 {
859     auto host = GetHost();
860     CHECK_NULL_VOID(host);
861     auto hostEventHub = host->GetEventHub<EventHub>();
862     auto imageEventHub = imageNode->GetEventHub<EventHub>();
863     CHECK_NULL_VOID(hostEventHub && imageEventHub);
864 
865     auto getJsonRange = [](const RefPtr<ImageSpanNode>& imageNode) -> std::string {
866         CHECK_NULL_RETURN(imageNode, "");
867         auto imageSpanItem = imageNode->GetSpanItem();
868         CHECK_NULL_RETURN(imageSpanItem, "");
869         auto jsonRange = JsonUtil::Create(true);
870         jsonRange->Put("rangeStart", imageSpanItem->rangeStart);
871         jsonRange->Put("rangeEnd", imageSpanItem->position);
872         return jsonRange->ToString();
873     };
874 
875     // start
876     auto start = hostEventHub->GetOnDragStart();
877     auto oneStepDragStart = [weakImageNode = WeakClaim(RawPtr(imageNode)), start, getJsonRange](
878         const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& extraParams) -> DragDropInfo {
879         auto imageNode = weakImageNode.Upgrade();
880         return start(event, getJsonRange(imageNode));
881     };
882     IF_TRUE(start, imageEventHub->SetOnDragStart(std::move(oneStepDragStart)));
883 
884     // end
885     auto resetOnDragEnd = [weakPtr = WeakClaim(this), scopeId = Container::CurrentId()]() {
886         ContainerScope scope(scopeId);
887         auto pattern = weakPtr.Upgrade();
888         CHECK_NULL_VOID(pattern);
889     };
890     auto end = hostEventHub->GetCustomerOnDragEndFunc();
891     auto oneStepDragEnd = [end, resetOnDragEnd](const RefPtr<OHOS::Ace::DragEvent>& event) {
892         resetOnDragEnd();
893         IF_TRUE(end, end(event));
894     };
895     imageEventHub->SetCustomerOnDragFunc(DragFuncType::DRAG_END, std::move(oneStepDragEnd));
896 
897     IF_TRUE(end, imageEventHub->SetCustomerOnDragFunc(DragFuncType::DRAG_END, std::move(end)));
898 }
899 
SetGestureOptions(UserGestureOptions options,RefPtr<SpanItem> spanItem)900 void RichEditorPattern::SetGestureOptions(UserGestureOptions options, RefPtr<SpanItem> spanItem)
901 {
902     IF_TRUE(options.onClick, spanItem->SetOnClickEvent(std::move(options.onClick)));
903     IF_TRUE(options.onLongPress, spanItem->SetLongPressEvent(std::move(options.onLongPress)));
904     IF_TRUE(options.onDoubleClick, spanItem->SetDoubleClickEvent(std::move(options.onDoubleClick)));
905 }
906 
AddSpanHoverEvent(RefPtr<SpanItem> spanItem,const RefPtr<FrameNode> & frameNode,const SpanOptionBase & options)907 void RichEditorPattern::AddSpanHoverEvent(
908     RefPtr<SpanItem> spanItem, const RefPtr<FrameNode>& frameNode, const SpanOptionBase& options)
909 {
910     auto onHoverFunc = options.userMouseOption.onHover;
911     CHECK_NULL_VOID(spanItem && frameNode && onHoverFunc);
912     auto tag = frameNode->GetTag();
913     spanItem->SetHoverEvent(std::move(onHoverFunc));
914     auto eventHub = frameNode->GetEventHub<EventHub>();
915     CHECK_NULL_VOID(eventHub);
916     auto inputHub = eventHub->GetOrCreateInputEventHub();
917     CHECK_NULL_VOID(inputHub);
918     auto hoverTask = [weak = WeakClaim(spanItem.GetRawPtr()), tag](bool isHover, HoverInfo& info) {
919         auto item = weak.Upgrade();
920         if (item && item->onHover) {
921             TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "[%{public}s] onHover status :[%{public}d]", tag.c_str(), isHover);
922             item->onHover(isHover, info);
923         }
924     };
925     auto hoverEvent = MakeRefPtr<InputEvent>(std::move(hoverTask));
926     inputHub->AddOnHoverEvent(hoverEvent);
927 }
928 
AddImageSpan(const ImageSpanOptions & options,bool isPaste,int32_t index,bool updateCaret)929 int32_t RichEditorPattern::AddImageSpan(const ImageSpanOptions& options, bool isPaste, int32_t index, bool updateCaret)
930 {
931     auto host = GetHost();
932     CHECK_NULL_RETURN(host, -1);
933     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "options=%{public}s", options.ToString().c_str());
934     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "isPaste=%{public}d, index=%{public}d, updateCaret=%{public}d",
935         isPaste, index, updateCaret);
936     auto imageNode = ImageSpanNode::GetOrCreateSpanNode(V2::IMAGE_ETS_TAG,
937         ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<ImagePattern>(); });
938     auto pattern = imageNode->GetPattern<ImagePattern>();
939     CHECK_NULL_RETURN(pattern, -1);
940     pattern->SetSyncLoad(true);
941     auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
942     int32_t insertIndex = std::min(options.offset.value_or(GetTextContentLength()), GetTextContentLength());
943     RichEditorChangeValue changeValue;
944     CHECK_NULL_RETURN(BeforeAddImage(changeValue, options, insertIndex), -1);
945     EnableImageDrag(imageNode, oneStepDragParam_ != nullptr);
946     AddOprationWhenAddImage(options.offset.value_or(static_cast<int32_t>(GetTextContentLength())));
947     int32_t spanIndex = TextSpanSplit(insertIndex);
948     if (spanIndex == -1) {
949         spanIndex = static_cast<int32_t>(host->GetChildren().size());
950     }
951     imageNode->MountToParent(host, spanIndex);
952     std::function<ImageSourceInfo()> createSourceInfoFunc = CreateImageSourceInfo(options);
953     imageLayoutProperty->UpdateImageSourceInfo(createSourceInfoFunc());
954     auto renderContext = imageNode->GetRenderContext();
955     if (renderContext) {
956         renderContext->SetNeedAnimateFlag(false);
957     }
958     SetImageLayoutProperty(imageNode, options);
959     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
960     host->MarkModifyDone();
961     auto spanItem = imageNode->GetSpanItem();
962     // The length of the imageSpan defaults to the length of a character to calculate the position
963     spanItem->content = " ";
964     spanItem->SetImageSpanOptions(options);
965     AddSpanItem(spanItem, spanIndex);
966     SetGestureOptions(options.userGestureOption, spanItem);
967     AddSpanHoverEvent(spanItem, imageNode, options);
968     placeholderCount_++;
969     if (updateCaret) {
970         SetCaretPosition(insertIndex + spanItem->content.length());
971         SetNeedMoveCaretToContentRect();
972     }
973     ResetSelectionAfterAddSpan(isPaste);
974     AfterContentChange(changeValue);
975     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "end");
976     return spanIndex;
977 }
978 
AddOprationWhenAddImage(int32_t beforeCaretPos)979 void RichEditorPattern::AddOprationWhenAddImage(int32_t beforeCaretPos)
980 {
981     OperationRecord record;
982     record.beforeCaretPosition = beforeCaretPos;
983     record.addText = " ";
984     ClearRedoOperationRecords();
985     record.afterCaretPosition = record.beforeCaretPosition + 1;
986     AddOperationRecord(record);
987 }
988 
ResetSelectionAfterAddSpan(bool isPaste)989 void RichEditorPattern::ResetSelectionAfterAddSpan(bool isPaste)
990 {
991     if (isPaste || !textSelector_.IsValid()) {
992         return;
993     }
994     CloseSelectOverlay();
995     ResetSelection();
996     if (isEditing_ && !caretVisible_) {
997         StartTwinkling();
998     }
999 }
1000 
AddSpanItem(const RefPtr<SpanItem> & item,int32_t offset)1001 void RichEditorPattern::AddSpanItem(const RefPtr<SpanItem>& item, int32_t offset)
1002 {
1003     auto host = GetHost();
1004     CHECK_NULL_VOID(host);
1005     if (offset == -1) {
1006         offset = static_cast<int32_t>(host->GetChildren().size());
1007     }
1008     offset = std::clamp(offset, 0, static_cast<int32_t>(host->GetChildren().size()) - 1);
1009     auto it = spans_.begin();
1010     std::advance(it, offset);
1011     spans_.insert(it, item);
1012     UpdateSpanPosition();
1013 }
1014 
OnAttachToFrameNode()1015 void RichEditorPattern::OnAttachToFrameNode()
1016 {
1017     TextPattern::OnAttachToFrameNode();
1018     richEditorInstanceId_ = Container::CurrentIdSafely();
1019     auto frameNode = GetHost();
1020     CHECK_NULL_VOID(frameNode);
1021     frameId_ = frameNode->GetId();
1022     auto renderContext = frameNode->GetRenderContext();
1023     CHECK_NULL_VOID(renderContext);
1024     renderContext->UpdateClipEdge(true);
1025     renderContext->SetClipToFrame(true);
1026     StylusDetectorMgr::GetInstance()->AddTextFieldFrameNode(frameNode, WeakClaim(this));
1027     auto context = GetContext();
1028     CHECK_NULL_VOID(context);
1029     context->AddWindowSizeChangeCallback(frameId_);
1030 }
1031 
OnDetachFromFrameNode(FrameNode * node)1032 void RichEditorPattern::OnDetachFromFrameNode(FrameNode* node)
1033 {
1034     TextPattern::OnDetachFromFrameNode(node);
1035     ScrollablePattern::OnDetachFromFrameNode(node);
1036     ClearOnFocusTextField();
1037     auto context = GetContext();
1038     CHECK_NULL_VOID(context);
1039     context->RemoveWindowSizeChangeCallback(frameId_);
1040 }
1041 
AddPlaceholderSpan(const RefPtr<UINode> & customNode,const SpanOptionBase & options)1042 int32_t RichEditorPattern::AddPlaceholderSpan(const RefPtr<UINode>& customNode, const SpanOptionBase& options)
1043 {
1044     auto host = GetHost();
1045     CHECK_NULL_RETURN(host, 0);
1046     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AddPlaceholderSpan");
1047     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "options=%{public}s", options.ToString().c_str());
1048     auto placeholderSpanNode = PlaceholderSpanNode::GetOrCreateSpanNode(V2::PLACEHOLDER_SPAN_ETS_TAG,
1049         ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<PlaceholderSpanPattern>(); });
1050     CHECK_NULL_RETURN(placeholderSpanNode, 0);
1051     customNode->MountToParent(placeholderSpanNode);
1052     SetSelfAndChildDraggableFalse(customNode);
1053     auto focusHub = placeholderSpanNode->GetOrCreateFocusHub();
1054     focusHub->SetFocusable(false);
1055     int32_t insertIndex = options.offset.value_or(GetTextContentLength());
1056     insertIndex = std::min(insertIndex, GetTextContentLength());
1057     int32_t spanIndex = TextSpanSplit(insertIndex);
1058     if (spanIndex == -1) {
1059         spanIndex = static_cast<int32_t>(host->GetChildren().size());
1060     }
1061     placeholderSpanNode->MountToParent(host, spanIndex);
1062     auto renderContext = placeholderSpanNode->GetRenderContext();
1063     if (renderContext) {
1064         renderContext->SetNeedAnimateFlag(false);
1065     }
1066     auto spanItem = placeholderSpanNode->GetSpanItem();
1067     spanItem->content = " ";
1068     spanItem->spanItemType = SpanItemType::CustomSpan;
1069     spanItem->SetCustomNode(customNode);
1070     AddSpanItem(spanItem, spanIndex);
1071     placeholderCount_++;
1072     SetCaretPosition(insertIndex + spanItem->content.length());
1073     ResetSelectionAfterAddSpan(false);
1074     auto placeholderPipelineContext = placeholderSpanNode->GetContext();
1075     if (placeholderPipelineContext) {
1076         placeholderPipelineContext->SetDoKeyboardAvoidAnimate(false);
1077     }
1078     SetNeedMoveCaretToContentRect();
1079     placeholderSpanNode->MarkModifyDone();
1080     placeholderSpanNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1081     host->MarkModifyDone();
1082     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1083     return spanIndex;
1084 }
1085 
SetSelfAndChildDraggableFalse(const RefPtr<UINode> & customNode)1086 void RichEditorPattern::SetSelfAndChildDraggableFalse(const RefPtr<UINode>& customNode)
1087 {
1088     CHECK_NULL_VOID(customNode);
1089     auto frameNode = DynamicCast<FrameNode>(customNode);
1090     if (frameNode) {
1091         auto eventHub = frameNode->GetEventHub<EventHub>();
1092         CHECK_NULL_VOID(eventHub);
1093         auto gestureEventHub = eventHub->GetGestureEventHub();
1094         CHECK_NULL_VOID(gestureEventHub);
1095         gestureEventHub->SetDragForbiddenForcely(true);
1096     }
1097     for (const auto& child : customNode->GetChildren()) {
1098         SetSelfAndChildDraggableFalse(child);
1099     }
1100 }
1101 
AddTextSpan(TextSpanOptions options,bool isPaste,int32_t index)1102 int32_t RichEditorPattern::AddTextSpan(TextSpanOptions options, bool isPaste, int32_t index)
1103 {
1104     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "options=%{private}s", options.ToString().c_str());
1105     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "isPaste=%{public}d, index=%{public}d", isPaste, index);
1106     NotifyExitTextPreview();
1107     OperationRecord record;
1108     auto textContentLength = GetTextContentLength();
1109     if (options.offset) {
1110         options.offset = std::clamp(options.offset.value(), 0, textContentLength);
1111     }
1112     record.beforeCaretPosition = std::clamp(options.offset.value_or(textContentLength), 0, textContentLength);
1113     record.addText = options.value;
1114     RichEditorChangeValue changeValue;
1115     CHECK_NULL_RETURN(BeforeChangeText(changeValue, options), -1);
1116     ClearRedoOperationRecords();
1117     record.afterCaretPosition =
1118         record.beforeCaretPosition + static_cast<int32_t>(StringUtils::ToWstring(options.value).length());
1119     AddOperationRecord(record);
1120     auto ret = AddTextSpanOperation(options, isPaste, index, false);
1121     SetNeedMoveCaretToContentRect();
1122     if (!previewTextRecord_.IsValid()) {
1123         AfterContentChange(changeValue);
1124     }
1125     return ret;
1126 }
1127 
AddTextSpanOperation(const TextSpanOptions & options,bool isPaste,int32_t index,bool needLeadingMargin,bool updateCaretPosition)1128 int32_t RichEditorPattern::AddTextSpanOperation(
1129     const TextSpanOptions& options, bool isPaste, int32_t index, bool needLeadingMargin, bool updateCaretPosition)
1130 {
1131     auto host = GetHost();
1132     CHECK_NULL_RETURN(host, -1);
1133 
1134     auto* stack = ViewStackProcessor::GetInstance();
1135     auto nodeId = stack->ClaimNodeId();
1136     auto spanNode = SpanNode::GetOrCreateSpanNode(nodeId);
1137 
1138     int32_t spanIndex = 0;
1139     int32_t offset = -1;
1140     if (options.offset.has_value()) {
1141         offset = TextSpanSplit(options.offset.value(), needLeadingMargin);
1142         if (offset == -1) {
1143             spanIndex = static_cast<int32_t>(host->GetChildren().size());
1144         } else {
1145             spanIndex = offset;
1146         }
1147         spanNode->MountToParent(host, offset);
1148     } else if (index != -1) {
1149         spanNode->MountToParent(host, index);
1150         spanIndex = index;
1151     } else {
1152         spanIndex = static_cast<int32_t>(host->GetChildren().size());
1153         spanNode->MountToParent(host);
1154     }
1155     UpdateSpanNode(spanNode, options);
1156     auto spanItem = spanNode->GetSpanItem();
1157     spanItem->content = options.value;
1158     spanItem->SetTextStyle(options.style);
1159     spanItem->useThemeFontColor = options.useThemeFontColor;
1160     spanItem->useThemeDecorationColor = options.useThemeDecorationColor;
1161     AddSpanItem(spanItem, offset);
1162     if (!options.style.has_value()) {
1163         SetDefaultColor(spanNode);
1164     }
1165     if (options.paraStyle) {
1166         UpdateParagraphStyle(spanNode, *options.paraStyle);
1167     }
1168     SetGestureOptions(options.userGestureOption, spanItem);
1169     if (updateCaretPosition && !previewTextRecord_.IsValid()) {
1170         if (options.offset.has_value()) {
1171             SetCaretPosition(options.offset.value() + StringUtils::ToWstring(options.value).length());
1172         } else {
1173             SetCaretPosition(GetTextContentLength());
1174         }
1175     }
1176     ResetSelectionAfterAddSpan(isPaste);
1177     SpanNodeFission(spanNode);
1178     return spanIndex;
1179 }
1180 
UpdateSpanNode(RefPtr<SpanNode> spanNode,const TextSpanOptions & options)1181 void RichEditorPattern::UpdateSpanNode(RefPtr<SpanNode> spanNode, const TextSpanOptions& options)
1182 {
1183     spanNode->UpdateContent(options.value);
1184     spanNode->AddPropertyInfo(PropertyInfo::NONE);
1185     CHECK_NULL_VOID(options.style.has_value());
1186 
1187     TextStyle textStyle = options.style.value();
1188     spanNode->UpdateTextColor(textStyle.GetTextColor());
1189     spanNode->AddPropertyInfo(PropertyInfo::FONTCOLOR);
1190     spanNode->UpdateFontSize(textStyle.GetFontSize());
1191     spanNode->AddPropertyInfo(PropertyInfo::FONTSIZE);
1192     spanNode->UpdateItalicFontStyle(textStyle.GetFontStyle());
1193     spanNode->AddPropertyInfo(PropertyInfo::FONTSTYLE);
1194     spanNode->UpdateFontWeight(textStyle.GetFontWeight());
1195     spanNode->AddPropertyInfo(PropertyInfo::FONTWEIGHT);
1196     spanNode->UpdateFontFamily(textStyle.GetFontFamilies());
1197     spanNode->AddPropertyInfo(PropertyInfo::FONTFAMILY);
1198     spanNode->UpdateTextDecoration(textStyle.GetTextDecoration());
1199     spanNode->AddPropertyInfo(PropertyInfo::TEXTDECORATION);
1200     spanNode->UpdateTextDecorationColor(textStyle.GetTextDecorationColor());
1201     spanNode->AddPropertyInfo(PropertyInfo::NONE);
1202     spanNode->UpdateTextDecorationStyle(textStyle.GetTextDecorationStyle());
1203     spanNode->AddPropertyInfo(PropertyInfo::NONE);
1204     spanNode->UpdateTextShadow(textStyle.GetTextShadows());
1205     spanNode->AddPropertyInfo(PropertyInfo::TEXTSHADOW);
1206     spanNode->UpdateLineHeight(textStyle.GetLineHeight());
1207     spanNode->AddPropertyInfo(PropertyInfo::LINEHEIGHT);
1208     spanNode->UpdateLetterSpacing(textStyle.GetLetterSpacing());
1209     spanNode->AddPropertyInfo(PropertyInfo::LETTERSPACE);
1210     spanNode->UpdateFontFeature(textStyle.GetFontFeatures());
1211     spanNode->AddPropertyInfo(PropertyInfo::FONTFEATURE);
1212 }
1213 
AddSymbolSpan(const SymbolSpanOptions & options,bool isPaste,int32_t index)1214 int32_t RichEditorPattern::AddSymbolSpan(const SymbolSpanOptions& options, bool isPaste, int32_t index)
1215 {
1216     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "options=%{public}s", options.ToString().c_str());
1217     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "isPaste=%{public}d, index=%{public}d", isPaste, index);
1218 
1219     RichEditorChangeValue changeValue;
1220     CHECK_NULL_RETURN(BeforeAddSymbol(changeValue, options), -1);
1221     OperationRecord record;
1222     record.beforeCaretPosition = options.offset.value_or(static_cast<int32_t>(GetTextContentLength()));
1223     record.addText = " ";
1224     ClearRedoOperationRecords();
1225     record.afterCaretPosition = record.beforeCaretPosition +
1226 	    static_cast<int32_t>(std::to_string(options.symbolId).length());
1227     AddOperationRecord(record);
1228     auto ret = AddSymbolSpanOperation(options, isPaste, index);
1229     SetNeedMoveCaretToContentRect();
1230     AfterContentChange(changeValue);
1231     return ret;
1232 }
1233 
AddSymbolSpanOperation(const SymbolSpanOptions & options,bool isPaste,int32_t index)1234 int32_t RichEditorPattern::AddSymbolSpanOperation(const SymbolSpanOptions& options, bool isPaste, int32_t index)
1235 {
1236     auto host = GetHost();
1237     CHECK_NULL_RETURN(host, -1);
1238 
1239     auto* stack = ViewStackProcessor::GetInstance();
1240     auto nodeId = stack->ClaimNodeId();
1241     auto spanNode = SpanNode::GetOrCreateSpanNode(V2::SYMBOL_SPAN_ETS_TAG, nodeId);
1242 
1243     int32_t insertIndex = options.offset.value_or(GetTextContentLength());
1244     insertIndex = std::min(insertIndex, GetTextContentLength());
1245     int32_t spanIndex = TextSpanSplit(insertIndex);
1246     if (spanIndex == -1) {
1247         spanIndex = static_cast<int32_t>(host->GetChildren().size());
1248     }
1249     spanNode->MountToParent(host, spanIndex);
1250     spanNode->UpdateContent(options.symbolId);
1251     spanNode->AddPropertyInfo(PropertyInfo::NONE);
1252     if (options.style.has_value()) {
1253         spanNode->UpdateFontSize(options.style.value().GetFontSize());
1254         spanNode->AddPropertyInfo(PropertyInfo::FONTSIZE);
1255         spanNode->UpdateFontWeight(options.style.value().GetFontWeight());
1256         spanNode->AddPropertyInfo(PropertyInfo::FONTWEIGHT);
1257         spanNode->UpdateSymbolColorList(options.style.value().GetSymbolColorList());
1258         spanNode->AddPropertyInfo(PropertyInfo::SYMBOL_COLOR);
1259         spanNode->UpdateSymbolRenderingStrategy(options.style.value().GetRenderStrategy());
1260         spanNode->AddPropertyInfo(PropertyInfo::SYMBOL_RENDERING_STRATEGY);
1261         spanNode->UpdateSymbolEffectStrategy(options.style.value().GetEffectStrategy());
1262         spanNode->AddPropertyInfo(PropertyInfo::SYMBOL_EFFECT_STRATEGY);
1263     }
1264     auto spanItem = spanNode->GetSpanItem();
1265     spanItem->content = "  ";
1266     spanItem->spanItemType = SpanItemType::SYMBOL;
1267     spanItem->SetSymbolId(options.symbolId);
1268     spanItem->SetTextStyle(options.style);
1269     spanItem->SetResourceObject(options.resourceObject);
1270     AddSpanItem(spanItem, spanIndex);
1271     SetCaretPosition(insertIndex + spanItem->content.length());
1272     ResetSelectionAfterAddSpan(false);
1273     SpanNodeFission(spanNode);
1274     return spanIndex;
1275 }
1276 
BeforeAddSymbol(RichEditorChangeValue & changeValue,const SymbolSpanOptions & options)1277 bool RichEditorPattern::BeforeAddSymbol(RichEditorChangeValue& changeValue, const SymbolSpanOptions& options)
1278 {
1279     auto eventHub = GetEventHub<RichEditorEventHub>();
1280     CHECK_NULL_RETURN(eventHub, false);
1281     CHECK_NULL_RETURN(eventHub->HasOnWillChange(), true);
1282 
1283     int32_t contentLength = GetTextContentLength();
1284     int32_t insertIndex = options.offset.value_or(contentLength);
1285     insertIndex = std::clamp(insertIndex, 0, contentLength);
1286 
1287     changeValue.SetRangeBefore({ insertIndex, insertIndex });
1288     changeValue.SetRangeAfter({ insertIndex, insertIndex + SYMBOL_SPAN_LENGTH });
1289     RichEditorAbstractSpanResult retInfo;
1290     TextInsertValueInfo info;
1291     CalcInsertValueObj(info, insertIndex, true);
1292     int32_t spanIndex = info.GetSpanIndex();
1293     retInfo.SetSpanIndex(spanIndex);
1294     retInfo.SetOffsetInSpan(0);
1295     retInfo.SetValue(std::to_string(options.symbolId));
1296     retInfo.SetEraseLength(SYMBOL_SPAN_LENGTH);
1297     retInfo.SetSpanRangeStart(insertIndex);
1298     retInfo.SetSpanRangeEnd(insertIndex + SYMBOL_SPAN_LENGTH);
1299     retInfo.SetSpanType(SpanResultType::SYMBOL);
1300 
1301     TextStyle style = options.style.value_or(TextStyle());
1302     retInfo.SetSymbolSpanStyle(SymbolSpanStyle(style));
1303     retInfo.SetValueResource(options.resourceObject);
1304 
1305     changeValue.SetRichEditorReplacedSymbolSpans(retInfo);
1306     auto ret = eventHub->FireOnWillChange(changeValue);
1307     return ret;
1308 }
1309 
AfterContentChange(RichEditorChangeValue & changeValue)1310 void RichEditorPattern::AfterContentChange(RichEditorChangeValue& changeValue)
1311 {
1312     auto eventHub = GetEventHub<RichEditorEventHub>();
1313     if (eventHub && eventHub->HasOnDidChange()) {
1314         eventHub->FireOnDidChange(changeValue);
1315     }
1316     ForceTriggerAvoidOnCaretChange();
1317 }
1318 
SpanNodeFission(RefPtr<SpanNode> & spanNode)1319 void RichEditorPattern::SpanNodeFission(RefPtr<SpanNode>& spanNode)
1320 {
1321     auto spanItem = spanNode->GetSpanItem();
1322     auto wContent = StringUtils::ToWstring(spanItem->content);
1323     auto spanStart = spanItem->position - static_cast<int32_t>(wContent.length());
1324     for (size_t i = 0; i < wContent.length(); i++) {
1325         if (wContent[i] == '\n') {
1326             TextSpanSplit(static_cast<int32_t>(spanStart + i + 1));
1327         }
1328     }
1329     UpdateSpanPosition();
1330 }
1331 
DeleteSpans(const RangeOptions & options)1332 void RichEditorPattern::DeleteSpans(const RangeOptions& options)
1333 {
1334     NotifyExitTextPreview();
1335     auto length = GetTextContentLength();
1336     int32_t start = options.start.value_or(0);
1337     int32_t end = options.end.value_or(length);
1338     if (start > end) {
1339         std::swap(start, end);
1340     }
1341     start = std::max(0, start);
1342     end = std::min(length, end);
1343     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "range=[%{public}d, %{public}d]", start, end);
1344     if (start > length || end < 0 || start == end) {
1345         return;
1346     }
1347     AdjustSelector(start, end);
1348     OperationRecord record;
1349     record.beforeCaretPosition = start;
1350     std::wstringstream wss;
1351     for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
1352         wss << StringUtils::ToWstring((*iter)->content);
1353     }
1354     std::wstring deleteText = wss.str().substr(start, end - start);
1355     record.deleteText = StringUtils::ToString(deleteText);
1356     RichEditorChangeValue changeValue;
1357     CHECK_NULL_VOID(BeforeChangeText(changeValue, record, RecordType::DEL_FORWARD, deleteText.length()));
1358     ClearRedoOperationRecords();
1359     record.afterCaretPosition = start;
1360     AddOperationRecord(record);
1361     DeleteSpansOperation(start, end);
1362     AfterContentChange(changeValue);
1363 }
1364 
DeleteSpansOperation(int32_t start,int32_t end)1365 void RichEditorPattern::DeleteSpansOperation(int32_t start, int32_t end)
1366 {
1367     auto startInfo = GetSpanPositionInfo(start);
1368     auto endInfo = GetSpanPositionInfo(end - 1);
1369     if (startInfo.spanIndex_ == endInfo.spanIndex_) {
1370         DeleteSpanByRange(start, end, startInfo);
1371     } else {
1372         DeleteSpansByRange(start, end, startInfo, endInfo);
1373     }
1374     RemoveEmptySpanItems();
1375     if (textSelector_.IsValid()) {
1376         SetCaretPosition(textSelector_.GetTextStart());
1377         CloseSelectOverlay();
1378         ResetSelection();
1379     }
1380     SetCaretOffset(start);
1381     auto host = GetHost();
1382     CHECK_NULL_VOID(host);
1383     auto childrens = host->GetChildren();
1384     if (childrens.empty() || GetTextContentLength() == 0) {
1385         SetCaretPosition(0);
1386     }
1387     UpdateSpanPosition();
1388 }
1389 
RemoveEmptySpanItems()1390 void RichEditorPattern::RemoveEmptySpanItems()
1391 {
1392     for (auto it = spans_.begin(); it != spans_.end();) {
1393         if ((*it)->content.empty()) {
1394             it = spans_.erase(it);
1395         } else {
1396             ++it;
1397         }
1398     }
1399 }
1400 
RemoveEmptySpanNodes()1401 void RichEditorPattern::RemoveEmptySpanNodes()
1402 {
1403     auto host = GetHost();
1404     CHECK_NULL_VOID(host);
1405     auto& spanNodes = host->GetChildren();
1406     for (auto it = spanNodes.begin(); it != spanNodes.end();) {
1407         auto spanNode = AceType::DynamicCast<SpanNode>(*it);
1408         if (!spanNode) {
1409             ++it;
1410             continue;
1411         }
1412         if (spanNode->GetSpanItem()->content.empty()) {
1413             it = host->RemoveChild(spanNode);
1414         } else {
1415             ++it;
1416         }
1417     }
1418 }
1419 
RemoveEmptySpans()1420 void RichEditorPattern::RemoveEmptySpans()
1421 {
1422     RemoveEmptySpanItems();
1423     RemoveEmptySpanNodes();
1424 }
1425 
DeleteSpanByRange(int32_t start,int32_t end,SpanPositionInfo info)1426 void RichEditorPattern::DeleteSpanByRange(int32_t start, int32_t end, SpanPositionInfo info)
1427 {
1428     auto host = GetHost();
1429     CHECK_NULL_VOID(host);
1430     auto childrens = host->GetChildren();
1431     auto it = childrens.begin();
1432     std::advance(it, info.spanIndex_);
1433     CHECK_NULL_VOID(it != childrens.end());
1434     if (start == info.spanStart_ && end == info.spanEnd_) {
1435         ClearContent(*it);
1436         host->RemoveChild(*it);
1437     } else {
1438         auto spanNode = DynamicCast<SpanNode>(*it);
1439         CHECK_NULL_VOID(spanNode);
1440         auto spanItem = spanNode->GetSpanItem();
1441         auto beforStr = StringUtils::ToWstring(spanItem->content).substr(0, start - info.spanStart_);
1442         auto endStr = StringUtils::ToWstring(spanItem->content).substr(end - info.spanStart_);
1443         std::wstring result = beforStr + endStr;
1444         auto str = StringUtils::ToString(result);
1445         spanNode->UpdateContent(str);
1446     }
1447     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1448     host->MarkModifyDone();
1449 }
1450 
DeleteSpansByRange(int32_t start,int32_t end,SpanPositionInfo startInfo,SpanPositionInfo endInfo)1451 void RichEditorPattern::DeleteSpansByRange(
1452     int32_t start, int32_t end, SpanPositionInfo startInfo, SpanPositionInfo endInfo)
1453 {
1454     auto host = GetHost();
1455     CHECK_NULL_VOID(host);
1456     auto childrens = host->GetChildren();
1457     CHECK_NULL_VOID(!childrens.empty());
1458 
1459     auto itStart = childrens.begin();
1460     if (startInfo.spanIndex_ >= static_cast<int32_t>(childrens.size())) {
1461         std::advance(itStart, static_cast<int32_t>(childrens.size()) - 1);
1462         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "startInfo.spanIndex_ is larger than childrens size");
1463     } else {
1464         std::advance(itStart, startInfo.spanIndex_);
1465     }
1466     CHECK_NULL_VOID(itStart != childrens.end());
1467     auto saveStartSpan = (start == startInfo.spanStart_) ? 0 : 1;
1468     if (saveStartSpan) {
1469         auto spanNodeStart = DynamicCast<SpanNode>(*itStart);
1470         CHECK_NULL_VOID(spanNodeStart);
1471         auto spanItemStart = spanNodeStart->GetSpanItem();
1472         auto beforStr = StringUtils::ToWstring(spanItemStart->content).substr(0, start - startInfo.spanStart_);
1473         auto strStart = StringUtils::ToString(beforStr);
1474         spanNodeStart->UpdateContent(strStart);
1475     }
1476     auto itEnd = childrens.begin();
1477     std::advance(itEnd, endInfo.spanIndex_);
1478     auto delEndSpan = (end == endInfo.spanEnd_) ? 1 : 0;
1479     if (!delEndSpan) {
1480         auto spanNodeEnd = DynamicCast<SpanNode>(*itEnd);
1481         CHECK_NULL_VOID(spanNodeEnd);
1482         auto spanItemEnd = spanNodeEnd->GetSpanItem();
1483         auto endStr =
1484             StringUtils::ToWstring(spanItemEnd->content).substr(end - endInfo.spanStart_, endInfo.spanEnd_ - end);
1485         auto strEnd = StringUtils::ToString(endStr);
1486         spanNodeEnd->UpdateContent(strEnd);
1487     }
1488     auto startIter = childrens.begin();
1489     std::advance(startIter, startInfo.spanIndex_ + saveStartSpan);
1490     auto endIter = childrens.begin();
1491     std::advance(endIter, endInfo.spanIndex_);
1492     for (auto iter = startIter; iter != endIter; ++iter) {
1493         ClearContent(*iter);
1494         host->RemoveChild(*iter);
1495     }
1496     if (endIter != childrens.end() && delEndSpan) {
1497         ClearContent(*endIter);
1498         host->RemoveChild(*endIter);
1499     }
1500     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1501     host->MarkModifyDone();
1502 }
1503 
GetLeftTextOfCursor(int32_t number)1504 std::u16string RichEditorPattern::GetLeftTextOfCursor(int32_t number)
1505 {
1506     if (number > caretPosition_) {
1507         number = caretPosition_;
1508     }
1509     auto start = caretPosition_;
1510     if (IsSelected()) {
1511         start = std::min(textSelector_.GetStart(), textSelector_.GetEnd());
1512     }
1513     auto stringText = GetSelectedText(start - number, start);
1514     return StringUtils::Str8ToStr16(stringText);
1515 }
1516 
GetRightTextOfCursor(int32_t number)1517 std::u16string RichEditorPattern::GetRightTextOfCursor(int32_t number)
1518 {
1519     auto end = caretPosition_;
1520     if (IsSelected()) {
1521         end = std::max(textSelector_.GetStart(), textSelector_.GetEnd());
1522     }
1523     auto stringText = GetSelectedText(end, end + number);
1524     return StringUtils::Str8ToStr16(stringText);
1525 }
1526 
GetTextIndexAtCursor()1527 int32_t RichEditorPattern::GetTextIndexAtCursor()
1528 {
1529     return caretPosition_;
1530 }
1531 
ClearContent(const RefPtr<UINode> & child)1532 void RichEditorPattern::ClearContent(const RefPtr<UINode>& child)
1533 {
1534     CHECK_NULL_VOID(child);
1535     if (child->GetTag() == V2::SPAN_ETS_TAG) {
1536         auto spanNode = DynamicCast<SpanNode>(child);
1537         if (spanNode) {
1538             spanNode->UpdateContent("");
1539             spanNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1540         }
1541         return;
1542     }
1543     auto imageSpanNode = DynamicCast<ImageSpanNode>(child);
1544     if (imageSpanNode) {
1545         imageSpanNode->GetSpanItem()->content.clear();
1546         imageSpanNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1547         return;
1548     }
1549     auto placeholderSpanNode = DynamicCast<PlaceholderSpanNode>(child);
1550     if (placeholderSpanNode) {
1551         placeholderSpanNode->GetSpanItem()->content.clear();
1552         placeholderSpanNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1553     }
1554 }
1555 
GetSpanPositionInfo(int32_t position)1556 SpanPositionInfo RichEditorPattern::GetSpanPositionInfo(int32_t position)
1557 {
1558     SpanPositionInfo spanPositionInfo(-1, -1, -1, -1);
1559     CHECK_NULL_RETURN(!spans_.empty(), spanPositionInfo);
1560     position = std::clamp(position, 0, GetTextContentLength());
1561     // find the spanItem where the position is
1562     auto it = std::find_if(spans_.begin(), spans_.end(), [position](const RefPtr<SpanItem>& spanItem) {
1563         return (spanItem->position - static_cast<int32_t>(StringUtils::ToWstring(spanItem->content).length()) <=
1564                    position) &&
1565                (position < spanItem->position);
1566     });
1567     if (it != spans_.end() && (*it)->unicode != 0 && (*it)->position - caretPosition_ + moveLength_ == 1) {
1568         it++;
1569         moveLength_++;
1570         position++;
1571     }
1572 
1573     // the position is at the end
1574     if (it == spans_.end()) {
1575         return spanPositionInfo;
1576     }
1577 
1578     spanPositionInfo.spanIndex_ = std::distance(spans_.begin(), it);
1579     auto contentLen = static_cast<int32_t>(StringUtils::ToWstring((*it)->content).length());
1580     spanPositionInfo.spanStart_ = (*it)->position - contentLen;
1581     spanPositionInfo.spanEnd_ = (*it)->position;
1582     spanPositionInfo.spanOffset_ = position - spanPositionInfo.spanStart_;
1583     return spanPositionInfo;
1584 }
1585 
CopyTextSpanStyle(RefPtr<SpanNode> & source,RefPtr<SpanNode> & target,bool needLeadingMargin)1586 void RichEditorPattern::CopyTextSpanStyle(RefPtr<SpanNode>& source, RefPtr<SpanNode>& target, bool needLeadingMargin)
1587 {
1588     CopyTextSpanFontStyle(source, target);
1589     CopyTextSpanLineStyle(source, target, needLeadingMargin);
1590 }
1591 
CopyTextSpanFontStyle(RefPtr<SpanNode> & source,RefPtr<SpanNode> & target)1592 void RichEditorPattern::CopyTextSpanFontStyle(RefPtr<SpanNode>& source, RefPtr<SpanNode>& target)
1593 {
1594     CHECK_NULL_VOID(source);
1595     CHECK_NULL_VOID(source->GetTag() == V2::SPAN_ETS_TAG);
1596     CHECK_NULL_VOID(target);
1597     COPY_SPAN_STYLE_IF_PRESENT(source, target, FontSize, PropertyInfo::FONTSIZE);
1598     COPY_SPAN_STYLE_IF_PRESENT(source, target, TextColor, PropertyInfo::FONTCOLOR);
1599     COPY_SPAN_STYLE_IF_PRESENT(source, target, ItalicFontStyle, PropertyInfo::FONTSTYLE);
1600     COPY_SPAN_STYLE_IF_PRESENT(source, target, FontWeight, PropertyInfo::FONTWEIGHT);
1601     COPY_SPAN_STYLE_IF_PRESENT(source, target, FontFamily, PropertyInfo::FONTFAMILY);
1602     COPY_SPAN_STYLE_IF_PRESENT(source, target, TextDecoration, PropertyInfo::TEXTDECORATION);
1603     COPY_SPAN_STYLE_IF_PRESENT(source, target, TextDecorationColor, PropertyInfo::NONE);
1604     COPY_SPAN_STYLE_IF_PRESENT(source, target, TextDecorationStyle, PropertyInfo::NONE);
1605     COPY_SPAN_STYLE_IF_PRESENT(source, target, TextCase, PropertyInfo::TEXTCASE);
1606     COPY_SPAN_STYLE_IF_PRESENT(source, target, LineHeight, PropertyInfo::LINEHEIGHT);
1607     COPY_SPAN_STYLE_IF_PRESENT(source, target, LetterSpacing, PropertyInfo::LETTERSPACE);
1608     COPY_SPAN_STYLE_IF_PRESENT(source, target, FontFeature, PropertyInfo::FONTFEATURE);
1609     COPY_SPAN_STYLE_IF_PRESENT(source, target, TextShadow, PropertyInfo::TEXTSHADOW);
1610     target->GetSpanItem()->useThemeFontColor = source->GetSpanItem()->useThemeFontColor;
1611     target->GetSpanItem()->useThemeDecorationColor = source->GetSpanItem()->useThemeDecorationColor;
1612 }
1613 
CopyTextSpanLineStyle(RefPtr<SpanNode> & source,RefPtr<SpanNode> & target,bool needLeadingMargin)1614 void RichEditorPattern::CopyTextSpanLineStyle(
1615     RefPtr<SpanNode>& source, RefPtr<SpanNode>& target, bool needLeadingMargin)
1616 {
1617     CHECK_NULL_VOID(source);
1618     CHECK_NULL_VOID(target);
1619     COPY_SPAN_STYLE_IF_PRESENT(source, target, TextAlign, PropertyInfo::TEXT_ALIGN);
1620     COPY_SPAN_STYLE_IF_PRESENT(source, target, WordBreak, PropertyInfo::WORD_BREAK);
1621     COPY_SPAN_STYLE_IF_PRESENT(source, target, LineBreakStrategy, PropertyInfo::LINE_BREAK_STRATEGY);
1622     needLeadingMargin |= previewTextRecord_.isPreviewTextInputting;
1623     if (source->HasLeadingMargin()) {
1624         auto leadingMargin = source->GetLeadingMarginValue({});
1625         if (!needLeadingMargin) {
1626             leadingMargin.pixmap.Reset();
1627         }
1628         target->UpdateLeadingMargin(leadingMargin);
1629         target->AddPropertyInfo(PropertyInfo::LEADING_MARGIN);
1630     }
1631 }
1632 
CopyGestureOption(const RefPtr<SpanNode> & source,RefPtr<SpanNode> & target)1633 void RichEditorPattern::CopyGestureOption(const RefPtr<SpanNode>& source, RefPtr<SpanNode>& target)
1634 {
1635     CHECK_NULL_VOID(source);
1636     CHECK_NULL_VOID(target);
1637     auto sourceItem = source->GetSpanItem();
1638     CHECK_NULL_VOID(sourceItem);
1639     auto targetItem = target->GetSpanItem();
1640     CHECK_NULL_VOID(targetItem);
1641 
1642     if (sourceItem->onClick) {
1643         auto tmpClickFunc = sourceItem->onClick;
1644         targetItem->SetOnClickEvent(std::move(tmpClickFunc));
1645     }
1646     if (sourceItem->onLongPress) {
1647         auto tmpLongPressFunc = sourceItem->onLongPress;
1648         targetItem->SetLongPressEvent(std::move(tmpLongPressFunc));
1649     }
1650 }
1651 
TextSpanSplit(int32_t position,bool needLeadingMargin)1652 int32_t RichEditorPattern::TextSpanSplit(int32_t position, bool needLeadingMargin)
1653 {
1654     CHECK_NULL_RETURN(!spans_.empty(), -1);
1655 
1656     SpanPositionInfo positionInfo = GetSpanPositionInfo(position);
1657     int32_t spanIndex = positionInfo.spanIndex_;
1658     int32_t spanStart = positionInfo.spanStart_;
1659     int32_t spanEnd = positionInfo.spanEnd_;
1660     int32_t offsetInSpan = positionInfo.spanOffset_;
1661     TAG_LOGD(AceLogTag::ACE_RICH_TEXT,
1662         "position=%{public}d, spanIndex=%{public}d, spanRange=[%{public}d,%{public}d], offsetInSpan=%{public}d",
1663         position, spanIndex, spanStart, spanEnd, offsetInSpan);
1664 
1665     CHECK_NULL_RETURN((offsetInSpan > 0), spanIndex);
1666 
1667     auto host = GetHost();
1668     CHECK_NULL_RETURN(host, -1);
1669     auto spanNode = DynamicCast<SpanNode>(host->GetChildAtIndex(spanIndex));
1670     CHECK_NULL_RETURN(spanNode, -1);
1671 
1672     auto spanItem = spanNode->GetSpanItem();
1673     auto spanItemContent = StringUtils::ToWstring(spanItem->content);
1674     offsetInSpan = std::min(offsetInSpan, static_cast<int32_t>(spanItemContent.length()));
1675 
1676     auto firstContent = spanItemContent.substr(0, offsetInSpan);
1677     spanNode->UpdateContent(StringUtils::ToString(firstContent));
1678     spanItem->position = spanStart + offsetInSpan;
1679 
1680     auto nodeId = ViewStackProcessor::GetInstance()->ClaimNodeId();
1681     auto newSpanNode = SpanNode::GetOrCreateSpanNode(nodeId);
1682     CHECK_NULL_RETURN(newSpanNode, -1);
1683 
1684     CopyTextSpanStyle(spanNode, newSpanNode, needLeadingMargin);
1685     CopyGestureOption(spanNode, newSpanNode);
1686     auto secondContent = spanItemContent.substr(offsetInSpan);
1687     newSpanNode->UpdateContent(StringUtils::ToString(secondContent));
1688     newSpanNode->MountToParent(host, spanIndex + 1);
1689 
1690     auto newSpanItem = newSpanNode->GetSpanItem();
1691     newSpanItem->rangeStart = spanStart + offsetInSpan;
1692     newSpanItem->position = spanEnd;
1693 
1694     auto spanIter = spans_.begin();
1695     std::advance(spanIter, spanIndex + 1);
1696     spans_.insert(spanIter, newSpanItem);
1697 
1698     return spanIndex + 1;
1699 }
1700 
GetCaretPosition()1701 int32_t RichEditorPattern::GetCaretPosition()
1702 {
1703     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "GetCaretPosition");
1704     return caretPosition_;
1705 }
1706 
SetCaretOffset(int32_t caretPosition)1707 bool RichEditorPattern::SetCaretOffset(int32_t caretPosition)
1708 {
1709     if (IsPreviewTextInputting()) {
1710         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "intercept operation in previewText state");
1711         return false;
1712     }
1713     int32_t inputCaretPosition = caretPosition;
1714     AdjustSelector(caretPosition, HandleType::SECOND);
1715     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "in=%{public}d, afterAdjust=%{public}d", inputCaretPosition, caretPosition);
1716     bool success = SetCaretPosition(caretPosition);
1717     auto host = GetHost();
1718     CHECK_NULL_RETURN(host, false);
1719     auto focusHub = host->GetOrCreateFocusHub();
1720     CHECK_NULL_RETURN(focusHub, false);
1721     if (focusHub->IsCurrentFocus()) {
1722         StartTwinkling();
1723     }
1724     CloseSelectOverlay();
1725     ResetSelection();
1726     return success;
1727 }
1728 
CalcCursorOffsetByPosition(int32_t position,float & selectLineHeight,bool downStreamFirst,bool needLineHighest)1729 OffsetF RichEditorPattern::CalcCursorOffsetByPosition(
1730     int32_t position, float& selectLineHeight, bool downStreamFirst, bool needLineHighest)
1731 {
1732     selectLineHeight = 0.0f;
1733     auto host = GetHost();
1734     CHECK_NULL_RETURN(host, OffsetF(0, 0));
1735     auto pipeline = host->GetContext();
1736     CHECK_NULL_RETURN(pipeline, OffsetF(0, 0));
1737     auto rootOffset = pipeline->GetRootRect().GetOffset();
1738     auto textPaintOffset = richTextRect_.GetOffset();
1739     needLineHighest |= IsCustomSpanInCaretPos(position, downStreamFirst);
1740     auto startOffset = paragraphs_.ComputeCursorOffset(position, selectLineHeight, downStreamFirst, needLineHighest);
1741     auto children = host->GetChildren();
1742     if (NearZero(selectLineHeight)) {
1743         if (children.empty() || GetTextContentLength() == 0) {
1744             return textPaintOffset - rootOffset;
1745         }
1746         if (std::all_of(children.begin(), children.end(), [](RefPtr<UINode>& node) {
1747                 CHECK_NULL_RETURN(node, false);
1748                 return (node->GetTag() == V2::IMAGE_ETS_TAG || node->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG);
1749             })) {
1750             bool isTail = false;
1751             auto it = children.begin();
1752             if (position >= static_cast<int32_t>(children.size())) {
1753                 std::advance(it, (static_cast<int32_t>(children.size()) - 1));
1754                 isTail = true;
1755             } else {
1756                 std::advance(it, position);
1757             }
1758             if (it == children.end()) {
1759                 return startOffset;
1760             }
1761             auto imageNode = DynamicCast<FrameNode>(*it);
1762             if (imageNode) {
1763                 auto geometryNode = imageNode->GetGeometryNode();
1764                 CHECK_NULL_RETURN(geometryNode, OffsetF(0.0f, 0.0f));
1765                 startOffset = geometryNode->GetMarginFrameOffset();
1766                 selectLineHeight = geometryNode->GetMarginFrameSize().Height();
1767                 startOffset += isTail ? OffsetF(geometryNode->GetMarginFrameSize().Width(), 0.0f) : OffsetF(0.0f, 0.0f);
1768             }
1769             return startOffset;
1770         }
1771     }
1772     auto caretOffset = startOffset + textPaintOffset + rootOffset;
1773     CHECK_NULL_RETURN(overlayMod_, caretOffset);
1774     caretOffset.SetX(std::clamp(caretOffset.GetX(), 0.0f, richTextRect_.Right()));
1775     return caretOffset;
1776 }
1777 
IsCustomSpanInCaretPos(int32_t position,bool downStreamFirst)1778 bool RichEditorPattern::IsCustomSpanInCaretPos(int32_t position, bool downStreamFirst)
1779 {
1780     CHECK_NULL_RETURN((isSpanStringMode_ && styledString_), false);
1781     auto start = downStreamFirst ? position : position - 1;
1782     start = std::clamp(start, 0, GetTextContentLength());
1783     auto lastStyles = styledString_->GetSpans(start, 1);
1784     for (auto& style : lastStyles) {
1785         if (style && style->GetSpanType() == SpanType::CustomSpan) {
1786             return true;
1787         }
1788     }
1789     return false;
1790 }
1791 
SetCaretPositionWithAffinity(PositionWithAffinity positionWithAffinity)1792 void RichEditorPattern::SetCaretPositionWithAffinity(PositionWithAffinity positionWithAffinity)
1793 {
1794     auto currentPosition = static_cast<int32_t>(positionWithAffinity.position_);
1795     SetCaretPosition(currentPosition);
1796     caretAffinityPolicy_ = (positionWithAffinity.affinity_ == TextAffinity::UPSTREAM)
1797                                 ? CaretAffinityPolicy::UPSTREAM_FIRST
1798                                 : CaretAffinityPolicy::DOWNSTREAM_FIRST;
1799 }
1800 
SetCaretPosition(int32_t pos,bool needNotifyImf)1801 bool RichEditorPattern::SetCaretPosition(int32_t pos, bool needNotifyImf)
1802 {
1803     auto correctPos = std::clamp(pos, 0, GetTextContentLength());
1804     ResetLastClickOffset();
1805     caretAffinityPolicy_ = CaretAffinityPolicy::DEFAULT;
1806     CHECK_NULL_RETURN((pos == correctPos), false);
1807     if (caretPosition_ != correctPos) {
1808         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "caret:%{public}d->%{public}d", caretPosition_, correctPos);
1809         caretPosition_ = correctPos;
1810         FireOnSelectionChange(caretPosition_);
1811         if (caretChangeListener_) {
1812             caretChangeListener_(caretPosition_);
1813         }
1814     }
1815     if (needNotifyImf) {
1816         UpdateCaretInfoToController();
1817     }
1818     return true;
1819 }
1820 
RegisiterCaretChangeListener(std::function<void (int32_t)> && listener)1821 void RichEditorPattern::RegisiterCaretChangeListener(std::function<void(int32_t)>&& listener)
1822 {
1823     caretChangeListener_ = listener;
1824 }
1825 
FireOnSelectionChange(const int32_t caretPosition)1826 void RichEditorPattern::FireOnSelectionChange(const int32_t caretPosition)
1827 {
1828     if (!textSelector_.SelectNothing() || !caretTwinkling_) {
1829         return;
1830     }
1831     FireOnSelectionChange(caretPosition, caretPosition);
1832 }
1833 
FireOnSelectionChange(const TextSelector & selector)1834 void RichEditorPattern::FireOnSelectionChange(const TextSelector& selector)
1835 {
1836     if (selector.SelectNothing()) {
1837         return;
1838     }
1839     FireOnSelectionChange(selector.GetStart(), selector.GetEnd());
1840 }
1841 
FireOnSelectionChange(int32_t start,int32_t end,bool isForced)1842 void RichEditorPattern::FireOnSelectionChange(int32_t start, int32_t end, bool isForced)
1843 {
1844     auto host = GetHost();
1845     CHECK_NULL_VOID(host);
1846     auto eventHub = host->GetEventHub<RichEditorEventHub>();
1847     CHECK_NULL_VOID(eventHub);
1848     CHECK_NULL_VOID(isForced || HasFocus() || dataDetectorAdapter_->hasClickedMenuOption_);
1849     bool isSingleHandle = selectOverlay_->IsSingleHandle();
1850     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "range=[%{public}d,%{public}d],isTwinkling=%{public}d,isSingleHandle=%{public}d",
1851         start, end, caretTwinkling_, isSingleHandle);
1852     if (start < 0 || end < 0) {
1853         return;
1854     }
1855     if (start == end && !caretTwinkling_ && !isSingleHandle) {
1856         return;
1857     }
1858     if (start > end) {
1859         std::swap(start, end);
1860     }
1861     auto range = SelectionRangeInfo(start, end);
1862     if (range == lastSelectionRange_) {
1863         return;
1864     }
1865     lastSelectionRange_ = std::move(range);
1866     eventHub->FireOnSelectionChange(&range);
1867 }
1868 
GetCaretVisible() const1869 bool RichEditorPattern::GetCaretVisible() const
1870 {
1871     return caretVisible_;
1872 }
1873 
OnWindowHide()1874 void RichEditorPattern::OnWindowHide()
1875 {
1876     ScrollablePattern::OnWindowHide();
1877 }
1878 
SetUpdateSpanStyle(struct UpdateSpanStyle updateSpanStyle)1879 void RichEditorPattern::SetUpdateSpanStyle(struct UpdateSpanStyle updateSpanStyle)
1880 {
1881     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "SetUpdateSpanStyle");
1882     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "updateSpanStyle=%{public}s", updateSpanStyle.ToString().c_str());
1883     updateSpanStyle_ = updateSpanStyle;
1884 }
1885 
SetTypingStyle(std::optional<struct UpdateSpanStyle> typingStyle,std::optional<TextStyle> textStyle)1886 void RichEditorPattern::SetTypingStyle(std::optional<struct UpdateSpanStyle> typingStyle,
1887     std::optional<TextStyle> textStyle)
1888 {
1889     typingStyle_ = typingStyle;
1890     typingTextStyle_ = textStyle;
1891     presetParagraph_ = nullptr;
1892     if (spans_.empty() || !previewTextRecord_.previewContent.empty()) {
1893         auto host = GetHost();
1894         CHECK_NULL_VOID(host);
1895         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1896     }
1897 }
1898 
GetUpdateSpanStyle()1899 UpdateSpanStyle RichEditorPattern::GetUpdateSpanStyle()
1900 {
1901     return updateSpanStyle_;
1902 }
1903 
GetTypingStyle()1904 std::optional<struct UpdateSpanStyle> RichEditorPattern::GetTypingStyle()
1905 {
1906     return typingStyle_;
1907 }
1908 
UpdateFontFeatureTextStyle(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle & updateSpanStyle,TextStyle & textStyle)1909 void RichEditorPattern::UpdateFontFeatureTextStyle(
1910     RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle& updateSpanStyle, TextStyle& textStyle)
1911 {
1912     if (updateSpanStyle.updateFontFeature.has_value()) {
1913         spanNode->UpdateFontFeature(textStyle.GetFontFeatures());
1914         spanNode->AddPropertyInfo(PropertyInfo::FONTFEATURE);
1915     }
1916 }
UpdateTextStyle(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle updateSpanStyle,TextStyle textStyle)1917 void RichEditorPattern::UpdateTextStyle(
1918     RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle updateSpanStyle, TextStyle textStyle)
1919 {
1920     CHECK_NULL_VOID(spanNode->GetTag() == V2::SPAN_ETS_TAG);
1921     auto host = GetHost();
1922     CHECK_NULL_VOID(host);
1923     UpdateFontFeatureTextStyle(spanNode, updateSpanStyle, textStyle);
1924     if (updateSpanStyle.updateTextColor.has_value()) {
1925         spanNode->UpdateTextColor(textStyle.GetTextColor());
1926         spanNode->GetSpanItem()->useThemeFontColor = false;
1927         spanNode->AddPropertyInfo(PropertyInfo::FONTCOLOR);
1928     }
1929     if (updateSpanStyle.updateLineHeight.has_value()) {
1930         spanNode->UpdateLineHeight(textStyle.GetLineHeight());
1931         spanNode->AddPropertyInfo(PropertyInfo::LINEHEIGHT);
1932     }
1933     if (updateSpanStyle.updateLetterSpacing.has_value()) {
1934         spanNode->UpdateLetterSpacing(textStyle.GetLetterSpacing());
1935         spanNode->AddPropertyInfo(PropertyInfo::LETTERSPACE);
1936     }
1937     if (updateSpanStyle.updateFontSize.has_value()) {
1938         spanNode->UpdateFontSize(textStyle.GetFontSize());
1939         spanNode->AddPropertyInfo(PropertyInfo::FONTSIZE);
1940     }
1941     if (updateSpanStyle.updateItalicFontStyle.has_value()) {
1942         spanNode->UpdateItalicFontStyle(textStyle.GetFontStyle());
1943         spanNode->AddPropertyInfo(PropertyInfo::FONTSTYLE);
1944     }
1945     if (updateSpanStyle.updateFontWeight.has_value()) {
1946         spanNode->UpdateFontWeight(textStyle.GetFontWeight());
1947         spanNode->AddPropertyInfo(PropertyInfo::FONTWEIGHT);
1948     }
1949     if (updateSpanStyle.updateFontFamily.has_value()) {
1950         spanNode->UpdateFontFamily(textStyle.GetFontFamilies());
1951         spanNode->AddPropertyInfo(PropertyInfo::FONTFAMILY);
1952     }
1953     UpdateDecoration(spanNode, updateSpanStyle, textStyle);
1954     if (updateSpanStyle.updateTextShadows.has_value()) {
1955         spanNode->UpdateTextShadow(textStyle.GetTextShadows());
1956         spanNode->AddPropertyInfo(PropertyInfo::TEXTSHADOW);
1957     }
1958     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1959     host->MarkModifyDone();
1960 }
1961 
UpdateDecoration(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle & updateSpanStyle,TextStyle & textStyle)1962 void RichEditorPattern::UpdateDecoration(
1963     RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle& updateSpanStyle, TextStyle& textStyle)
1964 {
1965     if (updateSpanStyle.updateTextDecoration.has_value()) {
1966         spanNode->UpdateTextDecoration(textStyle.GetTextDecoration());
1967         spanNode->GetSpanItem()->useThemeDecorationColor = false;
1968         spanNode->AddPropertyInfo(PropertyInfo::TEXTDECORATION);
1969     }
1970     if (updateSpanStyle.updateTextDecorationColor.has_value()) {
1971         spanNode->UpdateTextDecorationColor(textStyle.GetTextDecorationColor());
1972         spanNode->AddPropertyInfo(PropertyInfo::NONE);
1973     }
1974     if (updateSpanStyle.updateTextDecorationStyle.has_value()) {
1975         spanNode->UpdateTextDecorationStyle(textStyle.GetTextDecorationStyle());
1976         spanNode->AddPropertyInfo(PropertyInfo::NONE);
1977     }
1978 }
1979 
UpdateSymbolStyle(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle updateSpanStyle,TextStyle textStyle)1980 void RichEditorPattern::UpdateSymbolStyle(
1981     RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle updateSpanStyle, TextStyle textStyle)
1982 {
1983     CHECK_NULL_VOID(spanNode->GetTag() == V2::SYMBOL_SPAN_ETS_TAG);
1984     auto host = GetHost();
1985     CHECK_NULL_VOID(host);
1986     if (updateSpanStyle.updateSymbolFontSize.has_value()) {
1987         spanNode->UpdateFontSize(updateSpanStyle.updateSymbolFontSize.value());
1988         spanNode->AddPropertyInfo(PropertyInfo::FONTSIZE);
1989     }
1990     if (updateSpanStyle.updateSymbolFontWeight.has_value()) {
1991         spanNode->UpdateFontWeight(updateSpanStyle.updateSymbolFontWeight.value());
1992         spanNode->AddPropertyInfo(PropertyInfo::FONTWEIGHT);
1993     }
1994     if (updateSpanStyle.updateSymbolColor.has_value()) {
1995         spanNode->UpdateSymbolColorList(updateSpanStyle.updateSymbolColor.value());
1996         spanNode->AddPropertyInfo(PropertyInfo::SYMBOL_COLOR);
1997     }
1998     if (updateSpanStyle.updateSymbolRenderingStrategy.has_value()) {
1999         spanNode->UpdateSymbolRenderingStrategy(updateSpanStyle.updateSymbolRenderingStrategy.value());
2000         spanNode->AddPropertyInfo(PropertyInfo::SYMBOL_RENDERING_STRATEGY);
2001     }
2002     if (updateSpanStyle.updateSymbolEffectStrategy.has_value()) {
2003         spanNode->UpdateSymbolEffectStrategy(updateSpanStyle.updateSymbolEffectStrategy.value());
2004         spanNode->AddPropertyInfo(PropertyInfo::SYMBOL_EFFECT_STRATEGY);
2005     }
2006     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2007     host->MarkModifyDone();
2008 }
2009 
HasSameTypingStyle(const RefPtr<SpanNode> & spanNode)2010 bool RichEditorPattern::HasSameTypingStyle(const RefPtr<SpanNode>& spanNode)
2011 {
2012     auto spanItem = spanNode->GetSpanItem();
2013     CHECK_NULL_RETURN(spanItem, false);
2014     auto spanTextstyle = spanItem->GetTextStyle();
2015     if (spanTextstyle.has_value() && typingTextStyle_.has_value()) {
2016         return spanTextstyle.value() == typingTextStyle_.value();
2017     } else {
2018         return !(spanTextstyle.has_value() || typingTextStyle_.has_value());
2019     }
2020 }
2021 
UpdateImageStyle(RefPtr<FrameNode> & imageNode,const ImageSpanAttribute & imageStyle)2022 void RichEditorPattern::UpdateImageStyle(RefPtr<FrameNode>& imageNode, const ImageSpanAttribute& imageStyle)
2023 {
2024     CHECK_NULL_VOID(imageNode);
2025     CHECK_NULL_VOID(imageNode->GetTag() == V2::IMAGE_ETS_TAG);
2026     auto host = GetHost();
2027     CHECK_NULL_VOID(host);
2028     auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
2029     CHECK_NULL_VOID(imageLayoutProperty);
2030     if (updateSpanStyle_.updateImageWidth.has_value() || updateSpanStyle_.updateImageHeight.has_value()) {
2031         imageLayoutProperty->UpdateUserDefinedIdealSize(imageStyle.size->GetSize());
2032     }
2033     if (updateSpanStyle_.updateImageFit.has_value()) {
2034         imageLayoutProperty->UpdateImageFit(imageStyle.objectFit.value());
2035     }
2036     if (updateSpanStyle_.updateImageVerticalAlign.has_value()) {
2037         imageLayoutProperty->UpdateVerticalAlign(imageStyle.verticalAlign.value());
2038     }
2039     if (updateSpanStyle_.borderRadius.has_value()) {
2040         auto imageRenderCtx = imageNode->GetRenderContext();
2041         imageRenderCtx->UpdateBorderRadius(imageStyle.borderRadius.value());
2042         imageRenderCtx->SetClipToBounds(true);
2043     }
2044     if (updateSpanStyle_.marginProp.has_value()) {
2045         imageLayoutProperty->UpdateMargin(imageStyle.marginProp.value());
2046     }
2047     UpdateImageAttribute(imageNode, imageStyle);
2048     imageNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2049     imageNode->MarkModifyDone();
2050     IF_TRUE(oneStepDragParam_, dirtyImageNodes.push(WeakClaim((ImageSpanNode*) RawPtr(imageNode))));
2051     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2052     host->MarkModifyDone();
2053 }
2054 
UpdateImageAttribute(RefPtr<FrameNode> & imageNode,const ImageSpanAttribute & imageStyle)2055 void RichEditorPattern::UpdateImageAttribute(RefPtr<FrameNode>& imageNode, const ImageSpanAttribute& imageStyle)
2056 {
2057     CHECK_NULL_VOID(imageNode);
2058     auto node = DynamicCast<ImageSpanNode>(imageNode);
2059     CHECK_NULL_VOID(node);
2060     auto imageSpanItem = DynamicCast<ImageSpanItem>(node->GetSpanItem());
2061     CHECK_NULL_VOID(imageSpanItem);
2062     imageSpanItem->options.imageAttribute = imageStyle;
2063 }
2064 
SymbolSpanUpdateStyle(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle updateSpanStyle,TextStyle textStyle)2065 bool RichEditorPattern::SymbolSpanUpdateStyle(
2066     RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle updateSpanStyle, TextStyle textStyle)
2067 {
2068     if (spanNode->GetTag() == V2::SYMBOL_SPAN_ETS_TAG) {
2069         UpdateSymbolStyle(spanNode, updateSpanStyle_, textStyle);
2070         return true;
2071     }
2072     return false;
2073 }
2074 
UpdateSpanStyle(int32_t start,int32_t end,const TextStyle & textStyle,const ImageSpanAttribute & imageStyle)2075 void RichEditorPattern::UpdateSpanStyle(
2076     int32_t start, int32_t end, const TextStyle& textStyle, const ImageSpanAttribute& imageStyle)
2077 {
2078     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "range=[%{public}d,%{public}d]", start, end);
2079     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "textStyle=%{public}s, imageStyle=%{public}s",
2080         textStyle.ToString().c_str(), imageStyle.ToString().c_str());
2081     auto host = GetHost();
2082     CHECK_NULL_VOID(host);
2083     AdjustSelector(start, end);
2084     int32_t spanStart = 0;
2085     int32_t spanEnd = 0;
2086     for (auto it = host->GetChildren().begin(); it != host->GetChildren().end(); ++it) {
2087         auto spanNode = DynamicCast<SpanNode>(*it);
2088         auto imageNode = DynamicCast<FrameNode>(*it);
2089         if (!spanNode) {
2090             if (spanEnd != 0) {
2091                 spanStart = spanEnd;
2092             }
2093             spanEnd = spanStart + 1;
2094         } else {
2095             spanNode->GetSpanItem()->GetIndex(spanStart, spanEnd);
2096         }
2097         if (spanEnd < start) {
2098             continue;
2099         }
2100 
2101         if (spanStart >= start && spanEnd <= end) {
2102             if (spanNode) {
2103                 UpdateSymbolStyle(spanNode, updateSpanStyle_, textStyle);
2104                 UpdateTextStyle(spanNode, updateSpanStyle_, textStyle);
2105             } else {
2106                 UpdateImageStyle(imageNode, imageStyle);
2107             }
2108             if (spanEnd == end) {
2109                 break;
2110             }
2111         } else if ((spanStart < start && start < spanEnd) || (spanStart < end && end < spanEnd)) {
2112             if (SymbolSpanUpdateStyle(spanNode, updateSpanStyle_, textStyle)) {
2113                 continue;
2114             }
2115             auto index = spanStart < start && start < spanEnd ? start : end;
2116             TextSpanSplit(index, true);
2117             --it;
2118         } else if (spanStart >= end) {
2119             break;
2120         }
2121     }
2122 }
2123 
SetResultObjectText(ResultObject & resultObject,const RefPtr<SpanItem> & spanItem)2124 void RichEditorPattern::SetResultObjectText(ResultObject& resultObject, const RefPtr<SpanItem>& spanItem)
2125 {
2126     CHECK_NULL_VOID(spanItem);
2127     resultObject.valueString = spanItem->content;
2128     if (spanItem->rangeStart <= previewTextRecord_.startOffset && spanItem->position >= previewTextRecord_.endOffset) {
2129         resultObject.previewText = previewTextRecord_.previewContent;
2130     }
2131 }
2132 
GetContentBySpans()2133 std::string RichEditorPattern::GetContentBySpans()
2134 {
2135     std::stringstream ss;
2136     for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
2137         ss << (*iter)->content;
2138     }
2139     auto textContent = ss.str();
2140     return textContent;
2141 }
2142 
TextEmojiSplit(int32_t & start,int32_t end,std::string & content)2143 ResultObject RichEditorPattern::TextEmojiSplit(int32_t& start, int32_t end, std::string& content)
2144 {
2145     ResultObject resultObject;
2146     int32_t emojiStartIndex = 0;
2147     int32_t emojiEndIndex = 0;
2148     int32_t emojiLength = 0;
2149     bool isEmoji = false;
2150     int32_t initStart = start;
2151     for (auto index = start; index <= end; index++) {
2152         emojiStartIndex = 0;
2153         emojiEndIndex = 0;
2154         EmojiRelation indexRelationEmoji =
2155             TextEmojiProcessor::GetIndexRelationToEmoji(index - start, content, emojiStartIndex, emojiEndIndex);
2156         // caret position after emoji
2157         if (indexRelationEmoji == EmojiRelation::AFTER_EMOJI || indexRelationEmoji == EmojiRelation::MIDDLE_EMOJI) {
2158             resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = start;
2159             auto allTextContent = GetContentBySpans();
2160             std::u16string u16Content = StringUtils::Str8ToStr16(allTextContent);
2161             auto caretPos = std::clamp(index, 0, static_cast<int32_t>(u16Content.length()));
2162             emojiLength = TextEmojiProcessor::Delete(caretPos, 1, allTextContent, true);
2163             resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = index - emojiLength;
2164             resultObject.type = SelectSpanType::TYPESPAN;
2165             start = index;
2166             isEmoji = true;
2167             break;
2168         }
2169     }
2170 
2171     if (!isEmoji) {
2172         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = start;
2173         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = end;
2174         resultObject.type = SelectSpanType::TYPESPAN;
2175         start = end;
2176         return resultObject;
2177     }
2178     std::u16string u16Content = StringUtils::Str8ToStr16(content);
2179     CHECK_NULL_RETURN(!(end - start < 0), resultObject);
2180     std::u16string temp = u16Content.substr(start - initStart, end - start);
2181     content = StringUtils::Str16ToStr8(temp);
2182     return resultObject;
2183 }
2184 
GetEmojisBySelect(int32_t start,int32_t end)2185 SelectionInfo RichEditorPattern::GetEmojisBySelect(int32_t start, int32_t end)
2186 {
2187     SelectionInfo selection;
2188     std::list<ResultObject> resultObjects;
2189     CHECK_NULL_RETURN(textSelector_.IsValid(), selection);
2190 
2191     // get all content
2192     auto selectTextContent = GetContentBySpans();
2193     CHECK_NULL_RETURN(!selectTextContent.empty(), selection);
2194     std::u16string u16Content = StringUtils::Str8ToStr16(selectTextContent);
2195     // get select content
2196     std::u16string selectData16 = u16Content.substr(static_cast<int32_t>(start), static_cast<int32_t>(end - start));
2197     std::string selectData = StringUtils::Str16ToStr8(selectData16);
2198     // set SelectionInfo start position and end position
2199     selection.SetSelectionStart(start);
2200     selection.SetSelectionEnd(end);
2201     while (start != end) {
2202         ResultObject resultObject = TextEmojiSplit(start, end, selectData);
2203         resultObjects.emplace_back(resultObject);
2204     }
2205     selection.SetResultObjectList(resultObjects);
2206     return selection;
2207 }
2208 
MixTextEmojiUpdateStyle(int32_t start,int32_t end,TextStyle textStyle,ImageSpanAttribute imageStyle)2209 void RichEditorPattern::MixTextEmojiUpdateStyle(
2210     int32_t start, int32_t end, TextStyle textStyle, ImageSpanAttribute imageStyle)
2211 {
2212     SelectionInfo textSelectInfo = GetEmojisBySelect(start, end);
2213     std::list<ResultObject> textResultObjects = textSelectInfo.GetSelection().resultObjects;
2214     for (const auto& textIter : textResultObjects) {
2215         int32_t newStart = textIter.spanPosition.spanRange[RichEditorSpanRange::RANGESTART];
2216         int32_t newEnd = textIter.spanPosition.spanRange[RichEditorSpanRange::RANGEEND];
2217         if (newStart == newEnd) {
2218             continue;
2219         }
2220         UpdateSpanStyle(newStart, newEnd, textStyle, imageStyle);
2221     }
2222 }
2223 
SetSelectSpanStyle(int32_t start,int32_t end,KeyCode code,bool isStart)2224 void RichEditorPattern::SetSelectSpanStyle(int32_t start, int32_t end, KeyCode code, bool isStart)
2225 {
2226     TextStyle spanStyle;
2227     struct UpdateSpanStyle updateSpanStyle;
2228     ImageSpanAttribute imageStyle;
2229 
2230     auto it = std::find_if(spans_.begin(), spans_.end(), [start](const RefPtr<SpanItem>& spanItem) {
2231         return (spanItem->rangeStart <= start) && (start < spanItem->position);
2232     });
2233     if (it == spans_.end()) {
2234         return;
2235     }
2236     std::optional<TextStyle> spanTextStyle = (*it)->GetTextStyle();
2237     if (spanTextStyle.has_value()) {
2238         spanStyle = spanTextStyle.value();
2239     }
2240     HandleSelectFontStyleWrapper(code, spanStyle);
2241     updateSpanStyle.updateTextColor = spanStyle.GetTextColor();
2242     updateSpanStyle.updateFontSize = spanStyle.GetFontSize();
2243     updateSpanStyle.updateItalicFontStyle = spanStyle.GetFontStyle();
2244     updateSpanStyle.updateFontWeight = spanStyle.GetFontWeight();
2245     updateSpanStyle.updateFontFamily = spanStyle.GetFontFamilies();
2246     updateSpanStyle.updateTextDecoration = spanStyle.GetTextDecoration();
2247     if (!isStart) {
2248         auto updateSpanStyle_ = GetUpdateSpanStyle();
2249         switch (code) {
2250             case KeyCode::KEY_B:
2251                 updateSpanStyle.updateFontWeight = updateSpanStyle_.updateFontWeight;
2252                 spanStyle.SetFontWeight(updateSpanStyle_.updateFontWeight.value());
2253                 break;
2254             case KeyCode::KEY_I:
2255                 updateSpanStyle.updateItalicFontStyle = updateSpanStyle_.updateItalicFontStyle;
2256                 spanStyle.SetFontStyle(updateSpanStyle_.updateItalicFontStyle.value());
2257                 break;
2258             case KeyCode::KEY_U:
2259                 updateSpanStyle.updateTextDecoration = updateSpanStyle_.updateTextDecoration;
2260                 spanStyle.SetTextDecoration(updateSpanStyle_.updateTextDecoration.value());
2261                 break;
2262             default:
2263                 LOGW("Unsupported select operation for HandleSelectFontStyleWrapper");
2264                 return;
2265         }
2266     }
2267     SetUpdateSpanStyle(updateSpanStyle);
2268     MixTextEmojiUpdateStyle(start, end, spanStyle, imageStyle);
2269 }
2270 
GetSelectSpansPositionInfo(int32_t & start,int32_t & end,SpanPositionInfo & startPositionSpanInfo,SpanPositionInfo & endPositionSpanInfo)2271 void RichEditorPattern::GetSelectSpansPositionInfo(
2272     int32_t& start, int32_t& end, SpanPositionInfo& startPositionSpanInfo, SpanPositionInfo& endPositionSpanInfo)
2273 {
2274     bool isText = false;
2275     auto host = GetHost();
2276     CHECK_NULL_VOID(host);
2277     std::find_if(spans_.begin(), spans_.end(), [&start, &end, &isText](const RefPtr<SpanItem>& spanItem) {
2278         if ((spanItem->rangeStart <= start) && (start < spanItem->position) && start < end) {
2279             if (spanItem->spanItemType == NG::SpanItemType::NORMAL && spanItem->unicode == 0) {
2280                 isText = true;
2281                 return true;
2282             }
2283             start += StringUtils::ToWstring(spanItem->content).length();
2284         }
2285         return false;
2286     });
2287     CHECK_EQUAL_VOID(isText, false);
2288     std::find_if(spans_.rbegin(), spans_.rend(), [&end](const RefPtr<SpanItem>& spanItem) {
2289         if ((spanItem->rangeStart < end) && (end <= spanItem->position)) {
2290             if (spanItem->spanItemType == NG::SpanItemType::NORMAL && spanItem->unicode == 0) {
2291                 return true;
2292             }
2293             end = spanItem->rangeStart;
2294         }
2295         return false;
2296     });
2297     startPositionSpanInfo = GetSpanPositionInfo(start);
2298     startPositionSpanInfo.spanIndex_ =
2299         std::clamp(startPositionSpanInfo.spanIndex_, 0, static_cast<int32_t>(host->GetChildren().size()) - 1);
2300     if (end == GetTextContentLength()) {
2301         endPositionSpanInfo.spanIndex_ = spans_.size() - 1;
2302         auto spanIter = spans_.begin();
2303         endPositionSpanInfo.spanIndex_ =
2304             std::clamp(endPositionSpanInfo.spanIndex_, 0, static_cast<int32_t>(host->GetChildren().size()) - 1);
2305         std::advance(spanIter, endPositionSpanInfo.spanIndex_);
2306         auto contentLen = StringUtils::ToWstring((*spanIter)->content).length();
2307         endPositionSpanInfo.spanStart_ = (*spanIter)->position - contentLen;
2308         endPositionSpanInfo.spanEnd_ = (*spanIter)->position;
2309         endPositionSpanInfo.spanOffset_ = contentLen;
2310     } else {
2311         endPositionSpanInfo = GetSpanPositionInfo(end);
2312     }
2313     if (endPositionSpanInfo.spanIndex_ == -1) {
2314         endPositionSpanInfo = startPositionSpanInfo;
2315     }
2316 }
2317 
GetSpanNodeIter(int32_t index)2318 std::list<RefPtr<UINode>>::const_iterator RichEditorPattern::GetSpanNodeIter(int32_t index)
2319 {
2320     auto host = GetHost();
2321     CHECK_NULL_RETURN(host, {});
2322     auto spanNodeIter = host->GetChildren().begin();
2323     std::advance(spanNodeIter, index);
2324     return spanNodeIter;
2325 }
2326 
GetSelectSpanSplit(SpanPositionInfo & startPositionSpanInfo,SpanPositionInfo & endPositionSpanInfo)2327 std::list<SpanPosition> RichEditorPattern::GetSelectSpanSplit(
2328     SpanPositionInfo& startPositionSpanInfo, SpanPositionInfo& endPositionSpanInfo)
2329 {
2330     std::list<SpanPosition> resultObjects;
2331     int32_t spanIndex = 0;
2332     auto itStart = GetSpanNodeIter(startPositionSpanInfo.spanIndex_);
2333     auto itEnd = GetSpanNodeIter(endPositionSpanInfo.spanIndex_);
2334     auto itEndNext = GetSpanNodeIter(endPositionSpanInfo.spanIndex_ + 1);
2335     for (auto itSelect = itStart; itSelect != itEndNext; itSelect++) {
2336         SpanPosition resultObject;
2337         auto spanNode = DynamicCast<SpanNode>(*itSelect);
2338         if (!spanNode || spanNode->GetTag() != V2::SPAN_ETS_TAG) {
2339             continue;
2340         }
2341         auto spanItem = spanNode->GetSpanItem();
2342         if (itSelect == itStart) {
2343             if (startPositionSpanInfo.spanOffset_ == 0) {
2344                 resultObject.spanRange[RichEditorSpanRange::RANGESTART] = startPositionSpanInfo.spanStart_;
2345             } else {
2346                 resultObject.spanRange[RichEditorSpanRange::RANGESTART] =
2347                     startPositionSpanInfo.spanStart_ + startPositionSpanInfo.spanOffset_;
2348             }
2349             resultObject.spanRange[RichEditorSpanRange::RANGEEND] = startPositionSpanInfo.spanEnd_;
2350             resultObject.spanIndex = spanIndex;
2351             spanIndex++;
2352             resultObjects.emplace_back(resultObject);
2353             continue;
2354         }
2355         if (itSelect == itEnd) {
2356             resultObject.spanRange[RichEditorSpanRange::RANGESTART] = endPositionSpanInfo.spanStart_;
2357             if (endPositionSpanInfo.spanOffset_ == static_cast<int32_t>(spanItem->content.size())) {
2358                 resultObject.spanRange[RichEditorSpanRange::RANGEEND] = endPositionSpanInfo.spanEnd_;
2359             } else {
2360                 resultObject.spanRange[RichEditorSpanRange::RANGEEND] =
2361                     endPositionSpanInfo.spanStart_ + endPositionSpanInfo.spanOffset_;
2362             }
2363             resultObject.spanIndex = spanIndex;
2364             spanIndex++;
2365             resultObjects.emplace_back(resultObject);
2366             continue;
2367         }
2368         resultObject.spanRange[RichEditorSpanRange::RANGESTART] =
2369             spanItem->position - StringUtils::ToWstring(spanItem->content).length();
2370         resultObject.spanRange[RichEditorSpanRange::RANGEEND] = spanItem->position;
2371         resultObject.spanIndex = spanIndex;
2372         spanIndex++;
2373         resultObjects.emplace_back(resultObject);
2374     }
2375     return resultObjects;
2376 }
2377 
GetSelectSpanInfo(int32_t start,int32_t end)2378 std::list<SpanPosition> RichEditorPattern::GetSelectSpanInfo(int32_t start, int32_t end)
2379 {
2380     SpanPositionInfo startPositionSpanInfo(-1, -1, -1, -1);
2381     SpanPositionInfo endPositionSpanInfo(-1, -1, -1, -1);
2382     std::list<SpanPosition> resultObjects;
2383     int32_t spanIndex = 0;
2384     GetSelectSpansPositionInfo(start, end, startPositionSpanInfo, endPositionSpanInfo);
2385     CHECK_EQUAL_RETURN(startPositionSpanInfo.spanStart_, -1, resultObjects);
2386     if (startPositionSpanInfo.spanIndex_ == endPositionSpanInfo.spanIndex_) {
2387         SpanPosition resultObject;
2388         resultObject.spanRange[RichEditorSpanRange::RANGESTART] = start;
2389         resultObject.spanRange[RichEditorSpanRange::RANGEEND] = end;
2390         resultObject.spanIndex = spanIndex;
2391         resultObjects.emplace_back(resultObject);
2392     } else {
2393         resultObjects = GetSelectSpanSplit(startPositionSpanInfo, endPositionSpanInfo);
2394     }
2395     return resultObjects;
2396 }
2397 
IsTextSpanFromResult(int32_t & start,int32_t & end,KeyCode code)2398 bool RichEditorPattern::IsTextSpanFromResult(int32_t& start, int32_t& end, KeyCode code)
2399 {
2400     SelectionInfo textSelectInfo = GetEmojisBySelect(start, end);
2401     std::list<ResultObject> textResultObjects = textSelectInfo.GetSelection().resultObjects;
2402     for (const auto& textIter : textResultObjects) {
2403         if (textIter.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] !=
2404             textIter.spanPosition.spanRange[RichEditorSpanRange::RANGEEND]) {
2405             start = textIter.spanPosition.spanRange[RichEditorSpanRange::RANGESTART];
2406             SetSelectSpanStyle(start, end, code, true);
2407             return true;
2408         }
2409     }
2410     return false;
2411 }
2412 
UpdateSelectSpanStyle(int32_t start,int32_t end,KeyCode code)2413 void RichEditorPattern::UpdateSelectSpanStyle(int32_t start, int32_t end, KeyCode code)
2414 {
2415     bool isFirstText = false;
2416     std::list<SpanPosition> resultObjects;
2417     resultObjects = GetSelectSpanInfo(start, end);
2418     for (auto& spanStyleIter : resultObjects) {
2419         if (!isFirstText) {
2420             isFirstText = IsTextSpanFromResult(spanStyleIter.spanRange[RichEditorSpanRange::RANGESTART],
2421                 spanStyleIter.spanRange[RichEditorSpanRange::RANGEEND], code);
2422             continue;
2423         }
2424         SetSelectSpanStyle(spanStyleIter.spanRange[RichEditorSpanRange::RANGESTART],
2425             spanStyleIter.spanRange[RichEditorSpanRange::RANGEEND], code, false);
2426     }
2427 }
2428 
CloseSystemMenu()2429 void RichEditorPattern::CloseSystemMenu()
2430 {
2431     if (!SelectOverlayIsOn()) {
2432         return;
2433     }
2434     auto selectOverlayInfo = selectOverlay_->GetSelectOverlayInfo();
2435     if (selectOverlayInfo && !selectOverlayInfo->menuInfo.menuBuilder) {
2436         CloseSelectOverlay();
2437     }
2438 }
2439 
SetAccessibilityAction()2440 void RichEditorPattern::SetAccessibilityAction()
2441 {
2442     auto host = GetHost();
2443     CHECK_NULL_VOID(host);
2444     auto property = host->GetAccessibilityProperty<AccessibilityProperty>();
2445     CHECK_NULL_VOID(property);
2446     property->SetActionSetSelection([weakPtr = WeakClaim(this)](int32_t start, int32_t end, bool isForward) {
2447         TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
2448             "Accessibility SetSelection, range=[%{public}d,%{public}d], isForward=%{public}d", start, end, isForward);
2449         const auto& pattern = weakPtr.Upgrade();
2450         CHECK_NULL_VOID(pattern);
2451         pattern->SetSelection(start, end, std::nullopt, isForward);
2452     });
2453 
2454     property->SetActionSetIndex([weakPtr = WeakClaim(this)](int32_t index) {
2455         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Accessibility SetCaretOffset, index=%{public}d", index);
2456         const auto& pattern = weakPtr.Upgrade();
2457         CHECK_NULL_VOID(pattern);
2458         pattern->SetCaretOffset(index);
2459     });
2460 
2461     property->SetActionGetIndex([weakPtr = WeakClaim(this)]() -> int32_t {
2462         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Accessibility GetCaretPosition");
2463         const auto& pattern = weakPtr.Upgrade();
2464         CHECK_NULL_RETURN(pattern, -1);
2465         return pattern->GetCaretPosition();
2466     });
2467     SetAccessibilityEditAction();
2468 }
2469 
SetAccessibilityEditAction()2470 void RichEditorPattern::SetAccessibilityEditAction()
2471 {
2472     auto host = GetHost();
2473     CHECK_NULL_VOID(host);
2474     auto property = host->GetAccessibilityProperty<AccessibilityProperty>();
2475     CHECK_NULL_VOID(property);
2476     property->SetActionSetText([weakPtr = WeakClaim(this)](const std::string& value) {
2477         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "EditAction setText, length: %{public}d",
2478             static_cast<int32_t>(StringUtils::ToWstring(value).length()));
2479         const auto& pattern = weakPtr.Upgrade();
2480         CHECK_NULL_VOID(pattern);
2481         pattern->InsertValue(value);
2482     });
2483 
2484     property->SetActionCopy([weakPtr = WeakClaim(this)]() {
2485         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "EditAction copy");
2486         const auto& pattern = weakPtr.Upgrade();
2487         CHECK_NULL_VOID(pattern);
2488         pattern->HandleOnCopy();
2489         pattern->CloseSelectionMenu();
2490     });
2491 
2492     property->SetActionCut([weakPtr = WeakClaim(this)]() {
2493         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "EditAction cut");
2494         const auto& pattern = weakPtr.Upgrade();
2495         CHECK_NULL_VOID(pattern);
2496         pattern->HandleOnCut();
2497     });
2498 
2499     property->SetActionPaste([weakPtr = WeakClaim(this)]() {
2500         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "EditAction paste");
2501         const auto& pattern = weakPtr.Upgrade();
2502         CHECK_NULL_VOID(pattern);
2503         pattern->HandleOnPaste();
2504         pattern->CloseSelectionMenu();
2505     });
2506 
2507     property->SetActionClearSelection([weakPtr = WeakClaim(this)]() {
2508         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "EditAction clearSelection");
2509         const auto& pattern = weakPtr.Upgrade();
2510         CHECK_NULL_VOID(pattern);
2511         pattern->CloseSelectionMenu();
2512         pattern->ResetSelection();
2513         pattern->StartTwinkling();
2514     });
2515 }
2516 
GetParagraphInfo(int32_t start,int32_t end)2517 std::vector<ParagraphInfo> RichEditorPattern::GetParagraphInfo(int32_t start, int32_t end)
2518 {
2519     std::vector<ParagraphInfo> res;
2520     auto spanNodes = GetParagraphNodes(start, end);
2521     CHECK_NULL_RETURN(!spanNodes.empty(), {});
2522 
2523     auto&& firstSpan = spanNodes.front()->GetSpanItem();
2524     auto paraStart = firstSpan->position - static_cast<int32_t>(StringUtils::ToWstring(firstSpan->content).length());
2525 
2526     for (auto it = spanNodes.begin(); it != spanNodes.end(); ++it) {
2527         if (it == std::prev(spanNodes.end()) || StringUtils::ToWstring((*it)->GetSpanItem()->content).back() == L'\n') {
2528             ParagraphInfo info;
2529             auto lm = (*it)->GetLeadingMarginValue({});
2530 
2531             res.emplace_back(ParagraphInfo {
2532                 .leadingMarginPixmap = lm.pixmap,
2533                 .leadingMarginSize = { lm.size.Width().ToString(),
2534                     lm.size.Height().ToString() },
2535                 .textAlign = static_cast<int32_t>((*it)->GetTextAlignValue(TextAlign::START)),
2536                 .wordBreak = static_cast<int32_t>((*it)->GetWordBreakValue(WordBreak::BREAK_WORD)),
2537                 .lineBreakStrategy = static_cast<int32_t>((*it)->GetLineBreakStrategyValue(LineBreakStrategy::GREEDY)),
2538                 .range = { paraStart, (*it)->GetSpanItem()->position },
2539             });
2540             paraStart = (*it)->GetSpanItem()->position;
2541         }
2542     }
2543 
2544     return res;
2545 }
2546 
GetParagraphLength(const std::list<RefPtr<UINode>> & spans) const2547 int32_t RichEditorPattern::GetParagraphLength(const std::list<RefPtr<UINode>>& spans) const
2548 {
2549     if (spans.empty()) {
2550         return 0;
2551     }
2552     int32_t imageSpanCnt = 0;
2553     for (auto it = spans.rbegin(); it != spans.rend(); ++it) {
2554         auto spanNode = DynamicCast<SpanNode>(*it);
2555         if (spanNode) {
2556             return spanNode->GetSpanItem()->position + imageSpanCnt;
2557         }
2558         ++imageSpanCnt;
2559     }
2560     return imageSpanCnt;
2561 }
2562 
GetParagraphNodes(int32_t start,int32_t end) const2563 std::vector<RefPtr<SpanNode>> RichEditorPattern::GetParagraphNodes(int32_t start, int32_t end) const
2564 {
2565     CHECK_NULL_RETURN(start != end, {});
2566     auto host = GetHost();
2567     CHECK_NULL_RETURN(host, {});
2568     CHECK_NULL_RETURN(!host->GetChildren().empty(), {});
2569 
2570     const auto& spans = host->GetChildren();
2571     int32_t length = GetParagraphLength(spans);
2572     std::vector<RefPtr<SpanNode>> res;
2573 
2574     if (start >= length) {
2575         return res;
2576     }
2577 
2578     auto headIt = spans.begin();
2579     auto flagNode = headIt;
2580     bool isEnd = false;
2581     int32_t spanEnd = -1;
2582     while (flagNode != spans.end()) {
2583         auto spanNode = DynamicCast<SpanNode>(*flagNode);
2584         if (spanNode) {
2585             auto&& info = spanNode->GetSpanItem();
2586             spanEnd = info->position;
2587             isEnd = StringUtils::ToWstring(info->content).back() == '\n';
2588         } else {
2589             ++spanEnd;
2590             isEnd = false;
2591         }
2592         flagNode++;
2593         if (spanEnd > start) {
2594             break;
2595         }
2596         if (isEnd) {
2597             headIt = flagNode;
2598         }
2599     }
2600     while (headIt != flagNode) {
2601         auto spanNode = DynamicCast<SpanNode>(*headIt);
2602         if (spanNode) {
2603             res.emplace_back(spanNode);
2604         }
2605         headIt++;
2606     }
2607     while (flagNode != spans.end() && (spanEnd < end || !isEnd)) {
2608         auto spanNode = DynamicCast<SpanNode>(*flagNode);
2609         if (spanNode) {
2610             res.emplace_back(spanNode);
2611             auto&& info = spanNode->GetSpanItem();
2612             spanEnd = info->position;
2613             isEnd = StringUtils::ToWstring(info->content).back() == '\n';
2614         } else {
2615             ++spanEnd;
2616             isEnd = false;
2617         }
2618         flagNode++;
2619     }
2620 
2621     return res;
2622 }
2623 
UpdateParagraphStyle(int32_t start,int32_t end,const struct UpdateParagraphStyle & style)2624 void RichEditorPattern::UpdateParagraphStyle(int32_t start, int32_t end, const struct UpdateParagraphStyle& style)
2625 {
2626     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "range=[%{public}d,%{public}d]", start, end);
2627     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "style=%{public}s", style.ToString().c_str());
2628     auto spanNodes = GetParagraphNodes(start, end);
2629     for (const auto& spanNode : spanNodes) {
2630         UpdateParagraphStyle(spanNode, style);
2631     }
2632 }
2633 
UpdateParagraphStyle(RefPtr<SpanNode> spanNode,const struct UpdateParagraphStyle & style)2634 void RichEditorPattern::UpdateParagraphStyle(RefPtr<SpanNode> spanNode, const struct UpdateParagraphStyle& style)
2635 {
2636     CHECK_NULL_VOID(spanNode);
2637     spanNode->UpdateTextAlign(style.textAlign.value_or(TextAlign::START));
2638     spanNode->UpdateWordBreak(style.wordBreak.value_or(WordBreak::BREAK_WORD));
2639     spanNode->UpdateLineBreakStrategy(style.lineBreakStrategy.value_or(LineBreakStrategy::GREEDY));
2640     if (style.leadingMargin.has_value()) {
2641         spanNode->GetSpanItem()->leadingMargin = *style.leadingMargin;
2642         spanNode->UpdateLeadingMargin(*style.leadingMargin);
2643     }
2644 }
2645 
ScheduleCaretTwinkling()2646 void RichEditorPattern::ScheduleCaretTwinkling()
2647 {
2648     ContainerScope scope(richEditorInstanceId_);
2649     auto host = GetHost();
2650     CHECK_NULL_VOID(host);
2651     auto context = host->GetContext();
2652     CHECK_NULL_VOID(context);
2653 
2654     if (!context->GetTaskExecutor()) {
2655         return;
2656     }
2657 
2658     if (isCursorAlwaysDisplayed_) {
2659         return;
2660     }
2661 
2662     auto weak = WeakClaim(this);
2663     caretTwinklingTask_.Reset([weak, instanceId = richEditorInstanceId_] {
2664         ContainerScope scope(instanceId);
2665         auto client = weak.Upgrade();
2666         CHECK_NULL_VOID(client);
2667         client->OnCaretTwinkling();
2668     });
2669     auto taskExecutor = context->GetTaskExecutor();
2670     CHECK_NULL_VOID(taskExecutor);
2671     taskExecutor->PostDelayedTask(caretTwinklingTask_, TaskExecutor::TaskType::UI, RICH_EDITOR_TWINKLING_INTERVAL_MS,
2672         "ArkUIRichEditorScheduleCaretTwinkling");
2673 }
2674 
StartTwinkling()2675 void RichEditorPattern::StartTwinkling()
2676 {
2677     caretTwinklingTask_.Cancel();
2678     // Fire on selecion change when caret invisible -> visible
2679     if (!caretTwinkling_) {
2680         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "StartTwinkling");
2681         caretTwinkling_ = true;
2682         FireOnSelectionChange(caretPosition_, caretPosition_);
2683     }
2684     caretVisible_ = true;
2685     auto tmpHost = GetHost();
2686     CHECK_NULL_VOID(tmpHost);
2687     tmpHost->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2688     ScheduleCaretTwinkling();
2689 }
2690 
ShowCaretWithoutTwinkling()2691 void RichEditorPattern::ShowCaretWithoutTwinkling()
2692 {
2693     isCursorAlwaysDisplayed_ = true;
2694     StartTwinkling();
2695 }
2696 
OnCaretTwinkling()2697 void RichEditorPattern::OnCaretTwinkling()
2698 {
2699     caretTwinklingTask_.Cancel();
2700     caretVisible_ = !caretVisible_;
2701     GetHost()->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2702     ScheduleCaretTwinkling();
2703 }
2704 
StopTwinkling()2705 void RichEditorPattern::StopTwinkling()
2706 {
2707     if (caretTwinkling_) {
2708         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "StopTwinkling");
2709     }
2710     caretTwinkling_ = false;
2711     isCursorAlwaysDisplayed_ = false;
2712     caretTwinklingTask_.Cancel();
2713     if (caretVisible_) {
2714         caretVisible_ = false;
2715         GetHost()->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2716     }
2717 }
2718 
HandleClickEvent(GestureEvent & info)2719 void RichEditorPattern::HandleClickEvent(GestureEvent& info)
2720 {
2721     CHECK_NULL_VOID(!selectOverlay_->GetIsHandleMoving());
2722     auto focusHub = GetFocusHub();
2723     CHECK_NULL_VOID(focusHub);
2724     if (!focusHub->IsFocusable()) {
2725         return;
2726     }
2727 
2728     if (!HasFocus() && !focusHub->IsFocusOnTouch().value_or(true)) {
2729         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleClickEvent fail when IsFocusOnTouch false");
2730         CloseSelectOverlay();
2731         StopTwinkling();
2732         return;
2733     }
2734 
2735     selectionMenuOffsetClick_ = OffsetF(
2736         static_cast<float>(info.GetGlobalLocation().GetX()), static_cast<float>(info.GetGlobalLocation().GetY()));
2737     if (dataDetectorAdapter_->hasClickedAISpan_) {
2738         dataDetectorAdapter_->hasClickedAISpan_ = false;
2739     }
2740     multipleClickRecognizer_->Start(info);
2741     if (multipleClickRecognizer_->IsTripleClick()) {
2742         HandleTripleClickEvent(info);
2743     } else if (multipleClickRecognizer_->IsDoubleClick()) {
2744         HandleDoubleClickEvent(info);
2745     } else {
2746         HandleSingleClickEvent(info);
2747         NotifyCaretChange();
2748     }
2749 }
2750 
HandleClickSelection(const OHOS::Ace::GestureEvent & info)2751 bool RichEditorPattern::HandleClickSelection(const OHOS::Ace::GestureEvent& info)
2752 {
2753     CHECK_NULL_RETURN(!selectOverlay_->GetIsHandleMoving(), true);
2754     if (SelectOverlayIsOn()) {
2755         selectOverlay_->SwitchToOverlayMode();
2756         selectOverlay_->ToggleMenu();
2757     } else {
2758         CalculateHandleOffsetAndShowOverlay();
2759         selectOverlay_->ProcessOverlay({.animation = true, .requestCode = REQUEST_RECREATE});
2760     }
2761     return true;
2762 }
2763 
IsClickEventOnlyForMenuToggle(const OHOS::Ace::GestureEvent & info)2764 bool RichEditorPattern::IsClickEventOnlyForMenuToggle(const OHOS::Ace::GestureEvent& info)
2765 {
2766     CHECK_NULL_RETURN(info.GetSourceDevice() != SourceType::MOUSE, false);
2767     // In preview state or single handle showing, clicking handle has toggled the menu display
2768     bool hasHandledMenuToggleByClick =
2769         selectOverlay_->IsClickAtHandle(info) && (!isEditing_ || selectOverlay_->IsSingleHandleShow());
2770     CHECK_NULL_RETURN(!hasHandledMenuToggleByClick, true);
2771     if (BetweenSelection(info.GetGlobalLocation())) {
2772         return HandleClickSelection(info);
2773     }
2774     return false;
2775 }
2776 
HandleSingleClickEvent(OHOS::Ace::GestureEvent & info)2777 void RichEditorPattern::HandleSingleClickEvent(OHOS::Ace::GestureEvent& info)
2778 {
2779     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "handleSingleClick");
2780     hasClicked_ = true;
2781     lastClickTimeStamp_ = info.GetTimeStamp();
2782     CHECK_NULL_VOID(!IsClickEventOnlyForMenuToggle(info));
2783 
2784     if (HandleUrlSpanClickEvent(info)) {
2785         return;
2786     }
2787 
2788     Offset textOffset = ConvertTouchOffsetToTextOffset(info.GetLocalLocation());
2789     IF_TRUE(!isMousePressed_, HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY())));
2790 
2791     if (dataDetectorAdapter_->hasClickedAISpan_ || dataDetectorAdapter_->pressedByLeftMouse_) {
2792         IF_TRUE(SelectOverlayIsOn(), selectOverlay_->DisableMenu());
2793         return;
2794     }
2795 
2796     HandleUserClickEvent(info);
2797     CHECK_NULL_VOID(!info.IsPreventDefault());
2798 
2799     if (textSelector_.IsValid() && !isMouseSelect_) {
2800         CloseSelectOverlay();
2801         ResetSelection();
2802     }
2803     moveCaretState_.Reset();
2804     caretUpdateType_ = CaretUpdateType::PRESSED;
2805 
2806     CHECK_NULL_VOID(overlayMod_);
2807     RectF lastCaretRect = GetCaretRect();
2808     int32_t lastCaretPosition = caretPosition_;
2809     bool isCaretTwinkling = caretTwinkling_;
2810     auto position = paragraphs_.GetIndex(textOffset);
2811     AdjustCursorPosition(position);
2812     if (auto focusHub = GetFocusHub(); focusHub) {
2813         SetCaretPosition(position);
2814         if (focusHub->RequestFocusImmediately()) {
2815             StartTwinkling();
2816             RequestKeyboard(false, true, true);
2817             TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "focusHub: set HandleOnEditChanged to 1");
2818             HandleOnEditChanged(true);
2819         }
2820     }
2821     UseHostToUpdateTextFieldManager();
2822     CalcCaretInfoByClick(info.GetLocalLocation());
2823     CHECK_NULL_VOID(info.GetSourceDevice() != SourceType::MOUSE);
2824     if (IsShowSingleHandleByClick(info, lastCaretPosition, lastCaretRect, isCaretTwinkling)) {
2825         CreateAndShowSingleHandle();
2826     }
2827 }
2828 
GetTextOffset(const Offset & localLocation,const RectF & contentRect)2829 PointF RichEditorPattern::GetTextOffset(const Offset &localLocation, const RectF &contentRect)
2830 {
2831     PointF textOffset = {static_cast<float>(localLocation.GetX()) - GetTextRect().GetX(),
2832                          static_cast<float>(localLocation.GetY()) - GetTextRect().GetY()};
2833     return textOffset;
2834 }
2835 
GetSelectedRects(int32_t start,int32_t end)2836 std::vector<RectF> RichEditorPattern::GetSelectedRects(int32_t start, int32_t end)
2837 {
2838     return paragraphs_.GetRects(start, end);
2839 }
2840 
ConvertTouchOffsetToTextOffset(const Offset & touchOffset)2841 Offset RichEditorPattern::ConvertTouchOffsetToTextOffset(const Offset& touchOffset)
2842 {
2843     richTextRect_.SetTop(richTextRect_.GetY() - std::min(baselineOffset_, 0.0f));
2844     richTextRect_.SetHeight(richTextRect_.Height() - std::max(baselineOffset_, 0.0f));
2845     return touchOffset - Offset(richTextRect_.GetX(), richTextRect_.GetY());
2846 }
2847 
IsShowSingleHandleByClick(const OHOS::Ace::GestureEvent & info,int32_t lastCaretPosition,const RectF & lastCaretRect,bool isCaretTwinkling)2848 bool RichEditorPattern::IsShowSingleHandleByClick(
2849     const OHOS::Ace::GestureEvent& info, int32_t lastCaretPosition, const RectF& lastCaretRect, bool isCaretTwinkling)
2850 {
2851     CHECK_NULL_RETURN(isCaretTwinkling && (info.GetSourceDevice() != SourceType::MOUSE), false);
2852     auto offset = info.GetLocalLocation();
2853     Offset textOffset = ConvertTouchOffsetToTextOffset(offset);
2854     auto position = paragraphs_.GetIndex(textOffset);
2855     CHECK_NULL_RETURN(position == lastCaretPosition, false);
2856     auto paragraphEndPos = GetParagraphEndPosition(lastCaretPosition);
2857     if (lastCaretPosition == paragraphEndPos || IsTouchAtLineEnd(lastCaretPosition, textOffset)) {
2858         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "repeat click lineEnd or paragraphEndPos=%{public}d", paragraphEndPos);
2859         return true;
2860     }
2861     return RepeatClickCaret(offset, lastCaretRect);
2862 }
2863 
RepeatClickCaret(const Offset & offset,int32_t lastCaretPosition,const RectF & lastCaretRect)2864 bool RichEditorPattern::RepeatClickCaret(const Offset& offset, int32_t lastCaretPosition, const RectF& lastCaretRect)
2865 {
2866     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "caretTwinkling=%{public}d offset=%{public}s lastCaretRect=%{public}s",
2867         caretTwinkling_, offset.ToString().c_str(), lastCaretRect.ToString().c_str());
2868     CHECK_NULL_RETURN(caretTwinkling_, false);
2869     Offset textOffset = ConvertTouchOffsetToTextOffset(offset);
2870     auto position = paragraphs_.GetIndex(textOffset);
2871     if (position != lastCaretPosition) {
2872         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "clickCaretPosition=%{public}d but lastCaretPosition=%{public}d",
2873             position, lastCaretPosition);
2874         return false;
2875     }
2876     return RepeatClickCaret(offset, lastCaretRect);
2877 }
2878 
RepeatClickCaret(const Offset & offset,const RectF & lastCaretRect)2879 bool RichEditorPattern::RepeatClickCaret(const Offset& offset, const RectF& lastCaretRect)
2880 {
2881     auto lastCaretHeight = lastCaretRect.Height();
2882     auto handleHotZone = selectOverlay_->GetHandleHotZoneRadius();
2883     auto caretHotZoneRect =
2884         RectF(lastCaretRect.GetX() - handleHotZone, lastCaretRect.GetY(), handleHotZone * 2, lastCaretHeight);
2885     return caretHotZoneRect.IsInRegion(PointF(offset.GetX(), offset.GetY()));
2886 }
2887 
CreateAndShowSingleHandle()2888 void RichEditorPattern::CreateAndShowSingleHandle()
2889 {
2890     if (IsPreviewTextInputting()) {
2891         return;
2892     }
2893     textResponseType_ = TextResponseType::LONG_PRESS;
2894     selectOverlay_->SetIsSingleHandle(true);
2895     textSelector_.Update(caretPosition_);
2896     CalculateHandleOffsetAndShowOverlay();
2897     selectOverlay_->ProcessOverlay({ .animation = true });
2898 }
2899 
MoveCaretAndStartFocus(const Offset & textOffset)2900 void RichEditorPattern::MoveCaretAndStartFocus(const Offset& textOffset)
2901 {
2902     auto position = paragraphs_.GetIndex(textOffset);
2903     AdjustCursorPosition(position);
2904 
2905     auto focusHub = GetFocusHub();
2906     if (focusHub) {
2907         SetCaretPosition(position);
2908         if (focusHub->RequestFocusImmediately()) {
2909             StartTwinkling();
2910             if (overlayMod_) {
2911                 RequestKeyboard(false, true, true);
2912             }
2913             HandleOnEditChanged(true);
2914         }
2915     }
2916     UseHostToUpdateTextFieldManager();
2917 }
2918 
HandleDoubleClickEvent(OHOS::Ace::GestureEvent & info)2919 void RichEditorPattern::HandleDoubleClickEvent(OHOS::Ace::GestureEvent& info)
2920 {
2921     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleDoubleClickEvent");
2922     caretUpdateType_ = CaretUpdateType::DOUBLE_CLICK;
2923     HandleDoubleClickOrLongPress(info);
2924     caretUpdateType_ = CaretUpdateType::NONE;
2925 }
2926 
HandleUserGestureEvent(GestureEvent & info,std::function<bool (RefPtr<SpanItem> item,GestureEvent & info)> && gestureFunc)2927 bool RichEditorPattern::HandleUserGestureEvent(
2928     GestureEvent& info, std::function<bool(RefPtr<SpanItem> item, GestureEvent& info)>&& gestureFunc)
2929 {
2930     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleUserGestureEvent");
2931     RectF textContentRect = contentRect_;
2932     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
2933     textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
2934     if (!textContentRect.IsInRegion(PointF(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY())) ||
2935         spans_.empty()) {
2936         return false;
2937     }
2938     PointF textOffset = { info.GetLocalLocation().GetX() - GetTextRect().GetX(),
2939         info.GetLocalLocation().GetY() - GetTextRect().GetY() };
2940     int32_t start = 0;
2941     bool isParagraphHead = true;
2942     Offset paragraphOffset(0, 0);
2943     for (const auto& item : spans_) {
2944         if (!item) {
2945             continue;
2946         }
2947         std::vector<RectF> selectedRects = paragraphs_.GetRects(start, item->position);
2948         start = item->position;
2949         if (isParagraphHead && !selectedRects.empty()) {
2950             if (item->leadingMargin.has_value()) {
2951                 auto addWidth = item->leadingMargin.value().size.Width();
2952                 selectedRects[0].SetLeft(selectedRects[0].GetX() - addWidth.ConvertToPx());
2953                 selectedRects[0].SetWidth(selectedRects[0].GetSize().Width() + addWidth.ConvertToPx());
2954             }
2955             paragraphOffset.SetX(selectedRects[0].GetOffset().GetX());
2956             paragraphOffset.SetY(selectedRects[0].GetOffset().GetY());
2957             isParagraphHead = false;
2958         }
2959         if (!isParagraphHead && item->content.back() == '\n') {
2960             isParagraphHead = true;
2961         }
2962         for (auto&& rect : selectedRects) {
2963             if (!rect.IsInRegion(textOffset)) {
2964                 continue;
2965             }
2966             info = info.SetScreenLocation(
2967                 Offset(textOffset.GetX() - paragraphOffset.GetX(), textOffset.GetY() - paragraphOffset.GetY()));
2968             return gestureFunc(item, info);
2969         }
2970     }
2971     return false;
2972 }
2973 
HandleOnlyImageSelected(const Offset & globalOffset,const SourceTool sourceTool)2974 void RichEditorPattern::HandleOnlyImageSelected(const Offset& globalOffset, const SourceTool sourceTool)
2975 {
2976     CHECK_NULL_VOID(sourceTool != SourceTool::FINGER);
2977     CHECK_NULL_VOID(sourceTool != SourceTool::PEN);
2978     if (IsSelected()) {
2979         return;
2980     }
2981     auto textRect = GetTextRect();
2982     textRect.SetTop(textRect.GetY() - std::min(baselineOffset_, 0.0f));
2983     textRect.SetHeight(textRect.Height() - std::max(baselineOffset_, 0.0f));
2984     Offset offset = Offset(textRect.GetX(), textRect.GetY());
2985     auto textOffset = globalOffset - offset;
2986     int32_t currentPosition = paragraphs_.GetIndex(textOffset);
2987     currentPosition = std::min(currentPosition, GetTextContentLength());
2988     int32_t nextPosition = currentPosition + GetGraphemeClusterLength(GetWideText(), currentPosition);
2989     nextPosition = std::min(nextPosition, GetTextContentLength());
2990     AdjustPlaceholderSelection(currentPosition, nextPosition, textOffset);
2991     auto textSelectInfo = GetSpansInfo(currentPosition, nextPosition, GetSpansMethod::ONSELECT);
2992     auto results = textSelectInfo.GetSelection().resultObjects;
2993     if (results.size() == 1 && results.front().type == SelectSpanType::TYPEIMAGE && results.front().valueString != " "
2994         && !isOnlyImageDrag_) {
2995         textSelector_.Update(currentPosition, nextPosition);
2996         isOnlyImageDrag_ = true;
2997         showSelect_ = false;
2998         CalculateHandleOffsetAndShowOverlay();
2999         if (selectOverlay_->IsBothHandlesShow()) {
3000             TextPattern::CloseSelectOverlay(false);
3001         }
3002         auto focusHub = GetFocusHub();
3003         if (focusHub && sourceTool != SourceTool::FINGER) {
3004             auto isSuccess = focusHub->RequestFocusImmediately();
3005             TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "only image selected, textSelector=[%{public}d, %{public}d], "
3006                 "requestFocus=%{public}d, isFocus=%{public}d",
3007                 textSelector_.GetTextStart(), textSelector_.GetTextEnd(), isSuccess, HasFocus());
3008             FireOnSelectionChange(textSelector_);
3009         }
3010     }
3011 }
3012 
ClickAISpan(const PointF & textOffset,const AISpan & aiSpan)3013 bool RichEditorPattern::ClickAISpan(const PointF& textOffset, const AISpan& aiSpan)
3014 {
3015     auto calculateHandleFunc = [weak = WeakClaim(this)]() {
3016         auto pattern = weak.Upgrade();
3017         CHECK_NULL_VOID(pattern);
3018         pattern->CalculateHandleOffsetAndShowOverlay();
3019     };
3020     auto showSelectOverlayFunc = [weak = WeakClaim(this)](const RectF& firstHandle, const RectF& secondHandle) {
3021         auto pattern = weak.Upgrade();
3022         CHECK_NULL_VOID(pattern);
3023         pattern->SetCaretPosition(pattern->textSelector_.destinationOffset);
3024         auto focusHub = pattern->GetFocusHub();
3025         CHECK_NULL_VOID(focusHub);
3026         focusHub->RequestFocusImmediately();
3027         pattern->ShowSelectOverlay(firstHandle, secondHandle);
3028     };
3029 
3030     std::vector<RectF> aiRects = paragraphs_.GetRects(aiSpan.start, aiSpan.end);
3031     for (auto&& rect : aiRects) {
3032         if (rect.IsInRegion(textOffset)) {
3033             dataDetectorAdapter_->clickedAISpan_ = aiSpan;
3034             if (leftMousePress_) {
3035                 dataDetectorAdapter_->pressedByLeftMouse_ = true;
3036                 return true;
3037             }
3038             dataDetectorAdapter_->hasClickedAISpan_ = true;
3039             ShowAIEntityMenu(aiSpan, calculateHandleFunc, showSelectOverlayFunc);
3040             return true;
3041         }
3042     }
3043     return false;
3044 }
3045 
HandleUrlSpanClickEvent(const GestureEvent & info)3046 bool RichEditorPattern::HandleUrlSpanClickEvent(const GestureEvent& info)
3047 {
3048     RectF textContentRect = contentRect_;
3049     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
3050     textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
3051 
3052     CheckClickedOnSpanOrText(textContentRect, info.GetLocalLocation());
3053     auto clickedSpanPosition = GetClickedSpanPosition();
3054     if (LessNotEqual(clickedSpanPosition, 0)) {
3055         return false;
3056     }
3057     auto iter = spans_.begin();
3058     std::advance(iter, clickedSpanPosition);
3059     RefPtr<SpanItem> span;
3060     if (iter == spans_.end()) {
3061         span = spans_.back();
3062     } else {
3063         span = *iter;
3064     }
3065     if (span && span->urlOnRelease) {
3066         span->urlOnRelease();
3067         return true;
3068     }
3069     return false;
3070 }
3071 
HandleUserClickEvent(GestureEvent & info)3072 bool RichEditorPattern::HandleUserClickEvent(GestureEvent& info)
3073 {
3074     auto clickFunc = [](RefPtr<SpanItem> item, GestureEvent& info) -> bool {
3075         if (item && item->onClick) {
3076             item->onClick(info);
3077             return true;
3078         }
3079         return false;
3080     };
3081     return HandleUserGestureEvent(info, std::move(clickFunc));
3082 }
3083 
CalcCaretInfoByClick(const Offset & touchOffset)3084 void RichEditorPattern::CalcCaretInfoByClick(const Offset& touchOffset)
3085 {
3086     auto textRect = GetTextRect();
3087     textRect.SetTop(textRect.GetY() - std::min(baselineOffset_, 0.0f));
3088     textRect.SetHeight(textRect.Height() - std::max(baselineOffset_, 0.0f));
3089     Offset textOffset = { touchOffset.GetX() - textRect.GetX(), touchOffset.GetY() - textRect.GetY() };
3090     auto [lastClickOffset, caretHeight] = CalcAndRecordLastClickCaretInfo(textOffset);
3091     CHECK_NULL_VOID(overlayMod_);
3092     DynamicCast<RichEditorOverlayModifier>(overlayMod_)->SetCaretOffsetAndHeight(lastClickOffset, caretHeight);
3093     MoveCaretToContentRect();
3094 }
3095 
CalcAndRecordLastClickCaretInfo(const Offset & textOffset)3096 std::pair<OffsetF, float> RichEditorPattern::CalcAndRecordLastClickCaretInfo(const Offset& textOffset)
3097 {
3098     // get the caret position
3099     auto positionWithAffinity = paragraphs_.GetGlyphPositionAtCoordinate(textOffset);
3100     auto position = static_cast<int32_t>(positionWithAffinity.position_);
3101     // get the caret offset when click
3102     float caretHeight = 0.0f;
3103     auto lastClickOffset = paragraphs_.ComputeCursorInfoByClick(position, caretHeight,
3104         OffsetF(static_cast<float>(textOffset.GetX()), static_cast<float>(textOffset.GetY())));
3105 
3106     lastClickOffset += richTextRect_.GetOffset();
3107     if (isShowPlaceholder_) {
3108         auto [caretOffset, preferredHeight] = CalculateEmptyValueCaretRect();
3109         lastClickOffset = caretOffset;
3110     }
3111     SetLastClickOffset(lastClickOffset);
3112     caretAffinityPolicy_ = (positionWithAffinity.affinity_ == TextAffinity::UPSTREAM)
3113                                 ? CaretAffinityPolicy::UPSTREAM_FIRST
3114                                 : CaretAffinityPolicy::DOWNSTREAM_FIRST;
3115     return std::make_pair(lastClickOffset, caretHeight);
3116 }
3117 
InitClickEvent(const RefPtr<GestureEventHub> & gestureHub)3118 void RichEditorPattern::InitClickEvent(const RefPtr<GestureEventHub>& gestureHub)
3119 {
3120     CHECK_NULL_VOID(!clickEventInitialized_);
3121     auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
3122         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "click callback, sourceType=%{public}d", info.GetSourceDevice());
3123         auto pattern = weak.Upgrade();
3124         CHECK_NULL_VOID(pattern);
3125         pattern->sourceType_ = info.GetSourceDevice();
3126         pattern->HandleClickEvent(info);
3127     };
3128     auto clickListener = MakeRefPtr<ClickEvent>(std::move(clickCallback));
3129     gestureHub->AddClickAfterEvent(clickListener);
3130     clickEventInitialized_ = true;
3131 }
3132 
InitFocusEvent(const RefPtr<FocusHub> & focusHub)3133 void RichEditorPattern::InitFocusEvent(const RefPtr<FocusHub>& focusHub)
3134 {
3135     CHECK_NULL_VOID(!focusEventInitialized_);
3136     auto focusTask = [weak = WeakClaim(this)]() {
3137         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "rich editor in focus");
3138         auto pattern = weak.Upgrade();
3139         CHECK_NULL_VOID(pattern);
3140         pattern->HandleFocusEvent();
3141     };
3142     focusHub->SetOnFocusInternal(focusTask);
3143     auto blurTask = [weak = WeakClaim(this)]() {
3144         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "rich editor in blur");
3145         auto pattern = weak.Upgrade();
3146         CHECK_NULL_VOID(pattern);
3147         pattern->HandleBlurEvent();
3148     };
3149     focusHub->SetOnBlurInternal(blurTask);
3150     focusEventInitialized_ = true;
3151     auto keyTask = [weak = WeakClaim(this)](const KeyEvent& keyEvent) -> bool {
3152         auto pattern = weak.Upgrade();
3153         CHECK_NULL_RETURN(pattern, false);
3154         return pattern->OnKeyEvent(keyEvent);
3155     };
3156     focusHub->SetOnKeyEventInternal(std::move(keyTask));
3157 }
3158 
GetBlurReason()3159 BlurReason RichEditorPattern::GetBlurReason()
3160 {
3161     auto host = GetHost();
3162     CHECK_NULL_RETURN(host, BlurReason::FOCUS_SWITCH);
3163     auto curFocusHub = host->GetFocusHub();
3164     CHECK_NULL_RETURN(curFocusHub, BlurReason::FOCUS_SWITCH);
3165     return curFocusHub->GetBlurReason();
3166 }
3167 
HandleBlurEvent()3168 void RichEditorPattern::HandleBlurEvent()
3169 {
3170     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleBlurEvent/%{public}d", frameId_);
3171     CHECK_NULL_VOID(showSelect_ || !IsSelected());
3172     ClearOnFocusTextField();
3173     isLongPress_ = false;
3174     previewLongPress_ = false;
3175     editingLongPress_ = false;
3176     moveCaretState_.Reset();
3177     StopTwinkling();
3178     auto reason = GetBlurReason();
3179     // The pattern handles blurevent, Need to close the softkeyboard first.
3180     if ((customKeyboardBuilder_ && isCustomKeyboardAttached_) || reason == BlurReason::FRAME_DESTROY) {
3181         TAG_LOGI(AceLogTag::ACE_KEYBOARD, "RichEditor Blur, Close Keyboard.");
3182         CloseKeyboard(false);
3183     }
3184     if (magnifierController_) {
3185         magnifierController_->RemoveMagnifierFrameNode();
3186     }
3187     if (IsSelected()) {
3188         CalculateHandleOffsetAndShowOverlay();
3189         selectOverlay_->ProcessOverlay({ .menuIsShow = false});
3190     } else {
3191         CloseSelectOverlay();
3192     }
3193     isCaretInContentArea_ = reason == BlurReason::WINDOW_BLUR && IsCaretInContentArea();
3194     if (reason != BlurReason::WINDOW_BLUR) {
3195         lastSelectionRange_.reset();
3196         HandleOnEditChanged(false);
3197     }
3198     isMoveCaretAnywhere_ = false;
3199 }
3200 
HandleFocusEvent()3201 void RichEditorPattern::HandleFocusEvent()
3202 {
3203     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleFocusEvent/%{public}d", frameId_);
3204     UseHostToUpdateTextFieldManager();
3205     if (previewLongPress_ || isOnlyRequestFocus_) {
3206         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleFocusEvent, previewLongPress=%{public}d,"
3207             "OnlyRequestFocus=%{public}d", previewLongPress_, isOnlyRequestFocus_);
3208         isOnlyRequestFocus_ = false;
3209         return;
3210     }
3211     if (textSelector_.SelectNothing()) {
3212         StartTwinkling();
3213     }
3214     bool isFontScaleChanged = false;
3215     auto pipelineContext = PipelineBase::GetCurrentContext();
3216     if (pipelineContext) {
3217         auto currentFontScale = pipelineContext->GetFontScale() * pipelineContext->GetDipScale();
3218         isFontScaleChanged = !NearEqual(lastFontScale_, currentFontScale);
3219         lastFontScale_ = currentFontScale;
3220     }
3221     auto host = GetHost();
3222     if (host) {
3223         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
3224     }
3225     if (!usingMouseRightButton_ && !isLongPress_ && !dataDetectorAdapter_->hasClickedMenuOption_) {
3226         auto windowMode = GetWindowMode();
3227         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "onFocus, requestKeyboard=%{public}d, windowMode=%{public}d",
3228             needToRequestKeyboardOnFocus_, windowMode);
3229         bool needShowSoftKeyboard = needToRequestKeyboardOnFocus_ && windowMode != WindowMode::WINDOW_MODE_FLOATING;
3230         RequestKeyboard(false, true, needShowSoftKeyboard);
3231         HandleOnEditChanged(true);
3232     }
3233 }
3234 
GetWindowMode()3235 WindowMode RichEditorPattern::GetWindowMode()
3236 {
3237     auto pipelineContext = PipelineBase::GetCurrentContextSafely();
3238     CHECK_NULL_RETURN(pipelineContext, WindowMode::WINDOW_MODE_UNDEFINED);
3239     auto windowManager = pipelineContext->GetWindowManager();
3240     CHECK_NULL_RETURN(windowManager, WindowMode::WINDOW_MODE_UNDEFINED);
3241     return windowManager->GetWindowMode();
3242 }
3243 
UseHostToUpdateTextFieldManager()3244 void RichEditorPattern::UseHostToUpdateTextFieldManager()
3245 {
3246     auto host = GetHost();
3247     CHECK_NULL_VOID(host);
3248     auto context = host->GetContext();
3249     CHECK_NULL_VOID(context);
3250     auto globalOffset = host->GetPaintRectOffset() - context->GetRootRect().GetOffset();
3251     UpdateTextFieldManager(Offset(globalOffset.GetX(), globalOffset.GetY()), frameRect_.Height());
3252 }
3253 
OnVisibleChange(bool isVisible)3254 void RichEditorPattern::OnVisibleChange(bool isVisible)
3255 {
3256     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "isVisible=%{public}d", isVisible);
3257     TextPattern::OnVisibleChange(isVisible);
3258     StopTwinkling();
3259     CloseKeyboard(false);
3260 }
3261 
CloseKeyboard(bool forceClose)3262 bool RichEditorPattern::CloseKeyboard(bool forceClose)
3263 {
3264     CloseSelectOverlay();
3265     ResetSelection();
3266     if (customKeyboardBuilder_ && isCustomKeyboardAttached_) {
3267         return CloseCustomKeyboard();
3268     }
3269     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "Request close soft keyboard.");
3270 #if defined(ENABLE_STANDARD_INPUT)
3271 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
3272     if (!imeAttached_ && !forceClose) {
3273         return false;
3274     }
3275 #endif
3276     auto inputMethod = MiscServices::InputMethodController::GetInstance();
3277     CHECK_NULL_RETURN(inputMethod, false);
3278     inputMethod->HideTextInput();
3279     inputMethod->Close();
3280 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
3281     imeAttached_ = false;
3282 #endif
3283 #else
3284     if (HasConnection()) {
3285         connection_->Close(GetInstanceId());
3286         connection_ = nullptr;
3287     }
3288 #endif
3289     return true;
3290 }
3291 
HandleDraggableFlag(bool isTouchSelectArea)3292 void RichEditorPattern::HandleDraggableFlag(bool isTouchSelectArea)
3293 {
3294     auto gestureHub = GetGestureEventHub();
3295     CHECK_NULL_VOID(gestureHub);
3296     if (copyOption_ != CopyOptions::None && isTouchSelectArea) {
3297         bool isContentDraggalbe = JudgeContentDraggable();
3298         if (isContentDraggalbe) {
3299             dragBoxes_ = GetTextBoxes();
3300         }
3301         gestureHub->SetIsTextDraggable(isContentDraggalbe);
3302     } else {
3303         gestureHub->SetIsTextDraggable(false);
3304     }
3305 }
3306 
SetIsTextDraggable(bool isTextDraggable)3307 void RichEditorPattern::SetIsTextDraggable(bool isTextDraggable)
3308 {
3309     auto gestureHub = GetGestureEventHub();
3310     CHECK_NULL_VOID(gestureHub);
3311     gestureHub->SetIsTextDraggable(isTextDraggable);
3312 }
3313 
JudgeContentDraggable()3314 bool RichEditorPattern::JudgeContentDraggable()
3315 {
3316     if (!IsSelected() || copyOption_ == CopyOptions::None) {
3317         return false ;
3318     }
3319     auto selectInfo = GetSpansInfo(textSelector_.GetTextStart(), textSelector_.GetTextEnd(), GetSpansMethod::ONSELECT);
3320     auto selResult = selectInfo.GetSelection().resultObjects;
3321     auto iter = std::find_if(selResult.begin(), selResult.end(), [](ResultObject& obj) { return obj.isDraggable; });
3322     return iter != selResult.end();
3323 }
3324 
CalculateCaretOffsetAndHeight()3325 std::pair<OffsetF, float> RichEditorPattern::CalculateCaretOffsetAndHeight()
3326 {
3327     OffsetF caretOffset;
3328     float caretHeight = 0.0f;
3329     auto caretPosition = caretPosition_;
3330     float caretHeightUp = 0.0f;
3331     auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
3332     CHECK_NULL_RETURN(overlayModifier, std::make_pair(caretOffset, caretHeight));
3333     auto caretWidth = overlayModifier->GetCaretWidth();
3334     auto contentRect = GetTextContentRect();
3335     OffsetF caretOffsetUp = CalcCursorOffsetByPosition(caretPosition, caretHeightUp, false, false);
3336     if (isShowPlaceholder_) {
3337         auto textAlign = GetTextAlignByDirection();
3338         IF_TRUE(textAlign == TextAlign::END, caretOffsetUp.SetX(contentRect.Right() - caretWidth));
3339         return { caretOffsetUp, caretHeightUp };
3340     }
3341     if (GetTextContentLength() <= 0) {
3342         constexpr float DEFAULT_CARET_HEIGHT = 18.5f;
3343         auto [caretOffset, preferredHeight] = CalculateEmptyValueCaretRect();
3344         caretHeight = typingTextStyle_.has_value() ? preferredHeight
3345             : static_cast<float>(Dimension(DEFAULT_CARET_HEIGHT, DimensionUnit::VP).ConvertToPx());
3346         return { caretOffset, caretHeight };
3347     }
3348     float caretHeightDown = 0.0f;
3349     OffsetF caretOffsetDown = CalcCursorOffsetByPosition(caretPosition, caretHeightDown, true, false);
3350     bool isCaretPosInLineEnd = !NearEqual(caretOffsetDown.GetX(), caretOffsetUp.GetX(), 0.5f);
3351     bool isShowCaretDown = isCaretPosInLineEnd;
3352     if ((caretAffinityPolicy_ != CaretAffinityPolicy::DEFAULT) && isCaretPosInLineEnd) {
3353         // show caret by click
3354         isShowCaretDown = (caretAffinityPolicy_ == CaretAffinityPolicy::DOWNSTREAM_FIRST);
3355     }
3356     caretOffset = isShowCaretDown ? caretOffsetDown : caretOffsetUp;
3357     caretHeight = isShowCaretDown ? caretHeightDown : caretHeightUp;
3358     if (GreatOrEqual(caretOffset.GetX() + caretWidth, contentRect.Right())) {
3359         caretOffset.SetX(caretOffset.GetX() - caretWidth);
3360     }
3361     return std::make_pair(caretOffset, caretHeight);
3362 }
3363 
CalculateEmptyValueCaretRect()3364 std::pair<OffsetF, float> RichEditorPattern::CalculateEmptyValueCaretRect()
3365 {
3366     OffsetF offset;
3367     auto textAlign = GetTextAlignByDirection();
3368     switch (textAlign) {
3369         case TextAlign::START:
3370             offset.SetX(contentRect_.GetX());
3371             break;
3372         case TextAlign::CENTER:
3373             offset.SetX(contentRect_.GetX() + contentRect_.Width() / 2.0f);
3374             break;
3375         case TextAlign::END: {
3376             auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
3377             auto caretWidth = overlayModifier ? overlayModifier->GetCaretWidth() : 0.0f;
3378             offset.SetX(contentRect_.Right() - caretWidth);
3379             break;
3380         }
3381         default:
3382             break;
3383     }
3384     auto offsetY = richTextRect_.GetY();
3385     float caretHeight = 0.0f;
3386     if (!presetParagraph_) {
3387         PreferredParagraph();
3388     }
3389     if (presetParagraph_) {
3390         CaretMetricsF caretCaretMetric;
3391         presetParagraph_->CalcCaretMetricsByPosition(1, caretCaretMetric, TextAffinity::UPSTREAM, false);
3392         offsetY += caretCaretMetric.offset.GetY();
3393         caretHeight = caretCaretMetric.height;
3394     }
3395     offset.SetY(offsetY);
3396     return std::make_pair(offset, caretHeight);
3397 }
3398 
UpdateModifierCaretOffsetAndHeight()3399 void RichEditorPattern::UpdateModifierCaretOffsetAndHeight()
3400 {
3401     CHECK_NULL_VOID(overlayMod_);
3402     auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
3403     CHECK_NULL_VOID(overlayModifier);
3404     auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
3405     overlayModifier->SetCaretOffsetAndHeight(caretOffset, caretHeight);
3406 }
3407 
NotifyCaretChange()3408 void RichEditorPattern::NotifyCaretChange()
3409 {
3410     CHECK_NULL_VOID(!IsSelected());
3411     TriggerAvoidOnCaretChange();
3412 }
3413 
GetTextAlignByDirection()3414 TextAlign RichEditorPattern::GetTextAlignByDirection()
3415 {
3416     auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
3417     CHECK_NULL_RETURN(layoutProperty, TextAlign::START);
3418     auto textAlign = layoutProperty->GetTextAlignValue(TextAlign::START);
3419     auto direction = layoutProperty->GetNonAutoLayoutDirection();
3420     if (direction == TextDirection::RTL) {
3421         if (textAlign == TextAlign::START) {
3422             textAlign = TextAlign::END;
3423         } else {
3424             textAlign = TextAlign::START;
3425         }
3426     }
3427     return textAlign;
3428 }
3429 
HandleLongPress(GestureEvent & info)3430 void RichEditorPattern::HandleLongPress(GestureEvent& info)
3431 {
3432     CHECK_NULL_VOID(!selectOverlay_->GetIsHandleMoving());
3433     auto focusHub = GetFocusHub();
3434     CHECK_NULL_VOID(focusHub);
3435     if (!focusHub->IsFocusable()) {
3436         return;
3437     }
3438     if (info.GetFingerList().size() > 1) {
3439         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "More than one finger detected, ignoring this long press event");
3440         return;
3441     }
3442 
3443     if (sourceType_ == SourceType::MOUSE && hasUrlSpan_) {
3444         HandleUrlSpanShowShadow(info.GetLocalLocation(), info.GetGlobalLocation(), GetUrlPressColor());
3445     }
3446 
3447     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleLongPress");
3448     moveCaretState_.Reset();
3449     caretUpdateType_ = CaretUpdateType::LONG_PRESSED;
3450     selectionMenuOffsetClick_ = OffsetF(
3451         static_cast<float>(info.GetGlobalLocation().GetX()), static_cast<float>(info.GetGlobalLocation().GetY()));
3452     HandleDoubleClickOrLongPress(info);
3453     caretUpdateType_ = CaretUpdateType::NONE;
3454 }
3455 
HandleUrlSpanShowShadow(const Offset & localLocation,const Offset & globalOffset,const Color & color)3456 bool RichEditorPattern::HandleUrlSpanShowShadow(const Offset& localLocation, const Offset& globalOffset, const Color& color)
3457 {
3458     RectF textContentRect = contentRect_;
3459     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
3460     textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
3461 
3462     auto localLocationOffset = localLocation;
3463     if (selectOverlay_->HasRenderTransform()) {
3464         localLocationOffset = ConvertGlobalToLocalOffset(globalOffset);
3465     }
3466 
3467     PointF textOffset = {static_cast<float>(localLocationOffset.GetX()) - GetTextRect().GetX(),
3468                          static_cast<float>(localLocationOffset.GetY()) - GetTextRect().GetY()};
3469     return ShowShadow(textOffset, color);
3470 }
3471 
HandleDoubleClickOrLongPress(GestureEvent & info)3472 void RichEditorPattern::HandleDoubleClickOrLongPress(GestureEvent& info)
3473 {
3474     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "caretUpdateType=%{public}d", caretUpdateType_);
3475     if (IsPreviewTextInputting()) {
3476         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "do not handle DoubleClickOrLongPress in previewTextInputting");
3477         return;
3478     }
3479     if (IsDragging()) {
3480         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "do not handle DoubleClickOrLongPress during drag");
3481         return;
3482     }
3483     auto host = GetHost();
3484     CHECK_NULL_VOID(host);
3485     textResponseType_ = TextResponseType::LONG_PRESS;
3486     if (caretUpdateType_ == CaretUpdateType::LONG_PRESSED) {
3487         HandleUserLongPressEvent(info);
3488     } else if (caretUpdateType_ == CaretUpdateType::DOUBLE_CLICK) {
3489         HandleUserDoubleClickEvent(info);
3490     }
3491     bool isDoubleClick = caretUpdateType_== CaretUpdateType::DOUBLE_CLICK;
3492     if (isDoubleClick && info.GetSourceTool() == SourceTool::FINGER && IsSelected()) {
3493         showSelect_ = true;
3494         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3495         ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle);
3496     }
3497     bool isLongPressSelectArea = BetweenSelection(info.GetGlobalLocation()) && !isDoubleClick;
3498     HandleDraggableFlag(isLongPressSelectArea);
3499     bool isLongPressByMouse = isMousePressed_ && caretUpdateType_== CaretUpdateType::LONG_PRESSED;
3500     if (isLongPressSelectArea && !isLongPressByMouse) {
3501         StartVibratorByLongPress();
3502     }
3503     bool isInterceptEvent = isLongPressSelectArea || isLongPressByMouse;
3504     if (isInterceptEvent) {
3505         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "intercept when longPressSelectArea=%{public}d longPressByMouse=%{public}d",
3506             isLongPressSelectArea, isLongPressByMouse);
3507         return;
3508     }
3509     HandleDoubleClickOrLongPress(info, host);
3510     if (IsSelected()) {
3511         TriggerAvoidOnCaretChangeNextFrame();
3512     } else {
3513         ForceTriggerAvoidOnCaretChange(true);
3514     }
3515 }
3516 
ConvertGlobalToLocalOffset(const Offset & globalOffset)3517 Offset RichEditorPattern::ConvertGlobalToLocalOffset(const Offset& globalOffset)
3518 {
3519     parentGlobalOffset_ = GetPaintRectGlobalOffset();
3520     auto localPoint = OffsetF(globalOffset.GetX(), globalOffset.GetY());
3521     selectOverlay_->RevertLocalPointWithTransform(localPoint);
3522     return Offset(localPoint.GetX(), localPoint.GetY());
3523 }
3524 
HandleSelect(GestureEvent & info,int32_t selectStart,int32_t selectEnd)3525 void RichEditorPattern::HandleSelect(GestureEvent& info, int32_t selectStart, int32_t selectEnd)
3526 {
3527     initSelector_ = { selectStart, selectEnd };
3528     if (IsSelected()) {
3529         showSelect_ = true;
3530     }
3531     FireOnSelect(selectStart, selectEnd);
3532     SetCaretPositionWithAffinity({ selectEnd, TextAffinity::UPSTREAM });
3533     MoveCaretToContentRect();
3534     CalculateHandleOffsetAndShowOverlay();
3535     if (IsShowSelectMenuUsingMouse()) {
3536         CloseSelectOverlay();
3537     }
3538     selectionMenuOffset_ = info.GetGlobalLocation();
3539 }
3540 
HandleDoubleClickOrLongPress(GestureEvent & info,RefPtr<FrameNode> host)3541 void RichEditorPattern::HandleDoubleClickOrLongPress(GestureEvent& info, RefPtr<FrameNode> host)
3542 {
3543     auto focusHub = host->GetOrCreateFocusHub();
3544     CHECK_NULL_VOID(focusHub);
3545     isLongPress_ = true;
3546     auto localOffset = info.GetLocalLocation();
3547     if (selectOverlay_->HasRenderTransform()) {
3548         localOffset = ConvertGlobalToLocalOffset(info.GetGlobalLocation());
3549     }
3550     auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
3551     Offset textOffset = { localOffset.GetX() - textPaintOffset.GetX(), localOffset.GetY() - textPaintOffset.GetY() };
3552     if (caretUpdateType_ == CaretUpdateType::LONG_PRESSED) {
3553         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "LONG_PRESSED and isEditing=%{public}d", isEditing_);
3554         if (textSelector_.IsValid()) {
3555             CloseSelectOverlay();
3556             ResetSelection();
3557         }
3558         StartVibratorByLongPress();
3559         editingLongPress_ = isEditing_;
3560         previewLongPress_ = !isEditing_;
3561         IF_TRUE(previewLongPress_, CloseKeyboard(true));
3562     }
3563     focusHub->RequestFocusImmediately();
3564     InitSelection(textOffset);
3565     auto selectEnd = textSelector_.GetTextEnd();
3566     auto selectStart = textSelector_.GetTextStart();
3567     HandleSelect(info, selectStart, selectEnd);
3568     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3569     if (overlayMod_ && caretUpdateType_ == CaretUpdateType::DOUBLE_CLICK) {
3570         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "double click. shall enter edit state.set 1");
3571         HandleOnEditChanged(true);
3572         RequestKeyboard(false, true, true);
3573     }
3574     bool isDoubleClickByMouse =
3575         info.GetSourceDevice() == SourceType::MOUSE && caretUpdateType_ == CaretUpdateType::DOUBLE_CLICK;
3576     bool isShowSelectOverlay = !isDoubleClickByMouse && caretUpdateType_ != CaretUpdateType::LONG_PRESSED;
3577     if (isShowSelectOverlay) {
3578         selectOverlay_->ProcessOverlay({ .menuIsShow = !selectOverlay_->GetIsHandleMoving(), .animation = true });
3579         StopTwinkling();
3580     } else if (selectStart == selectEnd && isDoubleClickByMouse) {
3581         StartTwinkling();
3582     } else {
3583         StopTwinkling();
3584     }
3585 }
3586 
StartVibratorByLongPress()3587 void RichEditorPattern::StartVibratorByLongPress()
3588 {
3589     CHECK_NULL_VOID(isEnableHapticFeedback_);
3590     VibratorUtils::StartVibraFeedback("longPress.light");
3591 }
3592 
HandleUserLongPressEvent(GestureEvent & info)3593 bool RichEditorPattern::HandleUserLongPressEvent(GestureEvent& info)
3594 {
3595     auto longPressFunc = [](RefPtr<SpanItem> item, GestureEvent& info) -> bool {
3596         if (item && item->onLongPress) {
3597             item->onLongPress(info);
3598             return true;
3599         }
3600         return false;
3601     };
3602     return HandleUserGestureEvent(info, std::move(longPressFunc));
3603 }
3604 
HandleUserDoubleClickEvent(GestureEvent & info)3605 bool RichEditorPattern::HandleUserDoubleClickEvent(GestureEvent& info)
3606 {
3607     auto doubleClickFunc = [](RefPtr<SpanItem> item, GestureEvent& info) -> bool {
3608         if (item && item->onDoubleClick) {
3609             item->onDoubleClick(info);
3610             return true;
3611         }
3612         return false;
3613     };
3614     return HandleUserGestureEvent(info, std::move(doubleClickFunc));
3615 }
3616 
HandleMenuCallbackOnSelectAll()3617 void RichEditorPattern::HandleMenuCallbackOnSelectAll()
3618 {
3619     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleMenuCallbackOnSelectAll");
3620     auto textSize = GetTextContentLength();
3621     textSelector_.Update(0, textSize);
3622     SetCaretPosition(textSize);
3623     MoveCaretToContentRect();
3624 
3625     CalculateHandleOffsetAndShowOverlay();
3626     if (selectOverlay_->IsUsingMouse()) {
3627         CloseSelectOverlay();
3628         StopTwinkling();
3629     }
3630     FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
3631     auto selectOverlayInfo = selectOverlay_->GetSelectOverlayInfo();
3632     if (selectOverlayInfo && selectOverlay_->IsUsingMouse()) {
3633         textResponseType_ = static_cast<TextResponseType>(selectOverlayInfo->menuInfo.responseType.value_or(0));
3634     } else {
3635         textResponseType_ = TextResponseType::LONG_PRESS;
3636     }
3637     showSelect_ = true;
3638     if (!selectOverlay_->IsUsingMouse()) {
3639         selectOverlay_->ProcessOverlay({ .animation = true });
3640     }
3641     auto host = GetHost();
3642     CHECK_NULL_VOID(host);
3643     TriggerAvoidOnCaretChangeNextFrame();
3644     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3645 }
3646 
InitLongPressEvent(const RefPtr<GestureEventHub> & gestureHub)3647 void RichEditorPattern::InitLongPressEvent(const RefPtr<GestureEventHub>& gestureHub)
3648 {
3649     CHECK_NULL_VOID(!longPressEvent_);
3650     auto longPressCallback = [weak = WeakClaim(this)](GestureEvent& info) {
3651         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "long press callback");
3652         auto pattern = weak.Upgrade();
3653         CHECK_NULL_VOID(pattern);
3654         pattern->sourceType_ = info.GetSourceDevice();
3655         pattern->HandleLongPress(info);
3656     };
3657     longPressEvent_ = MakeRefPtr<LongPressEvent>(std::move(longPressCallback));
3658     gestureHub->SetLongPressEvent(longPressEvent_);
3659 
3660     auto onTextSelectorChange = [weak = WeakClaim(this), &selector = textSelector_]() {
3661         auto pattern = weak.Upgrade();
3662         CHECK_NULL_VOID(pattern);
3663         if (!selector.SelectNothing()) {
3664             pattern->StopTwinkling();
3665         }
3666         pattern->SetImageSelfResponseEvent(selector.SelectNothing());
3667         pattern->FireOnSelectionChange(selector);
3668         auto frameNode = pattern->GetHost();
3669         CHECK_NULL_VOID(frameNode);
3670         frameNode->OnAccessibilityEvent(AccessibilityEventType::TEXT_SELECTION_UPDATE);
3671     };
3672     textSelector_.SetOnAccessibility(std::move(onTextSelectorChange));
3673 }
3674 
UpdateSelector(int32_t start,int32_t end)3675 void RichEditorPattern::UpdateSelector(int32_t start, int32_t end)
3676 {
3677     AdjustSelector(start, end);
3678     textSelector_.Update(start, end);
3679 }
3680 
AdjustSelector(int32_t & start,int32_t & end,SelectorAdjustPolicy policy)3681 void RichEditorPattern::AdjustSelector(int32_t& start, int32_t& end, SelectorAdjustPolicy policy)
3682 {
3683     AdjustSelector(start, HandleType::FIRST, policy);
3684     AdjustSelector(end, HandleType::SECOND, policy);
3685 }
3686 
AdjustSelector(int32_t & index,HandleType handleType,SelectorAdjustPolicy policy)3687 void RichEditorPattern::AdjustSelector(int32_t& index, HandleType handleType,  SelectorAdjustPolicy policy)
3688 {
3689     bool isAdjust = AdjustSelectorForSymbol(index, handleType, policy);
3690     CHECK_NULL_VOID(!isAdjust);
3691     AdjustSelectorForEmoji(index, handleType, policy);
3692 }
3693 
AdjustSelectorForSymbol(int32_t & index,HandleType handleType,SelectorAdjustPolicy policy)3694 bool RichEditorPattern::AdjustSelectorForSymbol(int32_t& index, HandleType handleType, SelectorAdjustPolicy policy)
3695 {
3696     auto it = GetSpanIter(index);
3697     CHECK_NULL_RETURN((it != spans_.end()), false);
3698     auto spanItem = *it;
3699     CHECK_NULL_RETURN(spanItem, false);
3700 
3701     auto spanStart = spanItem->rangeStart;
3702     auto spanEnd = spanItem->position;
3703     if (spanItem->unicode != 0 && spanItem->Contains(index)) {
3704         auto it = SELECTOR_ADJUST_DIR_MAP.find({ handleType, policy });
3705         index = (it->second == MoveDirection::BACKWARD) ? spanStart : spanEnd;
3706         return true;
3707     }
3708     return false;
3709 }
3710 
GetEmojiRelation(int index)3711 EmojiRelation RichEditorPattern::GetEmojiRelation(int index)
3712 {
3713     auto it = GetSpanIter(index);
3714     CHECK_NULL_RETURN((it != spans_.end()), EmojiRelation::NO_EMOJI);
3715     auto spanItem = *it;
3716     CHECK_NULL_RETURN(spanItem, EmojiRelation::NO_EMOJI);
3717     int32_t emojiStartIndex;
3718     int32_t emojiEndIndex;
3719     return TextEmojiProcessor::GetIndexRelationToEmoji(index - spanItem->rangeStart, spanItem->content,
3720         emojiStartIndex, emojiEndIndex);
3721 }
3722 
AdjustSelectorForEmoji(int & index,HandleType handleType,SelectorAdjustPolicy policy)3723 bool RichEditorPattern::AdjustSelectorForEmoji(int& index, HandleType handleType, SelectorAdjustPolicy policy)
3724 {
3725     auto it = GetSpanIter(index);
3726     CHECK_NULL_RETURN((it != spans_.end()), false);
3727     auto spanItem = *it;
3728     CHECK_NULL_RETURN(spanItem, false);
3729 
3730     int32_t emojiStartIndex;
3731     int32_t emojiEndIndex;
3732     int32_t spanStart = spanItem->rangeStart;
3733     EmojiRelation relation = TextEmojiProcessor::GetIndexRelationToEmoji(index - spanStart, spanItem->content,
3734         emojiStartIndex, emojiEndIndex);
3735     if (relation != EmojiRelation::IN_EMOJI && relation != EmojiRelation::MIDDLE_EMOJI) {
3736         // no need adjusting when index is not warpped in emojis
3737         return false;
3738     }
3739     int32_t start = 0;
3740     int32_t end = 0;
3741     bool isBoundaryGet = paragraphs_.GetWordBoundary(index, start, end); // boundary from engine
3742     if (isBoundaryGet) {
3743         if (handleType == HandleType::FIRST) {
3744             index = start;
3745         } else {
3746             if (index > start) {
3747                 // index to emoji, move index to end of emoji, double check "in emoji state"
3748                 index = end;
3749             }
3750         }
3751     } else {
3752         if (relation == EmojiRelation::IN_EMOJI) {
3753             int32_t indexInSpan = (handleType == HandleType::FIRST) ? emojiStartIndex : emojiEndIndex;
3754             index = spanItem->rangeStart + indexInSpan;
3755         }
3756     }
3757     TAG_LOGD(AceLogTag::ACE_RICH_TEXT,
3758         "index=%{public}d, handleType=%{public}d, emojiRange=[%{public}d,%{public}d] isBoundaryGet=%{public}d "\
3759         "boundary=[%{public}d, %{public}d]",
3760         index, handleType, emojiStartIndex, emojiEndIndex, isBoundaryGet, start, end);
3761     return true;
3762 }
3763 
GetSpanIter(int32_t index)3764 std::list<RefPtr<SpanItem>>::iterator RichEditorPattern::GetSpanIter(int32_t index)
3765 {
3766     return std::find_if(spans_.begin(), spans_.end(), [index](const RefPtr<SpanItem>& spanItem) {
3767         return spanItem->rangeStart <= index && index < spanItem->position;
3768     });
3769 }
3770 
GetGlyphPositionAtCoordinate(int32_t x,int32_t y)3771 PositionWithAffinity RichEditorPattern::GetGlyphPositionAtCoordinate(int32_t x, int32_t y)
3772 {
3773     Offset offset(x, y);
3774     return paragraphs_.GetGlyphPositionAtCoordinate(ConvertTouchOffsetToTextOffset(offset));
3775 }
3776 
InitDragDropEvent()3777 void RichEditorPattern::InitDragDropEvent()
3778 {
3779     auto host = GetHost();
3780     CHECK_NULL_VOID(host);
3781     auto gestureHub = host->GetOrCreateGestureEventHub();
3782     CHECK_NULL_VOID(gestureHub);
3783     gestureHub->InitDragDropEvent();
3784     gestureHub->SetThumbnailCallback(GetThumbnailCallback());
3785     auto eventHub = host->GetEventHub<EventHub>();
3786     CHECK_NULL_VOID(eventHub);
3787     auto onDragMove = [weakPtr = WeakClaim(this)](
3788                           const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& extraParams) {
3789         auto pattern = weakPtr.Upgrade();
3790         CHECK_NULL_VOID(pattern);
3791         pattern->showSelect_ = false;
3792         pattern->OnDragMove(event);
3793     };
3794     eventHub->SetOnDragMove(std::move(onDragMove));
3795     OnDragStartAndEnd();
3796     onDragDropAndLeave();
3797 }
3798 
OnDragStartAndEnd()3799 void RichEditorPattern::OnDragStartAndEnd()
3800 {
3801     auto host = GetHost();
3802     CHECK_NULL_VOID(host);
3803     auto eventHub = host->GetEventHub<EventHub>();
3804     CHECK_NULL_VOID(eventHub);
3805     auto onDragStart = [weakPtr = WeakClaim(this)](const RefPtr<OHOS::Ace::DragEvent>& event,
3806                            const std::string& extraParams) -> NG::DragDropInfo {
3807         NG::DragDropInfo itemInfo;
3808         auto pattern = weakPtr.Upgrade();
3809         CHECK_NULL_RETURN(pattern, itemInfo);
3810         return pattern->HandleDragStart(event, extraParams);
3811     };
3812     eventHub->SetDefaultOnDragStart(std::move(onDragStart));
3813     auto onDragEnd = [weakPtr = WeakClaim(this), scopeId = Container::CurrentId()](
3814                          const RefPtr<OHOS::Ace::DragEvent>& event) {
3815         ContainerScope scope(scopeId);
3816         auto pattern = weakPtr.Upgrade();
3817         CHECK_NULL_VOID(pattern);
3818         pattern->isOnlyImageDrag_ = false;
3819         pattern->isDragSponsor_ = false;
3820         pattern->dragRange_ = { 0, 0 };
3821         pattern->showSelect_ = true;
3822         pattern->StopAutoScroll();
3823         pattern->ClearRedoOperationRecords();
3824         pattern->OnDragEnd(event);
3825     };
3826     eventHub->SetOnDragEnd(std::move(onDragEnd));
3827 }
3828 
HandleDragStart(const RefPtr<Ace::DragEvent> & event,const std::string & extraParams)3829 NG::DragDropInfo RichEditorPattern::HandleDragStart(const RefPtr<Ace::DragEvent>& event, const std::string& extraParams)
3830 {
3831     if (!isDragSponsor_) {
3832         isDragSponsor_ = true;
3833         dragRange_ = { textSelector_.GetTextStart(), textSelector_.GetTextEnd() };
3834     }
3835     timestamp_ = std::chrono::system_clock::now().time_since_epoch().count();
3836     auto eventHub = GetEventHub<RichEditorEventHub>();
3837     CHECK_NULL_RETURN(eventHub, {});
3838     eventHub->SetTimestamp(timestamp_);
3839     showSelect_ = false;
3840     auto dropInfo = OnDragStart(event, extraParams);
3841     if (isOnlyImageDrag_) {
3842         recoverStart_ = -1;
3843         recoverEnd_ = -1;
3844     }
3845     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleDragStart dragStatus=%{public}d", status_);
3846     return dropInfo;
3847 }
3848 
onDragDropAndLeave()3849 void RichEditorPattern::onDragDropAndLeave()
3850 {
3851     auto host = GetHost();
3852     CHECK_NULL_VOID(host);
3853     auto eventHub = host->GetEventHub<EventHub>();
3854     CHECK_NULL_VOID(eventHub);
3855     auto onDragDrop = [weakPtr = WeakClaim(this), scopeId = Container::CurrentId()](
3856                           const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& value) {
3857         ContainerScope scope(scopeId);
3858         auto pattern = weakPtr.Upgrade();
3859         CHECK_NULL_VOID(pattern);
3860         auto host = pattern->GetHost();
3861         CHECK_NULL_VOID(host);
3862         auto eventHub = host->GetEventHub<EventHub>();
3863         CHECK_NULL_VOID(eventHub);
3864         if (!eventHub->IsEnabled()) {
3865             return;
3866         }
3867         pattern->status_ = Status::ON_DROP;
3868         pattern->HandleOnDragDrop(event);
3869         pattern->status_ = Status::NONE;
3870     };
3871     eventHub->SetOnDrop(std::move(onDragDrop));
3872     auto onDragDragLeave = [weakPtr = WeakClaim(this), scopeId = Container::CurrentId()](
3873                                const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& value) {
3874         ContainerScope scope(scopeId);
3875         auto pattern = weakPtr.Upgrade();
3876         CHECK_NULL_VOID(pattern);
3877         pattern->StopAutoScroll();
3878     };
3879     eventHub->SetOnDragLeave(onDragDragLeave);
3880 }
3881 
ClearDragDropEvent()3882 void RichEditorPattern::ClearDragDropEvent()
3883 {
3884     auto host = GetHost();
3885     CHECK_NULL_VOID(host);
3886     auto gestureHub = host->GetOrCreateGestureEventHub();
3887     CHECK_NULL_VOID(gestureHub);
3888     gestureHub->SetIsTextDraggable(false);
3889     auto eventHub = host->GetEventHub<EventHub>();
3890     CHECK_NULL_VOID(eventHub);
3891     eventHub->SetDefaultOnDragStart(nullptr);
3892     eventHub->SetOnDragEnter(nullptr);
3893     eventHub->SetOnDragMove(nullptr);
3894     eventHub->SetOnDragLeave(nullptr);
3895     eventHub->SetOnDragEnd(nullptr);
3896     eventHub->SetOnDrop(nullptr);
3897 }
3898 
OnDragMove(const RefPtr<OHOS::Ace::DragEvent> & event)3899 void RichEditorPattern::OnDragMove(const RefPtr<OHOS::Ace::DragEvent>& event)
3900 {
3901     auto weakPtr = WeakClaim(this);
3902     auto pattern = weakPtr.Upgrade();
3903     CHECK_NULL_VOID(pattern);
3904     pattern->showSelect_ = true;
3905     auto pipeline = PipelineBase::GetCurrentContext();
3906     CHECK_NULL_VOID(pipeline);
3907     auto theme = pipeline->GetTheme<RichEditorTheme>();
3908     CHECK_NULL_VOID(theme);
3909     auto touchX = event->GetX();
3910     auto touchY = event->GetY();
3911     auto textRect = GetTextRect();
3912     textRect.SetTop(textRect.GetY() - std::min(baselineOffset_, 0.0f));
3913     Offset textOffset = { touchX - textRect.GetX() - GetParentGlobalOffset().GetX(),
3914         touchY - textRect.GetY() - GetParentGlobalOffset().GetY() - theme->GetInsertCursorOffset().ConvertToPx() };
3915     auto position = isShowPlaceholder_? 0 : paragraphs_.GetIndex(textOffset);
3916     ResetSelection();
3917     CloseSelectOverlay();
3918     SetCaretPosition(position);
3919     CalcAndRecordLastClickCaretInfo(textOffset);
3920     auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
3921     CHECK_NULL_VOID(overlayMod_);
3922     auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
3923     overlayModifier->SetCaretOffsetAndHeight(caretOffset, caretHeight);
3924 
3925     AutoScrollParam param = { .autoScrollEvent = AutoScrollEvent::DRAG, .showScrollbar = true };
3926     auto localOffset = OffsetF(touchX, touchY) - parentGlobalOffset_;
3927     AutoScrollByEdgeDetection(param, localOffset, EdgeDetectionStrategy::IN_BOUNDARY);
3928 }
3929 
OnDragEnd(const RefPtr<Ace::DragEvent> & event)3930 void RichEditorPattern::OnDragEnd(const RefPtr<Ace::DragEvent>& event)
3931 {
3932     ResetDragRecordSize(-1);
3933     auto host = GetHost();
3934     CHECK_NULL_VOID(host);
3935     if (status_ == Status::DRAGGING) {
3936         status_ = Status::NONE;
3937     }
3938     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "OnDragEnd dragStatus=%{public}d", status_);
3939     ResetDragSpanItems();
3940     if (recoverDragResultObjects_.empty()) {
3941         return;
3942     }
3943     UpdateSpanItemDragStatus(recoverDragResultObjects_, false);
3944     recoverDragResultObjects_.clear();
3945     auto focusHub = GetFocusHub();
3946     if (event && focusHub && event->GetResult() != DragRet::DRAG_SUCCESS && focusHub->IsFocusable()) {
3947         HandleSelectionChange(recoverStart_, recoverEnd_);
3948         showSelect_ = true;
3949         CalculateHandleOffsetAndShowOverlay();
3950         ResetSelection();
3951     }
3952     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
3953 }
3954 
ToStyledString(int32_t start,int32_t end)3955 RefPtr<SpanString> RichEditorPattern::ToStyledString(int32_t start, int32_t end)
3956 {
3957     auto length = GetTextContentLength();
3958     int32_t realStart = (start == -1) ? 0 : std::clamp(start, 0, length);
3959     int32_t realEnd = (end == -1) ? length : std::clamp(end, 0, length);
3960     if (realStart > realEnd) {
3961         std::swap(realStart, realEnd);
3962     }
3963     RefPtr<SpanString> spanString = MakeRefPtr<SpanString>("");
3964     if (aiWriteAdapter_->GetAIWrite()) {
3965         SetSubSpansWithAIWrite(spanString, realStart, realEnd);
3966     } else {
3967         SetSubSpans(spanString, realStart, realEnd);
3968     }
3969     SetSubMap(spanString);
3970     return spanString;
3971 }
3972 
FromStyledString(const RefPtr<SpanString> & spanString)3973 SelectionInfo RichEditorPattern::FromStyledString(const RefPtr<SpanString>& spanString)
3974 {
3975     std::list<ResultObject> resultObjects;
3976     int32_t start = 0;
3977     int32_t end = 0;
3978     if (spanString && !spanString->GetSpanItems().empty()) {
3979         auto spans = spanString->GetSpanItems();
3980         int32_t index = 0;
3981         std::for_each(spans.begin(), spans.end(),
3982             [&index, &resultObjects, weak = WeakClaim(this)](RefPtr<SpanItem>& item) {
3983                 CHECK_NULL_VOID(item);
3984                 auto pattern = weak.Upgrade();
3985                 CHECK_NULL_VOID(pattern);
3986                 auto obj = item->GetSpanResultObject(item->interval.first, item->interval.second);
3987                 if (AceType::InstanceOf<ImageSpanItem>(item)) {
3988                     obj.imageStyle = pattern->GetImageStyleBySpanItem(item);
3989                 } else if (!AceType::InstanceOf<CustomSpanItem>(item)) {
3990                     obj.textStyle = pattern->GetTextStyleBySpanItem(item);
3991                 }
3992                 obj.spanPosition.spanIndex = index;
3993                 ++index;
3994                 if (obj.isInit) {
3995                     resultObjects.emplace_back(obj);
3996                 }
3997         });
3998         if (spans.back()) {
3999             end = spans.back()->interval.second;
4000         }
4001         if (spans.front()) {
4002             start = spans.front()->interval.first;
4003         }
4004     }
4005     SelectionInfo selection;
4006     selection.SetSelectionEnd(end);
4007     selection.SetSelectionStart(start);
4008     selection.SetResultObjectList(resultObjects);
4009     return selection;
4010 }
4011 
GetTextStyleBySpanItem(const RefPtr<SpanItem> & spanItem)4012 TextStyleResult RichEditorPattern::GetTextStyleBySpanItem(const RefPtr<SpanItem>& spanItem)
4013 {
4014     TextStyleResult textStyle;
4015     CHECK_NULL_RETURN(spanItem, textStyle);
4016     auto theme = GetTheme<RichEditorTheme>();
4017     TextStyle style = theme ? theme->GetTextStyle() : TextStyle();
4018     if (spanItem->fontStyle) {
4019         textStyle.fontColor = spanItem->fontStyle->GetTextColor().value_or(style.GetTextColor()).ColorToString();
4020         textStyle.fontSize =
4021             spanItem->fontStyle->GetFontSize().value_or(Dimension(16.0f, DimensionUnit::VP)).ConvertToFp();
4022         textStyle.fontStyle =
4023             static_cast<int32_t>(spanItem->fontStyle->GetItalicFontStyle().value_or(OHOS::Ace::FontStyle::NORMAL));
4024         textStyle.fontWeight = static_cast<int32_t>(spanItem->fontStyle->GetFontWeight().value_or(FontWeight::NORMAL));
4025         std::string fontFamilyValue;
4026         const std::vector<std::string> defaultFontFamily = { "HarmonyOS Sans" };
4027         auto fontFamily = spanItem->fontStyle->GetFontFamily().value_or(defaultFontFamily);
4028         for (const auto& str : fontFamily) {
4029             fontFamilyValue += str;
4030             fontFamilyValue += ",";
4031         }
4032         fontFamilyValue = fontFamilyValue.substr(0, fontFamilyValue.size() > 0 ? fontFamilyValue.size() - 1 : 0);
4033         textStyle.fontFamily = !fontFamilyValue.empty() ? fontFamilyValue : defaultFontFamily.front();
4034         textStyle.decorationType =
4035             static_cast<int32_t>(spanItem->fontStyle->GetTextDecoration().value_or(TextDecoration::NONE));
4036         textStyle.decorationColor =
4037             spanItem->fontStyle->GetTextDecorationColor().value_or(style.GetTextDecorationColor()).ColorToString();
4038         textStyle.decorationStyle =
4039             static_cast<int32_t>(spanItem->fontStyle->GetTextDecorationStyle().value_or(TextDecorationStyle::SOLID));
4040         textStyle.fontFeature = spanItem->fontStyle->GetFontFeature().value_or(ParseFontFeatureSettings("\"pnum\" 1"));
4041         textStyle.letterSpacing = spanItem->fontStyle->GetLetterSpacing().value_or(Dimension()).ConvertToFp();
4042     }
4043     if (spanItem->textLineStyle) {
4044         textStyle.lineHeight = spanItem->textLineStyle->GetLineHeight().value_or(Dimension()).ConvertToFp();
4045         textStyle.lineSpacing = spanItem->textLineStyle->GetLineSpacing().value_or(Dimension()).ConvertToFp();
4046         textStyle.textAlign = static_cast<int32_t>(spanItem->textLineStyle->GetTextAlign().value_or(TextAlign::START));
4047         auto lm = spanItem->textLineStyle->GetLeadingMargin();
4048         if (lm.has_value()) {
4049             textStyle.leadingMarginSize[RichEditorLeadingRange::LEADING_START] = lm.value().size.Width().ToString();
4050             textStyle.leadingMarginSize[RichEditorLeadingRange::LEADING_END] = lm.value().size.Height().ToString();
4051         }
4052         textStyle.wordBreak =
4053             static_cast<int32_t>(spanItem->textLineStyle->GetWordBreak().value_or(WordBreak::BREAK_WORD));
4054         textStyle.lineBreakStrategy =
4055             static_cast<int32_t>(spanItem->textLineStyle->GetLineBreakStrategy().value_or(LineBreakStrategy::GREEDY));
4056     }
4057     return textStyle;
4058 }
4059 
GetImageStyleBySpanItem(const RefPtr<SpanItem> & spanItem)4060 ImageStyleResult RichEditorPattern::GetImageStyleBySpanItem(const RefPtr<SpanItem>& spanItem)
4061 {
4062     ImageStyleResult imageStyle;
4063     auto imageSpanItem = DynamicCast<ImageSpanItem>(spanItem);
4064     CHECK_NULL_RETURN(imageSpanItem, imageStyle);
4065     auto imageAttributeOp = imageSpanItem->options.imageAttribute;
4066     CHECK_NULL_RETURN(imageAttributeOp.has_value(), imageStyle);
4067     auto imageSizeOp = imageAttributeOp->size;
4068     if (imageSizeOp.has_value() && imageSizeOp->width.has_value() && imageSizeOp->height.has_value()) {
4069         imageStyle.size[RichEditorImageSize::SIZEWIDTH] = imageSizeOp->width->ConvertToPx();
4070         imageStyle.size[RichEditorImageSize::SIZEHEIGHT] = imageSizeOp->height->ConvertToPx();
4071     }
4072     if (imageAttributeOp->verticalAlign.has_value()) {
4073         imageStyle.verticalAlign = static_cast<int32_t>(imageAttributeOp->verticalAlign.value());
4074     }
4075     if (imageAttributeOp->objectFit.has_value()) {
4076         imageStyle.objectFit = static_cast<int32_t>(imageAttributeOp->objectFit.value());
4077     }
4078     if (imageAttributeOp->marginProp.has_value()) {
4079         imageStyle.margin = imageAttributeOp->marginProp->ToString();
4080     }
4081     if (imageAttributeOp->borderRadius.has_value()) {
4082         imageStyle.borderRadius = imageAttributeOp->borderRadius->ToString();
4083     }
4084     return imageStyle;
4085 }
4086 
SetSubSpansWithAIWrite(RefPtr<SpanString> & spanString,int32_t start,int32_t end)4087 void RichEditorPattern::SetSubSpansWithAIWrite(RefPtr<SpanString>& spanString, int32_t start, int32_t end)
4088 {
4089     placeholderSpansMap_.clear();
4090     CHECK_NULL_VOID(spanString);
4091     std::list<RefPtr<SpanItem>> subSpans;
4092     std::string text;
4093     size_t index = 0;
4094     size_t placeholderGains = 0;
4095     for (const auto& spanItem : spans_) {
4096         if (!spanItem) {
4097             continue;
4098         }
4099         auto oldEnd = spanItem->position;
4100         auto oldStart = spanItem->rangeStart;
4101         if (oldEnd <= start || oldStart >= end) {
4102             continue;
4103         }
4104         RefPtr<SpanItem> newSpanItem = MakeRefPtr<SpanItem>();
4105         auto spanStart = oldStart <= start ? 0 : oldStart - start;
4106         auto spanEnd = oldEnd < end ? oldEnd - start : end - start;
4107         spanStart += static_cast<int32_t>(placeholderGains);
4108         if (spanItem->spanItemType == SpanItemType::NORMAL) {
4109             newSpanItem = spanItem->GetSameStyleSpanItem();
4110             newSpanItem->content = StringUtils::ToString(
4111                 StringUtils::ToWstring(spanItem->content)
4112                     .substr(std::max(start - oldStart, 0), std::min(end, oldEnd) - std::max(start, oldStart)));
4113         } else {
4114             InitPlaceholderSpansMap(newSpanItem, spanItem, index, placeholderGains);
4115             spanEnd += static_cast<int32_t>(placeholderGains);
4116         }
4117         newSpanItem->interval = {spanStart, spanEnd};
4118         newSpanItem->position = spanStart;
4119         newSpanItem->rangeStart = spanEnd;
4120         newSpanItem->textLineStyle->ResetLeadingMargin();
4121         text.append(newSpanItem->content);
4122         subSpans.emplace_back(newSpanItem);
4123     }
4124     spanString->SetString(text);
4125     spanString->SetSpanItems(std::move(subSpans));
4126 }
4127 
InitPlaceholderSpansMap(RefPtr<SpanItem> & newSpanItem,const RefPtr<SpanItem> & spanItem,size_t & index,size_t & placeholderGains)4128 void RichEditorPattern::InitPlaceholderSpansMap(
4129     RefPtr<SpanItem>& newSpanItem, const RefPtr<SpanItem>& spanItem, size_t& index, size_t& placeholderGains)
4130 {
4131     newSpanItem->content = "![id" + std::to_string(index++) + "]";
4132     switch (spanItem->spanItemType) {
4133         case SpanItemType::SYMBOL: {
4134             placeholderSpansMap_[newSpanItem->content] = spanItem;
4135             placeholderGains += PLACEHOLDER_LENGTH - SYMBOL_CONTENT_LENGTH;
4136             break;
4137         }
4138         case SpanItemType::CustomSpan: {
4139             if (!isSpanStringMode_) {
4140                 placeholderSpansMap_[newSpanItem->content] = spanItem;
4141             } else {
4142                 auto customSpanItem = DynamicCast<CustomSpanItem>(spanItem);
4143                 placeholderSpansMap_[newSpanItem->content] = customSpanItem;
4144             }
4145             placeholderGains += PLACEHOLDER_LENGTH - CUSTOM_CONTENT_LENGTH;
4146             break;
4147         }
4148         case SpanItemType::IMAGE: {
4149             placeholderSpansMap_[newSpanItem->content] = spanItem;
4150             placeholderGains += PLACEHOLDER_LENGTH - CUSTOM_CONTENT_LENGTH;
4151             break;
4152         }
4153         default:
4154             break;
4155     }
4156 }
4157 
SetSubSpans(RefPtr<SpanString> & spanString,int32_t start,int32_t end)4158 void RichEditorPattern::SetSubSpans(RefPtr<SpanString>& spanString, int32_t start, int32_t end)
4159 {
4160     CHECK_NULL_VOID(spanString);
4161     std::list<RefPtr<SpanItem>> subSpans;
4162     std::string text;
4163     for (const auto& spanItem : spans_) {
4164         if (!spanItem ||
4165             (!AceType::InstanceOf<ImageSpanItem>(spanItem) && AceType::InstanceOf<PlaceholderSpanItem>(spanItem))) {
4166             continue;
4167         }
4168         auto spanEndPos = spanItem->position;
4169         auto spanStartPos = spanItem->rangeStart;
4170         if (spanEndPos > start && spanStartPos < end) {
4171             int32_t oldStart = spanStartPos;
4172             int32_t oldEnd = spanEndPos;
4173             auto spanStart = oldStart <= start ? 0 : oldStart - start;
4174             auto spanEnd = oldEnd < end ? oldEnd - start : end - start;
4175             auto newSpanItem = GetSameSpanItem(spanItem);
4176             CHECK_NULL_CONTINUE(newSpanItem);
4177             newSpanItem->spanItemType = spanItem->spanItemType;
4178             newSpanItem->interval = {spanStart, spanEnd};
4179             newSpanItem->position = spanStart;
4180             newSpanItem->rangeStart = spanEnd;
4181             newSpanItem->content = StringUtils::ToString(
4182                 StringUtils::ToWstring(spanItem->content)
4183                     .substr(std::max(start - oldStart, 0), std::min(end, oldEnd) - std::max(start, oldStart)));
4184             text.append(newSpanItem->content);
4185             subSpans.emplace_back(newSpanItem);
4186         }
4187     }
4188     spanString->SetString(text);
4189     spanString->SetSpanItems(std::move(subSpans));
4190 }
4191 
GetSameSpanItem(const RefPtr<SpanItem> & spanItem)4192 RefPtr<SpanItem> RichEditorPattern::GetSameSpanItem(const RefPtr<SpanItem>& spanItem)
4193 {
4194     CHECK_NULL_RETURN(spanItem, nullptr);
4195     if (spanItem->spanItemType == SpanItemType::IMAGE) {
4196         auto imageSpanItem = DynamicCast<ImageSpanItem>(spanItem);
4197         CHECK_NULL_RETURN(imageSpanItem, nullptr);
4198         auto newSpanItem = MakeRefPtr<ImageSpanItem>();
4199         auto options = imageSpanItem->options;
4200         if (!options.imagePixelMap) {
4201             auto imageNode = GetImageSpanNodeBySpanItem(imageSpanItem);
4202             CHECK_NULL_RETURN(imageNode, nullptr);
4203             auto pattern = imageNode->GetPattern<ImagePattern>();
4204             CHECK_NULL_RETURN(pattern, nullptr);
4205             auto image = pattern->GetCanvasImage();
4206             CHECK_NULL_RETURN(image, nullptr);
4207             auto pixelMap = image->GetPixelMap();
4208             if (!pixelMap) {
4209                 pixelMap = imageNode->GetPixelMap();
4210             }
4211             options.imagePixelMap = pixelMap;
4212         }
4213         newSpanItem->SetImageSpanOptions(options);
4214         return newSpanItem;
4215     } else if (spanItem->spanItemType == SpanItemType::NORMAL) {
4216         auto newSpanItem = spanItem->GetSameStyleSpanItem();
4217         return newSpanItem;
4218     }
4219     return nullptr;
4220 }
4221 
GetImageSpanNodeBySpanItem(const RefPtr<ImageSpanItem> & spanItem)4222 RefPtr<ImageSpanNode> RichEditorPattern::GetImageSpanNodeBySpanItem(const RefPtr<ImageSpanItem>& spanItem)
4223 {
4224     auto host = GetHost();
4225     CHECK_NULL_RETURN(host, nullptr);
4226     auto uiNodes = host->GetChildren();
4227     auto it = std::find_if(uiNodes.begin(), uiNodes.end(), [spanItem](const RefPtr<UINode>& uiNode) {
4228         auto imageSpanNode = DynamicCast<ImageSpanNode>(uiNode);
4229         CHECK_NULL_RETURN(imageSpanNode, false);
4230         return imageSpanNode->GetSpanItem() == spanItem;
4231     });
4232     CHECK_NULL_RETURN(it != uiNodes.end(), nullptr);
4233     return DynamicCast<ImageSpanNode>(*it);
4234 }
4235 
SetSubMap(RefPtr<SpanString> & spanString)4236 void RichEditorPattern::SetSubMap(RefPtr<SpanString>& spanString)
4237 {
4238     CHECK_NULL_VOID(spanString);
4239     auto subSpans = spanString->GetSpanItems();
4240     std::unordered_map<SpanType, std::list<RefPtr<SpanBase>>> subMap;
4241     for (auto& spanItem : subSpans) {
4242         if (!spanItem) {
4243             continue;
4244         }
4245         auto start = spanItem->rangeStart;
4246         auto end = spanItem->position;
4247         std::list<RefPtr<SpanBase>> spanBases;
4248         if (spanItem->spanItemType == NG::SpanItemType::IMAGE) {
4249             spanBases = { spanString->ToImageSpan(spanItem, start, end) };
4250         } else if (spanItem->spanItemType == NG::SpanItemType::NORMAL) {
4251             spanBases = { spanString->ToFontSpan(spanItem, start, end),
4252                 spanString->ToDecorationSpan(spanItem, start, end),
4253                 spanString->ToBaselineOffsetSpan(spanItem, start, end),
4254                 spanString->ToLetterSpacingSpan(spanItem, start, end),
4255                 spanString->ToGestureSpan(spanItem, start, end),
4256                 spanString->ToParagraphStyleSpan(spanItem, start, end),
4257                 spanString->ToLineHeightSpan(spanItem, start, end) };
4258         }
4259         for (auto& spanBase : spanBases) {
4260             if (!spanBase) {
4261                 continue;
4262             }
4263             auto it = subMap.find(spanBase->GetSpanType());
4264             if (it == subMap.end()) {
4265                 subMap.insert({ spanBase->GetSpanType(), { spanBase } });
4266             } else {
4267                 it->second.emplace_back(std::move(spanBase));
4268             }
4269         }
4270     }
4271     spanString->SetSpanMap(std::move(subMap));
4272 }
4273 
AddSpanByPasteData(const RefPtr<SpanString> & spanString)4274 void RichEditorPattern::AddSpanByPasteData(const RefPtr<SpanString>& spanString)
4275 {
4276     CHECK_NULL_VOID(spanString);
4277     if (spanString->GetSpansMap().empty()) {
4278         CompleteStyledString(const_cast<RefPtr<SpanString>&>(spanString));
4279     }
4280     if (isSpanStringMode_) {
4281         InsertStyledStringByPaste(spanString);
4282     } else {
4283         AddSpansByPaste(spanString->GetSpanItems());
4284     }
4285 
4286     if (aiWriteAdapter_->GetAIWrite()) {
4287         return;
4288     }
4289     StartTwinkling();
4290     auto host = GetHost();
4291     CHECK_NULL_VOID(host);
4292     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
4293     host->MarkModifyDone();
4294 }
4295 
CompleteStyledString(RefPtr<SpanString> & spanString)4296 void RichEditorPattern::CompleteStyledString(RefPtr<SpanString>& spanString)
4297 {
4298     CHECK_NULL_VOID(spanString);
4299     std::string text;
4300     auto spans = spanString->GetSpanItems();
4301     std::for_each(spans.begin(), spans.end(), [&text](RefPtr<SpanItem>& item) {
4302         CHECK_NULL_VOID(item);
4303         text.append(item->content);
4304         item->position = item->interval.second;
4305         item->rangeStart = item->interval.first;
4306     });
4307     spanString->SetString(std::move(text));
4308     SetSubMap(spanString);
4309 }
4310 
InsertStyledStringByPaste(const RefPtr<SpanString> & spanString)4311 void RichEditorPattern::InsertStyledStringByPaste(const RefPtr<SpanString>& spanString)
4312 {
4313     CHECK_NULL_VOID(spanString && styledString_);
4314     int32_t changeStart = caretPosition_;
4315     int32_t changeLength = 0;
4316     if (textSelector_.IsValid()) {
4317         changeStart = textSelector_.GetTextStart();
4318         changeLength = textSelector_.GetTextEnd() - textSelector_.GetTextStart();
4319     }
4320     CHECK_NULL_VOID(BeforeStyledStringChange(changeStart, changeLength, spanString));
4321     if (changeLength > 0) {
4322         DeleteForwardInStyledString(changeLength, false);
4323     }
4324     ResetSelection();
4325     styledString_->InsertSpanString(changeStart, spanString);
4326     SetCaretPosition(caretPosition_ + spanString->GetLength());
4327     AfterStyledStringChange(changeStart, changeLength, spanString->GetString());
4328 }
4329 
HandleOnDragInsertStyledString(const RefPtr<SpanString> & spanString)4330 void RichEditorPattern::HandleOnDragInsertStyledString(const RefPtr<SpanString>& spanString)
4331 {
4332     CHECK_NULL_VOID(spanString);
4333     int currentCaretPosition = caretPosition_;
4334     auto strLength = spanString->GetLength();
4335     if (isDragSponsor_) {
4336         bool isInsertForward = currentCaretPosition < dragRange_.first;
4337         bool isInsertBackward = currentCaretPosition > dragRange_.second;
4338         CHECK_NULL_VOID(isInsertForward || isInsertBackward);
4339         CHECK_NULL_VOID(BeforeStyledStringChange(currentCaretPosition, 0, spanString));
4340         styledString_->InsertSpanString(currentCaretPosition, spanString);
4341         AfterStyledStringChange(currentCaretPosition, 0, spanString->GetString());
4342         if (isInsertForward) {
4343             SetCaretPosition(currentCaretPosition + strLength);
4344             dragRange_.first += strLength;
4345             dragRange_.second += strLength;
4346         }
4347         DeleteValueInStyledString(dragRange_.first, strLength, true, false);
4348     } else {
4349         CHECK_NULL_VOID(BeforeStyledStringChange(currentCaretPosition, 0, spanString));
4350         styledString_->InsertSpanString(currentCaretPosition, spanString);
4351         SetCaretPosition(currentCaretPosition + strLength);
4352         AfterStyledStringChange(currentCaretPosition, 0, spanString->GetString());
4353     }
4354     StartTwinkling();
4355     auto host = GetHost();
4356     CHECK_NULL_VOID(host);
4357     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
4358 }
4359 
AddSpansByPaste(const std::list<RefPtr<NG::SpanItem>> & spans)4360 void RichEditorPattern::AddSpansByPaste(const std::list<RefPtr<NG::SpanItem>>& spans)
4361 {
4362     if (textSelector_.IsValid()) {
4363         SetCaretPosition(textSelector_.GetTextStart());
4364         DeleteForward(textSelector_.GetTextStart(), textSelector_.GetTextEnd() - textSelector_.GetTextStart());
4365         ResetSelection();
4366     }
4367     for (const auto& spanItem : spans) {
4368         if (!spanItem) {
4369             continue;
4370         }
4371         auto imageSpanItem = DynamicCast<ImageSpanItem>(spanItem);
4372         if (imageSpanItem) {
4373             auto options = imageSpanItem->options;
4374             options.offset = caretPosition_;
4375             AddImageSpan(options, true, caretPosition_, true);
4376         } else {
4377             auto options = GetTextSpanOptions(spanItem);
4378             AddTextSpan(options, true, caretPosition_);
4379         }
4380     }
4381 }
4382 
GetTextSpanOptions(const RefPtr<SpanItem> & spanItem)4383 TextSpanOptions RichEditorPattern::GetTextSpanOptions(const RefPtr<SpanItem>& spanItem)
4384 {
4385     CHECK_NULL_RETURN(spanItem, {});
4386     TextStyle textStyle = GetDefaultTextStyle();
4387     UseSelfStyle(spanItem->fontStyle, spanItem->textLineStyle, textStyle);
4388     struct UpdateParagraphStyle paraStyle;
4389     paraStyle.textAlign = spanItem->textLineStyle->GetTextAlign();
4390     paraStyle.leadingMargin = spanItem->textLineStyle->GetLeadingMargin();
4391     paraStyle.wordBreak = spanItem->textLineStyle->GetWordBreak();
4392     paraStyle.lineBreakStrategy = spanItem->textLineStyle->GetLineBreakStrategy();
4393     TextSpanOptions options;
4394     options.value = spanItem->content;
4395     options.offset = caretPosition_;
4396     UserGestureOptions gestureOption;
4397     gestureOption.onClick = spanItem->onClick;
4398     gestureOption.onLongPress = spanItem->onLongPress;
4399     options.userGestureOption = gestureOption;
4400     options.style = textStyle;
4401     options.paraStyle = paraStyle;
4402     return options;
4403 }
4404 
ResetDragSpanItems()4405 void RichEditorPattern::ResetDragSpanItems()
4406 {
4407     auto host = GetHost();
4408     CHECK_NULL_VOID(host);
4409     std::unordered_set<int32_t> nodeIds;
4410     std::for_each(dragSpanItems_.begin(), dragSpanItems_.end(), [&nodeIds](RefPtr<SpanItem>& item) {
4411         CHECK_NULL_VOID(item);
4412         item->EndDrag();
4413         auto imageSpanItem = DynamicCast<ImageSpanItem>(item);
4414         if (imageSpanItem) {
4415             nodeIds.emplace(imageSpanItem->imageNodeId);
4416             return;
4417         }
4418         auto placeholderSpanItem = DynamicCast<PlaceholderSpanItem>(item);
4419         if (placeholderSpanItem) {
4420             nodeIds.emplace(placeholderSpanItem->placeholderSpanNodeId);
4421         }
4422     });
4423     const auto& childrens = host->GetChildren();
4424     for (const auto& child : childrens) {
4425         auto findResult = nodeIds.find(child->GetId());
4426         if (findResult == nodeIds.end()) {
4427             continue;
4428         }
4429         auto node = DynamicCast<FrameNode>(child);
4430         if (!node) {
4431             continue;
4432         }
4433         auto renderContext = node->GetRenderContext();
4434         if (!renderContext) {
4435             continue;
4436         }
4437         renderContext->UpdateOpacity(1);
4438         node->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
4439     }
4440     dragSpanItems_.clear();
4441 }
4442 
SelectOverlayIsOn()4443 bool RichEditorPattern::SelectOverlayIsOn()
4444 {
4445     return selectOverlay_->SelectOverlayIsOn();
4446 }
4447 
UpdateEditingValue(const std::shared_ptr<TextEditingValue> & value,bool needFireChangeEvent)4448 void RichEditorPattern::UpdateEditingValue(const std::shared_ptr<TextEditingValue>& value, bool needFireChangeEvent)
4449 {
4450 #ifdef ENABLE_STANDARD_INPUT
4451     InsertValue(value->text, true);
4452 #else
4453     if (value->isDelete) {
4454         HandleOnDelete(true);
4455     } else {
4456         InsertValue(value->appendText);
4457     }
4458 #endif
4459 }
4460 
InitMouseEvent()4461 void RichEditorPattern::InitMouseEvent()
4462 {
4463     CHECK_NULL_VOID(!mouseEventInitialized_);
4464     auto host = GetHost();
4465     CHECK_NULL_VOID(host);
4466     auto eventHub = host->GetEventHub<EventHub>();
4467     CHECK_NULL_VOID(eventHub);
4468     auto inputHub = eventHub->GetOrCreateInputEventHub();
4469     CHECK_NULL_VOID(inputHub);
4470 
4471     auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
4472         auto pattern = weak.Upgrade();
4473         CHECK_NULL_VOID(pattern);
4474         pattern->sourceType_ = info.GetSourceDevice();
4475         pattern->HandleMouseEvent(info);
4476     };
4477     auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
4478     inputHub->AddOnMouseEvent(mouseEvent);
4479     auto hoverTask = [weak = WeakClaim(this)](bool isHover) {
4480         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "on hover event isHover=%{public}d", isHover);
4481         auto pattern = weak.Upgrade();
4482         if (pattern) {
4483             pattern->OnHover(isHover);
4484         }
4485     };
4486     auto hoverEvent = MakeRefPtr<InputEvent>(std::move(hoverTask));
4487     inputHub->AddOnHoverEvent(hoverEvent);
4488     mouseEventInitialized_ = true;
4489 }
4490 
OnHover(bool isHover)4491 void RichEditorPattern::OnHover(bool isHover)
4492 {
4493     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "isHover=%{public}d", isHover);
4494     auto frame = GetHost();
4495     CHECK_NULL_VOID(frame);
4496     auto frameId = frame->GetId();
4497     auto pipeline = frame->GetContext();
4498     CHECK_NULL_VOID(pipeline);
4499     auto scrollBar = GetScrollBar();
4500     if (isHover && (!scrollBar || !scrollBar->IsPressed())) {
4501         pipeline->SetMouseStyleHoldNode(frameId);
4502         pipeline->ChangeMouseStyle(frameId, MouseFormat::TEXT_CURSOR);
4503         currentMouseStyle_ = MouseFormat::TEXT_CURSOR;
4504     } else {
4505         pipeline->ChangeMouseStyle(frameId, MouseFormat::DEFAULT);
4506         currentMouseStyle_ = MouseFormat::DEFAULT;
4507         pipeline->FreeMouseStyleHoldNode(frameId);
4508         HandleUrlSpanForegroundClear();
4509     }
4510 }
4511 
RequestKeyboard(bool isFocusViewChanged,bool needStartTwinkling,bool needShowSoftKeyboard)4512 bool RichEditorPattern::RequestKeyboard(bool isFocusViewChanged, bool needStartTwinkling, bool needShowSoftKeyboard)
4513 {
4514     auto host = GetHost();
4515     CHECK_NULL_RETURN(host, false);
4516     auto context = host->GetContext();
4517     CHECK_NULL_RETURN(context, false);
4518     CHECK_NULL_RETURN(needShowSoftKeyboard, false);
4519     if (needShowSoftKeyboard && customKeyboardBuilder_) {
4520         return RequestCustomKeyboard();
4521     }
4522 #if defined(ENABLE_STANDARD_INPUT)
4523     if (!EnableStandardInput(needShowSoftKeyboard)) {
4524         return false;
4525     }
4526 #else
4527     if (!UnableStandardInput(isFocusViewChanged)) {
4528         return false;
4529     }
4530 #endif
4531     return true;
4532 }
4533 
4534 #if defined(ENABLE_STANDARD_INPUT)
4535 #ifdef WINDOW_SCENE_SUPPORTED
GetSCBSystemWindowId()4536 uint32_t RichEditorPattern::GetSCBSystemWindowId()
4537 {
4538     RefPtr<FrameNode> frameNode = GetHost();
4539     CHECK_NULL_RETURN(frameNode, {});
4540     auto focusSystemWindowId = WindowSceneHelper::GetFocusSystemWindowId(frameNode);
4541     TAG_LOGD(AceLogTag::ACE_KEYBOARD, "RichEditor Find SCBSystemWindowId End, (%{public}d).", focusSystemWindowId);
4542     return focusSystemWindowId;
4543 }
4544 #endif
4545 
EnableStandardInput(bool needShowSoftKeyboard)4546 bool RichEditorPattern::EnableStandardInput(bool needShowSoftKeyboard)
4547 {
4548     auto host = GetHost();
4549     CHECK_NULL_RETURN(host, false);
4550     auto context = host->GetContext();
4551     CHECK_NULL_RETURN(context, false);
4552     MiscServices::Configuration configuration;
4553     configuration.SetEnterKeyType(static_cast<MiscServices::EnterKeyType>(
4554         static_cast<int32_t>(GetTextInputActionValue(GetDefaultTextInputAction()))));
4555     configuration.SetTextInputType(
4556         static_cast<MiscServices::TextInputType>(static_cast<int32_t>(TextInputType::UNSPECIFIED)));
4557     MiscServices::InputMethodController::GetInstance()->OnConfigurationChange(configuration);
4558     if (richEditTextChangeListener_ == nullptr) {
4559         richEditTextChangeListener_ = new OnTextChangedListenerImpl(WeakClaim(this));
4560     }
4561     auto inputMethod = MiscServices::InputMethodController::GetInstance();
4562     CHECK_NULL_RETURN(inputMethod, false);
4563     auto miscTextConfig = GetMiscTextConfig();
4564     CHECK_NULL_RETURN(miscTextConfig.has_value(), false);
4565     TAG_LOGD(
4566         AceLogTag::ACE_RICH_TEXT, "RequestKeyboard set calling window id is : %{public}u", miscTextConfig->windowId);
4567     MiscServices::TextConfig textconfig = miscTextConfig.value();
4568 #ifdef WINDOW_SCENE_SUPPORTED
4569     auto systemWindowId = GetSCBSystemWindowId();
4570     if (systemWindowId) {
4571         TAG_LOGD(AceLogTag::ACE_KEYBOARD, "windowid(%{public}u->%{public}u.", miscTextConfig->windowId, systemWindowId);
4572         miscTextConfig->windowId = systemWindowId;
4573     }
4574 #endif
4575     auto textFieldManager = DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
4576     if (host && textFieldManager) {
4577         textFieldManager->SetLastRequestKeyboardId(host->GetId());
4578     }
4579     auto ret = inputMethod->Attach(richEditTextChangeListener_, needShowSoftKeyboard, textconfig);
4580     if (ret == MiscServices::ErrorCode::NO_ERROR) {
4581         textFieldManager->SetIsImeAttached(true);
4582     }
4583     UpdateCaretInfoToController();
4584     if (context) {
4585         inputMethod->SetCallingWindow(context->GetWindowId());
4586     }
4587 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
4588     imeAttached_ = true;
4589 #endif
4590     return true;
4591 }
4592 
GetMiscTextConfig()4593 std::optional<MiscServices::TextConfig> RichEditorPattern::GetMiscTextConfig()
4594 {
4595     auto tmpHost = GetHost();
4596     CHECK_NULL_RETURN(tmpHost, {});
4597     auto pipeline = tmpHost->GetContextRefPtr();
4598     auto renderContext = tmpHost->GetRenderContext();
4599     CHECK_NULL_RETURN(pipeline && renderContext, {});
4600 
4601     float caretHeight = 0.0f;
4602     OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight);
4603     if (NearZero(caretHeight)) {
4604         auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
4605         caretHeight = overlayModifier ? overlayModifier->GetCaretHeight() : DEFAULT_CARET_HEIGHT;
4606     }
4607     if (NearZero(caretHeight)) {
4608         auto [caretAdjustOffset, caretAdjustHeight] = CalculateCaretOffsetAndHeight();
4609         caretHeight = caretAdjustHeight;
4610     }
4611 
4612     // richeditor relative to root node offset(without transform)
4613     auto parentGlobalOffset = renderContext->GetPaintRectWithoutTransform().GetOffset() -
4614         pipeline->GetRootRect().GetOffset();
4615     // caret top (without transform)
4616     auto caretTop = caretOffset.GetY() + parentGlobalOffset.GetY();
4617     double positionY = parentGlobalOffset.GetY();
4618     double height = caretTop + caretHeight + KEYBOARD_AVOID_OFFSET.ConvertToPx() - positionY;
4619 
4620     if (auto manager = pipeline->GetSafeAreaManager(); manager) {
4621         auto keyboardOffset = manager->GetKeyboardOffset();
4622         positionY -= keyboardOffset;
4623     }
4624     OffsetF caretLeftTopPoint(caretOffset.GetX() + parentGlobalOffset.GetX(), caretTop);
4625     OffsetF caretRightBottomPoint(caretLeftTopPoint.GetX() + CARET_WIDTH, caretLeftTopPoint.GetY() + caretHeight);
4626     HandlePointWithTransform(caretLeftTopPoint);
4627     HandlePointWithTransform(caretRightBottomPoint);
4628     // window rect relative to screen
4629     auto windowRect = pipeline->GetCurrentWindowRect();
4630     MiscServices::CursorInfo cursorInfo { .left = caretLeftTopPoint.GetX() + windowRect.Left(),
4631         .top = caretLeftTopPoint.GetY() + windowRect.Top(),
4632         .width = std::abs(caretLeftTopPoint.GetX() - caretRightBottomPoint.GetX()),
4633         .height = std::abs(caretLeftTopPoint.GetY() - caretRightBottomPoint.GetY()) };
4634     MiscServices::InputAttribute inputAttribute = { .inputPattern = (int32_t)TextInputType::UNSPECIFIED,
4635         .enterKeyType = (int32_t)GetTextInputActionValue(GetDefaultTextInputAction()),
4636         .isTextPreviewSupported = !isSpanStringMode_ && isTextPreviewSupported_ };
4637     auto start = textSelector_.IsValid() ? textSelector_.GetStart() : caretPosition_;
4638     auto end = textSelector_.IsValid() ? textSelector_.GetEnd() : caretPosition_;
4639     MiscServices::TextConfig textConfig = { .inputAttribute = inputAttribute,
4640         .cursorInfo = cursorInfo,
4641         .range = { .start = start, .end = end },
4642         .windowId = pipeline->GetFocusWindowId(),
4643         .positionY = positionY + windowRect.Top(),
4644         .height = height };
4645     return textConfig;
4646 }
4647 #else
UnableStandardInput(bool isFocusViewChanged)4648 bool RichEditorPattern::UnableStandardInput(bool isFocusViewChanged)
4649 {
4650     auto host = GetHost();
4651     CHECK_NULL_RETURN(host, false);
4652     auto context = host->GetContext();
4653     CHECK_NULL_RETURN(context, false);
4654     if (HasConnection()) {
4655         connection_->Show(isFocusViewChanged, GetInstanceId());
4656         return true;
4657     }
4658     TextInputConfiguration config;
4659     config.type = TextInputType::UNSPECIFIED;
4660     config.action = TextInputAction::DONE;
4661     config.obscureText = false;
4662     connection_ =
4663         TextInputProxy::GetInstance().Attach(WeakClaim(this), config, context->GetTaskExecutor(), GetInstanceId());
4664     if (!HasConnection()) {
4665         return false;
4666     }
4667     TextEditingValue value;
4668     if (spans_.empty()) {
4669         value.text = textForDisplay_;
4670     } else {
4671         for (auto it = spans_.begin(); it != spans_.end(); it++) {
4672             if ((*it)->placeholderIndex < 0) {
4673                 value.text.append((*it)->content);
4674             } else {
4675                 value.text.append(" ");
4676             }
4677         }
4678     }
4679     value.selection.Update(caretPosition_, caretPosition_);
4680     connection_->SetEditingState(value, GetInstanceId());
4681     connection_->Show(isFocusViewChanged, GetInstanceId());
4682     return true;
4683 }
4684 #endif
4685 
OnColorConfigurationUpdate()4686 void RichEditorPattern::OnColorConfigurationUpdate()
4687 {
4688     auto host = GetHost();
4689     CHECK_NULL_VOID(host);
4690     auto theme = GetTheme<RichEditorTheme>();
4691     CHECK_NULL_VOID(theme);
4692     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4693     CHECK_NULL_VOID(textLayoutProperty);
4694     const auto& themeTextStyle = theme->GetTextStyle();
4695     auto themeTextColor = themeTextStyle.GetTextColor();
4696     auto themeTextDecorationColor = themeTextStyle.GetTextDecorationColor();
4697     textLayoutProperty->UpdateTextColor(themeTextColor);
4698     textLayoutProperty->UpdateTextDecorationColor(themeTextDecorationColor);
4699 
4700     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "theme, TextColor=%{public}s, DecorationColor=%{public}s",
4701         themeTextColor.ToString().c_str(), themeTextDecorationColor.ToString().c_str());
4702 
4703     const auto& spans = host->GetChildren();
4704     for (const auto& uiNode : spans) {
4705         auto spanNode = DynamicCast<SpanNode>(uiNode);
4706         if (!spanNode) {
4707             continue;
4708         }
4709         auto spanItem = spanNode->GetSpanItem();
4710         if (!spanItem) {
4711             continue;
4712         }
4713         IF_TRUE(spanItem->useThemeFontColor, spanNode->UpdateTextColor(themeTextColor));
4714         IF_TRUE(spanItem->useThemeDecorationColor, spanNode->UpdateTextDecorationColor(themeTextDecorationColor));
4715         spanNode->UpdateColorByResourceId();
4716     }
4717     IF_PRESENT(typingTextStyle_, UpdateColorByResourceId());
4718     IF_PRESENT(typingStyle_, UpdateColorByResourceId());
4719 
4720     IF_PRESENT(magnifierController_, SetColorModeChange(true));
4721     auto scrollBar = GetScrollBar();
4722     auto scrollbarTheme = GetTheme<ScrollBarTheme>();
4723     CHECK_NULL_VOID(scrollBar && scrollbarTheme);
4724     scrollBar->SetForegroundColor(scrollbarTheme->GetForegroundColor());
4725     scrollBar->SetBackgroundColor(scrollbarTheme->GetBackgroundColor());
4726 }
4727 
UpdateCaretInfoToController()4728 void RichEditorPattern::UpdateCaretInfoToController()
4729 {
4730     CHECK_NULL_VOID(HasFocus());
4731     std::string text = "";
4732     for (auto iter = spans_.begin(); iter != spans_.end(); iter++) {
4733         text += (*iter)->content;
4734     }
4735     auto start = textSelector_.IsValid() ? textSelector_.GetStart() : caretPosition_;
4736     auto end = textSelector_.IsValid() ? textSelector_.GetEnd() : caretPosition_;
4737 #if defined(ENABLE_STANDARD_INPUT)
4738     auto miscTextConfig = GetMiscTextConfig();
4739     CHECK_NULL_VOID(miscTextConfig.has_value());
4740     MiscServices::CursorInfo cursorInfo = miscTextConfig.value().cursorInfo;
4741     MiscServices::InputMethodController::GetInstance()->OnCursorUpdate(cursorInfo);
4742     MiscServices::InputMethodController::GetInstance()->OnSelectionChange(
4743         StringUtils::Str8ToStr16(text), start, end);
4744     TAG_LOGD(AceLogTag::ACE_RICH_TEXT,
4745         "CursorInfo: pos=[%{public}.2f,%{public}.2f], size=[%{public}.2f,%{public}.2f], caret=%{public}d;"
4746         "OnSelectionChange: textLen=%{public}zu, range=[%{public}d,%{public}d]",
4747         cursorInfo.left, cursorInfo.top, cursorInfo.width, cursorInfo.height, caretPosition_,
4748         StringUtils::Str8ToStr16(text).length(), start, end);
4749 #else
4750     if (HasConnection()) {
4751         TextEditingValue editingValue;
4752         editingValue.text = text;
4753         editingValue.hint = "";
4754         editingValue.selection.Update(start, end);
4755         connection_->SetEditingState(editingValue, GetInstanceId());
4756     }
4757 #endif
4758 }
4759 
HasConnection() const4760 bool RichEditorPattern::HasConnection() const
4761 {
4762 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
4763     return imeAttached_;
4764 #else
4765     return connection_ != nullptr;
4766 #endif
4767 }
4768 
SetCustomKeyboardOption(bool supportAvoidance)4769 void RichEditorPattern::SetCustomKeyboardOption(bool supportAvoidance)
4770 {
4771     keyboardAvoidance_ = supportAvoidance;
4772 }
4773 
RequestCustomKeyboard()4774 bool RichEditorPattern::RequestCustomKeyboard()
4775 {
4776 #if defined(ENABLE_STANDARD_INPUT)
4777     auto inputMethod = MiscServices::InputMethodController::GetInstance();
4778     if (inputMethod) {
4779         TAG_LOGD(AceLogTag::ACE_KEYBOARD, "RichRequest CustomKeyboard, Close keyboard Successfully.");
4780         inputMethod->RequestHideInput();
4781         inputMethod->Close();
4782     }
4783 #else
4784     if (HasConnection()) {
4785         connection_->Close(GetInstanceId());
4786         connection_ = nullptr;
4787     }
4788 #endif
4789 
4790     if (isCustomKeyboardAttached_) {
4791         return true;
4792     }
4793     CHECK_NULL_RETURN(customKeyboardBuilder_, false);
4794     auto frameNode = GetHost();
4795     CHECK_NULL_RETURN(frameNode, false);
4796     auto pipeline = frameNode->GetContext();
4797     CHECK_NULL_RETURN(pipeline, false);
4798     auto overlayManager = pipeline->GetOverlayManager();
4799     CHECK_NULL_RETURN(overlayManager, false);
4800     overlayManager->SetCustomKeyboardOption(keyboardAvoidance_);
4801     overlayManager->BindKeyboard(customKeyboardBuilder_, frameNode->GetId());
4802     isCustomKeyboardAttached_ = true;
4803     contentChange_ = false;
4804     keyboardOverlay_ = overlayManager;
4805     auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
4806     keyboardOverlay_->AvoidCustomKeyboard(frameNode->GetId(), caretHeight);
4807     return true;
4808 }
4809 
CloseCustomKeyboard()4810 bool RichEditorPattern::CloseCustomKeyboard()
4811 {
4812     auto frameNode = GetHost();
4813     CHECK_NULL_RETURN(frameNode, false);
4814     CHECK_NULL_RETURN(keyboardOverlay_, false);
4815     keyboardOverlay_->CloseKeyboard(frameNode->GetId());
4816     isCustomKeyboardAttached_ = false;
4817     contentChange_ = false;
4818     return true;
4819 }
4820 
SetPreviewText(const std::string & previewTextValue,const PreviewRange range)4821 int32_t RichEditorPattern::SetPreviewText(const std::string& previewTextValue, const PreviewRange range)
4822 {
4823     CHECK_NULL_RETURN(!isSpanStringMode_, ERROR_BAD_PARAMETERS);
4824     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "previewTextValue=%{private}s, range=[%{public}d,%{public}d]",
4825         previewTextValue.c_str(), range.start, range.end);
4826     auto host = GetHost();
4827     CHECK_NULL_RETURN(host, ERROR_BAD_PARAMETERS);
4828     previewTextRecord_.hasDiff = true;
4829     if (!IsPreviewTextInputting()) {
4830         if (!InitPreviewText(previewTextValue, range)) {
4831             previewTextRecord_.hasDiff = false;
4832             return ERROR_BAD_PARAMETERS;
4833         }
4834     } else {
4835         if (!UpdatePreviewText(previewTextValue, range)) {
4836             previewTextRecord_.hasDiff = false;
4837             return ERROR_BAD_PARAMETERS;
4838         }
4839     }
4840     previewTextRecord_.hasDiff = false;
4841     previewTextRecord_.replacedRange.Set(previewTextRecord_.startOffset, previewTextRecord_.endOffset);
4842     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
4843     return NO_ERRORS;
4844 }
4845 
InitPreviewText(const std::string & previewTextValue,const PreviewRange range)4846 bool RichEditorPattern::InitPreviewText(const std::string& previewTextValue, const PreviewRange range)
4847 {
4848     if (range.start != -1 || range.end != -1) {
4849         return ReplacePreviewText(previewTextValue, range);
4850     }
4851     auto& record = previewTextRecord_;
4852     record.isPreviewTextInputting = true;
4853     record.replacedRange = range;
4854     record.startOffset = textSelector_.SelectNothing() ? caretPosition_ : textSelector_.GetTextStart();
4855     record.newPreviewContent = previewTextValue;
4856     ProcessInsertValue(previewTextValue, OperationType::IME, false);
4857     record.previewContent = record.newPreviewContent;
4858     auto length = static_cast<int32_t>(StringUtils::ToWstring(previewTextValue).length());
4859     record.endOffset = record.startOffset + length;
4860     record.newPreviewContent.clear();
4861     return true;
4862 }
4863 
ReplacePreviewText(const std::string & previewTextValue,const PreviewRange & range)4864 bool RichEditorPattern::ReplacePreviewText(const std::string& previewTextValue, const PreviewRange& range)
4865 {
4866     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "ReplacePreviewText");
4867     if (range.start < 0 || range.end < range.start || range.end > GetTextContentLength()) {
4868         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "bad PreviewRange");
4869         return false;
4870     }
4871     previewTextRecord_.replacedRange = range;
4872     previewTextRecord_.startOffset = range.start;
4873     previewTextRecord_.endOffset = range.end;
4874     ProcessInsertValue(previewTextValue, OperationType::IME, false);
4875     return true;
4876 }
4877 
4878 // Used for text replacement, without notifying developer caret change
DeleteByRange(OperationRecord * const record,int32_t start,int32_t end)4879 void RichEditorPattern::DeleteByRange(OperationRecord* const record, int32_t start, int32_t end)
4880 {
4881     auto length = end - start;
4882     CHECK_NULL_VOID(length > 0);
4883     caretPosition_ = std::clamp(start, 0, GetTextContentLength());
4884     std::wstring deleteText = DeleteForwardOperation(length);
4885     if (record && deleteText.length() != 0) {
4886         record->deleteText = StringUtils::ToString(deleteText);
4887     }
4888 }
4889 
UpdatePreviewText(const std::string & previewTextValue,const PreviewRange range)4890 bool RichEditorPattern::UpdatePreviewText(const std::string& previewTextValue, const PreviewRange range)
4891 {
4892     auto& record = previewTextRecord_;
4893     if (range.start == -1 && range.end == -1 && !record.previewContent.empty()) {
4894         record.replacedRange.Set(record.startOffset, record.endOffset);
4895         record.newPreviewContent = previewTextValue;
4896         ProcessInsertValue(previewTextValue, OperationType::IME, false);
4897         record.previewContent = record.newPreviewContent;
4898         record.newPreviewContent.clear();
4899         record.endOffset =
4900             record.startOffset + static_cast<int32_t>(StringUtils::ToWstring(previewTextValue).length());
4901     } else {
4902         if (range.start < record.startOffset || range.end > record.endOffset || range.end < range.start) {
4903             TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "bad PreviewRange");
4904             return false;
4905         }
4906         if (previewTextValue.empty() && range.start == range.end) {
4907             SetCaretPosition(range.end);
4908             return false;
4909         }
4910         auto replaceIndex = range.start - record.startOffset;
4911         auto replaceLength = range.end - range.start;
4912         auto oldContent = record.previewContent;
4913         auto oldPreviewLength = static_cast<int32_t>(StringUtils::ToWstring(oldContent).length());
4914         if (replaceIndex < 0 || replaceIndex + replaceLength > oldPreviewLength) {
4915             TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "bad replaced range ");
4916             return false;
4917         }
4918         auto newContent =
4919             StringUtils::ToString(StringUtils::ToWstring(oldContent)
4920                                       .replace(replaceIndex, replaceLength, StringUtils::ToWstring(previewTextValue)));
4921         record.replacedRange = range;
4922         record.newPreviewContent = newContent;
4923         ProcessInsertValue(previewTextValue, OperationType::IME, false);
4924         record.previewContent = record.newPreviewContent;
4925         record.newPreviewContent.clear();
4926         record.endOffset =
4927             record.startOffset + static_cast<int32_t>(StringUtils::ToWstring(newContent).length());
4928     }
4929     return true;
4930 }
4931 
GetPreviewTextInfo() const4932 const PreviewTextInfo RichEditorPattern::GetPreviewTextInfo() const
4933 {
4934     PreviewTextInfo info;
4935     if (!previewTextRecord_.previewContent.empty()) {
4936         info.value = previewTextRecord_.previewContent;
4937         info.offset = previewTextRecord_.startOffset;
4938     }
4939     return info;
4940 }
4941 
FinishTextPreview()4942 void RichEditorPattern::FinishTextPreview()
4943 {
4944     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "FinishTextPreview byImf");
4945     if (previewTextRecord_.previewContent.empty()) {
4946         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "previewContent is empty");
4947         RemoveEmptySpans();
4948         previewTextRecord_.Reset();
4949         return;
4950     }
4951     auto previewContent = previewTextRecord_.previewContent;
4952     FinishTextPreviewInner();
4953     ProcessInsertValue(previewContent, OperationType::IME, false);
4954 }
4955 
FinishTextPreviewInner()4956 void RichEditorPattern::FinishTextPreviewInner()
4957 {
4958     CHECK_NULL_VOID(IsPreviewTextInputting());
4959     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "FinishTextPreviewInner");
4960     DeleteByRange(nullptr, previewTextRecord_.startOffset, previewTextRecord_.endOffset);
4961     previewTextRecord_.Reset();
4962 }
4963 
NotifyExitTextPreview()4964 void RichEditorPattern::NotifyExitTextPreview()
4965 {
4966     CHECK_NULL_VOID(IsPreviewTextInputting());
4967     CHECK_NULL_VOID(HasFocus());
4968     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "NotifyExitTextPreview");
4969     FinishTextPreviewInner();
4970     std::string text = "";
4971 #if defined(ENABLE_STANDARD_INPUT)
4972     MiscServices::InputMethodController::GetInstance()->OnSelectionChange(
4973         StringUtils::Str8ToStr16(text), caretPosition_, caretPosition_);
4974     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "notify imf that richEditor exit textPreview");
4975 #endif
4976 }
4977 
GetPreviewTextRects()4978 std::vector<RectF> RichEditorPattern::GetPreviewTextRects()
4979 {
4980     auto rects = paragraphs_.GetRects(previewTextRecord_.startOffset, previewTextRecord_.endOffset,
4981         RectHeightPolicy::COVER_TEXT);
4982     auto offset = GetTextRect().GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
4983     for (RectF& rect : rects) {
4984         rect += offset;
4985     }
4986     return rects;
4987 }
4988 
GetPreviewTextStyle() const4989 PreviewTextStyle RichEditorPattern::GetPreviewTextStyle() const
4990 {
4991     auto previewTextStyle = PreviewTextStyle::UNDERLINE;
4992     auto property = GetLayoutProperty<RichEditorLayoutProperty>();
4993     if (property && property->HasPreviewTextStyle()) {
4994         auto style = property->GetPreviewTextStyle();
4995         if (style == PREVIEW_STYLE_NORMAL) {
4996             previewTextStyle = PreviewTextStyle::NORMAL;
4997         } else if (style == PREVIEW_STYLE_UNDERLINE) {
4998             previewTextStyle = PreviewTextStyle::UNDERLINE;
4999         } else {
5000             TAG_LOGW(
5001                 AceLogTag::ACE_RICH_TEXT, "invalid previewTextStyle of RichEditorLayoutProperty");
5002         }
5003     }
5004     return previewTextStyle;
5005 }
5006 
GetPreviewTextDecorationColor() const5007 const Color& RichEditorPattern::GetPreviewTextDecorationColor() const
5008 {
5009     auto pipeline = PipelineBase::GetCurrentContext();
5010     CHECK_NULL_RETURN(pipeline, Color::TRANSPARENT);
5011     auto theme = pipeline->GetTheme<RichEditorTheme>();
5012     CHECK_NULL_RETURN(theme, Color::TRANSPARENT);
5013     if (GetPreviewTextStyle() == PreviewTextStyle::UNDERLINE) {
5014         return theme->GetPreviewUnderLineColor();
5015     }
5016     return Color::TRANSPARENT;
5017 }
5018 
GetPreviewTextUnderlineWidth() const5019 float RichEditorPattern::GetPreviewTextUnderlineWidth() const
5020 {
5021     auto pipeline = PipelineBase::GetCurrentContext();
5022     CHECK_NULL_RETURN(pipeline, 0.0f);
5023     auto theme = pipeline->GetTheme<RichEditorTheme>();
5024     CHECK_NULL_RETURN(theme, 0.0f);
5025     return theme->GetPreviewUnderlineWidth().ConvertToPx();
5026 }
5027 
IsIMEOperation(OperationType operationType)5028 bool RichEditorPattern::IsIMEOperation(OperationType operationType)
5029 {
5030     return operationType == OperationType::IME;
5031 }
5032 
InsertValue(const std::string & insertValue,bool isIME)5033 void RichEditorPattern::InsertValue(const std::string& insertValue, bool isIME)
5034 {
5035     InsertValueByOperationType(insertValue, isIME ? OperationType::IME : OperationType::DEFAULT);
5036 }
5037 
InsertValueByOperationType(const std::string & insertValue,OperationType operationType)5038 void RichEditorPattern::InsertValueByOperationType(const std::string& insertValue, OperationType operationType)
5039 {
5040     ProcessInsertValue(insertValue, operationType, true);
5041 }
5042 
5043 // operationType: when type is IME, it controls whether to perform ime callbacks
5044 // calledByImf: true means real input; false means preview input
ProcessInsertValue(const std::string & insertValue,OperationType operationType,bool calledByImf)5045 void RichEditorPattern::ProcessInsertValue(const std::string& insertValue, OperationType operationType,
5046     bool calledByImf)
5047 {
5048     CONTENT_MODIFY_LOCK(this);
5049     bool isIME = IsIMEOperation(operationType);
5050     TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
5051         "insertLen=%{public}zu, isIME=%{public}d, calledByImf=%{public}d, isSpanString=%{public}d",
5052         StringUtils::ToWstring(insertValue).length(), isIME, calledByImf, isSpanStringMode_);
5053     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "insertValue=[%{private}s]", StringUtils::RestoreEscape(insertValue).c_str());
5054 
5055     if (isIME && calledByImf && (!isEditing_ || IsDragging())) {
5056         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "NOT allow input, isEditing=%{public}d, isDragging=%{public}d",
5057             isEditing_, IsDragging());
5058         return;
5059     }
5060     if (isSpanStringMode_) {
5061         InsertValueInStyledString(insertValue);
5062         return;
5063     }
5064     OperationRecord record;
5065     record.beforeCaretPosition = caretPosition_ + moveLength_;
5066     if (textSelector_.IsValid()) {
5067         record.beforeCaretPosition = textSelector_.GetTextStart();
5068     }
5069     record.addText = insertValue;
5070 
5071     RichEditorChangeValue changeValue;
5072     bool allowContentChange = BeforeChangeText(changeValue, record, RecordType::INSERT);
5073     if (calledByImf && previewTextRecord_.IsValid()) {
5074         FinishTextPreviewInner();
5075     }
5076     bool allowImeInput = isIME ? BeforeIMEInsertValue(insertValue) : true;
5077     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "allowContentChange=%{public}d, allowImeInput=%{public}d, hasDiff=%{public}d",
5078         allowContentChange, allowImeInput, previewTextRecord_.hasDiff);
5079     CHECK_NULL_VOID(allowContentChange && allowImeInput || previewTextRecord_.hasDiff);
5080 
5081     ClearRedoOperationRecords();
5082     InsertValueOperation(insertValue, &record, operationType);
5083     record.afterCaretPosition = caretPosition_;
5084     if (isDragSponsor_) {
5085         record.deleteCaretPostion = dragRange_.first;
5086     }
5087     AddOperationRecord(record);
5088     AfterContentChange(changeValue);
5089 }
5090 
InsertValueOperation(const std::string & insertValue,OperationRecord * const record,OperationType operationType)5091 void RichEditorPattern::InsertValueOperation(const std::string& insertValue, OperationRecord* const record,
5092     OperationType operationType)
5093 {
5094     bool isSelector = textSelector_.IsValid();
5095     if (isSelector) {
5096         DeleteByRange(record, textSelector_.GetTextStart(), textSelector_.GetTextEnd());
5097         CloseSelectOverlay();
5098         ResetSelection();
5099     } else if (previewTextRecord_.hasDiff) {
5100         DeleteByRange(record, previewTextRecord_.replacedRange.start, previewTextRecord_.replacedRange.end);
5101     }
5102     TextInsertValueInfo info;
5103     CalcInsertValueObj(info);
5104     if (!caretVisible_) {
5105         StartTwinkling();
5106     }
5107     auto host = GetHost();
5108     CHECK_NULL_VOID(host);
5109     bool isIME = IsIMEOperation(operationType);
5110     RefPtr<SpanNode> spanNode = DynamicCast<SpanNode>(host->GetChildAtIndex(info.GetSpanIndex()));
5111     RefPtr<SpanNode> spanNodeBefore = DynamicCast<SpanNode>(host->GetChildAtIndex(info.GetSpanIndex() - 1));
5112     RefPtr<SpanNode> targetSpanNode = spanNode;
5113     bool needCreateNewSpan = host->GetChildren().empty();
5114     if (info.GetOffsetInSpan() == 0) {
5115         bool spanNodeBeforeCanInsert = spanNodeBefore && spanNodeBefore->GetTag() == V2::SPAN_ETS_TAG;
5116         bool spanNodeCanInsert = spanNode && spanNode->GetTag() == V2::SPAN_ETS_TAG;
5117         bool insertToBeforeNode = spanNodeBeforeCanInsert && !spanNodeCanInsert;
5118         insertToBeforeNode |= spanNodeBeforeCanInsert && spanNodeCanInsert && !IsLineSeparatorInLast(spanNodeBefore);
5119         if (insertToBeforeNode) {
5120             auto spanItem = spanNodeBefore->GetSpanItem();
5121             info.SetSpanIndex(info.GetSpanIndex() - 1);
5122             info.SetOffsetInSpan(spanItem->position - spanItem->rangeStart);
5123             targetSpanNode = spanNodeBefore;
5124         }
5125         needCreateNewSpan |= !spanNodeBeforeCanInsert && !spanNodeCanInsert;
5126     }
5127     if (needCreateNewSpan) {
5128         CreateTextSpanNode(targetSpanNode, info, insertValue, isIME);
5129         return;
5130     }
5131     if (typingStyle_.has_value() && !HasSameTypingStyle(targetSpanNode) && operationType != OperationType::DRAG) {
5132         InsertDiffStyleValueInSpan(targetSpanNode, info, insertValue, isIME);
5133         return;
5134     }
5135     InsertValueToSpanNode(targetSpanNode, insertValue, info);
5136     AfterInsertValue(targetSpanNode, static_cast<int32_t>(StringUtils::ToWstring(insertValue).length()), false, isIME);
5137 }
5138 
DeleteSelectOperation(OperationRecord * const record)5139 void RichEditorPattern::DeleteSelectOperation(OperationRecord* const record)
5140 {
5141     std::wstring deleteText = DeleteForwardOperation(textSelector_.GetTextEnd() - textSelector_.GetTextStart());
5142     if (record && deleteText.length() != 0) {
5143         record->deleteText = StringUtils::ToString(deleteText);
5144     }
5145     CloseSelectOverlay();
5146     ResetSelection();
5147 }
5148 
InsertDiffStyleValueInSpan(RefPtr<SpanNode> & spanNode,const TextInsertValueInfo & info,const std::string & insertValue,bool isIME)5149 void RichEditorPattern::InsertDiffStyleValueInSpan(
5150     RefPtr<SpanNode>& spanNode, const TextInsertValueInfo& info, const std::string& insertValue, bool isIME)
5151 {
5152     auto host = GetHost();
5153     CHECK_NULL_VOID(host);
5154     TextSpanOptions options;
5155     options.value = insertValue;
5156     options.offset = caretPosition_;
5157     auto theme = GetTheme<RichEditorTheme>();
5158     options.style = theme ? theme->GetTextStyle() : TextStyle();
5159     options.useThemeFontColor = typingStyle_->useThemeFontColor;
5160     options.useThemeDecorationColor = typingStyle_->useThemeDecorationColor;
5161     auto newSpanIndex = AddTextSpanOperation(options, false, -1,  true, false);
5162     auto newSpanNode = DynamicCast<SpanNode>(host->GetChildAtIndex(newSpanIndex));
5163     if (typingStyle_.has_value() && typingTextStyle_.has_value()) {
5164         UpdateTextStyle(newSpanNode, typingStyle_.value(), typingTextStyle_.value());
5165     }
5166     CopyTextSpanLineStyle(spanNode, newSpanNode, true);
5167     AfterInsertValue(newSpanNode, static_cast<int32_t>(StringUtils::ToWstring(insertValue).length()), true, isIME);
5168 }
5169 
IsLineSeparatorInLast(RefPtr<SpanNode> & spanNode)5170 bool RichEditorPattern::IsLineSeparatorInLast(RefPtr<SpanNode>& spanNode)
5171 {
5172     std::string content = spanNode->GetSpanItem()->content;
5173     std::wstring wContent = StringUtils::ToWstring(content);
5174     return !wContent.empty() && wContent.back() == L'\n';
5175 }
5176 
InsertValueToSpanNode(RefPtr<SpanNode> & spanNode,const std::string & insertValue,const TextInsertValueInfo & info)5177 void RichEditorPattern::InsertValueToSpanNode(
5178     RefPtr<SpanNode>& spanNode, const std::string& insertValue, const TextInsertValueInfo& info)
5179 {
5180     auto spanItem = spanNode->GetSpanItem();
5181     CHECK_NULL_VOID(spanItem);
5182     auto text = spanItem->content;
5183     std::wstring textTemp = StringUtils::ToWstring(text);
5184     auto textTempSize = static_cast<int32_t>(textTemp.size());
5185     if (textTempSize < info.GetOffsetInSpan()) {
5186         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "InsertValue error, offsetInSpan is greater than the size of spanItem, "
5187             "spanItemSize = %{public}d, offsetInSpan = %{public}d", textTempSize, info.GetOffsetInSpan());
5188         return;
5189     }
5190     std::wstring insertValueTemp = StringUtils::ToWstring(insertValue);
5191     textTemp.insert(info.GetOffsetInSpan(), insertValueTemp);
5192     text = StringUtils::ToString(textTemp);
5193     spanNode->UpdateContent(text);
5194     UpdateSpanPosition();
5195     SpanNodeFission(spanNode);
5196 }
5197 
InsertValueToBeforeSpan(RefPtr<SpanNode> & spanNodeBefore,const std::string & insertValue)5198 RefPtr<SpanNode> RichEditorPattern::InsertValueToBeforeSpan(
5199     RefPtr<SpanNode>& spanNodeBefore, const std::string& insertValue)
5200 {
5201     auto spanItem = spanNodeBefore->GetSpanItem();
5202     CHECK_NULL_RETURN(spanItem, spanNodeBefore);
5203     auto text = spanItem->content;
5204     std::wstring textTemp = StringUtils::ToWstring(text);
5205     std::wstring insertValueTemp = StringUtils::ToWstring(insertValue);
5206     textTemp.append(insertValueTemp);
5207 
5208     auto index = textTemp.find(lineSeparator);
5209     if (index != std::wstring::npos) {
5210         auto textBefore = textTemp.substr(0, index + 1);
5211         auto textAfter = textTemp.substr(index + 1);
5212         text = StringUtils::ToString(textBefore);
5213         spanNodeBefore->UpdateContent(text);
5214         spanItem->position += static_cast<int32_t>(insertValueTemp.length()) - static_cast<int32_t>(textAfter.length());
5215         if (!textAfter.empty()) {
5216             auto host = GetHost();
5217             CHECK_NULL_RETURN(spanItem, spanNodeBefore);
5218             TextInsertValueInfo infoAfter;
5219             infoAfter.SetSpanIndex(host->GetChildIndex(spanNodeBefore) + 1);
5220             infoAfter.SetOffsetInSpan(0);
5221             auto nodeId = ViewStackProcessor::GetInstance()->ClaimNodeId();
5222             RefPtr<SpanNode> spanNodeAfter = SpanNode::GetOrCreateSpanNode(nodeId);
5223             spanNodeAfter->MountToParent(host, infoAfter.GetSpanIndex());
5224             spanNodeAfter->UpdateContent(StringUtils::ToString(textAfter));
5225             CopyTextSpanStyle(spanNodeBefore, spanNodeAfter);
5226             auto spanItemAfter = spanNodeAfter->GetSpanItem();
5227             spanItemAfter->position = static_cast<int32_t>(textTemp.length());
5228             spanItemAfter->useThemeFontColor = spanItem->useThemeFontColor;
5229             spanItemAfter->useThemeDecorationColor = spanItem->useThemeDecorationColor;
5230             AddSpanItem(spanItemAfter, host->GetChildIndex(spanNodeBefore) + 1);
5231             SpanNodeFission(spanNodeAfter);
5232             return spanNodeAfter;
5233         }
5234     } else {
5235         text = StringUtils::ToString(textTemp);
5236         spanNodeBefore->UpdateContent(text);
5237         spanItem->position += static_cast<int32_t>(StringUtils::ToWstring(insertValue).length());
5238     }
5239     return spanNodeBefore;
5240 }
5241 
CreateTextSpanNode(RefPtr<SpanNode> & spanNode,const TextInsertValueInfo & info,const std::string & insertValue,bool isIME)5242 void RichEditorPattern::CreateTextSpanNode(
5243     RefPtr<SpanNode>& spanNode, const TextInsertValueInfo& info, const std::string& insertValue, bool isIME)
5244 {
5245     auto host = GetHost();
5246     CHECK_NULL_VOID(host);
5247     auto nodeId = ViewStackProcessor::GetInstance()->ClaimNodeId();
5248     spanNode = SpanNode::GetOrCreateSpanNode(nodeId);
5249     spanNode->MountToParent(host, info.GetSpanIndex());
5250     auto spanItem = spanNode->GetSpanItem();
5251 
5252     if (typingStyle_.has_value() && typingTextStyle_.has_value()) {
5253         spanItem->useThemeFontColor = typingStyle_->useThemeFontColor;
5254         spanItem->useThemeDecorationColor = typingStyle_->useThemeDecorationColor;
5255         UpdateTextStyle(spanNode, typingStyle_.value(), typingTextStyle_.value());
5256         auto spanItem = spanNode->GetSpanItem();
5257         spanItem->SetTextStyle(typingTextStyle_);
5258     } else {
5259         spanNode->UpdateFontSize(Dimension(DEFAULT_TEXT_SIZE, DimensionUnit::FP));
5260         spanNode->AddPropertyInfo(PropertyInfo::FONTSIZE);
5261         SetDefaultColor(spanNode);
5262     }
5263     spanNode->UpdateContent(insertValue);
5264     AddSpanItem(spanItem, info.GetSpanIndex());
5265     UpdateSpanPosition();
5266     SpanNodeFission(spanNode);
5267     AfterInsertValue(spanNode, static_cast<int32_t>(StringUtils::ToWstring(insertValue).length()), true, isIME);
5268 }
5269 
SetDefaultColor(RefPtr<SpanNode> & spanNode)5270 void RichEditorPattern::SetDefaultColor(RefPtr<SpanNode>& spanNode)
5271 {
5272     auto host = GetHost();
5273     CHECK_NULL_VOID(host);
5274     auto pipeline = host->GetContext();
5275     CHECK_NULL_VOID(pipeline);
5276     auto richEditorTheme = pipeline->GetTheme<RichEditorTheme>();
5277     CHECK_NULL_VOID(richEditorTheme);
5278     Color textColor = richEditorTheme->GetTextStyle().GetTextColor();
5279     spanNode->UpdateTextColor(textColor);
5280     spanNode->AddPropertyInfo(PropertyInfo::FONTCOLOR);
5281     spanNode->UpdateTextDecorationColor(textColor);
5282     spanNode->AddPropertyInfo(PropertyInfo::NONE);
5283 }
5284 
BeforeIMEInsertValue(const std::string & insertValue)5285 bool RichEditorPattern::BeforeIMEInsertValue(const std::string& insertValue)
5286 {
5287     auto eventHub = GetEventHub<RichEditorEventHub>();
5288     CHECK_NULL_RETURN(eventHub, true);
5289     RichEditorInsertValue insertValueInfo;
5290     insertValueInfo.SetInsertOffset(caretPosition_);
5291     if (!previewTextRecord_.newPreviewContent.empty()) {
5292         insertValueInfo.SetPreviewText(previewTextRecord_.newPreviewContent);
5293     } else {
5294         insertValueInfo.SetInsertValue(insertValue);
5295     }
5296     return eventHub->FireAboutToIMEInput(insertValueInfo);
5297 }
5298 
AfterInsertValue(const RefPtr<SpanNode> & spanNode,int32_t insertValueLength,bool isCreate,bool isIME)5299 void RichEditorPattern::AfterInsertValue(
5300     const RefPtr<SpanNode>& spanNode, int32_t insertValueLength, bool isCreate, bool isIME)
5301 {
5302     isTextChange_ = true;
5303     moveDirection_ = MoveDirection::FORWARD;
5304     moveLength_ += insertValueLength;
5305     UpdateSpanPosition();
5306     if (isIME || aiWriteAdapter_->GetAIWrite()) {
5307         AfterIMEInsertValue(spanNode, insertValueLength, isCreate);
5308         return;
5309     }
5310     MoveCaretAfterTextChange();
5311 }
5312 
AfterIMEInsertValue(const RefPtr<SpanNode> & spanNode,int32_t insertValueLength,bool isCreate)5313 bool RichEditorPattern::AfterIMEInsertValue(const RefPtr<SpanNode>& spanNode, int32_t insertValueLength, bool isCreate)
5314 {
5315     ACE_SCOPED_TRACE("RichEditorAfterIMEInsertValue");
5316     auto host = GetHost();
5317     CHECK_NULL_RETURN(host, false);
5318     auto eventHub = GetEventHub<RichEditorEventHub>();
5319     CHECK_NULL_RETURN(eventHub, false);
5320 
5321     RichEditorAbstractSpanResult retInfo;
5322     retInfo.SetSpanIndex(host->GetChildIndex(spanNode));
5323     retInfo.SetEraseLength(insertValueLength);
5324     auto spanItem = spanNode->GetSpanItem();
5325     if (!previewTextRecord_.newPreviewContent.empty()) {
5326         retInfo.SetPreviewText(previewTextRecord_.newPreviewContent);
5327     } else {
5328         retInfo.SetValue(spanItem->content);
5329     }
5330     auto contentLength = static_cast<int32_t>(StringUtils::ToWstring(spanItem->content).length());
5331     retInfo.SetSpanRangeStart(spanItem->position - contentLength);
5332     retInfo.SetSpanRangeEnd(spanItem->position);
5333     retInfo.SetOffsetInSpan(caretPosition_ - retInfo.GetSpanRangeStart());
5334     retInfo.SetFontColor(spanNode->GetTextColorValue(Color::BLACK).ColorToString());
5335     retInfo.SetFontSize(spanNode->GetFontSizeValue(Dimension(16.0f, DimensionUnit::VP)).ConvertToVp());
5336     retInfo.SetFontStyle(spanNode->GetItalicFontStyleValue(OHOS::Ace::FontStyle::NORMAL));
5337     retInfo.SetFontWeight(static_cast<int32_t>(spanNode->GetFontWeightValue(FontWeight::NORMAL)));
5338     retInfo.SetTextStyle(GetTextStyleObject(spanNode));
5339     std::string fontFamilyValue;
5340     auto fontFamily = spanNode->GetFontFamilyValue({ "HarmonyOS Sans" });
5341     for (const auto& str : fontFamily) {
5342         fontFamilyValue += str;
5343     }
5344     retInfo.SetFontFamily(fontFamilyValue);
5345     retInfo.SetTextDecoration(spanNode->GetTextDecorationValue(TextDecoration::NONE));
5346     retInfo.SetTextDecorationStyle(spanNode->GetTextDecorationStyleValue(TextDecorationStyle::SOLID));
5347     retInfo.SetFontFeature(spanNode->GetFontFeatureValue(ParseFontFeatureSettings("\"pnum\" 1")));
5348     retInfo.SetColor(spanNode->GetTextDecorationColorValue(Color::BLACK).ColorToString());
5349     retInfo.SetTextStyle(GetTextStyleObject(spanNode));
5350     TextRange onDidIMEInputRange{ caretPosition_, caretPosition_ + insertValueLength };
5351     MoveCaretAfterTextChange();
5352     eventHub->FireOnIMEInputComplete(retInfo);
5353     eventHub->FireOnDidIMEInput(onDidIMEInputRange);
5354     return true;
5355 }
5356 
ResetFirstNodeStyle()5357 void RichEditorPattern::ResetFirstNodeStyle()
5358 {
5359     auto tmpHost = GetHost();
5360     CHECK_NULL_VOID(tmpHost);
5361     auto spans = tmpHost->GetChildren();
5362     if (!spans.empty()) {
5363         auto&& firstNode = DynamicCast<SpanNode>(*(spans.begin()));
5364         if (firstNode) {
5365             firstNode->ResetTextAlign();
5366             firstNode->ResetLeadingMargin();
5367             tmpHost->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
5368         }
5369     }
5370 }
5371 
DoDeleteActions(int32_t currentPosition,int32_t length,RichEditorDeleteValue & info)5372 bool RichEditorPattern::DoDeleteActions(int32_t currentPosition, int32_t length, RichEditorDeleteValue& info)
5373 {
5374     auto eventHub = GetEventHub<RichEditorEventHub>();
5375     CHECK_NULL_RETURN(eventHub, false);
5376     auto allowDelete = eventHub->FireAboutToDelete(info);
5377     info.ResetRichEditorDeleteSpans();
5378     CalcDeleteValueObj(currentPosition, length, info);
5379     bool doDelete = allowDelete || IsPreviewTextInputting();
5380     if (doDelete) {
5381         CloseSelectOverlay();
5382         ResetSelection();
5383         DeleteByDeleteValueInfo(info);
5384         if (!caretVisible_) {
5385             StartTwinkling();
5386         }
5387         eventHub->FireOnDeleteComplete();
5388     }
5389 #if !defined(PREVIEW) && defined(OHOS_PLATFORM)
5390     UiSessionManager::GetInstance().ReportComponentChangeEvent("event", "RichEditor.OnDeleteComplete");
5391 #endif
5392     return doDelete;
5393 }
5394 
IsEmojiOnCaretPosition(int32_t & emojiLength,bool isBackward,int32_t length)5395 std::pair<bool, bool> RichEditorPattern::IsEmojiOnCaretPosition(int32_t& emojiLength, bool isBackward, int32_t length)
5396 {
5397     bool isEmojiOnCaretBackward = false;
5398     bool isEmojiOnCaretForward = false;
5399     std::stringstream ss;
5400     for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
5401         ss << (*iter)->content;
5402     }
5403 
5404     auto content = ss.str();
5405     std::u16string u16 = StringUtils::Str8ToStr16(content);
5406     auto caretPos = std::clamp(caretPosition_, 0, static_cast<int32_t>(u16.length()));
5407     emojiLength = TextEmojiProcessor::Delete(caretPos, length, content, isBackward);
5408     if (emojiLength > 0) {
5409         if (isBackward) {
5410             isEmojiOnCaretBackward = true;
5411         } else {
5412             isEmojiOnCaretForward = true;
5413         }
5414     }
5415     return std::make_pair(isEmojiOnCaretBackward, isEmojiOnCaretForward);
5416 }
5417 
HandleOnDelete(bool backward)5418 void RichEditorPattern::HandleOnDelete(bool backward)
5419 {
5420     if (backward) {
5421 #if defined(PREVIEW)
5422         DeleteForward(1);
5423 #else
5424         DeleteBackward(1);
5425 #endif
5426     } else {
5427 #if defined(PREVIEW)
5428         DeleteBackward(1);
5429 #else
5430         DeleteForward(1);
5431 #endif
5432     }
5433 }
5434 
CalculateDeleteLength(int32_t length,bool isBackward)5435 int32_t RichEditorPattern::CalculateDeleteLength(int32_t length, bool isBackward)
5436 {
5437     // handle selector
5438     if (!textSelector_.SelectNothing()) {
5439         caretPosition_ = isBackward ? textSelector_.GetTextEnd() : textSelector_.GetTextStart();
5440         return textSelector_.GetTextEnd() - textSelector_.GetTextStart();
5441     }
5442 
5443     // handle symbol, assume caret is not within symbol
5444     auto iter = std::find_if(spans_.begin(), spans_.end(), [index = caretPosition_, isBackward]
5445     (const RefPtr<SpanItem>& spanItem) {
5446         return isBackward
5447         ? (spanItem->rangeStart < index && index <= spanItem->position)
5448         : (spanItem->rangeStart <= index && index < spanItem->position);
5449     });
5450     CHECK_NULL_RETURN(iter == spans_.end() || !(*iter) || (*iter)->unicode == 0, SYMBOL_SPAN_LENGTH);
5451 
5452     // handle emoji
5453     int32_t emojiLength = 0;
5454     auto [isEmojiOnCaretBackward, isEmojiOnCaretForward] = IsEmojiOnCaretPosition(emojiLength, isBackward, length);
5455     if ((isBackward && isEmojiOnCaretBackward) || (!isBackward && isEmojiOnCaretForward)) {
5456         return emojiLength;
5457     }
5458 
5459     return length;
5460 }
5461 
DeleteBackward(int32_t oriLength)5462 void RichEditorPattern::DeleteBackward(int32_t oriLength)
5463 {
5464     int32_t length = isAPI14Plus ? std::clamp(oriLength, 0, caretPosition_) : oriLength;
5465     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "oriLength=%{public}d, length=%{public}d, isDragging=%{public}d",
5466         oriLength, length, IsDragging());
5467     CHECK_NULL_VOID(!IsDragging());
5468     if (isSpanStringMode_) {
5469         DeleteBackwardInStyledString(length);
5470         return;
5471     }
5472     if (IsPreviewTextInputting()) {
5473         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "do not handle DeleteBackward on previewTextInputting");
5474         return;
5475     }
5476     OperationRecord record;
5477     record.beforeCaretPosition = caretPosition_;
5478     RichEditorChangeValue changeValue;
5479     CHECK_NULL_VOID(BeforeChangeText(changeValue, record, RecordType::DEL_BACKWARD, length));
5480     std::wstring deleteText = DeleteBackwardOperation(length);
5481     if (deleteText.length() != 0) {
5482         ClearRedoOperationRecords();
5483         record.deleteText = StringUtils::ToString(deleteText);
5484         record.afterCaretPosition = caretPosition_;
5485         AddOperationRecord(record);
5486         AfterContentChange(changeValue);
5487     }
5488 }
5489 
DeleteBackwardOperation(int32_t length)5490 std::wstring RichEditorPattern::DeleteBackwardOperation(int32_t length)
5491 {
5492     length = CalculateDeleteLength(length, true);
5493     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "delete length=%{public}d", length);
5494     std::wstringstream wss;
5495     for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
5496         wss << StringUtils::ToWstring((*iter)->content);
5497     }
5498     auto textContent = wss.str();
5499     if (static_cast<int32_t>(textContent.length()) != GetTextContentLength()) {
5500         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "textContent length mismatch, %{public}d vs. %{public}d",
5501             static_cast<int32_t>(textContent.length()), GetTextContentLength());
5502     }
5503     auto start = std::clamp(caretPosition_ - length, 0, static_cast<int32_t>(textContent.length()));
5504     std::wstring deleteText =
5505         textContent.substr(static_cast<uint32_t>(start), static_cast<uint32_t>(caretPosition_ - start));
5506     RichEditorDeleteValue info;
5507     info.SetRichEditorDeleteDirection(RichEditorDeleteDirection::BACKWARD);
5508     if (caretPosition_ == 0) {
5509         info.SetLength(0);
5510         ResetFirstNodeStyle();
5511         DoDeleteActions(0, 0, info);
5512         return deleteText;
5513     }
5514     info.SetOffset(caretPosition_ - length);
5515     info.SetLength(length);
5516     int32_t currentPosition = std::clamp((caretPosition_ - length), 0, static_cast<int32_t>(GetTextContentLength()));
5517     if (!spans_.empty()) {
5518         CalcDeleteValueObj(currentPosition, length, info);
5519         bool doDelete = DoDeleteActions(currentPosition, length, info);
5520         if (!doDelete) {
5521             return L"";
5522         }
5523     }
5524     auto host = GetHost();
5525     if (host && host->GetChildren().empty()) {
5526         textForDisplay_.clear();
5527     }
5528     RequestKeyboardToEdit();
5529     return deleteText;
5530 }
5531 
DeleteForward(int32_t oriLength)5532 void RichEditorPattern::DeleteForward(int32_t oriLength)
5533 {
5534     int32_t length = isAPI14Plus ? std::clamp(oriLength, 0, GetTextContentLength() - caretPosition_) : oriLength;
5535     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "oriLength=%{public}d, length=%{public}d, isDragging=%{public}d",
5536         oriLength, length, IsDragging());
5537     CHECK_NULL_VOID(!IsDragging());
5538     if (isSpanStringMode_) {
5539         DeleteForwardInStyledString(length);
5540         return;
5541     }
5542     if (IsPreviewTextInputting()) {
5543         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "do not handle DeleteForward in previewTextInputting");
5544         return;
5545     }
5546     OperationRecord record;
5547     record.beforeCaretPosition = caretPosition_;
5548     RichEditorChangeValue changeValue;
5549     CHECK_NULL_VOID(BeforeChangeText(changeValue, record, RecordType::DEL_FORWARD, length));
5550     std::wstring deleteText = DeleteForwardOperation(length);
5551     if (deleteText.length() != 0) {
5552         ClearRedoOperationRecords();
5553         record.deleteText = StringUtils::ToString(deleteText);
5554         record.afterCaretPosition = caretPosition_;
5555         AddOperationRecord(record);
5556         AfterContentChange(changeValue);
5557     }
5558 }
5559 
DeleteForwardOperation(int32_t length)5560 std::wstring RichEditorPattern::DeleteForwardOperation(int32_t length)
5561 {
5562     length = CalculateDeleteLength(length, false);
5563     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "delete length=%{public}d", length);
5564     std::wstringstream wss;
5565     for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
5566         wss << StringUtils::ToWstring((*iter)->content);
5567     }
5568     auto textContent = wss.str();
5569     if (static_cast<int32_t>(textContent.length()) != GetTextContentLength()) {
5570         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "textContent length mismatch, %{public}d vs. %{public}d",
5571             static_cast<int32_t>(textContent.length()), GetTextContentLength());
5572     }
5573     auto end = std::clamp(caretPosition_ + length, 0, static_cast<int32_t>(textContent.length()));
5574     std::wstring deleteText = textContent.substr(
5575         static_cast<uint32_t>(std::clamp(caretPosition_, 0, static_cast<int32_t>(textContent.length()))),
5576         static_cast<uint32_t>(end - caretPosition_));
5577     if (caretPosition_ == GetTextContentLength()) {
5578         return deleteText;
5579     }
5580     RichEditorDeleteValue info;
5581     info.SetOffset(caretPosition_);
5582     info.SetRichEditorDeleteDirection(RichEditorDeleteDirection::FORWARD);
5583     info.SetLength(length);
5584     int32_t currentPosition = caretPosition_;
5585     if (!spans_.empty()) {
5586         CalcDeleteValueObj(currentPosition, length, info);
5587         bool doDelete = DoDeleteActions(currentPosition, length, info);
5588         if (!doDelete) {
5589             return L"";
5590         }
5591     }
5592     return deleteText;
5593 }
5594 
OnBackPressed()5595 bool RichEditorPattern::OnBackPressed()
5596 {
5597     auto tmpHost = GetHost();
5598     CHECK_NULL_RETURN(tmpHost, false);
5599     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "RichEditor %{public}d receives back press event", tmpHost->GetId());
5600     if (SelectOverlayIsOn()) {
5601         CloseSelectOverlay();
5602         textSelector_.Update(textSelector_.destinationOffset);
5603         StartTwinkling();
5604         return true;
5605     }
5606 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
5607     if (!imeShown_ && !isCustomKeyboardAttached_) {
5608 #else
5609     if (!isCustomKeyboardAttached_) {
5610 #endif
5611         return false;
5612     }
5613     tmpHost->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
5614     CloseKeyboard(false);
5615     FocusHub::LostFocusToViewRoot();
5616 #if defined(ANDROID_PLATFORM)
5617     return false;
5618 #else
5619     return true;
5620 #endif
5621 }
5622 
5623 void RichEditorPattern::SetInputMethodStatus(bool keyboardShown)
5624 {
5625 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
5626     imeShown_ = keyboardShown;
5627 #endif
5628 }
5629 
5630 bool RichEditorPattern::BeforeStatusCursorMove(bool isLeft)
5631 {
5632     CHECK_NULL_RETURN(textSelector_.IsValid(), true);
5633     CHECK_NULL_RETURN(!selectOverlay_->IsSingleHandleShow(), true);
5634     SetCaretPosition(isLeft ? textSelector_.GetTextStart() : textSelector_.GetTextEnd());
5635     MoveCaretToContentRect();
5636     StartTwinkling();
5637     CloseSelectOverlay();
5638     ResetSelection();
5639     return false;
5640 }
5641 
5642 bool RichEditorPattern::CursorMoveLeft()
5643 {
5644     CHECK_NULL_RETURN(BeforeStatusCursorMove(true), false);
5645     CloseSelectOverlay();
5646     ResetSelection();
5647     int32_t emojiLength = 0;
5648     int32_t caretPosition = caretPosition_;
5649     constexpr int32_t DELETE_COUNT = 1;
5650     auto [isEmojiOnCaretBackward, isEmojiOnCaretForward] = IsEmojiOnCaretPosition(emojiLength, true, DELETE_COUNT);
5651     if (isEmojiOnCaretBackward) {
5652         caretPosition = std::clamp((caretPosition_ - emojiLength), 0, static_cast<int32_t>(GetTextContentLength()));
5653     } else {
5654         caretPosition = std::clamp((caretPosition_ - 1), 0, static_cast<int32_t>(GetTextContentLength()));
5655     }
5656     AdjustSelectorForSymbol(caretPosition, HandleType::SECOND, SelectorAdjustPolicy::EXCLUDE);
5657     if (caretPosition_ == caretPosition) {
5658         return false;
5659     }
5660     SetCaretPosition(caretPosition);
5661     MoveCaretToContentRect();
5662     StartTwinkling();
5663     auto host = GetHost();
5664     CHECK_NULL_RETURN(host, false);
5665     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
5666     return true;
5667 }
5668 
5669 bool RichEditorPattern::CursorMoveRight()
5670 {
5671     CHECK_NULL_RETURN(BeforeStatusCursorMove(false), false);
5672     CloseSelectOverlay();
5673     ResetSelection();
5674     int32_t emojiLength = 0;
5675     int32_t caretPosition = caretPosition_;
5676     constexpr int32_t DELETE_COUNT = 1;
5677     auto [isEmojiOnCaretBackward, isEmojiOnCaretForward] = IsEmojiOnCaretPosition(emojiLength, false, DELETE_COUNT);
5678     if (isEmojiOnCaretForward) {
5679         caretPosition = std::clamp((caretPosition_ + emojiLength), 0, static_cast<int32_t>(GetTextContentLength()));
5680     } else {
5681         caretPosition = std::clamp((caretPosition_ + 1), 0, static_cast<int32_t>(GetTextContentLength()));
5682     }
5683     AdjustSelectorForSymbol(caretPosition, HandleType::SECOND, SelectorAdjustPolicy::INCLUDE);
5684     if (caretPosition_ == caretPosition) {
5685         return false;
5686     }
5687     SetCaretPosition(caretPosition);
5688     MoveCaretToContentRect();
5689     StartTwinkling();
5690     auto host = GetHost();
5691     CHECK_NULL_RETURN(host, false);
5692     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
5693     return true;
5694 }
5695 
5696 bool RichEditorPattern::CursorMoveUp()
5697 {
5698     CHECK_NULL_RETURN(!SelectOverlayIsOn(), false);
5699     ResetSelection();
5700     float caretHeight = 0.0f;
5701     float leadingMarginOffset = 0.0f;
5702     CaretOffsetInfo caretInfo;
5703     if (static_cast<int32_t>(GetTextContentLength()) > 1) {
5704         caretInfo = GetCaretOffsetInfoByPosition();
5705         int32_t caretPosition = CalcMoveUpPos(leadingMarginOffset);
5706         CHECK_NULL_RETURN(overlayMod_, false);
5707         auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
5708         auto currentCaretOffsetOverlay = overlayMod->GetCaretOffset();
5709         auto caretOffsetWidth = overlayMod->GetCaretWidth();
5710         auto rectLineInfo = CalcLineInfoByPosition();
5711         caretPosition = std::clamp(caretPosition, 0, static_cast<int32_t>(GetTextContentLength()));
5712         if (caretPosition_ == caretPosition) {
5713             caretPosition = 0;
5714         }
5715         // at line middle or line end
5716         bool cursorNotAtLineStart =
5717             NearEqual(currentCaretOffsetOverlay.GetX(), caretInfo.caretOffsetUp.GetX(), caretOffsetWidth);
5718         bool isEnter = NearZero(currentCaretOffsetOverlay.GetX() - richTextRect_.GetX(), rectLineInfo.GetX());
5719         SetCaretPosition(caretPosition);
5720         MoveCaretToContentRect();
5721         if (cursorNotAtLineStart && !isEnter) {
5722             OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight, false, false);
5723             SetLastClickOffset(caretOffset);
5724         }
5725     }
5726     StartTwinkling();
5727     auto host = GetHost();
5728     CHECK_NULL_RETURN(host, false);
5729     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
5730     return true;
5731 }
5732 
5733 bool RichEditorPattern::CursorMoveDown()
5734 {
5735     CHECK_NULL_RETURN(!SelectOverlayIsOn(), false);
5736     ResetSelection();
5737     if (static_cast<int32_t>(GetTextContentLength()) > 1) {
5738         float caretHeight = 0.0f;
5739         float leadingMarginOffset = 0.0f;
5740         float caretHeightEnd = 0.0f;
5741         CaretOffsetInfo caretInfo;
5742         int32_t caretPositionEnd;
5743         caretInfo = GetCaretOffsetInfoByPosition();
5744         caretPositionEnd = CalcMoveDownPos(leadingMarginOffset);
5745         CHECK_NULL_RETURN(overlayMod_, false);
5746         auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
5747         auto caretOffsetOverlay = overlayMod->GetCaretOffset();
5748         auto caretOffsetWidth = overlayMod->GetCaretWidth();
5749         bool cursorNotAtLineStart =
5750             NearEqual(caretOffsetOverlay.GetX(), caretInfo.caretOffsetUp.GetX(), caretOffsetWidth);
5751         bool isEnter = NearZero(caretInfo.caretOffsetUp.GetX() - richTextRect_.GetX(), leadingMarginOffset);
5752         caretPositionEnd = std::clamp(caretPositionEnd, 0, static_cast<int32_t>(GetTextContentLength()));
5753         auto currentLineInfo = CalcLineInfoByPosition();
5754         if (caretPositionEnd <= caretPosition_) {
5755             OffsetF caretOffsetEnd = CalcCursorOffsetByPosition(GetTextContentLength(), caretHeightEnd);
5756             if (NearEqual(caretOffsetEnd.GetY() - GetTextRect().GetY(), currentLineInfo.GetY(), 0.5f)) {
5757                 caretPositionEnd = GetTextContentLength();
5758             } else {
5759                 caretPositionEnd += 1;
5760             }
5761         }
5762         SetCaretPosition(caretPositionEnd);
5763         if (cursorNotAtLineStart && caretPosition_ != 0 && !isEnter) {
5764             OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight, false, false);
5765             SetLastClickOffset(caretOffset);
5766         }
5767         MoveCaretToContentRect();
5768     }
5769     StartTwinkling();
5770     auto host = GetHost();
5771     CHECK_NULL_RETURN(host, false);
5772     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
5773     return true;
5774 }
5775 
5776 bool RichEditorPattern::CursorMoveLeftWord()
5777 {
5778     CloseSelectOverlay();
5779     ResetSelection();
5780     int32_t newPos = 0;
5781     int32_t index = GetCaretPosition();
5782     auto aiContentStart = std::clamp(index - RICH_DEFAULT_AI_WORD, 0, GetTextContentLength());
5783     AIDeleteComb(aiContentStart, index, newPos, true);
5784     if (newPos == caretPosition_) {
5785         return false;
5786     }
5787     SetCaretPosition(newPos);
5788     MoveCaretToContentRect();
5789     StartTwinkling();
5790     auto host = GetHost();
5791     CHECK_NULL_RETURN(host, false);
5792     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
5793     return true;
5794 }
5795 
5796 bool RichEditorPattern::CursorMoveRightWord()
5797 {
5798     CloseSelectOverlay();
5799     ResetSelection();
5800     int32_t newPos = 0;
5801     int32_t index = GetCaretPosition();
5802     auto aiContentEnd = std::clamp(index + RICH_DEFAULT_AI_WORD, 0, GetTextContentLength());
5803     AIDeleteComb(index, aiContentEnd, newPos, false);
5804     if (newPos == caretPosition_) {
5805         return false;
5806     }
5807     SetCaretPosition(newPos);
5808     MoveCaretToContentRect();
5809     StartTwinkling();
5810     auto host = GetHost();
5811     CHECK_NULL_RETURN(host, false);
5812     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
5813     return true;
5814 }
5815 
5816 bool RichEditorPattern::CursorMoveToParagraphBegin()
5817 {
5818     CloseSelectOverlay();
5819     ResetSelection();
5820     auto newPos = GetParagraphBeginPosition(caretPosition_);
5821     if (newPos == caretPosition_ && caretPosition_ > 0) {
5822         newPos = GetParagraphBeginPosition(caretPosition_ - 1);
5823     }
5824     if (newPos == caretPosition_) {
5825         return false;
5826     }
5827     SetCaretPosition(newPos);
5828     MoveCaretToContentRect();
5829     StartTwinkling();
5830     auto host = GetHost();
5831     CHECK_NULL_RETURN(host, false);
5832     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
5833     return true;
5834 }
5835 
5836 bool RichEditorPattern::CursorMoveToParagraphEnd()
5837 {
5838     CloseSelectOverlay();
5839     ResetSelection();
5840     auto newPos = GetParagraphEndPosition(caretPosition_);
5841     if (newPos == caretPosition_ && caretPosition_ < static_cast<int32_t>(GetTextContentLength())) {
5842         newPos = GetParagraphEndPosition(caretPosition_ + 1);
5843     }
5844     if (newPos == caretPosition_) {
5845         return false;
5846     }
5847     SetCaretPosition(newPos);
5848     MoveCaretToContentRect();
5849     StartTwinkling();
5850     auto host = GetHost();
5851     CHECK_NULL_RETURN(host, false);
5852     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
5853     return true;
5854 }
5855 
5856 bool RichEditorPattern::CursorMoveHome()
5857 {
5858     CloseSelectOverlay();
5859     ResetSelection();
5860     if (0 == caretPosition_) {
5861         return false;
5862     }
5863     SetCaretPosition(0);
5864     MoveCaretToContentRect();
5865     StartTwinkling();
5866     auto host = GetHost();
5867     CHECK_NULL_RETURN(host, false);
5868     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
5869     return true;
5870 }
5871 
5872 bool RichEditorPattern::CursorMoveEnd()
5873 {
5874     int32_t currentPositionIndex = 0;
5875     if (textSelector_.SelectNothing()) {
5876         currentPositionIndex = caretPosition_;
5877     } else {
5878         currentPositionIndex = textSelector_.GetTextEnd();
5879     }
5880     CloseSelectOverlay();
5881     ResetSelection();
5882     auto newPos = GetTextContentLength();
5883     if (newPos == currentPositionIndex) {
5884         StartTwinkling();
5885         return false;
5886     }
5887     SetCaretPosition(newPos);
5888     MoveCaretToContentRect();
5889     StartTwinkling();
5890     auto host = GetHost();
5891     CHECK_NULL_RETURN(host, false);
5892     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
5893     return true;
5894 }
5895 
5896 int32_t RichEditorPattern::GetLeftWordPosition(int32_t caretPosition)
5897 {
5898     int32_t offset = 0;
5899     bool jumpSpace = true;
5900     for (auto iter = spans_.rbegin(); iter != spans_.rend(); iter++) {
5901         auto span = *iter;
5902         auto content = StringUtils::ToWstring(span->content);
5903         if (caretPosition <= span->position - static_cast<int32_t>(content.length())) {
5904             continue;
5905         }
5906         int32_t position = span->position;
5907         for (auto iterContent = content.rbegin(); iterContent != content.rend(); iterContent++) {
5908             if (position-- > caretPosition) {
5909                 continue;
5910             }
5911             if (*iterContent != L' ' || span->placeholderIndex >= 0) {
5912                 jumpSpace = false;
5913             }
5914             if (position + 1 == caretPosition) {
5915                 if (!(StringUtils::IsLetterOrNumberForWchar(*iterContent) ||
5916                         (*iterContent == L' ' && span->placeholderIndex < 0))) {
5917                     return std::clamp(caretPosition - 1, 0, static_cast<int32_t>(GetTextContentLength()));
5918                 }
5919             }
5920             if (!jumpSpace) {
5921                 if (!StringUtils::IsLetterOrNumberForWchar(*iterContent)) {
5922                     return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
5923                 }
5924             } else {
5925                 if (*iterContent == L' ' && span->placeholderIndex >= 0) {
5926                     return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
5927                 }
5928             }
5929             offset++;
5930         }
5931     }
5932     return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
5933 }
5934 
5935 int32_t RichEditorPattern::GetRightWordPosition(int32_t caretPosition)
5936 {
5937     int32_t offset = 0;
5938     bool jumpSpace = false;
5939     for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
5940         auto span = *iter;
5941         auto content = StringUtils::ToWstring(span->content);
5942         if (caretPosition > span->position) {
5943             continue;
5944         }
5945         int32_t position = span->position - static_cast<int32_t>(content.length());
5946         for (auto iterContent = content.cbegin(); iterContent != content.cend(); iterContent++) {
5947             if (position++ < caretPosition) {
5948                 continue;
5949             }
5950             if (*iterContent == L' ' && span->placeholderIndex < 0) {
5951                 jumpSpace = true;
5952                 offset++;
5953                 continue;
5954             }
5955             if (position - 1 == caretPosition) {
5956                 if (!StringUtils::IsLetterOrNumberForWchar(*iterContent)) {
5957                     return std::clamp(caretPosition + 1, 0, static_cast<int32_t>(GetTextContentLength()));
5958                 }
5959             }
5960             if (jumpSpace) {
5961                 if (*iterContent != L' ' || span->placeholderIndex >= 0) {
5962                     return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
5963                 }
5964             } else {
5965                 if (!(StringUtils::IsLetterOrNumberForWchar(*iterContent) ||
5966                         (*iterContent == L' ' && span->placeholderIndex < 0))) {
5967                     return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
5968                 }
5969             }
5970             offset++;
5971         }
5972     }
5973     return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
5974 }
5975 
5976 int32_t RichEditorPattern::GetParagraphBeginPosition(int32_t caretPosition)
5977 {
5978     int32_t offset = 0;
5979     for (auto iter = spans_.rbegin(); iter != spans_.rend(); iter++) {
5980         auto span = *iter;
5981         auto content = StringUtils::ToWstring(span->content);
5982         if (caretPosition <= span->position - static_cast<int32_t>(content.length())) {
5983             continue;
5984         }
5985         int32_t position = span->position;
5986         for (auto iterContent = content.rbegin(); iterContent != content.rend(); iterContent++) {
5987             if (position-- > caretPosition) {
5988                 continue;
5989             }
5990             if (*iterContent == L'\n') {
5991                 return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
5992             }
5993             offset++;
5994         }
5995     }
5996     return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
5997 }
5998 
5999 int32_t RichEditorPattern::GetParagraphEndPosition(int32_t caretPosition)
6000 {
6001     int32_t offset = 0;
6002     for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
6003         auto span = *iter;
6004         auto content = StringUtils::ToWstring(span->content);
6005         if (caretPosition > span->position) {
6006             continue;
6007         }
6008         int32_t position = span->position - static_cast<int32_t>(content.length());
6009         for (auto iterContent = content.cbegin(); iterContent != content.cend(); iterContent++) {
6010             if (position++ < caretPosition) {
6011                 continue;
6012             }
6013             if (*iterContent == L'\n') {
6014                 return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
6015             }
6016             offset++;
6017         }
6018     }
6019     return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
6020 }
6021 
6022 void RichEditorPattern::HandleOnSelectAll()
6023 {
6024     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnSelectAll");
6025     CloseSelectOverlay();
6026     auto host = GetHost();
6027     CHECK_NULL_VOID(host);
6028     int32_t newPos = static_cast<int32_t>(GetTextContentLength());
6029     textSelector_.Update(0, newPos);
6030     FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
6031     SetCaretPosition(newPos);
6032     MoveCaretToContentRect();
6033     StopTwinkling();
6034     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6035 }
6036 
6037 int32_t RichEditorPattern::CaretPositionSelectEmoji(CaretMoveIntent direction)
6038 {
6039     int32_t newPos = caretPosition_;
6040     int32_t emojiLength = 0;
6041     constexpr int32_t DELETE_COUNT = 1;
6042     if (direction == CaretMoveIntent::Left) {
6043         auto [isEmojiOnCaretBackward, isEmojiOnCaretForward] = IsEmojiOnCaretPosition(emojiLength, true, DELETE_COUNT);
6044         if (isEmojiOnCaretBackward) {
6045             newPos = caretPosition_ - emojiLength;
6046         } else {
6047             newPos = caretPosition_ - 1;
6048         }
6049         return newPos;
6050     }
6051     auto [isEmojiOnCaretBackward, isEmojiOnCaretForward] = IsEmojiOnCaretPosition(emojiLength, false, DELETE_COUNT);
6052     if (direction == CaretMoveIntent::Right) {
6053         if (isEmojiOnCaretForward) {
6054             newPos = caretPosition_ + emojiLength;
6055         } else {
6056             newPos = caretPosition_ + 1;
6057         }
6058     }
6059     return newPos;
6060 }
6061 
6062 void RichEditorPattern::HandleSelect(CaretMoveIntent direction)
6063 {
6064     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "direction=%{public}d", direction);
6065     CloseSelectOverlay();
6066     auto host = GetHost();
6067     CHECK_NULL_VOID(host);
6068     int32_t newPos, fixedPos = caretPosition_;
6069     if (IsSelected()) {
6070         fixedPos = (caretPosition_ == textSelector_.GetTextStart() ? textSelector_.GetTextEnd()
6071                                                                    : textSelector_.GetTextStart());
6072     }
6073     newPos = HandleSelectWrapper(direction, fixedPos);
6074     if (newPos == -1) {
6075         return;
6076     }
6077     newPos = std::clamp(newPos, 0, static_cast<int32_t>(GetTextContentLength()));
6078     if (newPos == caretPosition_) {
6079         return;
6080     }
6081     UpdateSelector(newPos, fixedPos);
6082     FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
6083     SetCaretPosition(newPos);
6084     MoveCaretToContentRect();
6085     if (textSelector_.SelectNothing()) {
6086         StartTwinkling();
6087     } else {
6088         StopTwinkling();
6089     }
6090     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6091 }
6092 
6093 void RichEditorPattern::ClearOperationRecords()
6094 {
6095     ClearRedoOperationRecords();
6096     if (operationRecords_.empty()) {
6097         return;
6098     }
6099     operationRecords_.clear();
6100 }
6101 
6102 void RichEditorPattern::ClearRedoOperationRecords()
6103 {
6104     if (redoOperationRecords_.empty()) {
6105         return;
6106     }
6107     redoOperationRecords_.clear();
6108 }
6109 
6110 void RichEditorPattern::AddOperationRecord(const OperationRecord& record)
6111 {
6112     if (operationRecords_.size() >= RECORD_MAX_LENGTH) {
6113         // case of max length is 0
6114         if (operationRecords_.empty()) {
6115             return;
6116         }
6117         operationRecords_.erase(operationRecords_.begin());
6118     }
6119     operationRecords_.emplace_back(record);
6120 }
6121 
6122 bool RichEditorPattern::HandleOnEscape()
6123 {
6124     CloseSelectOverlay();
6125     return false;
6126 }
6127 
6128 void RichEditorPattern::HandleOnUndoAction()
6129 {
6130     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnUndoAction");
6131     if (operationRecords_.empty()) {
6132         return;
6133     }
6134     auto value = operationRecords_.back();
6135     RichEditorChangeValue changeValue;
6136     CHECK_NULL_VOID(BeforeChangeText(changeValue, value, RecordType::UNDO));
6137     operationRecords_.pop_back();
6138     if (redoOperationRecords_.size() >= RECORD_MAX_LENGTH && !(redoOperationRecords_.empty())) {
6139         redoOperationRecords_.erase(redoOperationRecords_.begin());
6140     }
6141     redoOperationRecords_.push_back(value);
6142     CloseSelectOverlay();
6143     ResetSelection();
6144     if (value.addText.has_value() && value.deleteCaretPostion != -1) {
6145         UndoDrag(value);
6146         AfterContentChange(changeValue);
6147         return;
6148     }
6149     if (value.addText.has_value() && value.deleteText.has_value()) {
6150         SetCaretPosition(value.afterCaretPosition);
6151         DeleteBackwardOperation(TextEmojiProcessor::GetCharacterNum(value.addText.value_or("")));
6152         InsertValueOperation(value.deleteText.value_or(""));
6153         AfterContentChange(changeValue);
6154         return;
6155     }
6156     if (value.addText.has_value()) {
6157         SetCaretPosition(value.afterCaretPosition);
6158         DeleteBackwardOperation(TextEmojiProcessor::GetCharacterNum(value.addText.value_or("")));
6159     }
6160     if (value.deleteText.has_value()) {
6161         SetCaretPosition(value.afterCaretPosition);
6162         InsertValueOperation(value.deleteText.value_or(""));
6163     }
6164     AfterContentChange(changeValue);
6165 }
6166 
6167 void RichEditorPattern::HandleOnRedoAction()
6168 {
6169     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnRedoAction");
6170     if (redoOperationRecords_.empty()) {
6171         return;
6172     }
6173     auto value = redoOperationRecords_.back();
6174     RichEditorChangeValue changeValue;
6175     CHECK_NULL_VOID(BeforeChangeText(changeValue, value, RecordType::REDO));
6176     redoOperationRecords_.pop_back();
6177     if (value.addText.has_value() && value.deleteCaretPostion != -1) {
6178         RedoDrag(value);
6179         AfterContentChange(changeValue);
6180         return;
6181     }
6182     if (value.addText.has_value() && value.deleteText.has_value()) {
6183         SetCaretPosition(value.beforeCaretPosition);
6184         DeleteForwardOperation(StringUtils::ToWstring(value.deleteText.value_or("")).length());
6185         InsertValueOperation(value.addText.value_or(""));
6186         operationRecords_.push_back(value);
6187         AfterContentChange(changeValue);
6188         return;
6189     }
6190     if (value.deleteText.has_value()) {
6191         SetCaretPosition(value.beforeCaretPosition);
6192         if (value.beforeCaretPosition != value.afterCaretPosition) {
6193             DeleteBackwardOperation(StringUtils::ToWstring(value.deleteText.value_or("")).length());
6194         } else {
6195             DeleteForwardOperation(StringUtils::ToWstring(value.deleteText.value_or("")).length());
6196         }
6197     }
6198     if (value.addText.has_value()) {
6199         SetCaretPosition(value.beforeCaretPosition);
6200         InsertValueOperation(value.addText.value_or(""));
6201     }
6202     operationRecords_.push_back(value);
6203     AfterContentChange(changeValue);
6204 }
6205 
6206 void RichEditorPattern::CalcInsertValueObj(TextInsertValueInfo& info)
6207 {
6208     if (spans_.empty()) {
6209         info.SetSpanIndex(0);
6210         info.SetOffsetInSpan(0);
6211         return;
6212     }
6213     auto it = std::find_if(
6214         spans_.begin(), spans_.end(), [caretPosition = caretPosition_ + moveLength_](const RefPtr<SpanItem>& spanItem) {
6215             if (spanItem->content.empty()) {
6216                 return spanItem->position == caretPosition;
6217             }
6218             return spanItem->rangeStart <= caretPosition && caretPosition < spanItem->position;
6219         });
6220     if (it != spans_.end() && (*it)->unicode != 0 && (*it)->position - caretPosition_ + moveLength_ == 1) {
6221         it++;
6222         moveLength_++;
6223     }
6224     info.SetSpanIndex(std::distance(spans_.begin(), it));
6225     if (it == spans_.end()) {
6226         info.SetOffsetInSpan(0);
6227         return;
6228     }
6229     info.SetOffsetInSpan(
6230         caretPosition_ + moveLength_ - ((*it)->position - StringUtils::ToWstring((*it)->content).length()));
6231 }
6232 
6233 void RichEditorPattern::CalcDeleteValueObj(int32_t currentPosition, int32_t length, RichEditorDeleteValue& info)
6234 {
6235     auto it =
6236         std::find_if(spans_.begin(), spans_.end(), [caretPosition = currentPosition](const RefPtr<SpanItem>& spanItem) {
6237             return (spanItem->position - static_cast<int32_t>(StringUtils::ToWstring(spanItem->content).length()) <=
6238                        caretPosition) &&
6239                    (caretPosition < spanItem->position);
6240         });
6241     while (it != spans_.end() && length > 0) {
6242         if ((*it)->placeholderIndex >= 0 || (*it)->unicode != 0) {
6243             RichEditorAbstractSpanResult spanResult;
6244             spanResult.SetSpanIndex(std::distance(spans_.begin(), it));
6245             int32_t eraseLength = 0;
6246             if ((*it)->unicode != 0) {
6247                 eraseLength = DeleteValueSetSymbolSpan(*it, spanResult);
6248             } else if (AceType::InstanceOf<ImageSpanItem>(*it)) {
6249                 eraseLength = DeleteValueSetImageSpan(*it, spanResult);
6250             } else {
6251                 eraseLength = DeleteValueSetBuilderSpan(*it, spanResult);
6252             }
6253             currentPosition += eraseLength;
6254             length -= eraseLength;
6255             info.SetRichEditorDeleteSpans(spanResult);
6256         } else {
6257             RichEditorAbstractSpanResult spanResult;
6258             spanResult.SetSpanIndex(std::distance(spans_.begin(), it));
6259             auto eraseLength = DeleteValueSetTextSpan(*it, currentPosition, length, spanResult);
6260             length -= eraseLength;
6261             currentPosition += eraseLength;
6262             info.SetRichEditorDeleteSpans(spanResult);
6263         }
6264         std::advance(it, 1);
6265     }
6266 }
6267 
6268 RefPtr<SpanNode> RichEditorPattern::GetSpanNodeBySpanItem(const RefPtr<SpanItem> spanItem)
6269 {
6270     RefPtr<SpanNode> spanNode;
6271     auto iter = std::find(spans_.begin(), spans_.end(), spanItem);
6272     if (iter == spans_.end()) {
6273         return spanNode;
6274     }
6275     auto spanIndex = std::distance(spans_.begin(), iter);
6276     auto host = GetHost();
6277     CHECK_NULL_RETURN(host, spanNode);
6278     auto it = host->GetChildren().begin();
6279     std::advance(it, spanIndex);
6280     spanNode = AceType::DynamicCast<SpanNode>(*it);
6281     return spanNode;
6282 }
6283 
6284 int32_t RichEditorPattern::DeleteValueSetSymbolSpan(
6285     const RefPtr<SpanItem>& spanItem, RichEditorAbstractSpanResult& spanResult)
6286 {
6287     spanResult.SetSpanType(SpanResultType::SYMBOL);
6288     spanResult.SetSpanRangeEnd(spanItem->position);
6289     spanResult.SetSpanRangeStart(spanItem->position - SYMBOL_SPAN_LENGTH);
6290     spanResult.SetEraseLength(SYMBOL_SPAN_LENGTH);
6291     spanResult.SetValueString(std::to_string(spanItem->unicode));
6292     spanResult.SetValueResource(spanItem->GetResourceObject());
6293     auto spanNode = GetSpanNodeBySpanItem(spanItem);
6294     if (spanNode) {
6295         spanResult.SetSymbolSpanStyle(GetSymbolSpanStyleObject(spanNode));
6296     }
6297     return SYMBOL_SPAN_LENGTH;
6298 }
6299 
6300 int32_t RichEditorPattern::DeleteValueSetImageSpan(
6301     const RefPtr<SpanItem>& spanItem, RichEditorAbstractSpanResult& spanResult)
6302 {
6303     spanResult.SetSpanType(SpanResultType::IMAGE);
6304     spanResult.SetSpanRangeEnd(spanItem->position);
6305     spanResult.SetSpanRangeStart(spanItem->position - 1);
6306     spanResult.SetEraseLength(1);
6307     auto host = GetHost();
6308     CHECK_NULL_RETURN(host, IMAGE_SPAN_LENGTH);
6309     auto uiNode = host->GetChildAtIndex(spanResult.GetSpanIndex());
6310     CHECK_NULL_RETURN(uiNode, IMAGE_SPAN_LENGTH);
6311     auto imageNode = AceType::DynamicCast<FrameNode>(uiNode);
6312     CHECK_NULL_RETURN(imageNode, IMAGE_SPAN_LENGTH);
6313     auto imageRenderCtx = imageNode->GetRenderContext();
6314     if (imageRenderCtx->GetBorderRadius()) {
6315         BorderRadiusProperty brp;
6316         auto jsonObject = JsonUtil::Create(true);
6317         auto jsonBorder = JsonUtil::Create(true);
6318         InspectorFilter filter;
6319         imageRenderCtx->GetBorderRadiusValue(brp).ToJsonValue(jsonObject, jsonBorder, filter);
6320         spanResult.SetBorderRadius(jsonObject->GetValue("borderRadius")->IsObject()
6321                                        ? jsonObject->GetValue("borderRadius")->ToString()
6322                                        : jsonObject->GetString("borderRadius"));
6323     }
6324     auto geometryNode = imageNode->GetGeometryNode();
6325     CHECK_NULL_RETURN(geometryNode, IMAGE_SPAN_LENGTH);
6326     auto imageLayoutProperty = DynamicCast<ImageLayoutProperty>(imageNode->GetLayoutProperty());
6327     CHECK_NULL_RETURN(imageLayoutProperty, IMAGE_SPAN_LENGTH);
6328     spanResult.SetSizeWidth(geometryNode->GetMarginFrameSize().Width());
6329     spanResult.SetSizeHeight(geometryNode->GetMarginFrameSize().Height());
6330     if (imageLayoutProperty->GetMarginProperty()) {
6331         spanResult.SetMargin(imageLayoutProperty->GetMarginProperty()->ToString());
6332     }
6333     if (!imageLayoutProperty->GetImageSourceInfo()->GetPixmap()) {
6334         spanResult.SetValueResourceStr(imageLayoutProperty->GetImageSourceInfo()->GetSrc());
6335     } else {
6336         spanResult.SetValuePixelMap(imageLayoutProperty->GetImageSourceInfo()->GetPixmap());
6337     }
6338     if (imageLayoutProperty->HasImageFit()) {
6339         spanResult.SetImageFit(imageLayoutProperty->GetImageFitValue());
6340     }
6341     if (imageLayoutProperty->HasVerticalAlign()) {
6342         spanResult.SetVerticalAlign(imageLayoutProperty->GetVerticalAlignValue());
6343     }
6344     return IMAGE_SPAN_LENGTH;
6345 }
6346 
6347 int32_t RichEditorPattern::DeleteValueSetBuilderSpan(
6348     const RefPtr<SpanItem>& spanItem, RichEditorAbstractSpanResult& spanResult)
6349 {
6350     spanResult.SetSpanType(SpanResultType::IMAGE);
6351     spanResult.SetSpanRangeEnd(spanItem->position);
6352     spanResult.SetSpanRangeStart(spanItem->position - 1);
6353     spanResult.SetEraseLength(1);
6354     auto host = GetHost();
6355     CHECK_NULL_RETURN(host, 1);
6356     auto uiNode = host->GetChildAtIndex(spanResult.GetSpanIndex());
6357     CHECK_NULL_RETURN(uiNode, 1);
6358     auto builderNode = AceType::DynamicCast<FrameNode>(uiNode);
6359     CHECK_NULL_RETURN(builderNode, 1);
6360     auto geometryNode = builderNode->GetGeometryNode();
6361     CHECK_NULL_RETURN(geometryNode, 1);
6362     spanResult.SetSizeWidth(geometryNode->GetMarginFrameSize().Width());
6363     spanResult.SetSizeHeight(geometryNode->GetMarginFrameSize().Height());
6364     return 1;
6365 }
6366 
6367 int32_t RichEditorPattern::DeleteValueSetTextSpan(
6368     const RefPtr<SpanItem>& spanItem, int32_t currentPosition, int32_t length, RichEditorAbstractSpanResult& spanResult)
6369 {
6370     spanResult.SetSpanType(SpanResultType::TEXT);
6371     auto contentStartPosition
6372         = spanItem->position - static_cast<int32_t>(StringUtils::ToWstring(spanItem->content).length());
6373     spanResult.SetSpanRangeStart(contentStartPosition);
6374     int32_t eraseLength = 0;
6375     if (spanItem->position - currentPosition >= length) {
6376         eraseLength = length;
6377     } else {
6378         eraseLength = spanItem->position - currentPosition;
6379     }
6380     spanResult.SetSpanRangeEnd(spanItem->position);
6381     if (!previewTextRecord_.previewContent.empty()) {
6382         spanResult.SetPreviewText(previewTextRecord_.previewContent);
6383     } else {
6384         spanResult.SetValue(spanItem->content);
6385     }
6386     spanResult.SetOffsetInSpan(currentPosition - contentStartPosition);
6387     spanResult.SetEraseLength(eraseLength);
6388     if (!spanItem->GetTextStyle().has_value()) {
6389         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "SpanItem text style is empty.");
6390         return eraseLength;
6391     }
6392     spanResult.SetFontColor(spanItem->GetTextStyle()->GetTextColor().ColorToString());
6393     spanResult.SetFontSize(spanItem->GetTextStyle()->GetFontSize().Value());
6394     spanResult.SetFontStyle(spanItem->GetTextStyle()->GetFontStyle());
6395     spanResult.SetFontWeight((int32_t)(spanItem->GetTextStyle()->GetFontWeight()));
6396     if (!spanItem->GetTextStyle()->GetFontFamilies().empty()) {
6397         spanResult.SetFontFamily(spanItem->GetTextStyle()->GetFontFamilies().at(0));
6398     }
6399     spanResult.SetColor(spanItem->GetTextStyle()->GetTextDecorationColor().ColorToString());
6400     spanResult.SetTextDecoration(spanItem->GetTextStyle()->GetTextDecoration());
6401     spanResult.SetTextDecorationStyle(spanItem->GetTextStyle()->GetTextDecorationStyle());
6402     spanResult.SetFontFeature(spanItem->GetTextStyle()->GetFontFeatures());
6403     auto host = GetHost();
6404     CHECK_NULL_RETURN(host, eraseLength);
6405     auto uiNode = host->GetChildAtIndex(spanResult.GetSpanIndex());
6406     CHECK_NULL_RETURN(uiNode, eraseLength);
6407     auto spanNode = DynamicCast<SpanNode>(uiNode);
6408     CHECK_NULL_RETURN(spanNode, eraseLength);
6409     spanResult.SetTextStyle(GetTextStyleObject(spanNode));
6410     return eraseLength;
6411 }
6412 
6413 void RichEditorPattern::DeleteByDeleteValueInfo(const RichEditorDeleteValue& info)
6414 {
6415     auto deleteSpans = info.GetRichEditorDeleteSpans();
6416     if (deleteSpans.empty()) {
6417         return;
6418     }
6419     auto host = GetHost();
6420     CHECK_NULL_VOID(host);
6421     ProcessDeleteNodes(deleteSpans);
6422     UpdateSpanPosition();
6423     SetCaretPosition(info.GetOffset(), false);
6424     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
6425     OnModifyDone();
6426 }
6427 
6428 int32_t RichEditorPattern::ProcessDeleteNodes(std::list<RichEditorAbstractSpanResult>& deleteSpans)
6429 {
6430     auto eraseLength = 0;
6431     auto host = GetHost();
6432     CHECK_NULL_RETURN(host, eraseLength);
6433     std::set<int32_t, std::greater<int32_t>> deleteNodes;
6434     for (const auto& it : deleteSpans) {
6435         eraseLength += it.GetEraseLength();
6436         switch (it.GetType()) {
6437             case SpanResultType::TEXT: {
6438                 auto ui_node = host->GetChildAtIndex(it.GetSpanIndex());
6439                 CHECK_NULL_RETURN(ui_node, eraseLength);
6440                 auto spanNode = DynamicCast<SpanNode>(ui_node);
6441                 CHECK_NULL_RETURN(spanNode, eraseLength);
6442                 auto spanItem = spanNode->GetSpanItem();
6443                 CHECK_NULL_RETURN(spanItem, eraseLength);
6444                 auto text = spanItem->content;
6445                 std::wstring textTemp = StringUtils::ToWstring(text);
6446                 auto textTempSize = static_cast<int32_t>(textTemp.size());
6447                 if (textTempSize < it.OffsetInSpan()) {
6448                     TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "ProcessDeleteNodes failed, "
6449                         "content = %{private}s, spanItemSize = %{public}d, offsetInSpan = %{public}d",
6450                         text.c_str(), textTempSize, it.OffsetInSpan());
6451                     continue;
6452                 }
6453                 textTemp.erase(it.OffsetInSpan(), it.GetEraseLength());
6454                 if (textTemp.size() == 0) {
6455                     deleteNodes.emplace(it.GetSpanIndex());
6456                 }
6457                 text = StringUtils::ToString(textTemp);
6458                 spanNode->UpdateContent(text);
6459                 spanItem->position -= it.GetEraseLength();
6460                 break;
6461             }
6462             case SpanResultType::IMAGE:
6463                 deleteNodes.emplace(it.GetSpanIndex());
6464                 break;
6465             case SpanResultType::SYMBOL:
6466                 deleteNodes.emplace(it.GetSpanIndex());
6467                 break;
6468             default:
6469                 break;
6470         }
6471     }
6472     RemoveEmptySpan(deleteNodes);
6473     return eraseLength;
6474 }
6475 
6476 void RichEditorPattern::RemoveEmptySpan(std::set<int32_t, std::greater<int32_t>>& deleteSpanIndexs)
6477 {
6478     auto host = GetHost();
6479     CHECK_NULL_VOID(host);
6480     for (auto index : deleteSpanIndexs) {
6481         host->RemoveChildAtIndex(index);
6482         auto it = spans_.begin();
6483         std::advance(it, index);
6484         if (it != spans_.end()) {
6485             spans_.erase(it);
6486         }
6487     }
6488 }
6489 
6490 RefPtr<GestureEventHub> RichEditorPattern::GetGestureEventHub() {
6491     auto host = GetHost();
6492     CHECK_NULL_RETURN(host, nullptr);
6493     return host->GetOrCreateGestureEventHub();
6494 }
6495 
6496 bool RichEditorPattern::OnKeyEvent(const KeyEvent& keyEvent)
6497 {
6498     return TextInputClient::HandleKeyEvent(keyEvent);
6499 }
6500 
6501 void RichEditorPattern::CursorMove(CaretMoveIntent direction)
6502 {
6503     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "direction=%{public}d", direction);
6504     switch (direction) {
6505         case CaretMoveIntent::Left:
6506             CursorMoveLeft();
6507             break;
6508         case CaretMoveIntent::Right:
6509             CursorMoveRight();
6510             break;
6511         case CaretMoveIntent::Up:
6512             CursorMoveUp();
6513             break;
6514         case CaretMoveIntent::Down:
6515             CursorMoveDown();
6516             break;
6517         case CaretMoveIntent::LeftWord:
6518             CursorMoveLeftWord();
6519             break;
6520         case CaretMoveIntent::RightWord:
6521             CursorMoveRightWord();
6522             break;
6523         case CaretMoveIntent::ParagraghBegin:
6524             CursorMoveToParagraphBegin();
6525             break;
6526         case CaretMoveIntent::ParagraghEnd:
6527             CursorMoveToParagraphEnd();
6528             break;
6529         case CaretMoveIntent::Home:
6530             CursorMoveHome();
6531             break;
6532         case CaretMoveIntent::End:
6533             CursorMoveEnd();
6534             break;
6535         case CaretMoveIntent::LineBegin:
6536             CursorMoveLineBegin();
6537             break;
6538         case CaretMoveIntent::LineEnd:
6539             CursorMoveLineEnd();
6540             break;
6541         default:
6542             LOGW("Unsupported cursor move operation for rich editor");
6543     }
6544 }
6545 
6546 void RichEditorPattern::MoveCaretAfterTextChange()
6547 {
6548     CHECK_NULL_VOID(isTextChange_);
6549     isTextChange_ = false;
6550     switch (moveDirection_) {
6551         case MoveDirection::BACKWARD:
6552             SetCaretPosition(
6553                 std::clamp((caretPosition_ - moveLength_), 0, static_cast<int32_t>(GetTextContentLength())), false);
6554             break;
6555         case MoveDirection::FORWARD:
6556             SetCaretPosition(
6557                 std::clamp((caretPosition_ + moveLength_), 0, static_cast<int32_t>(GetTextContentLength())), false);
6558             break;
6559         default:
6560             break;
6561     }
6562     moveLength_ = 0;
6563 }
6564 
6565 void RichEditorPattern::InitTouchEvent()
6566 {
6567     CHECK_NULL_VOID(!touchListener_);
6568     auto gesture = GetGestureEventHub();
6569     CHECK_NULL_VOID(gesture);
6570     auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
6571         auto pattern = weak.Upgrade();
6572         CHECK_NULL_VOID(pattern);
6573         pattern->HandleTouchEvent(info);
6574     };
6575     touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
6576     gesture->AddTouchEvent(touchListener_);
6577 }
6578 
6579 void RichEditorPattern::InitPanEvent()
6580 {
6581     CHECK_NULL_VOID(!panEvent_);
6582     auto host = GetHost();
6583     CHECK_NULL_VOID(host);
6584     auto gestureHub = host->GetOrCreateGestureEventHub();
6585     CHECK_NULL_VOID(gestureHub);
6586     auto actionStartTask = [](const GestureEvent& info) {};
6587     auto actionUpdateTask = [](const GestureEvent& info) {};
6588     auto actionEndTask = [](const GestureEvent& info) {};
6589     GestureEventNoParameter actionCancelTask;
6590     panEvent_ = MakeRefPtr<PanEvent>(std::move(actionStartTask), std::move(actionUpdateTask),
6591         std::move(actionEndTask), std::move(actionCancelTask));
6592     PanDirection panDirection = { .type = PanDirection::ALL };
6593     gestureHub->AddPanEvent(panEvent_, panDirection, 1, DEFAULT_PAN_DISTANCE);
6594     gestureHub->SetPanEventType(GestureTypeName::PAN_GESTURE);
6595     gestureHub->SetOnGestureJudgeNativeBegin([weak = WeakClaim(this)](const RefPtr<NG::GestureInfo>& gestureInfo,
6596                                                  const std::shared_ptr<BaseGestureEvent>& info) -> GestureJudgeResult {
6597         auto pattern = weak.Upgrade();
6598         CHECK_NULL_RETURN(pattern, GestureJudgeResult::CONTINUE);
6599         auto gestureType = gestureInfo->GetType();
6600         auto inputEventType = gestureInfo->GetInputEventType();
6601         bool isDraggingCaret = (gestureType == GestureTypeName::PAN_GESTURE)
6602             && (inputEventType == InputEventType::TOUCH_SCREEN) && pattern->moveCaretState_.isMoveCaret;
6603         bool isMouseSelecting = (gestureType == GestureTypeName::PAN_GESTURE)
6604             && (inputEventType == InputEventType::MOUSE_BUTTON) && !pattern->blockPress_;
6605         if (isDraggingCaret || isMouseSelecting) {
6606             TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "prevent pan gesture draggingCaret=%{public}d mouseSelecting=%{public}d",
6607                 isDraggingCaret, isMouseSelecting);
6608             return GestureJudgeResult::CONTINUE;
6609         }
6610         CHECK_NULL_RETURN(gestureInfo->GetType() != GestureTypeName::PAN_GESTURE, GestureJudgeResult::REJECT);
6611         return GestureJudgeResult::CONTINUE;
6612     });
6613 }
6614 
6615 void RichEditorPattern::HandleTouchEvent(const TouchEventInfo& info)
6616 {
6617     CHECK_NULL_VOID(!selectOverlay_->IsTouchAtHandle(info));
6618     CHECK_NULL_VOID(!info.GetTouches().empty());
6619     auto touchInfo = info.GetTouches().front();
6620     auto touchType = touchInfo.GetTouchType();
6621     if (touchType == TouchType::DOWN) {
6622         HandleTouchDown(info);
6623         HandleOnlyImageSelected(touchInfo.GetLocalLocation(), info.GetSourceTool());
6624         if (hasUrlSpan_) {
6625             HandleUrlSpanShowShadow(touchInfo.GetLocalLocation(), touchInfo.GetGlobalLocation(), GetUrlPressColor());
6626         }
6627     } else if (touchType == TouchType::UP) {
6628         isOnlyImageDrag_ = false;
6629         HandleTouchUp();
6630         if (hasUrlSpan_) {
6631             HandleUrlSpanForegroundClear();
6632         }
6633     } else if (touchType == TouchType::MOVE) {
6634         auto originalLocaloffset = touchInfo.GetLocalLocation();
6635         auto localOffset = AdjustLocalOffsetOnMoveEvent(originalLocaloffset);
6636         HandleTouchMove(localOffset);
6637     }
6638 }
6639 
6640 void RichEditorPattern::HandleUrlSpanForegroundClear()
6641 {
6642     overlayMod_->ClearSelectedForegroundColorAndRects();
6643     MarkDirtySelf();
6644 }
6645 
6646 void RichEditorPattern::HandleTouchDown(const TouchEventInfo& info)
6647 {
6648     auto sourceTool = info.GetSourceTool();
6649     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Touch down longPressState=[%{public}d, %{public}d], source=%{public}d",
6650         previewLongPress_, editingLongPress_, sourceTool);
6651     globalOffsetOnMoveStart_ = GetParentGlobalOffset();
6652     moveCaretState_.Reset();
6653     isMoveCaretAnywhere_ = false;
6654     previewLongPress_ = false;
6655     editingLongPress_ = false;
6656     CHECK_NULL_VOID(HasFocus() && sourceTool == SourceTool::FINGER);
6657     auto touchDownOffset = info.GetTouches().front().GetLocalLocation();
6658     moveCaretState_.touchDownOffset = touchDownOffset;
6659     RectF lastCaretRect = GetCaretRect();
6660     if (RepeatClickCaret(touchDownOffset, caretPosition_, lastCaretRect)) {
6661         moveCaretState_.isTouchCaret = true;
6662         auto host = GetHost();
6663         CHECK_NULL_VOID(host);
6664     }
6665 }
6666 
6667 void RichEditorPattern::HandleTouchUp()
6668 {
6669     HandleTouchUpAfterLongPress();
6670     if (moveCaretState_.isMoveCaret) {
6671         isCursorAlwaysDisplayed_ = false;
6672         StartTwinkling();
6673     }
6674     CheckScrollable();
6675     moveCaretState_.Reset();
6676     isMoveCaretAnywhere_ = false;
6677     editingLongPress_ = false;
6678     if (magnifierController_) {
6679         magnifierController_->RemoveMagnifierFrameNode();
6680     }
6681 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
6682     if (isLongPress_) {
6683         isLongPress_ = false;
6684     }
6685 #endif
6686 }
6687 
6688 void RichEditorPattern::HandleTouchUpAfterLongPress()
6689 {
6690     CHECK_NULL_VOID(editingLongPress_ || previewLongPress_);
6691     auto selectStart = textSelector_.GetTextStart();
6692     auto selectEnd = textSelector_.GetTextEnd();
6693     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "after long press textSelector=[%{public}d, %{public}d] isEditing=%{public}d",
6694         selectStart, selectEnd, isEditing_);
6695     FireOnSelect(selectStart, selectEnd);
6696     SetCaretPositionWithAffinity({ selectEnd, TextAffinity::UPSTREAM });
6697     CalculateHandleOffsetAndShowOverlay();
6698     selectOverlay_->ProcessOverlay({ .animation = true });
6699     FireOnSelectionChange(selectStart, selectEnd);
6700     IF_TRUE(IsSingleHandle(), ForceTriggerAvoidOnCaretChange());
6701 }
6702 
6703 void RichEditorPattern::HandleTouchMove(const Offset& offset)
6704 {
6705     if (previewLongPress_ || editingLongPress_) {
6706         UpdateSelectionByTouchMove(offset);
6707         return;
6708     }
6709     CHECK_NULL_VOID(moveCaretState_.isTouchCaret);
6710     if (!moveCaretState_.isMoveCaret) {
6711         auto moveDistance = (offset - moveCaretState_.touchDownOffset).GetDistance();
6712         if (GreatNotEqual(moveDistance, moveCaretState_.minDistance.ConvertToPx())) {
6713             TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Move caret distance greater than minDistance");
6714             moveCaretState_.isMoveCaret = true;
6715             ShowCaretWithoutTwinkling();
6716         }
6717     }
6718     UpdateCaretByTouchMove(offset);
6719 }
6720 
6721 void RichEditorPattern::UpdateCaretByTouchMove(const Offset& offset)
6722 {
6723     CHECK_NULL_VOID(moveCaretState_.isMoveCaret);
6724     auto host = GetHost();
6725     CHECK_NULL_VOID(host);
6726     scrollable_ = false;
6727     SetScrollEnabled(scrollable_);
6728     if (SelectOverlayIsOn()) {
6729         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Close select overlay while dragging caret");
6730         selectOverlay_->CloseOverlay(false, CloseReason::CLOSE_REASON_NORMAL);
6731     }
6732     auto preCaretPosition = caretPosition_;
6733     Offset textOffset = ConvertTouchOffsetToTextOffset(offset);
6734     auto positionWithAffinity = paragraphs_.GetGlyphPositionAtCoordinate(textOffset);
6735     SetCaretPositionWithAffinity(positionWithAffinity);
6736     MoveCaretToContentRect();
6737     StartVibratorByIndexChange(caretPosition_, preCaretPosition);
6738     CalcAndRecordLastClickCaretInfo(textOffset);
6739     auto localOffset = OffsetF(offset.GetX(), offset.GetY());
6740     if (magnifierController_) {
6741         magnifierController_->SetLocalOffset(localOffset);
6742     }
6743     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6744 }
6745 
6746 Offset RichEditorPattern::AdjustLocalOffsetOnMoveEvent(const Offset& originalOffset)
6747 {
6748     auto deltaOffset = GetParentGlobalOffset() - globalOffsetOnMoveStart_;
6749     return { originalOffset.GetX() - deltaOffset.GetX(), originalOffset.GetY() - deltaOffset.GetY() };
6750 }
6751 
6752 void RichEditorPattern::StartVibratorByIndexChange(int32_t currentIndex, int32_t preIndex)
6753 {
6754     CHECK_NULL_VOID(isEnableHapticFeedback_ && (currentIndex != preIndex));
6755     VibratorUtils::StartVibraFeedback("slide");
6756 }
6757 
6758 bool RichEditorPattern::IsScrollBarPressed(const MouseInfo& info)
6759 {
6760     auto scrollBar = GetScrollBar();
6761     bool isScrollBarShow = scrollBar && scrollBar->NeedPaint();
6762     CHECK_NULL_RETURN(isScrollBarShow, false);
6763     Point point(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY());
6764     return scrollBar->InBarRectRegion(point);
6765 }
6766 
6767 void RichEditorPattern::HandleMouseLeftButtonMove(const MouseInfo& info)
6768 {
6769     ACE_SCOPED_TRACE("RichEditorHandleMouseLeftButtonMove");
6770     if (blockPress_) {
6771         ACE_SCOPED_TRACE("RichEditorUpdateDragBoxes");
6772         dragBoxes_ = GetTextBoxes();
6773         return;
6774     }
6775     CHECK_NULL_VOID(leftMousePress_);
6776 
6777     auto localOffset = AdjustLocalOffsetOnMoveEvent(info.GetLocalLocation());
6778     Offset textOffset = ConvertTouchOffsetToTextOffset(localOffset);
6779     if (dataDetectorAdapter_->pressedByLeftMouse_) {
6780         dataDetectorAdapter_->pressedByLeftMouse_ = false;
6781         MoveCaretAndStartFocus(textOffset);
6782     }
6783 
6784     auto focusHub = GetFocusHub();
6785     CHECK_NULL_VOID(focusHub);
6786     CHECK_NULL_VOID(focusHub->IsCurrentFocus());
6787 
6788     mouseStatus_ = MouseStatus::MOVE;
6789     if (isFirstMouseSelect_) {
6790         int32_t extend = paragraphs_.GetIndex(textOffset);
6791         UpdateSelector(textSelector_.baseOffset, extend);
6792         isFirstMouseSelect_ = false;
6793     } else {
6794         int32_t extend = paragraphs_.GetIndex(textOffset);
6795         UpdateSelector(textSelector_.baseOffset, extend);
6796         auto position = paragraphs_.GetIndex(textOffset);
6797         AdjustCursorPosition(position);
6798         SetCaretPosition(position);
6799         AutoScrollParam param = {
6800             .autoScrollEvent = AutoScrollEvent::MOUSE, .showScrollbar = true, .eventOffset = info.GetLocalLocation()
6801         };
6802         AutoScrollByEdgeDetection(param, OffsetF(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY()),
6803             EdgeDetectionStrategy::OUT_BOUNDARY);
6804         showSelect_ = true;
6805     }
6806     if (textSelector_.SelectNothing()) {
6807         if (!caretTwinkling_) {
6808             StartTwinkling();
6809         }
6810     } else {
6811         StopTwinkling();
6812     }
6813     isMouseSelect_ = true;
6814     auto host = GetHost();
6815     CHECK_NULL_VOID(host);
6816     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6817 }
6818 
6819 void RichEditorPattern::HandleMouseLeftButtonPress(const MouseInfo& info)
6820 {
6821     isMousePressed_ = true;
6822     HandleOnlyImageSelected(info.GetLocalLocation(), SourceTool::MOUSE);
6823     if (IsScrollBarPressed(info) || BetweenSelectedPosition(info.GetGlobalLocation())) {
6824         blockPress_ = true;
6825         return;
6826     }
6827     auto focusHub = GetFocusHub();
6828     CHECK_NULL_VOID(focusHub);
6829     if (!focusHub->IsFocusable()) {
6830         return;
6831     }
6832     auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
6833     Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
6834         info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
6835     if (textSelector_.baseOffset != textSelector_.destinationOffset) {
6836         ResetSelection();
6837     }
6838     int32_t extend = paragraphs_.GetIndex(textOffset);
6839     textSelector_.Update(extend);
6840     leftMousePress_ = true;
6841     globalOffsetOnMoveStart_ = GetParentGlobalOffset();
6842     mouseStatus_ = MouseStatus::PRESSED;
6843     blockPress_ = false;
6844     caretUpdateType_ = CaretUpdateType::PRESSED;
6845     dataDetectorAdapter_->pressedByLeftMouse_ = false;
6846     HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY()));
6847     if (dataDetectorAdapter_->pressedByLeftMouse_) {
6848         return;
6849     }
6850     UseHostToUpdateTextFieldManager();
6851     MoveCaretAndStartFocus(textOffset);
6852     CalcCaretInfoByClick(info.GetLocalLocation());
6853 }
6854 
6855 void RichEditorPattern::HandleMouseLeftButtonRelease(const MouseInfo& info)
6856 {
6857     blockPress_ = false;
6858     leftMousePress_ = false;
6859     auto oldMouseStatus = mouseStatus_;
6860     mouseStatus_ = MouseStatus::RELEASED;
6861     isMouseSelect_ = false;
6862     isFirstMouseSelect_ = true;
6863     isOnlyImageDrag_ = false;
6864     if (!showSelect_) {
6865         showSelect_ = true;
6866         ResetSelection();
6867     }
6868     if (dataDetectorAdapter_->pressedByLeftMouse_ && oldMouseStatus != MouseStatus::MOVE && !IsDragging()) {
6869         dataDetectorAdapter_->ResponseBestMatchItem(dataDetectorAdapter_->clickedAISpan_);
6870         dataDetectorAdapter_->pressedByLeftMouse_ = false;
6871         isMousePressed_ = false;
6872         return;
6873     }
6874 
6875     auto selectStart = std::min(textSelector_.baseOffset, textSelector_.destinationOffset);
6876     auto selectEnd = std::max(textSelector_.baseOffset, textSelector_.destinationOffset);
6877     if (selectStart != selectEnd) {
6878         FireOnSelect(selectStart, selectEnd);
6879     }
6880     StopAutoScroll(false);
6881     if (textSelector_.IsValid() && !textSelector_.StartEqualToDest() && IsSelectedBindSelectionMenu() &&
6882         oldMouseStatus == MouseStatus::MOVE) {
6883         auto offsetX = static_cast<float>(info.GetGlobalLocation().GetX());
6884         auto offsetY = static_cast<float>(info.GetGlobalLocation().GetY());
6885         selectionMenuOffsetByMouse_ = OffsetF(offsetX, offsetY);
6886         selectionMenuOffsetClick_ = OffsetF(offsetX, offsetY);
6887         ShowSelectOverlay(RectF(), RectF(), false, TextResponseType::SELECTED_BY_MOUSE);
6888     }
6889     isMousePressed_ = false;
6890     if (HasFocus()) {
6891         HandleOnEditChanged(true);
6892     }
6893 }
6894 
6895 void RichEditorPattern::HandleMouseLeftButton(const MouseInfo& info)
6896 {
6897     if (info.GetAction() == MouseAction::MOVE) {
6898         HandleMouseLeftButtonMove(info);
6899     } else if (info.GetAction() == MouseAction::PRESS) {
6900         HandleMouseLeftButtonPress(info);
6901     } else if (info.GetAction() == MouseAction::RELEASE) {
6902         HandleMouseLeftButtonRelease(info);
6903     }
6904 }
6905 
6906 void RichEditorPattern::HandleMouseRightButton(const MouseInfo& info)
6907 {
6908     auto focusHub = GetFocusHub();
6909     CHECK_NULL_VOID(focusHub);
6910     if (!focusHub->IsFocusable()) {
6911         return;
6912     }
6913     if (info.GetAction() == MouseAction::PRESS) {
6914         isMousePressed_ = true;
6915         usingMouseRightButton_ = true;
6916         CloseSelectOverlay();
6917     } else if (info.GetAction() == MouseAction::RELEASE) {
6918         auto offsetX = static_cast<float>(info.GetGlobalLocation().GetX());
6919         auto offsetY = static_cast<float>(info.GetGlobalLocation().GetY());
6920         selectionMenuOffsetByMouse_ = OffsetF(offsetX, offsetY);
6921         selectionMenuOffsetClick_ = OffsetF(offsetX, offsetY);
6922         selectOverlay_->SetIsSingleHandle(false);
6923         if (textSelector_.IsValid() && BetweenSelection(info.GetGlobalLocation())) {
6924             ShowSelectOverlay(RectF(), RectF());
6925             isMousePressed_ = false;
6926             usingMouseRightButton_ = false;
6927             return;
6928         }
6929         auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
6930         Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
6931             info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
6932         HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY()));
6933         if (dataDetectorAdapter_->hasClickedAISpan_) {
6934             dataDetectorAdapter_->hasClickedAISpan_ = false;
6935             isMousePressed_ = false;
6936             usingMouseRightButton_ = false;
6937             return;
6938         }
6939         if (textSelector_.IsValid()) {
6940             CloseSelectOverlay();
6941             ResetSelection();
6942         }
6943         MouseRightFocus(info);
6944         ShowSelectOverlay(RectF(), RectF());
6945         isMousePressed_ = false;
6946         usingMouseRightButton_ = false;
6947     }
6948 }
6949 
6950 void RichEditorPattern::MouseRightFocus(const MouseInfo& info)
6951 {
6952     auto textRect = GetTextRect();
6953     textRect.SetTop(textRect.GetY() - std::min(baselineOffset_, 0.0f));
6954     textRect.SetHeight(textRect.Height() - std::max(baselineOffset_, 0.0f));
6955     Offset textOffset = { info.GetLocalLocation().GetX() - textRect.GetX(),
6956         info.GetLocalLocation().GetY() - textRect.GetY() };
6957     InitSelection(textOffset);
6958     auto selectStart = std::min(textSelector_.baseOffset, textSelector_.destinationOffset);
6959     auto selectEnd = std::max(textSelector_.baseOffset, textSelector_.destinationOffset);
6960     auto host = GetHost();
6961     CHECK_NULL_VOID(host);
6962     auto focusHub = host->GetOrCreateFocusHub();
6963     CHECK_NULL_VOID(focusHub);
6964     focusHub->RequestFocusImmediately();
6965     SetCaretPosition(selectEnd);
6966 
6967     TextInsertValueInfo spanInfo;
6968     CalcInsertValueObj(spanInfo);
6969     auto spanNode = DynamicCast<FrameNode>(GetChildByIndex(spanInfo.GetSpanIndex() - 1));
6970     auto isNeedSelected = spanNode && (spanNode->GetTag() == V2::IMAGE_ETS_TAG ||
6971         spanNode->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG);
6972     if (isNeedSelected && BetweenSelectedPosition(info.GetGlobalLocation())) {
6973         selectedType_ = TextSpanType::IMAGE;
6974         FireOnSelect(selectStart, selectEnd);
6975         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6976         return;
6977     }
6978     if (textSelector_.IsValid()) {
6979         ResetSelection();
6980     }
6981     auto position = paragraphs_.GetIndex(textOffset);
6982     SetCaretPosition(position);
6983     CalcAndRecordLastClickCaretInfo(textOffset);
6984     auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
6985     selectedType_ = TextSpanType::TEXT;
6986     CHECK_NULL_VOID(overlayMod_);
6987     DynamicCast<RichEditorOverlayModifier>(overlayMod_)->SetCaretOffsetAndHeight(caretOffset, caretHeight);
6988     StartTwinkling();
6989 }
6990 
6991 void RichEditorPattern::FireOnSelect(int32_t selectStart, int32_t selectEnd)
6992 {
6993     auto host = GetHost();
6994     CHECK_NULL_VOID(host);
6995     auto eventHub = host->GetEventHub<RichEditorEventHub>();
6996     CHECK_NULL_VOID(eventHub);
6997     auto textSelectInfo = GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT);
6998     if (!textSelectInfo.GetSelection().resultObjects.empty()) {
6999         eventHub->FireOnSelect(&textSelectInfo);
7000     }
7001     UpdateSelectionType(textSelectInfo);
7002 }
7003 
7004 void RichEditorPattern::UpdateSelectionType(const SelectionInfo& textSelectInfo)
7005 {
7006     if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
7007         TextPattern::UpdateSelectionType(GetAdjustedSelectionInfo(textSelectInfo));
7008     } else {
7009         TextPattern::UpdateSelectionType(textSelectInfo);
7010     }
7011 }
7012 
7013 SelectionInfo RichEditorPattern::GetAdjustedSelectionInfo(const SelectionInfo& textSelectInfo)
7014 {
7015     auto selection = textSelectInfo.GetSelection();
7016     auto resultObjects = selection.resultObjects;
7017     std::for_each(resultObjects.begin(), resultObjects.end(), [](ResultObject& object) {
7018         if (object.type == SelectSpanType::TYPEIMAGE && object.valueString == " " && object.valuePixelMap == nullptr) {
7019             object.type = SelectSpanType::TYPEBUILDERSPAN;
7020         }
7021     });
7022     SelectionInfo adjustedInfo;
7023     adjustedInfo.SetSelectionStart(selection.selection[RichEditorSpanRange::RANGESTART]);
7024     adjustedInfo.SetSelectionEnd(selection.selection[RichEditorSpanRange::RANGEEND]);
7025     adjustedInfo.SetResultObjectList(resultObjects);
7026     return adjustedInfo;
7027 }
7028 
7029 void RichEditorPattern::HandleMouseEvent(const MouseInfo& info)
7030 {
7031     auto tmpHost = GetHost();
7032     CHECK_NULL_VOID(tmpHost);
7033     auto frameId = tmpHost->GetId();
7034     auto pipeline = tmpHost->GetContext();
7035     CHECK_NULL_VOID(pipeline);
7036     if (selectOverlay_->IsHandleShow() && info.GetAction() == MouseAction::PRESS) {
7037         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Close selectOverlay when handle is showing");
7038         CloseSelectOverlay();
7039     }
7040     auto scrollBar = GetScrollBar();
7041     if (scrollBar && (scrollBar->IsHover() || scrollBar->IsPressed())) {
7042         pipeline->SetMouseStyleHoldNode(frameId);
7043         pipeline->ChangeMouseStyle(frameId, MouseFormat::DEFAULT);
7044         currentMouseStyle_ = MouseFormat::DEFAULT;
7045         HandleUrlSpanForegroundClear();
7046         return;
7047     }
7048 
7049     if (hasUrlSpan_) {
7050         auto show = HandleUrlSpanShowShadow(info.GetLocalLocation(), info.GetGlobalLocation(), GetUrlHoverColor());
7051         if (show) {
7052             pipeline->SetMouseStyleHoldNode(frameId);
7053             pipeline->ChangeMouseStyle(frameId, MouseFormat::HAND_POINTING);
7054         } else {
7055             pipeline->SetMouseStyleHoldNode(frameId);
7056             pipeline->ChangeMouseStyle(frameId, MouseFormat::TEXT_CURSOR);
7057         }
7058     }
7059 
7060     if (currentMouseStyle_ == MouseFormat::DEFAULT) {
7061         pipeline->SetMouseStyleHoldNode(frameId);
7062         pipeline->ChangeMouseStyle(frameId, MouseFormat::TEXT_CURSOR);
7063         currentMouseStyle_ = MouseFormat::TEXT_CURSOR;
7064     }
7065 
7066     caretUpdateType_ = CaretUpdateType::NONE;
7067     if (info.GetButton() == MouseButton::LEFT_BUTTON) {
7068         HandleMouseLeftButton(info);
7069     } else if (info.GetButton() == MouseButton::RIGHT_BUTTON) {
7070         HandleMouseRightButton(info);
7071     }
7072 }
7073 
7074 Color RichEditorPattern::GetUrlHoverColor()
7075 {
7076     auto pipeline = PipelineContext::GetCurrentContextSafely();
7077     CHECK_NULL_RETURN(pipeline, Color());
7078     auto theme = pipeline->GetTheme<RichEditorTheme>();
7079     CHECK_NULL_RETURN(theme, Color());
7080     return theme->GetUrlHoverColor();
7081 }
7082 
7083 Color RichEditorPattern::GetUrlPressColor()
7084 {
7085     auto pipeline = PipelineContext::GetCurrentContextSafely();
7086     CHECK_NULL_RETURN(pipeline, Color());
7087     auto theme = pipeline->GetTheme<RichEditorTheme>();
7088     CHECK_NULL_RETURN(theme, Color());
7089     return theme->GetUrlPressColor();
7090 }
7091 
7092 Color RichEditorPattern::GetUrlSpanColor()
7093 {
7094     auto pipeline = PipelineContext::GetCurrentContextSafely();
7095     CHECK_NULL_RETURN(pipeline, Color());
7096     auto theme = pipeline->GetTheme<RichEditorTheme>();
7097     CHECK_NULL_RETURN(theme, Color());
7098     return theme->GetUrlDefaultColor();
7099 }
7100 
7101 void RichEditorPattern::TriggerAvoidOnCaretChange()
7102 {
7103     CHECK_NULL_VOID(HasFocus());
7104     auto host = GetHost();
7105     CHECK_NULL_VOID(host);
7106     auto pipeline = host->GetContext();
7107     CHECK_NULL_VOID(pipeline);
7108     auto textFieldManager = DynamicCast<TextFieldManagerNG>(pipeline->GetTextFieldManager());
7109     CHECK_NULL_VOID(textFieldManager);
7110     CHECK_NULL_VOID(pipeline->UsingCaretAvoidMode());
7111     auto safeAreaManager = pipeline->GetSafeAreaManager();
7112     if (!safeAreaManager || NearZero(safeAreaManager->GetKeyboardInset().Length(), 0)) {
7113         return;
7114     }
7115     textFieldManager->SetHeight(GetCaretRect().Height());
7116     auto taskExecutor = pipeline->GetTaskExecutor();
7117     CHECK_NULL_VOID(taskExecutor);
7118     taskExecutor->PostTask([manager = WeakPtr<TextFieldManagerNG>(textFieldManager)] {
7119         auto textFieldManager = manager.Upgrade();
7120         CHECK_NULL_VOID(textFieldManager);
7121         textFieldManager->TriggerAvoidOnCaretChange();
7122     }, TaskExecutor::TaskType::UI, "ArkUIRichEditorNotifyCaretChange");}
7123 
7124 void RichEditorPattern::OnWindowSizeChanged(int32_t width, int32_t height, WindowSizeChangeReason type)
7125 {
7126     CHECK_NULL_VOID(type == WindowSizeChangeReason::ROTATION);
7127     if (SelectOverlayIsOn()) {
7128         CalculateHandleOffsetAndShowOverlay();
7129         selectOverlay_->ProcessOverlayOnAreaChanged({ .menuIsShow = false });
7130     }
7131     auto host = GetHost();
7132     CHECK_NULL_VOID(host);
7133     auto context = host->GetContextRefPtr();
7134     CHECK_NULL_VOID(context);
7135     auto taskExecutor = context->GetTaskExecutor();
7136     CHECK_NULL_VOID(taskExecutor);
7137     auto textFieldManager = DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
7138     CHECK_NULL_VOID(textFieldManager);
7139     textFieldManager->ResetOptionalClickPosition();
7140     taskExecutor->PostTask(
7141         [weak = WeakClaim(this), manager = WeakPtr<TextFieldManagerNG>(textFieldManager)] {
7142             auto pattern = weak.Upgrade();
7143             CHECK_NULL_VOID(pattern);
7144             pattern->parentGlobalOffset_ = pattern->GetPaintRectGlobalOffset();
7145             pattern->UpdateModifierCaretOffsetAndHeight();
7146             pattern->UpdateTextFieldManager(Offset(pattern->parentGlobalOffset_.GetX(),
7147                 pattern->parentGlobalOffset_.GetY()), pattern->frameRect_.Height());
7148             pattern->UpdateCaretInfoToController();
7149         },
7150         TaskExecutor::TaskType::UI, "ArkUIRichEditorOnWindowSizeChangedRotation");
7151 }
7152 
7153 void RichEditorPattern::CopySelectionMenuParams(SelectOverlayInfo& selectInfo, TextResponseType responseType)
7154 {
7155     auto selectType = selectedType_.value_or(TextSpanType::NONE);
7156     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "textSpanType=%{public}d, responseType=%{public}d", selectType, responseType);
7157     std::shared_ptr<SelectionMenuParams> menuParams = GetMenuParams(selectType, responseType);
7158     CHECK_NULL_VOID(menuParams);
7159 
7160     // long pressing on the image needs to set the position of the pop-up menu following the long pressing position
7161     if (selectType == TextSpanType::IMAGE && !selectInfo.isUsingMouse) {
7162         selectInfo.menuInfo.menuOffset = OffsetF(selectionMenuOffset_.GetX(), selectionMenuOffset_.GetY());
7163     }
7164 
7165     CopyBindSelectionMenuParams(selectInfo, menuParams);
7166 }
7167 
7168 void RichEditorPattern::ShowSelectOverlay(const RectF& firstHandle, const RectF& secondHandle, bool isCopyAll,
7169     TextResponseType responseType, bool handleReverse)
7170 {
7171     CHECK_NULL_VOID(!IsPreviewTextInputting());
7172     textResponseType_ = responseType;
7173     selectOverlay_->ProcessOverlay({.animation = true});
7174 }
7175 
7176 void RichEditorPattern::OnCopyOperationExt(RefPtr<PasteDataMix>& pasteData)
7177 {
7178     auto subSpanString =
7179         ToStyledString(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
7180     std::vector<uint8_t> tlvData;
7181     subSpanString->EncodeTlv(tlvData);
7182     auto text = subSpanString->GetString();
7183     clipboard_->AddSpanStringRecord(pasteData, tlvData);
7184 }
7185 
7186 void RichEditorPattern::HandleOnCopyStyledString()
7187 {
7188     RefPtr<PasteDataMix> pasteData = clipboard_->CreatePasteDataMix();
7189     auto subSpanString = styledString_->GetSubSpanString(textSelector_.GetTextStart(),
7190         textSelector_.GetTextEnd() - textSelector_.GetTextStart());
7191     std::vector<uint8_t> tlvData;
7192     subSpanString->EncodeTlv(tlvData);
7193     clipboard_->AddSpanStringRecord(pasteData, tlvData);
7194     clipboard_->AddTextRecord(pasteData, subSpanString->GetString());
7195     clipboard_->SetData(pasteData, copyOption_);
7196 }
7197 
7198 void RichEditorPattern::OnCopyOperation(bool isUsingExternalKeyboard)
7199 {
7200     if (isSpanStringMode_) {
7201         HandleOnCopyStyledString();
7202         return;
7203     }
7204     RefPtr<PasteDataMix> pasteData = clipboard_->CreatePasteDataMix();
7205     auto selectStart = textSelector_.GetTextStart();
7206     auto selectEnd = textSelector_.GetTextEnd();
7207     auto textSelectInfo = GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT);
7208     auto copyResultObjects = textSelectInfo.GetSelection().resultObjects;
7209     caretUpdateType_ = CaretUpdateType::NONE;
7210     if (copyResultObjects.empty()) {
7211         return;
7212     }
7213     for (auto resultObj = copyResultObjects.rbegin(); resultObj != copyResultObjects.rend(); ++resultObj) {
7214         ProcessResultObject(pasteData, *resultObj);
7215     }
7216     clipboard_->SetData(pasteData, copyOption_);
7217 }
7218 
7219 void RichEditorPattern::ProcessResultObject(RefPtr<PasteDataMix> pasteData, const ResultObject& result)
7220 {
7221     CHECK_NULL_VOID(pasteData);
7222     auto multiTypeRecordImpl = AceType::MakeRefPtr<MultiTypeRecordImpl>();
7223     if (result.type == SelectSpanType::TYPESPAN) {
7224         auto data = GetSelectedSpanText(StringUtils::ToWstring(result.valueString),
7225             result.offsetInSpan[RichEditorSpanRange::RANGESTART], result.offsetInSpan[RichEditorSpanRange::RANGEEND]);
7226 #ifdef PREVIEW
7227         clipboard_->SetData(data, CopyOptions::Distributed);
7228 #else
7229         multiTypeRecordImpl->SetPlainText(data);
7230         EncodeTlvDataByResultObject(result, multiTypeRecordImpl->GetSpanStringBuffer());
7231         clipboard_->AddMultiTypeRecord(pasteData, multiTypeRecordImpl);
7232 #endif
7233         return;
7234     }
7235     if (result.type == SelectSpanType::TYPEIMAGE) {
7236 #ifdef PREVIEW
7237         if (result.valuePixelMap) {
7238             clipboard_->AddPixelMapRecord(pasteData, result.valuePixelMap);
7239         } else {
7240             clipboard_->AddImageRecord(pasteData, result.valueString);
7241         }
7242 #else
7243         if (result.valuePixelMap) {
7244             multiTypeRecordImpl->SetPixelMap(result.valuePixelMap);
7245         } else {
7246             multiTypeRecordImpl->SetUri(result.valueString);
7247         }
7248         EncodeTlvDataByResultObject(result, multiTypeRecordImpl->GetSpanStringBuffer());
7249         clipboard_->AddMultiTypeRecord(pasteData, multiTypeRecordImpl);
7250 #endif
7251     }
7252 }
7253 
7254 void RichEditorPattern::EncodeTlvDataByResultObject(const ResultObject& result, std::vector<uint8_t>& tlvData)
7255 {
7256     auto selectStart = result.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] + result.offsetInSpan[RichEditorSpanRange::RANGESTART];
7257     auto selectEnd = result.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] + result.offsetInSpan[RichEditorSpanRange::RANGEEND];
7258     auto spanString = ToStyledString(selectStart, selectEnd);
7259     spanString->EncodeTlv(tlvData);
7260 }
7261 
7262 void RichEditorPattern::HandleOnCopy(bool isUsingExternalKeyboard)
7263 {
7264     CHECK_NULL_VOID(clipboard_);
7265     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "isUsingExternalKeyboard=%{public}d, copyOption=%{public}d",
7266         isUsingExternalKeyboard, copyOption_);
7267     if (copyOption_ == CopyOptions::None) {
7268         return;
7269     }
7270     auto host = GetHost();
7271     CHECK_NULL_VOID(host);
7272     auto eventHub = host->GetEventHub<RichEditorEventHub>();
7273     CHECK_NULL_VOID(eventHub);
7274     TextCommonEvent event;
7275     eventHub->FireOnCopy(event);
7276     if (event.IsPreventDefault()) {
7277         CloseSelectOverlay();
7278         ResetSelection();
7279         StartTwinkling();
7280         return;
7281     }
7282     OnCopyOperation(isUsingExternalKeyboard);
7283     if (selectOverlay_->IsUsingMouse() || isUsingExternalKeyboard) {
7284         CloseSelectOverlay();
7285     } else {
7286         selectOverlay_->HideMenu();
7287     }
7288 }
7289 
7290 void RichEditorPattern::ResetAfterPaste()
7291 {
7292     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "ResetAfterPaste");
7293     auto pasteStr = GetPasteStr();
7294     SetCaretSpanIndex(-1);
7295     StartTwinkling();
7296     RequestKeyboardToEdit();
7297     CloseSelectOverlay();
7298     InsertValueByPaste(pasteStr);
7299     ClearPasteStr();
7300 }
7301 
7302 void RichEditorPattern::InsertValueByPaste(const std::string& pasteStr)
7303 {
7304     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "InsertValueByPaste");
7305     if (isSpanStringMode_) {
7306         InsertValueInStyledString(pasteStr);
7307         return;
7308     }
7309     InsertValueByOperationType(pasteStr, OperationType::DEFAULT);
7310 }
7311 
7312 void RichEditorPattern::HandleOnPaste()
7313 {
7314     auto host = GetHost();
7315     CHECK_NULL_VOID(host);
7316     auto eventHub = host->GetEventHub<RichEditorEventHub>();
7317     CHECK_NULL_VOID(eventHub);
7318     TextCommonEvent event;
7319     eventHub->FireOnPaste(event);
7320     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleOnPaste, preventDefault=%{public}d", event.IsPreventDefault());
7321     if (event.IsPreventDefault()) {
7322         CloseSelectOverlay();
7323         ResetSelection();
7324         StartTwinkling();
7325         RequestKeyboardToEdit();
7326         return;
7327     }
7328     auto isSpanStringMode = isSpanStringMode_;
7329     auto pasteCallback = [weak = WeakClaim(this), isSpanStringMode](std::vector<std::vector<uint8_t>>& arrs,
7330                              const std::string& text, bool& isMulitiTypeRecord) {
7331         TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
7332             "pasteCallback callback, isMulitiTypeRecord : [%{public}d], isSpanStringMode : [%{public}d]",
7333             isMulitiTypeRecord, isSpanStringMode);
7334         auto richEditor = weak.Upgrade();
7335         CHECK_NULL_VOID(richEditor);
7336         std::list<RefPtr<SpanString>> spanStrings;
7337         for (auto arr : arrs) {
7338             spanStrings.push_back(SpanString::DecodeTlv(arr));
7339         }
7340         if (!spanStrings.empty() && !isMulitiTypeRecord) {
7341             for (auto spanString : spanStrings) {
7342                 richEditor->AddSpanByPasteData(spanString);
7343                 richEditor->RequestKeyboardToEdit();
7344             }
7345             return;
7346         }
7347         if (text.empty()) {
7348             richEditor->ResetSelection();
7349             richEditor->StartTwinkling();
7350             richEditor->CloseSelectOverlay();
7351             richEditor->RequestKeyboardToEdit();
7352             return;
7353         }
7354         richEditor->AddPasteStr(text);
7355         richEditor->ResetAfterPaste();
7356     };
7357     CHECK_NULL_VOID(clipboard_);
7358     clipboard_->GetSpanStringData(pasteCallback);
7359 }
7360 
7361 void RichEditorPattern::SetCaretSpanIndex(int32_t index)
7362 {
7363     caretSpanIndex_ = index;
7364 }
7365 
7366 void RichEditorPattern::HandleOnCut()
7367 {
7368     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "copyOption=%{public}d, textSelector_.IsValid()=%{public}d",
7369         copyOption_, textSelector_.IsValid());
7370     if (copyOption_ == CopyOptions::None) {
7371         return;
7372     }
7373     if (!textSelector_.IsValid()) {
7374         return;
7375     }
7376     auto host = GetHost();
7377     CHECK_NULL_VOID(host);
7378     auto eventHub = host->GetEventHub<RichEditorEventHub>();
7379     CHECK_NULL_VOID(eventHub);
7380     TextCommonEvent event;
7381     eventHub->FireOnCut(event);
7382     if (event.IsPreventDefault()) {
7383         CloseSelectOverlay();
7384         ResetSelection();
7385         StartTwinkling();
7386         RequestKeyboardToEdit();
7387         return;
7388     }
7389 
7390     caretUpdateType_ = CaretUpdateType::NONE;
7391     OnCopyOperation();
7392     DeleteBackward();
7393 }
7394 
7395 std::function<void(Offset)> RichEditorPattern::GetThumbnailCallback()
7396 {
7397     return [wk = WeakClaim(this)](const Offset& point) {
7398         auto pattern = wk.Upgrade();
7399         CHECK_NULL_VOID(pattern);
7400         if (!pattern->BetweenSelectedPosition(point)) {
7401             return;
7402         }
7403         auto gesture = pattern->GetGestureEventHub();
7404         CHECK_NULL_VOID(gesture);
7405         auto isContentDraggable = pattern->JudgeContentDraggable();
7406         if (!isContentDraggable) {
7407             gesture->SetIsTextDraggable(false);
7408             return;
7409         }
7410         auto host = pattern->GetHost();
7411         auto children = host->GetChildren();
7412         std::list<RefPtr<FrameNode>> imageChildren;
7413         for (const auto& child : children) {
7414             auto node = DynamicCast<FrameNode>(child);
7415             if (!node) {
7416                 continue;
7417             }
7418             auto tag = node->GetTag();
7419             if (tag == V2::IMAGE_ETS_TAG || tag == V2::PLACEHOLDER_SPAN_ETS_TAG) {
7420                 imageChildren.emplace_back(node);
7421             }
7422         }
7423         RichEditorDragInfo info;
7424         info.handleColor = pattern->GetCaretColor();
7425         info.selectedBackgroundColor = pattern->GetSelectedBackgroundColor();
7426         pattern->CalculateHandleOffsetAndShowOverlay();
7427         auto firstHandleInfo = pattern->GetFirstHandleInfo();
7428         if (firstHandleInfo.has_value() && firstHandleInfo.value().isShow) {
7429             info.firstHandle = pattern->textSelector_.firstHandle;
7430         }
7431         auto secondHandleInfo = pattern->GetSecondHandleInfo();
7432         if (secondHandleInfo.has_value() && secondHandleInfo.value().isShow) {
7433             info.secondHandle = pattern->textSelector_.secondHandle;
7434         }
7435         pattern->dragNode_ = RichEditorDragPattern::CreateDragNode(host, imageChildren, info);
7436         auto textDragPattern = pattern->dragNode_->GetPattern<TextDragPattern>();
7437         auto option = host->GetDragPreviewOption();
7438         option.options.shadowPath = textDragPattern->GetBackgroundPath()->ConvertToSVGString();
7439         option.options.shadow = Shadow(RICH_DEFAULT_ELEVATION, {0.0, 0.0}, Color(RICH_DEFAULT_SHADOW_COLOR),
7440             ShadowStyle::OuterFloatingSM);
7441         host->SetDragPreviewOptions(option);
7442         FrameNode::ProcessOffscreenNode(pattern->dragNode_);
7443         auto gestureHub = host->GetOrCreateGestureEventHub();
7444         CHECK_NULL_VOID(gestureHub);
7445         gestureHub->SetPixelMap(nullptr);
7446     };
7447 }
7448 
7449 void RichEditorPattern::CreateHandles()
7450 {
7451     if (IsDragging()) {
7452         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "do not show handles when dragging");
7453         return;
7454     }
7455     auto host = GetHost();
7456     CHECK_NULL_VOID(host);
7457     CalculateHandleOffsetAndShowOverlay();
7458     selectOverlay_->ProcessOverlay({ .menuIsShow = selectOverlay_->IsCurrentMenuVisibile(), .animation = true });
7459 }
7460 
7461 void RichEditorPattern::ShowHandles(const bool isNeedShowHandles)
7462 {
7463     if (!isNeedShowHandles) {
7464         auto info = GetSpansInfo(textSelector_.GetTextStart(), textSelector_.GetTextEnd(), GetSpansMethod::ONSELECT);
7465         auto selResult = info.GetSelection().resultObjects;
7466         if (isMousePressed_ && selResult.size() == 1 && selResult.front().type == SelectSpanType::TYPEIMAGE) {
7467             textSelector_.Update(-1, -1);
7468             auto host = GetHost();
7469             CHECK_NULL_VOID(host);
7470             host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
7471             return;
7472         }
7473     }
7474     ShowHandles();
7475 }
7476 
7477 void RichEditorPattern::ShowHandles()
7478 {
7479     auto host = GetHost();
7480     CHECK_NULL_VOID(host);
7481     if (!selectOverlay_->IsBothHandlesShow() && !selectOverlay_->SelectOverlayIsCreating()) {
7482         showSelect_ = true;
7483         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
7484         CalculateHandleOffsetAndShowOverlay();
7485         selectOverlay_->ProcessOverlay({.animation = false});
7486     }
7487 }
7488 
7489 void RichEditorPattern::OnAreaChangedInner()
7490 {
7491     auto host = GetHost();
7492     CHECK_NULL_VOID(host);
7493     auto context = host->GetContext();
7494     CHECK_NULL_VOID(context);
7495     auto parentGlobalOffset = GetPaintRectGlobalOffset(); // offset on screen(with transformation)
7496     if (parentGlobalOffset != parentGlobalOffset_) {
7497         parentGlobalOffset_ = parentGlobalOffset;
7498         UpdateTextFieldManager(Offset(parentGlobalOffset_.GetX(), parentGlobalOffset_.GetY()), frameRect_.Height());
7499         selectOverlay_->UpdateSelectOverlayOnAreaChanged();
7500     }
7501 }
7502 
7503 void RichEditorPattern::CloseSelectionMenu()
7504 {
7505     // used by sdk
7506     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "CloseSelectionMenu");
7507     CloseSelectOverlay();
7508 }
7509 
7510 void RichEditorPattern::CloseSelectOverlay()
7511 {
7512     // used by inner
7513     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "CloseSelectOverlay");
7514     selectOverlay_->CloseOverlay(true, CloseReason::CLOSE_REASON_NORMAL);
7515 }
7516 
7517 void RichEditorPattern::CloseHandleAndSelect()
7518 {
7519     selectOverlay_->CloseOverlay(false, CloseReason::CLOSE_REASON_DRAG_FLOATING);
7520     showSelect_ = false;
7521 }
7522 
7523 void RichEditorPattern::CalculateHandleOffsetAndShowOverlay(bool isUsingMouse)
7524 {
7525     auto globalOffset = GetGlobalOffset();
7526     if (!selectOverlay_->GetIsHandleMoving()) {
7527         textSelector_.ReverseTextSelector();
7528     }
7529     int32_t baseOffset = std::min(textSelector_.baseOffset, GetTextContentLength());
7530     int32_t destinationOffset = std::min(textSelector_.destinationOffset, GetTextContentLength());
7531     SizeF firstHandlePaintSize;
7532     SizeF secondHandlePaintSize;
7533     OffsetF firstHandleOffset;
7534     OffsetF secondHandleOffset;
7535     auto isSingleHandle = IsSingleHandle();
7536     selectOverlay_->SetIsSingleHandle(isSingleHandle);
7537     if (isSingleHandle) {
7538         auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
7539         // only show the second handle.
7540         secondHandlePaintSize = { SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), caretHeight };
7541         secondHandleOffset = caretOffset + globalOffset;
7542     } else {
7543         float startSelectHeight = 0.0f;
7544         float endSelectHeight = 0.0f;
7545         auto startOffset = CalcCursorOffsetByPosition(baseOffset, startSelectHeight, true, false);
7546         auto endOffset = CalcCursorOffsetByPosition(destinationOffset, endSelectHeight, false, false);
7547         firstHandlePaintSize = { SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), startSelectHeight };
7548         secondHandlePaintSize = { SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), endSelectHeight };
7549         firstHandleOffset = startOffset + globalOffset;
7550         secondHandleOffset = endOffset + globalOffset;
7551         firstHandleOffset.SetX(firstHandleOffset.GetX() - firstHandlePaintSize.Width() / 2.0f);
7552         secondHandleOffset.SetX(secondHandleOffset.GetX() - secondHandlePaintSize.Width() / 2.0f);
7553     }
7554     textSelector_.selectionBaseOffset = firstHandleOffset;
7555     textSelector_.selectionDestinationOffset = secondHandleOffset;
7556     textSelector_.firstHandle = RectF{ firstHandleOffset, firstHandlePaintSize };
7557     textSelector_.secondHandle = RectF{ secondHandleOffset, secondHandlePaintSize };
7558 }
7559 
7560 void RichEditorPattern::CalculateDefaultHandleHeight(float& height)
7561 {
7562 #ifdef ENABLE_ROSEN_BACKEND
7563     MeasureContext content;
7564     content.textContent = "a";
7565     content.fontSize = TEXT_DEFAULT_FONT_SIZE;
7566     auto fontweight = StringUtils::FontWeightToString(FontWeight::NORMAL);
7567     content.fontWeight = fontweight;
7568     height = std::max(static_cast<float>(RosenRenderCustomPaint::MeasureTextSizeInner(content).Height()), 0.0f);
7569 #endif
7570 }
7571 
7572 OffsetF RichEditorPattern::GetGlobalOffset() const
7573 {
7574     auto host = GetHost();
7575     CHECK_NULL_RETURN(host, OffsetF());
7576     auto pipeline = host->GetContext();
7577     CHECK_NULL_RETURN(pipeline, OffsetF());
7578     auto rootOffset = pipeline->GetRootRect().GetOffset();
7579     auto richEditorPaintOffset = host->GetPaintRectOffset();
7580     if (selectOverlay_->HasRenderTransform()) {
7581         richEditorPaintOffset = selectOverlay_->GetPaintOffsetWithoutTransform();
7582     }
7583     return richEditorPaintOffset - rootOffset;
7584 }
7585 
7586 bool RichEditorPattern::IsSingleHandle()
7587 {
7588     return GetTextContentLength() == 0 || !IsSelected();
7589 }
7590 
7591 bool RichEditorPattern::IsHandlesShow()
7592 {
7593     return selectOverlay_->IsBothHandlesShow();
7594 }
7595 
7596 void RichEditorPattern::ResetSelection()
7597 {
7598     bool selectNothing = textSelector_.SelectNothing();
7599     textSelector_.Update(-1, -1);
7600     if (!selectNothing) {
7601         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "ResetSelection");
7602         auto host = GetHost();
7603         CHECK_NULL_VOID(host);
7604         auto eventHub = host->GetEventHub<RichEditorEventHub>();
7605         CHECK_NULL_VOID(eventHub);
7606         auto textSelectInfo = GetSpansInfo(-1, -1, GetSpansMethod::ONSELECT);
7607         eventHub->FireOnSelect(&textSelectInfo);
7608         UpdateSelectionType(textSelectInfo);
7609         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
7610     }
7611 }
7612 
7613 bool RichEditorPattern::BetweenSelection(const Offset& globalOffset)
7614 {
7615     auto host = GetHost();
7616     CHECK_NULL_RETURN(host, false);
7617     auto offset = host->GetPaintRectOffset();
7618     auto localOffset = globalOffset - Offset(offset.GetX(), offset.GetY());
7619     if (selectOverlay_->HasRenderTransform()) {
7620         localOffset = ConvertGlobalToLocalOffset(globalOffset);
7621     }
7622     auto eventHub = host->GetEventHub<EventHub>();
7623     if (GreatNotEqual(textSelector_.GetTextEnd(), textSelector_.GetTextStart())) {
7624         // Determine if the pan location is in the selected area
7625         auto selectedRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
7626         auto panOffset = OffsetF(localOffset.GetX(), localOffset.GetY()) - GetTextRect().GetOffset() +
7627                          OffsetF(0.0, std::min(baselineOffset_, 0.0f));
7628         for (const auto& selectedRect : selectedRects) {
7629             if (selectedRect.IsInRegion(PointF(panOffset.GetX(), panOffset.GetY()))) {
7630                 return true;
7631             }
7632         }
7633     }
7634     return false;
7635 }
7636 
7637 bool RichEditorPattern::BetweenSelectedPosition(const Offset& globalOffset)
7638 {
7639     return copyOption_ != CopyOptions::None && BetweenSelection(globalOffset);
7640 }
7641 
7642 void RichEditorPattern::HandleSurfaceChanged(int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight)
7643 {
7644     if (newWidth != prevWidth || newHeight != prevHeight) {
7645         TextPattern::HandleSurfaceChanged(newWidth, newHeight, prevWidth, prevHeight);
7646         UpdateOriginIsMenuShow(false);
7647     }
7648     UpdateCaretInfoToController();
7649     previewLongPress_ = false;
7650     editingLongPress_ = false;
7651     if (magnifierController_) {
7652         magnifierController_->RemoveMagnifierFrameNode();
7653     }
7654 }
7655 
7656 void RichEditorPattern::HandleSurfacePositionChanged(int32_t posX, int32_t posY)
7657 {
7658     UpdateCaretInfoToController();
7659 }
7660 
7661 void RichEditorPattern::DumpInfo()
7662 {
7663     auto& dumpLog = DumpLog::GetInstance();
7664     if (customKeyboardBuilder_) {
7665         dumpLog.AddDesc(std::string("CustomKeyboard, Attached: ").append(std::to_string(isCustomKeyboardAttached_)));
7666     }
7667     auto host = GetHost();
7668     CHECK_NULL_VOID(host);
7669     auto context = host->GetContext();
7670     CHECK_NULL_VOID(context);
7671     auto richEditorTheme = context->GetTheme<RichEditorTheme>();
7672     CHECK_NULL_VOID(richEditorTheme);
7673     dumpLog.AddDesc(std::string("caret offset: ").append(GetCaretRect().GetOffset().ToString()));
7674     dumpLog.AddDesc(std::string("caret height: ")
7675             .append(std::to_string(NearZero(GetCaretRect().Height())
7676                                        ? richEditorTheme->GetDefaultCaretHeight().ConvertToPx()
7677                                        : GetCaretRect().Height())));
7678     dumpLog.AddDesc(std::string("text rect: ").append(richTextRect_.ToString()));
7679     dumpLog.AddDesc(std::string("content rect: ").append(contentRect_.ToString()));
7680     auto richEditorPaintOffset = host->GetPaintRectOffset();
7681     bool hasRenderTransform = selectOverlay_->HasRenderTransform();
7682     if (hasRenderTransform) {
7683         richEditorPaintOffset = selectOverlay_->GetPaintOffsetWithoutTransform();
7684     }
7685     dumpLog.AddDesc(std::string("hasRenderTransform: ").append(std::to_string(hasRenderTransform)));
7686     dumpLog.AddDesc(std::string("richEditorPaintOffset: ").append(richEditorPaintOffset.ToString()));
7687     auto selectOverlayInfo = selectOverlay_->GetSelectOverlayInfo();
7688     CHECK_NULL_VOID(selectOverlayInfo);
7689     dumpLog.AddDesc(std::string("selectOverlay info: ").append(selectOverlayInfo->ToString()));
7690     dumpLog.AddDesc(std::string("IsAIWrite: ").append(std::to_string(IsShowAIWrite())));
7691 }
7692 
7693 bool RichEditorPattern::HasFocus() const
7694 {
7695     auto focusHub = GetFocusHub();
7696     CHECK_NULL_RETURN(focusHub, false);
7697     return focusHub->IsCurrentFocus();
7698 }
7699 
7700 void RichEditorPattern::UpdateTextFieldManager(const Offset& offset, float height)
7701 {
7702     if (!HasFocus()) {
7703         return;
7704     }
7705     auto context = GetHost()->GetContext();
7706     CHECK_NULL_VOID(context);
7707     auto richEditorTheme = context->GetTheme<RichEditorTheme>();
7708     CHECK_NULL_VOID(richEditorTheme);
7709     auto textFieldManager = DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
7710     CHECK_NULL_VOID(textFieldManager);
7711     auto safeAreaManager = context->GetSafeAreaManager();
7712     CHECK_NULL_VOID(safeAreaManager);
7713     auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
7714     textFieldManager->SetClickPosition({ offset.GetX() + caretOffset.GetX(), offset.GetY() + caretOffset.GetY() });
7715     textFieldManager->SetHeight(NearZero(caretHeight)
7716                                     ? richEditorTheme->GetDefaultCaretHeight().ConvertToPx()
7717                                     : caretHeight);
7718     textFieldManager->SetClickPositionOffset(safeAreaManager->GetKeyboardOffset());
7719     textFieldManager->SetOnFocusTextField(WeakClaim(this));
7720     if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_FOURTEEN)) {
7721         textFieldManager->SetUsingCustomKeyboardAvoid(keyboardAvoidance_);
7722     }
7723     if (!isTextChange_) {
7724         return;
7725     }
7726     auto taskExecutor = context->GetTaskExecutor();
7727     CHECK_NULL_VOID(taskExecutor);
7728     taskExecutor->PostTask(
7729         [weak = WeakClaim(this)] {
7730             auto pattern = weak.Upgrade();
7731             CHECK_NULL_VOID(pattern);
7732             pattern->ScrollToSafeArea();
7733         },
7734         TaskExecutor::TaskType::UI, "ArkUIRichEditorScrollToSafeArea");
7735 }
7736 
7737 bool RichEditorPattern::IsDisabled() const
7738 {
7739     auto eventHub = GetHost()->GetEventHub<RichEditorEventHub>();
7740     CHECK_NULL_RETURN(eventHub, true);
7741     return !eventHub->IsEnabled();
7742 }
7743 
7744 void RichEditorPattern::MouseDoubleClickParagraphEnd(int32_t& index)
7745 {
7746     bool isMouseDoubleClick = caretUpdateType_ == CaretUpdateType::DOUBLE_CLICK && sourceType_ == SourceType::MOUSE;
7747     CHECK_NULL_VOID(isMouseDoubleClick);
7748     auto paragraphEndPos = GetParagraphEndPosition(index);
7749     auto paragraphBeginPos = GetParagraphBeginPosition(index);
7750     bool isBeginEqualEnd = paragraphBeginPos == paragraphEndPos;
7751     CHECK_NULL_VOID(!isBeginEqualEnd);
7752     if (index == paragraphEndPos) {
7753         index -= 1;
7754     }
7755 }
7756 
7757 void RichEditorPattern::AdjustSelectionExcludeSymbol(int32_t& start, int32_t& end)
7758 {
7759     AdjustSelectorForSymbol(start, HandleType::FIRST, SelectorAdjustPolicy::EXCLUDE);
7760     AdjustSelectorForSymbol(end, HandleType::SECOND, SelectorAdjustPolicy::EXCLUDE);
7761 }
7762 
7763 void RichEditorPattern::InitSelection(const Offset& pos)
7764 {
7765     auto [currentPosition, selectType] = JudgeSelectType(pos);
7766     switch (selectType) {
7767         case SelectType::SELECT_NOTHING:
7768             TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "select nothing currentPos=%{public}d", currentPosition);
7769             textSelector_.Update(currentPosition, currentPosition);
7770             return;
7771         case SelectType::SELECT_BACKWARD:
7772             currentPosition = std::max(0, currentPosition - 1);
7773             break;
7774         case SelectType::SELECT_FORWARD:
7775             break;
7776         default:
7777             TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "exception select type");
7778     }
7779     int32_t nextPosition = std::min(currentPosition + 1, GetTextContentLength());
7780     AdjustSelectionExcludeSymbol(currentPosition, nextPosition);
7781     if (!IsCustomSpanInCaretPos(currentPosition, true)) {
7782         AdjustWordSelection(currentPosition, nextPosition);
7783     }
7784     AdjustSelector(currentPosition, nextPosition);
7785     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "init select [%{public}d--%{public}d]", currentPosition, nextPosition);
7786     textSelector_.Update(currentPosition, nextPosition);
7787     if (IsSelectEmpty(currentPosition, nextPosition)) {
7788         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "select rect is empty, select nothing");
7789         textSelector_.Update(currentPosition, currentPosition);
7790     }
7791 }
7792 
7793 std::pair<int32_t, SelectType> RichEditorPattern::JudgeSelectType(const Offset& pos)
7794 {
7795     auto positionWithAffinity = paragraphs_.GetGlyphPositionAtCoordinate(pos);
7796     auto currentPosition = (GetTextContentLength() == 0) ? 0 : static_cast<int32_t>(positionWithAffinity.position_);
7797     auto selectType = SelectType::SELECT_NOTHING;
7798     CHECK_NULL_RETURN(GetTextContentLength() != 0, std::make_pair(currentPosition, selectType));
7799     bool isNeedSkipLineSeparator = !editingLongPress_ && IsSelectEmpty(currentPosition, currentPosition + 1);
7800     if (isNeedSkipLineSeparator && AdjustIndexSkipLineSeparator(currentPosition)) {
7801         return std::make_pair(currentPosition, SelectType::SELECT_BACKWARD);
7802     }
7803     auto height = paragraphs_.GetHeight();
7804     if (GreatNotEqual(pos.GetY(), height)) {
7805         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "touchPosY[%{public}f] > paragraphsHeight[%{public}f]", pos.GetY(), height);
7806         IF_TRUE(!editingLongPress_, selectType = SelectType::SELECT_BACKWARD);
7807         return std::make_pair(GetTextContentLength(), selectType);
7808     }
7809     TextAffinity currentAffinity = positionWithAffinity.affinity_;
7810     bool isTouchLineEnd = currentAffinity == TextAffinity::UPSTREAM && !IsTouchBeforeCaret(currentPosition, pos);
7811     if (editingLongPress_ && isTouchLineEnd) {
7812         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "touchLineEnd select nothing currentAffinity=%{public}d", currentAffinity);
7813         return std::make_pair(currentPosition, selectType);
7814     }
7815     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "currentPosition=%{public}d, currentAffinity=%{public}d",
7816         currentPosition, currentAffinity);
7817     selectType = (currentAffinity == TextAffinity::UPSTREAM) ? SelectType::SELECT_BACKWARD : SelectType::SELECT_FORWARD;
7818     return std::make_pair(currentPosition, selectType);
7819 }
7820 
7821 bool RichEditorPattern::IsSelectEmpty(int32_t start, int32_t end)
7822 {
7823     auto selectedRects = paragraphs_.GetRects(start, end);
7824     return selectedRects.empty() || (selectedRects.size() == 1 && NearZero((selectedRects[0].Width())));
7825 }
7826 
7827 bool RichEditorPattern::AdjustIndexSkipLineSeparator(int32_t& currentPosition)
7828 {
7829     std::wstringstream wss;
7830     for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
7831         auto span = *iter;
7832         auto content = StringUtils::ToWstring(span->content);
7833         wss << content;
7834         CHECK_NULL_BREAK(currentPosition > span->position);
7835     }
7836     auto contentText = wss.str();
7837     auto contentLength = static_cast<int32_t>(contentText.length());
7838     if (currentPosition > contentLength) {
7839         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "currentPosition=%{public}d but contentLength=%{public}d",
7840             currentPosition, contentLength);
7841         return false;
7842     }
7843     auto index = currentPosition - 1;
7844     while (index > 0) {
7845         CHECK_NULL_BREAK(contentText[index] == L'\n');
7846         index--;
7847     }
7848     if (index != currentPosition - 1) {
7849         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "skip lineSeparator %{public}d->%{public}d", currentPosition, index + 1);
7850         currentPosition = index + 1;
7851         return true;
7852     }
7853     return false;
7854 }
7855 
7856 void RichEditorPattern::HandleSelectOverlayWithOptions(const SelectionOptions& options)
7857 {
7858     if (options.menuPolicy == MenuPolicy::SHOW) {
7859         if (isMousePressed_ || sourceType_ == SourceType::MOUSE) {
7860             selectionMenuOffsetByMouse_ = selectionMenuOffsetClick_;
7861         }
7862         if (SelectOverlayIsOn()) {
7863             selectOverlay_->ProcessOverlay({.animation = true, .requestCode = REQUEST_RECREATE});
7864         } else {
7865             ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, IsSelectAll());
7866         }
7867     } else if (options.menuPolicy == MenuPolicy::HIDE) {
7868         if (SelectOverlayIsOn()) {
7869             CloseSelectOverlay();
7870         }
7871     }
7872 }
7873 
7874 bool RichEditorPattern::ResetOnInvalidSelection(int32_t start, int32_t end)
7875 {
7876     if (start < end) {
7877         return false;
7878     }
7879     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "SetSelection failed, the selected area is empty.");
7880     CloseSelectOverlay();
7881     ResetSelection();
7882     StartTwinkling();
7883     return true;
7884 }
7885 
7886 void RichEditorPattern::RefreshSelectOverlay(bool isMousePressed, bool selectedTypeChange)
7887 {
7888     if (isMousePressed && !selectedTypeChange) {
7889         return;
7890     }
7891     CloseSelectOverlay();
7892     auto responseType = static_cast<TextResponseType>(
7893         selectOverlayProxy_->GetSelectOverlayMangerInfo().menuInfo.responseType.value_or(0));
7894     ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, IsSelectAll(), responseType);
7895 }
7896 
7897 bool RichEditorPattern::IsShowHandle()
7898 {
7899     auto pipeline = PipelineBase::GetCurrentContext();
7900     CHECK_NULL_RETURN(pipeline, false);
7901     auto richEditorTheme = pipeline->GetTheme<RichEditorTheme>();
7902     CHECK_NULL_RETURN(richEditorTheme, false);
7903     return !richEditorTheme->IsRichEditorShowHandle();
7904 }
7905 
7906 void RichEditorPattern::UpdateSelectionInfo(int32_t start, int32_t end)
7907 {
7908     UpdateSelectionType(GetSpansInfo(start, end, GetSpansMethod::ONSELECT));
7909     auto selectOverlayInfo = selectOverlay_->GetSelectOverlayInfo();
7910     textResponseType_ = selectOverlayInfo
7911                         ? static_cast<TextResponseType>(selectOverlayInfo->menuInfo.responseType.value_or(0))
7912                         : TextResponseType::LONG_PRESS;
7913     if (IsShowHandle() && !selectOverlay_->IsUsingMouse()) {
7914         ResetIsMousePressed();
7915         sourceType_ = SourceType::TOUCH;
7916     } else {
7917         isMousePressed_ = true;
7918     }
7919 }
7920 
7921 void RichEditorPattern::SetSelection(int32_t start, int32_t end, const std::optional<SelectionOptions>& options,
7922     bool isForward)
7923 {
7924     bool hasFocus = HasFocus();
7925     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "range=[%{public}d,%{public}d], hasFocus=%{public}d, isForward=%{public}d",
7926         start, end, hasFocus, isForward);
7927     CHECK_NULL_VOID(hasFocus);
7928     if (IsPreviewTextInputting()) {
7929         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "SetSelection failed for previewText inputting");
7930         return;
7931     }
7932     if (start == -1 && end == -1) {
7933         start = 0;
7934         end = GetTextContentLength();
7935     } else {
7936         start = std::clamp(start, 0, GetTextContentLength());
7937         end = std::clamp(end, 0, GetTextContentLength());
7938     }
7939     if (ResetOnInvalidSelection(start, end)) {
7940         return;
7941     }
7942     UpdateSelector(start, end);
7943 
7944     if (textSelector_.IsValid() && !textSelector_.StartEqualToDest()) {
7945         StopTwinkling();
7946         if (start != textSelector_.GetTextStart() || end != textSelector_.GetTextEnd()) {
7947             FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
7948         }
7949     }
7950     SetCaretPosition(isForward ? textSelector_.GetTextStart() : textSelector_.GetTextEnd());
7951     MoveCaretToContentRect();
7952     CalculateHandleOffsetAndShowOverlay();
7953     UpdateSelectionInfo(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
7954     ProcessOverlayOnSetSelection(options);
7955     auto host = GetHost();
7956     CHECK_NULL_VOID(host);
7957     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
7958 }
7959 
7960 void RichEditorPattern::ProcessOverlayOnSetSelection(const std::optional<SelectionOptions>& options)
7961 {
7962     if (!IsShowHandle()) {
7963         CloseSelectOverlay();
7964     } else if (!options.has_value() || options.value().menuPolicy == MenuPolicy::DEFAULT) {
7965         selectOverlay_->ProcessOverlay({ .menuIsShow = selectOverlay_->IsCurrentMenuVisibile(),
7966             .animation = true, .requestCode = REQUEST_RECREATE });
7967     } else if (options.value().menuPolicy == MenuPolicy::HIDE) {
7968         if (selectOverlay_->IsUsingMouse()) {
7969             CloseSelectOverlay();
7970         } else {
7971             selectOverlay_->ProcessOverlay({ .menuIsShow = false, .animation = true });
7972         }
7973     } else if (options.value().menuPolicy == MenuPolicy::SHOW) {
7974         if (selectOverlay_->IsUsingMouse() || sourceType_ == SourceType::MOUSE) {
7975             selectionMenuOffsetByMouse_ = selectionMenuOffsetClick_;
7976         }
7977         selectOverlay_->ProcessOverlay({ .animation = true, .requestCode = REQUEST_RECREATE });
7978     }
7979 }
7980 
7981 void RichEditorPattern::BindSelectionMenu(TextResponseType type, TextSpanType richEditorType,
7982     std::function<void()>& menuBuilder, std::function<void(int32_t, int32_t)>& onAppear,
7983     std::function<void()>& onDisappear)
7984 {
7985     TextPattern::BindSelectionMenu(richEditorType, type, menuBuilder, onAppear, onDisappear);
7986 }
7987 
7988 RefPtr<NodePaintMethod> RichEditorPattern::CreateNodePaintMethod()
7989 {
7990     if (!contentMod_) {
7991         contentMod_ = MakeRefPtr<RichEditorContentModifier>(textStyle_, &paragraphs_, WeakClaim(this));
7992     }
7993     if (!overlayMod_) {
7994         auto scrollBar = GetScrollBar();
7995         if (scrollBar) {
7996             auto scrollBarModifier = AceType::MakeRefPtr<ScrollBarOverlayModifier>();
7997             scrollBarModifier->SetRect(scrollBar->GetActiveRect());
7998             scrollBarModifier->SetPositionMode(scrollBar->GetPositionMode());
7999             SetScrollBarOverlayModifier(scrollBarModifier);
8000         }
8001         SetEdgeEffect(EdgeEffect::FADE, GetAlwaysEnabled());
8002         SetEdgeEffect();
8003         overlayMod_ = AceType::MakeRefPtr<RichEditorOverlayModifier>(
8004             WeakClaim(this), GetScrollBarOverlayModifier(), GetScrollEdgeEffect());
8005     }
8006 
8007     if (GetIsCustomFont()) {
8008         contentMod_->SetIsCustomFont(true);
8009     }
8010     return MakeRefPtr<RichEditorPaintMethod>(WeakClaim(this), &paragraphs_, baselineOffset_, contentMod_, overlayMod_);
8011 }
8012 
8013 int32_t RichEditorPattern::GetHandleIndex(const Offset& offset) const
8014 {
8015     return paragraphs_.GetIndex(Offset(offset.GetX() + contentRect_.GetX() - richTextRect_.GetX(),
8016         offset.GetY() + contentRect_.GetY() - richTextRect_.GetY()));
8017 }
8018 
8019 std::vector<RectF> RichEditorPattern::GetTextBoxes()
8020 {
8021     auto selectedRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
8022     std::vector<RectF> res;
8023     res.reserve(selectedRects.size());
8024     for (auto&& rect : selectedRects) {
8025         res.emplace_back(rect);
8026     }
8027     if (!res.empty() && paragraphs_.IsSelectLineHeadAndUseLeadingMargin(textSelector_.GetTextStart())) {
8028         // To make drag screenshot include LeadingMarginPlaceholder when not single line
8029         if (res.front().GetY() != res.back().GetY()) {
8030             res.front().SetLeft(0.0f);
8031         }
8032     }
8033     return res;
8034 }
8035 
8036 float RichEditorPattern::GetLineHeight() const
8037 {
8038     auto selectedRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
8039     CHECK_NULL_RETURN(selectedRects.size(), 0.0f);
8040     return selectedRects.front().Height();
8041 }
8042 
8043 size_t RichEditorPattern::GetLineCount() const
8044 {
8045     return paragraphs_.GetLineCount();
8046 }
8047 
8048 TextLineMetrics RichEditorPattern::GetLineMetrics(int32_t lineNumber)
8049 {
8050     if (lineNumber < 0 || GetLineCount() == 0 || static_cast<uint32_t>(lineNumber) > GetLineCount() - 1) {
8051         TAG_LOGE(AceLogTag::ACE_RICH_TEXT,
8052                 "GetLineMetrics failed, lineNumber not between 0 and max lines:%{public}d", lineNumber);
8053         return TextLineMetrics();
8054     }
8055     auto lineMetrics = paragraphs_.GetLineMetrics(lineNumber);
8056     const auto& textRect = GetTextRect();
8057     lineMetrics.x += textRect.GetX();
8058     lineMetrics.y += textRect.GetY();
8059     lineMetrics.baseline += textRect.GetY();
8060     return lineMetrics;
8061 }
8062 
8063 std::vector<ParagraphManager::TextBox> RichEditorPattern::GetRectsForRange(
8064     int32_t start, int32_t end, RectHeightStyle heightStyle, RectWidthStyle widthStyle)
8065 {
8066     if (start < 0 || end < 0 || start > end) {
8067         return {};
8068     }
8069     std::vector<ParagraphManager::TextBox> textBoxes =
8070         paragraphs_.GetRectsForRange(start, end, heightStyle, widthStyle);
8071     const auto& textRect = richTextRect_;
8072     std::vector<ParagraphManager::TextBox> adjustedTextBoxes;
8073     for (auto& textBox : textBoxes) {
8074         ParagraphManager::TextBox adjustedTextBox = textBox;
8075         adjustedTextBox.rect_.SetLeft(textBox.rect_.Left() + textRect.Left());
8076         adjustedTextBox.rect_.SetTop(textBox.rect_.Top() + textRect.Top());
8077         adjustedTextBoxes.push_back(adjustedTextBox);
8078     }
8079     return adjustedTextBoxes;
8080 }
8081 
8082 float RichEditorPattern::GetLetterSpacing() const
8083 {
8084     auto selectedRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
8085     CHECK_NULL_RETURN(!selectedRects.empty(), 0.0f);
8086     return selectedRects.front().Width();
8087 }
8088 
8089 void RichEditorPattern::UpdateSelectMenuInfo(SelectMenuInfo& menuInfo)
8090 {
8091     bool isSupportCameraInput = false;
8092 #if defined(ENABLE_STANDARD_INPUT)
8093     auto inputMethod = MiscServices::InputMethodController::GetInstance();
8094     isSupportCameraInput =
8095         inputMethod && inputMethod->IsInputTypeSupported(MiscServices::InputType::CAMERA_INPUT);
8096 #endif
8097     menuInfo.showCameraInput = !IsSelected() && isSupportCameraInput && !customKeyboardBuilder_;
8098 }
8099 
8100 bool RichEditorPattern::IsShowSelectMenuUsingMouse()
8101 {
8102     auto pipeline = PipelineContext::GetCurrentContext();
8103     CHECK_NULL_RETURN(pipeline, false);
8104     auto selectOverlayManager = pipeline->GetSelectOverlayManager();
8105     CHECK_NULL_RETURN(selectOverlayManager, false);
8106     return selectOverlayManager->GetSelectOverlayInfo().isUsingMouse;
8107 }
8108 
8109 RectF RichEditorPattern::GetCaretRect() const
8110 {
8111     RectF rect;
8112     CHECK_NULL_RETURN(overlayMod_, rect);
8113     auto richEditorOverlay = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
8114     CHECK_NULL_RETURN(richEditorOverlay, rect);
8115     rect.SetOffset(richEditorOverlay->GetCaretOffset());
8116     rect.SetHeight(richEditorOverlay->GetCaretHeight());
8117     return rect;
8118 }
8119 
8120 void RichEditorPattern::ScrollToSafeArea() const
8121 {
8122     auto host = GetHost();
8123     CHECK_NULL_VOID(host);
8124     auto pipeline = host->GetContext();
8125     CHECK_NULL_VOID(pipeline);
8126     if (pipeline->UsingCaretAvoidMode()) {
8127         // using TriggerAvoidOnCaretChange instead in CaretAvoidMode
8128         return;
8129     }
8130     auto textFieldManager = DynamicCast<TextFieldManagerNG>(pipeline->GetTextFieldManager());
8131     CHECK_NULL_VOID(textFieldManager);
8132     textFieldManager->ScrollTextFieldToSafeArea();
8133 }
8134 
8135 void RichEditorPattern::InitScrollablePattern()
8136 {
8137     auto layoutProperty = GetLayoutProperty<RichEditorLayoutProperty>();
8138     CHECK_NULL_VOID(layoutProperty);
8139     auto barState = layoutProperty->GetDisplayModeValue(DisplayMode::AUTO);
8140     CHECK_NULL_VOID(!barDisplayMode_ || barDisplayMode_.value() != barState);
8141     barDisplayMode_ = barState;
8142     if (!GetScrollableEvent()) {
8143         AddScrollEvent();
8144     }
8145     SetAxis(Axis::VERTICAL);
8146     // not paint bar in overlay modifier when state=off
8147     if (barState != DisplayMode::AUTO) {
8148         barState = DisplayMode::ON;
8149     }
8150     SetScrollBar(barState);
8151     auto scrollBar = GetScrollBar();
8152     if (scrollBar) {
8153         auto host = GetHost();
8154         CHECK_NULL_VOID(host);
8155         auto pipeline = host->GetContext();
8156         CHECK_NULL_VOID(pipeline);
8157         auto richEditorTheme = pipeline->GetTheme<RichEditorTheme>();
8158         CHECK_NULL_VOID(richEditorTheme);
8159         scrollBar->SetMinHeight(richEditorTheme->GetScrollbarMinHeight());
8160     }
8161     if (overlayMod_) {
8162         UpdateScrollBarOffset();
8163     }
8164     auto& paddingProperty = layoutProperty->GetPaddingProperty();
8165     if (paddingProperty) {
8166         auto offsetY = paddingProperty->top.has_value() ? paddingProperty->top->GetDimension().ConvertToPx() : 0.0f;
8167         auto offsetX = paddingProperty->left.has_value() ? paddingProperty->left->GetDimension().ConvertToPx() : 0.0f;
8168         richTextRect_.SetOffset(OffsetF(offsetX, offsetY));
8169     }
8170 }
8171 
8172 void RichEditorPattern::ProcessInnerPadding()
8173 {
8174     auto context = PipelineBase::GetCurrentContext();
8175     CHECK_NULL_VOID(context);
8176     auto theme = context->GetTheme<RichEditorTheme>();
8177     CHECK_NULL_VOID(theme);
8178     auto host = GetHost();
8179     CHECK_NULL_VOID(host);
8180     auto layoutProperty = host->GetLayoutProperty<RichEditorLayoutProperty>();
8181     CHECK_NULL_VOID(layoutProperty);
8182     auto themePadding = theme->GetPadding();
8183     auto& paddingProp = layoutProperty->GetPaddingProperty();
8184     auto left = !paddingProp ? CalcLength(themePadding.Left()).GetDimension()
8185                              : paddingProp->left.value_or(CalcLength(themePadding.Left())).GetDimension();
8186     auto top = !paddingProp ? CalcLength(themePadding.Top()).GetDimension()
8187                             : paddingProp->top.value_or(CalcLength(themePadding.Top())).GetDimension();
8188     auto bottom = !paddingProp ? CalcLength(themePadding.Bottom()).GetDimension()
8189                                : paddingProp->bottom.value_or(CalcLength(themePadding.Bottom())).GetDimension();
8190     auto right = !paddingProp ? CalcLength(themePadding.Right()).GetDimension()
8191                               : paddingProp->right.value_or(CalcLength(themePadding.Right())).GetDimension();
8192     PaddingProperty paddings;
8193     paddings.top = NG::CalcLength(top);
8194     paddings.bottom = NG::CalcLength(bottom);
8195     paddings.left = NG::CalcLength(left);
8196     paddings.right = NG::CalcLength(right);
8197     layoutProperty->UpdatePadding(paddings);
8198 }
8199 
8200 void RichEditorPattern::UpdateScrollStateAfterLayout(bool shouldDisappear)
8201 {
8202     bool hasTextOffsetChanged = false;
8203     if (GreatNotEqual(richTextRect_.GetY(), contentRect_.GetY())) {
8204         auto offset = richTextRect_.GetOffset();
8205         offset.AddY(contentRect_.GetY() - richTextRect_.GetY());
8206         richTextRect_.SetOffset(offset);
8207         hasTextOffsetChanged = true;
8208     }
8209     if (GreatNotEqual(richTextRect_.Height(), contentRect_.Height()) &&
8210         LessNotEqual(richTextRect_.Bottom(), contentRect_.Bottom())) {
8211         auto offset = richTextRect_.GetOffset();
8212         offset.AddY(contentRect_.Bottom() - richTextRect_.Bottom());
8213         richTextRect_.SetOffset(offset);
8214         hasTextOffsetChanged = true;
8215     }
8216     if (LessOrEqual(richTextRect_.Height(), contentRect_.Height()) &&
8217         LessNotEqual(richTextRect_.GetY(), contentRect_.GetY())) {
8218         richTextRect_.SetOffset(contentRect_.GetOffset());
8219         hasTextOffsetChanged = true;
8220     }
8221     if (hasTextOffsetChanged) {
8222         UpdateChildrenOffset();
8223     }
8224     StopScrollable();
8225     CheckScrollable();
8226     if (overlayMod_) {
8227         UpdateScrollBarOffset();
8228     }
8229     auto scrollBar = GetScrollBar();
8230     CHECK_NULL_VOID(scrollBar);
8231 
8232     if (isFirstCallOnReady_) {
8233         isFirstCallOnReady_ = false;
8234         scrollBar->ScheduleDisappearDelayTask();
8235         return;
8236     }
8237     if (shouldDisappear) {
8238         scrollBar->ScheduleDisappearDelayTask();
8239     }
8240 }
8241 
8242 bool RichEditorPattern::OnScrollCallback(float offset, int32_t source)
8243 {
8244     if (source == SCROLL_FROM_START) {
8245         auto scrollBar = GetScrollBar();
8246         if (scrollBar) {
8247             scrollBar->PlayScrollBarAppearAnimation();
8248         }
8249         if (SelectOverlayIsOn()) {
8250             selectOverlay_->HideMenu(true);
8251         }
8252         UIObserverHandler::GetInstance().NotifyScrollEventStateChange(
8253             AceType::WeakClaim(this), ScrollEventType::SCROLL_START);
8254         return true;
8255     }
8256     if (IsReachedBoundary(offset)) {
8257         return false;
8258     }
8259     auto newOffset = MoveTextRect(offset);
8260     MoveFirstHandle(newOffset);
8261     MoveSecondHandle(newOffset);
8262     return true;
8263 }
8264 
8265 float RichEditorPattern::GetCrossOverHeight() const
8266 {
8267     if (!keyboardAvoidance_ || !contentChange_ || AceApplicationInfo::GetInstance().
8268         GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_FOURTEEN)) {
8269         return 0.0f;
8270     }
8271     auto host = GetHost();
8272     CHECK_NULL_RETURN(host, 0.0f);
8273     auto pipeline = host->GetContext();
8274     CHECK_NULL_RETURN(pipeline, 0.0f);
8275     auto rootHeight = pipeline->GetRootHeight();
8276     auto keyboardY = rootHeight - pipeline->GetSafeAreaManager()->GetKeyboardInset().Length();
8277     if (GreatOrEqual(keyboardY, rootHeight)) {
8278         return 0.0f;
8279     }
8280     float height = contentRect_.Bottom();
8281     float frameY = parentGlobalOffset_.GetY() + contentRect_.GetY();
8282     float bottom = frameY + height;
8283     auto crossOverHeight = bottom - keyboardY;
8284     if (LessOrEqual(crossOverHeight, 0.0f)) {
8285         return 0.0f;
8286     }
8287     return crossOverHeight;
8288 }
8289 
8290 float RichEditorPattern::MoveTextRect(float offset)
8291 {
8292     auto keyboardOffset = GetCrossOverHeight();
8293     if (GreatNotEqual(richTextRect_.Height(), contentRect_.Height() - keyboardOffset)) {
8294         if (GreatNotEqual(richTextRect_.GetY() + offset, contentRect_.GetY())) {
8295             offset = contentRect_.GetY() - richTextRect_.GetY();
8296         } else if (LessNotEqual(richTextRect_.Bottom() + offset, contentRect_.Bottom() - keyboardOffset)) {
8297             offset = contentRect_.Bottom() - keyboardOffset - richTextRect_.Bottom();
8298         }
8299     } else if (!NearEqual(richTextRect_.GetY(), contentRect_.GetY())) {
8300         offset = contentRect_.GetY() - richTextRect_.GetY();
8301     } else {
8302         return 0.0f;
8303     }
8304     if (NearEqual(offset, 0.0f)) {
8305         return offset;
8306     }
8307     scrollOffset_ = richTextRect_.GetY() + offset;
8308     richTextRect_.SetOffset(OffsetF(richTextRect_.GetX(), scrollOffset_));
8309     UpdateScrollBarOffset();
8310     UpdateChildrenOffset();
8311     if (auto host = GetHost(); host) {
8312         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
8313     }
8314     return offset;
8315 }
8316 
8317 void RichEditorPattern::MoveFirstHandle(float offset)
8318 {
8319     if (SelectOverlayIsOn() && !NearEqual(offset, 0.0f)) {
8320         textSelector_.selectionBaseOffset.AddY(offset);
8321         auto firstHandleOffset = textSelector_.firstHandle.GetOffset();
8322         firstHandleOffset.AddY(offset);
8323         textSelector_.firstHandle.SetOffset(firstHandleOffset);
8324         selectOverlay_->UpdateFirstHandleOffset();
8325     }
8326 }
8327 
8328 void RichEditorPattern::MoveSecondHandle(float offset)
8329 {
8330     if (SelectOverlayIsOn() && !NearEqual(offset, 0.0f)) {
8331         textSelector_.selectionDestinationOffset.AddY(offset);
8332         auto secondHandleOffset = textSelector_.secondHandle.GetOffset();
8333         secondHandleOffset.AddY(offset);
8334         textSelector_.secondHandle.SetOffset(secondHandleOffset);
8335         selectOverlay_->UpdateSecondHandleOffset();
8336     }
8337 }
8338 
8339 void RichEditorPattern::SetNeedMoveCaretToContentRect()
8340 {
8341     CHECK_NULL_VOID(isRichEditorInit_);
8342     needMoveCaretToContentRect_ = true;
8343 }
8344 
8345 void RichEditorPattern::MoveCaretToContentRect()
8346 {
8347     auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
8348     MoveCaretToContentRect(caretOffset, caretHeight);
8349 }
8350 
8351 void RichEditorPattern::MoveCaretToContentRect(const OffsetF& caretOffset, float caretHeight)
8352 {
8353     auto keyboardOffset = GetCrossOverHeight();
8354     auto contentRect = GetTextContentRect();
8355     auto textRect = GetTextRect();
8356     if (LessOrEqual(textRect.Height(), contentRect.Height() - keyboardOffset) || isShowPlaceholder_) {
8357         return;
8358     }
8359     if (LessNotEqual(contentRect.GetSize().Height(), caretHeight) &&
8360         !NearEqual(caretOffset.GetY() + caretHeight, contentRect.Bottom() - keyboardOffset)) {
8361         OnScrollCallback(contentRect.Bottom() - keyboardOffset - caretOffset.GetY() - caretHeight, SCROLL_FROM_NONE);
8362     }
8363     if (LessNotEqual(contentRect.GetSize().Height(), caretHeight)) {
8364         return;
8365     }
8366     if (LessNotEqual(caretOffset.GetY(), contentRect.GetY())) {
8367         if (LessOrEqual(caretOffset.GetX(), GetTextRect().GetX())) {
8368             OnScrollCallback(contentRect.GetY() - caretOffset.GetY() + caretHeight, SCROLL_FROM_NONE);
8369         } else {
8370             OnScrollCallback(contentRect.GetY() - caretOffset.GetY(), SCROLL_FROM_NONE);
8371         }
8372     } else if (GreatNotEqual(caretOffset.GetY() + caretHeight, contentRect.Bottom() - keyboardOffset)) {
8373         auto distance = contentRect.Bottom() - keyboardOffset - caretOffset.GetY() - caretHeight -
8374             CARET_BOTTOM_DISTANCE.ConvertToPx();
8375         OnScrollCallback(distance, SCROLL_FROM_NONE);
8376     }
8377 }
8378 
8379 void RichEditorPattern::MoveCaretToContentRect(float offset, int32_t source)
8380 {
8381     float caretHeight = 0.0f;
8382     auto caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight);
8383     auto keyboardOffset = GetCrossOverHeight();
8384     auto contentRect = GetTextContentRect();
8385     auto distance = contentRect.Bottom() - keyboardOffset - caretOffset.GetY() - caretHeight - offset;
8386     OnScrollCallback(distance, source);
8387 }
8388 
8389 bool RichEditorPattern::IsCaretInContentArea()
8390 {
8391     float caretHeight = 0.0f;
8392     auto caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight);
8393     auto keyboardOffset = GetCrossOverHeight();
8394     auto contentRect = GetTextContentRect();
8395     return GreatNotEqual(caretOffset.GetY() + caretHeight, contentRect.GetY())
8396         && LessNotEqual(caretOffset.GetY(), contentRect.Bottom() - keyboardOffset);
8397 }
8398 
8399 void RichEditorPattern::UpdateScrollBarOffset()
8400 {
8401     if (!GetScrollBar() && !GetScrollBarProxy()) {
8402         return;
8403     }
8404     Size size(frameRect_.Width(), frameRect_.Height());
8405     auto verticalGap = frameRect_.Height() - contentRect_.Height();
8406     UpdateScrollBarRegion(
8407         contentRect_.GetY() - richTextRect_.GetY(), richTextRect_.Height() + verticalGap, size, Offset(0.0, 0.0));
8408     auto tmpHost = GetHost();
8409     CHECK_NULL_VOID(tmpHost);
8410     tmpHost->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
8411 }
8412 
8413 void RichEditorPattern::OnScrollEndCallback()
8414 {
8415     auto scrollBar = GetScrollBar();
8416     if (scrollBar) {
8417         scrollBar->ScheduleDisappearDelayTask();
8418     }
8419     CHECK_NULL_VOID(!selectOverlay_->GetIsHandleMoving());
8420     if (IsSelectAreaVisible()) {
8421         selectOverlay_->UpdateMenuOffset();
8422         selectOverlay_->ShowMenu();
8423     }
8424     if (AnimateStoped()) {
8425         UIObserverHandler::GetInstance().NotifyScrollEventStateChange(
8426             AceType::WeakClaim(this), ScrollEventType::SCROLL_STOP);
8427     }
8428 }
8429 
8430 bool RichEditorPattern::IsSelectAreaVisible()
8431 {
8432     auto host = GetHost();
8433     CHECK_NULL_RETURN(host, false);
8434     auto pipeline = host->GetContext();
8435     CHECK_NULL_RETURN(pipeline, false);
8436     auto safeAreaManager = pipeline->GetSafeAreaManager();
8437     CHECK_NULL_RETURN(safeAreaManager, false);
8438     auto keyboardInsert = safeAreaManager->GetKeyboardInset();
8439     auto selectArea = GetSelectArea();
8440 
8441     return !selectArea.IsEmpty() && LessNotEqual(selectArea.Top(), keyboardInsert.start);
8442 }
8443 
8444 bool RichEditorPattern::IsReachedBoundary(float offset)
8445 {
8446     auto keyboardOffset = GetCrossOverHeight();
8447     return (NearEqual(richTextRect_.GetY(), contentRect_.GetY()) && GreatNotEqual(offset, 0.0f)) ||
8448            (NearEqual(richTextRect_.GetY() + richTextRect_.Height(),
8449                 contentRect_.GetY() + contentRect_.Height() - keyboardOffset) &&
8450                LessNotEqual(offset, 0.0f));
8451 }
8452 
8453 void RichEditorPattern::CheckScrollable()
8454 {
8455     auto gestureHub = GetGestureEventHub();
8456     CHECK_NULL_VOID(gestureHub);
8457     scrollable_ = GetTextContentLength() > 0 && GreatNotEqual(richTextRect_.Height(), contentRect_.Height());
8458     SetScrollEnabled(scrollable_);
8459 }
8460 
8461 void RichEditorPattern::UpdateChildrenOffset()
8462 {
8463     auto host = GetHost();
8464     CHECK_NULL_VOID(host);
8465     std::vector<int32_t> placeholderIndex;
8466     for (const auto& child : spans_) {
8467         if (!child) {
8468             continue;
8469         }
8470         if (AceType::InstanceOf<ImageSpanItem>(child) || AceType::InstanceOf<PlaceholderSpanItem>(child)) {
8471             placeholderIndex.emplace_back(child->placeholderIndex);
8472         }
8473     }
8474     if (spans_.empty() || placeholderIndex.empty()) {
8475         return;
8476     }
8477     size_t index = 0;
8478     std::vector<RectF> rectsForPlaceholders = paragraphs_.GetPlaceholderRects();
8479     auto childrenNodes = host->GetChildren();
8480     auto textOffset = GetTextRect().GetOffset();
8481     for (const auto& child : childrenNodes) {
8482         auto childNode = AceType::DynamicCast<FrameNode>(child);
8483         if (!childNode) {
8484             continue;
8485         }
8486         if (!(childNode->GetPattern<ImagePattern>() || childNode->GetPattern<PlaceholderSpanPattern>())) {
8487             continue;
8488         }
8489         if (isSpanStringMode_) {
8490             auto imageSpanNode = AceType::DynamicCast<ImageSpanNode>(child);
8491             if (imageSpanNode && imageSpanNode->GetSpanItem()) {
8492                 index = static_cast<uint32_t>(imageSpanNode->GetSpanItem()->placeholderIndex);
8493             }
8494         }
8495         if (index >= rectsForPlaceholders.size()) {
8496             break;
8497         }
8498         auto rect = rectsForPlaceholders.at(index);
8499         auto geometryNode = childNode->GetGeometryNode();
8500         if (geometryNode) {
8501             geometryNode->SetMarginFrameOffset(textOffset + OffsetF(rect.Left(), rect.Top()));
8502             childNode->ForceSyncGeometryNode();
8503             childNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
8504         }
8505         ++index;
8506     }
8507 }
8508 
8509 void RichEditorPattern::AutoScrollByEdgeDetection(AutoScrollParam param, OffsetF offset, EdgeDetectionStrategy strategy)
8510 {
8511     if (NearEqual(prevAutoScrollOffset_.GetY(), offset.GetY())) {
8512         return;
8513     }
8514     prevAutoScrollOffset_ = offset;
8515     auto contentRect = GetTextContentRect();
8516     auto isDragging = param.autoScrollEvent == AutoScrollEvent::DRAG;
8517     float edgeThreshold = isDragging ? AUTO_SCROLL_DRAG_EDGE_DISTANCE.ConvertToPx()
8518                                      : AUTO_SCROLL_EDGE_DISTANCE.ConvertToPx();
8519     auto maxHeight = isDragging ? frameRect_.Height() : contentRect.Height();
8520     if (GreatNotEqual(edgeThreshold * 2, maxHeight)) {
8521         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AutoScrollByEdgeDetection: hot area height is great than max height.");
8522         return;
8523     }
8524     float topEdgeThreshold = isDragging ? edgeThreshold : edgeThreshold + contentRect.GetY();
8525     float bottomThreshold = isDragging ? frameRect_.Height() - edgeThreshold : contentRect.Bottom() - edgeThreshold;
8526     if (param.autoScrollEvent == AutoScrollEvent::HANDLE) {
8527         auto handleTopOffset = offset;
8528         auto handleBottomOffset = OffsetF(offset.GetX(), offset.GetY() + param.handleRect.Height());
8529         if (GreatNotEqual(handleBottomOffset.GetY(), bottomThreshold)) {
8530             param.offset = bottomThreshold - handleBottomOffset.GetY();
8531             ScheduleAutoScroll(param);
8532         } else if (LessNotEqual(handleTopOffset.GetY(), topEdgeThreshold)) {
8533             param.offset = topEdgeThreshold - handleTopOffset.GetY();
8534             ScheduleAutoScroll(param);
8535         } else {
8536             StopAutoScroll();
8537         }
8538         return;
8539     }
8540     // drag and mouse
8541     if (GreatNotEqual(offset.GetY(), bottomThreshold)) {
8542         param.offset = isDragging ? -CalcDragSpeed(bottomThreshold, frameRect_.Height(), offset.GetY())
8543                                   : bottomThreshold - offset.GetY();
8544         ScheduleAutoScroll(param);
8545     } else if (LessNotEqual(offset.GetY(), topEdgeThreshold)) {
8546         param.offset = isDragging ? CalcDragSpeed(topEdgeThreshold, 0, offset.GetY())
8547                                   : topEdgeThreshold - offset.GetY();
8548         ScheduleAutoScroll(param);
8549     } else {
8550         StopAutoScroll();
8551     }
8552 }
8553 
8554 float RichEditorPattern::CalcDragSpeed(float hotAreaStart, float hotAreaEnd, float point)
8555 {
8556     auto distanceRatio = (point - hotAreaStart) / (hotAreaEnd - hotAreaStart);
8557     auto speedFactor = Curves::SHARP->MoveInternal(distanceRatio);
8558     return ((MAX_DRAG_SCROLL_SPEED * speedFactor) / TIME_UNIT) * AUTO_SCROLL_INTERVAL;
8559 }
8560 
8561 void RichEditorPattern::ScheduleAutoScroll(AutoScrollParam param)
8562 {
8563     if (GreatNotEqual(param.offset, 0.0f) && IsReachTop()) {
8564         return;
8565     }
8566     if (LessNotEqual(param.offset, 0.0f) && IsReachBottom()) {
8567         return;
8568     }
8569     auto host = GetHost();
8570     CHECK_NULL_VOID(host);
8571     auto context = host->GetContext();
8572     CHECK_NULL_VOID(context);
8573     auto taskExecutor = context->GetTaskExecutor();
8574     CHECK_NULL_VOID(taskExecutor);
8575     if (param.isFirstRun_) {
8576         param.isFirstRun_ = false;
8577         currentScrollParam_ = param;
8578         if (isAutoScrollRunning_) {
8579             return;
8580         }
8581     }
8582     autoScrollTask_.Reset([weak = WeakClaim(this)]() {
8583         auto client = weak.Upgrade();
8584         CHECK_NULL_VOID(client);
8585         client->OnAutoScroll(client->currentScrollParam_);
8586         if (client->IsReachTop() || client->IsReachBottom()) {
8587             client->StopAutoScroll();
8588         }
8589     });
8590     isAutoScrollRunning_ = true;
8591     taskExecutor->PostDelayedTask(autoScrollTask_, TaskExecutor::TaskType::UI, AUTO_SCROLL_INTERVAL,
8592         "ArkUIRichEditorScheduleAutoScroll");
8593 }
8594 
8595 void RichEditorPattern::OnAutoScroll(AutoScrollParam param)
8596 {
8597     if (param.showScrollbar) {
8598         auto scrollBar = GetScrollBar();
8599         if (scrollBar) {
8600             scrollBar->PlayScrollBarAppearAnimation();
8601         }
8602         param.showScrollbar = false;
8603     }
8604 
8605     if (param.autoScrollEvent == AutoScrollEvent::HANDLE) {
8606         auto newOffset = MoveTextRect(param.offset);
8607         if (param.isFirstHandle) {
8608             MoveSecondHandle(newOffset);
8609         } else {
8610             MoveFirstHandle(newOffset);
8611         }
8612         selectOverlay_->OnHandleMove(param.handleRect, param.isFirstHandle);
8613         if (NearEqual(newOffset, 0.0f)) {
8614             return;
8615         }
8616         ScheduleAutoScroll(param);
8617         return;
8618     }
8619 
8620     if (param.autoScrollEvent == AutoScrollEvent::DRAG) {
8621         auto newOffset = MoveTextRect(param.offset);
8622         if (NearEqual(newOffset, 0.0f)) {
8623             return;
8624         }
8625         ScheduleAutoScroll(param);
8626         return;
8627     }
8628 
8629     if (param.autoScrollEvent == AutoScrollEvent::MOUSE) {
8630         auto newOffset = MoveTextRect(param.offset);
8631         auto textOffset =
8632             Offset(param.eventOffset.GetX() - GetTextRect().GetX(), param.eventOffset.GetY() - GetTextRect().GetY());
8633         int32_t extend = paragraphs_.GetIndex(textOffset);
8634         UpdateSelector(textSelector_.baseOffset, extend);
8635         SetCaretPosition(std::max(textSelector_.baseOffset, extend));
8636         if (NearEqual(newOffset, 0.0f)) {
8637             return;
8638         }
8639         ScheduleAutoScroll(param);
8640     }
8641 }
8642 
8643 void RichEditorPattern::StopAutoScroll(bool hideBarImmediately)
8644 {
8645     isAutoScrollRunning_ = false;
8646     autoScrollTask_.Cancel();
8647     prevAutoScrollOffset_ = OffsetF(0.0f, 0.0f);
8648     auto scrollBar = GetScrollBar();
8649     if (scrollBar && hideBarImmediately) {
8650         scrollBar->PlayScrollBarDisappearAnimation();
8651     }
8652 }
8653 
8654 bool RichEditorPattern::NeedAiAnalysis(
8655     const CaretUpdateType targeType, const int32_t pos, const int32_t& spanStart, const std::string& content)
8656 {
8657     if (spanStart < 0) {
8658         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "NeedAiAnalysis -spanStart:%{public}d, return!", spanStart);
8659         return false;
8660     }
8661 
8662     if (!InputAIChecker::NeedAIAnalysis(content, targeType, lastClickTimeStamp_ - lastAiPosTimeStamp_)) {
8663         return false;
8664     }
8665 
8666     if (IsClickBoundary(pos) && targeType == CaretUpdateType::PRESSED) {
8667         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "NeedAiAnalysis IsClickBoundary, return!");
8668         return false;
8669     }
8670     EmojiRelation relation = GetEmojiRelation(pos);
8671     if (relation == EmojiRelation::IN_EMOJI || relation == EmojiRelation::MIDDLE_EMOJI ||
8672         relation == EmojiRelation::BEFORE_EMOJI) {
8673         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "NeedAiAnalysis emoji relation=%{public}d, return!", relation);
8674         return false;
8675     }
8676     return true;
8677 }
8678 
8679 void RichEditorPattern::AdjustCursorPosition(int32_t& pos)
8680 {
8681     // the rich text has some spans, the pos is belong to the whole richtext content, should use (pos - spanStarint)
8682     int32_t spanStart = -1;
8683     // get the span text by the position, maybe text is empty
8684     std::string content = GetPositionSpansText(pos, spanStart);
8685 
8686     if (NeedAiAnalysis(CaretUpdateType::PRESSED, pos, spanStart, content)) {
8687         int32_t aiPos = pos - spanStart;
8688         DataDetectorMgr::GetInstance().AdjustCursorPosition(aiPos, content, lastAiPosTimeStamp_, lastClickTimeStamp_);
8689         if (aiPos < 0) {
8690             return;
8691         }
8692         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "get ai pos:%{public}d--spanStart%{public}d", aiPos, spanStart);
8693         pos = aiPos + spanStart;
8694     }
8695 }
8696 
8697 bool RichEditorPattern::AdjustWordSelection(int32_t& start, int32_t& end)
8698 {
8699     // the rich text has some spans, the pos is belong to the whole richtext content, should use (pos - spanStarint)
8700     int32_t spanStart = -1;
8701     // get the span text by the position, maybe text is empty
8702     std::string content = GetPositionSpansText(start, spanStart);
8703     if (NeedAiAnalysis(CaretUpdateType::DOUBLE_CLICK, start, spanStart, content)) {
8704         int32_t aiPosStart = start - spanStart;
8705         int32_t aiPosEnd = end - spanStart;
8706         DataDetectorMgr::GetInstance().AdjustWordSelection(aiPosStart, content, aiPosStart, aiPosEnd);
8707         if (aiPosStart < 0 || aiPosEnd < 0) {
8708             return false;
8709         }
8710 
8711         start = std::min(aiPosStart + spanStart, GetTextContentLength());
8712         end = std::min(aiPosEnd + spanStart, GetTextContentLength());
8713         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "get ai selector [%{public}d--%{public}d]", start, end);
8714         return true;
8715     }
8716     return false;
8717 }
8718 
8719 void RichEditorPattern::AdjustPlaceholderSelection(int32_t& start, int32_t& end, const Offset& touchPos)
8720 {
8721     CHECK_NULL_VOID(!spans_.empty());
8722     if (!IsTouchBeforeCaret(start, touchPos)) {
8723         return;
8724     }
8725     auto it = std::find_if(spans_.begin(), spans_.end(), [start](const RefPtr<SpanItem>& spanItem) {
8726         return spanItem->position == start;
8727     });
8728     if (it != spans_.end()) {
8729         // adjust selection if touch right of image or placeholder
8730         auto spanIndex = std::distance(spans_.begin(), it);
8731         auto spanNodeBefore = DynamicCast<FrameNode>(GetChildByIndex(spanIndex));
8732         if (spanNodeBefore && (spanNodeBefore->GetTag() == V2::IMAGE_ETS_TAG ||
8733             spanNodeBefore->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG)) {
8734             end = start;
8735             --start;
8736             TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "get placeholder selector [%{public}d--%{public}d]", start, end);
8737         }
8738     }
8739 }
8740 
8741 bool RichEditorPattern::IsTouchAtLineEnd(int32_t caretPos, const Offset& textOffset)
8742 {
8743     auto positionWithAffinity = paragraphs_.GetGlyphPositionAtCoordinate(textOffset);
8744     TextAffinity currentAffinity = positionWithAffinity.affinity_;
8745     return currentAffinity == TextAffinity::UPSTREAM && !IsTouchBeforeCaret(caretPos, textOffset);
8746 }
8747 
8748 bool RichEditorPattern::IsTouchBeforeCaret(int32_t caretPos, const Offset& textOffset) {
8749     CHECK_NULL_RETURN(!spans_.empty(), false);
8750     float selectLineHeight = 0.0f;
8751     OffsetF caretOffsetUp = paragraphs_.ComputeCursorOffset(caretPos, selectLineHeight);
8752     auto needAdjustRect = RectF{ 0, caretOffsetUp.GetY(), caretOffsetUp.GetX(), selectLineHeight };
8753     return needAdjustRect.IsInRegion(PointF{ textOffset.GetX(), textOffset.GetY() });
8754 }
8755 
8756 bool RichEditorPattern::IsClickBoundary(const int32_t position)
8757 {
8758     if (InputAIChecker::IsSingleClickAtBoundary(position, GetTextContentLength())) {
8759         return true;
8760     }
8761 
8762     float height = 0;
8763     auto handleOffset = CalcCursorOffsetByPosition(position, height);
8764     if (InputAIChecker::IsMultiClickAtBoundary(handleOffset, TextPattern::GetTextRect())) {
8765         return true;
8766     }
8767     return false;
8768 }
8769 
8770 std::string RichEditorPattern::GetPositionSpansText(int32_t position, int32_t& startSpan)
8771 {
8772     int32_t start = position - AI_TEXT_RANGE_LEFT;
8773     int32_t end = position + AI_TEXT_RANGE_RIGHT;
8774 
8775     start = std::clamp(start, 0, GetTextContentLength());
8776     end = std::clamp(end, 0, GetTextContentLength());
8777     AdjustSelector(start, end);
8778     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "caret=%{public}d, range=[%{public}d,%{public}d]", position, start, end);
8779 
8780     // get all the spans between start and end, then filter the valid text
8781     auto infos = GetSpansInfo(start, end, GetSpansMethod::ONSELECT);
8782     if (infos.GetSelection().resultObjects.empty()) {
8783         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "get spans text is null pos:%{public}d,return", position);
8784         return "";
8785     }
8786     auto list = infos.GetSelection().resultObjects;
8787 
8788     std::stringstream sstream;
8789     for (const auto& obj : list) {
8790         if (obj.type == SelectSpanType::TYPEIMAGE || obj.type == SelectSpanType::TYPESYMBOLSPAN) {
8791             if (obj.spanPosition.spanRange[0] == position) {
8792                 startSpan = -1;
8793                 return "";
8794             } else if (obj.spanPosition.spanRange[1] <= position) {
8795                 sstream.str("");
8796                 startSpan = -1;
8797             } else {
8798                 break;
8799             }
8800         } else if (obj.type == SelectSpanType::TYPESPAN) {
8801             if (startSpan < 0) {
8802                 startSpan = obj.spanPosition.spanRange[0] + obj.offsetInSpan[0];
8803             }
8804             // we should use the wide string deal to avoid crash
8805             auto wideText = StringUtils::ToWstring(obj.valueString);
8806             int32_t textLen = static_cast<int32_t>(wideText.length());
8807             if (obj.offsetInSpan[0] < textLen && obj.offsetInSpan[1] <= textLen) {
8808                 sstream << StringUtils::ToString(
8809                     wideText.substr(obj.offsetInSpan[0], obj.offsetInSpan[1] - obj.offsetInSpan[0]));
8810             } else {
8811                 TAG_LOGE(AceLogTag::ACE_RICH_TEXT,
8812                     "wideText substr out of range, wideText.length = %{public}zu, substr = [%{public}d, %{public}d]",
8813                     wideText.length(), obj.offsetInSpan[0], obj.offsetInSpan[1] - obj.offsetInSpan[0]);
8814             }
8815         }
8816     }
8817 
8818     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "get spans text ret spanStart:%{public}d", startSpan);
8819     return sstream.str();
8820 }
8821 
8822 void RichEditorPattern::HandleOnCameraInput()
8823 {
8824 #if defined(ENABLE_STANDARD_INPUT)
8825     if (richEditTextChangeListener_ == nullptr) {
8826         richEditTextChangeListener_ = new OnTextChangedListenerImpl(WeakClaim(this));
8827     }
8828     auto inputMethod = MiscServices::InputMethodController::GetInstance();
8829     if (!inputMethod) {
8830         return;
8831     }
8832     StartTwinkling();
8833 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
8834     if (imeShown_) {
8835         inputMethod->StartInputType(MiscServices::InputType::CAMERA_INPUT);
8836     } else {
8837         HandleOnEditChanged(true);
8838         auto optionalTextConfig = GetMiscTextConfig();
8839         CHECK_NULL_VOID(optionalTextConfig.has_value());
8840         MiscServices::TextConfig textConfig = optionalTextConfig.value();
8841         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnCameraInput set calling window id is : %{public}u",
8842             textConfig.windowId);
8843 #ifdef WINDOW_SCENE_SUPPORTED
8844         auto systemWindowId = GetSCBSystemWindowId();
8845         if (systemWindowId) {
8846             TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Rich windowId From %{public}u to %{public}u.", textConfig.windowId,
8847                 systemWindowId);
8848             textConfig.windowId = systemWindowId;
8849         }
8850 #endif
8851         inputMethod->Attach(richEditTextChangeListener_, false, textConfig);
8852         inputMethod->StartInputType(MiscServices::InputType::CAMERA_INPUT);
8853         inputMethod->ShowTextInput();
8854     }
8855     CloseSelectOverlay();
8856 #endif
8857 #endif
8858 }
8859 
8860 RefPtr<FocusHub> RichEditorPattern::GetFocusHub() const
8861 {
8862     auto host = GetHost();
8863     CHECK_NULL_RETURN(host, nullptr);
8864     auto focusHub = host->GetOrCreateFocusHub();
8865     return focusHub;
8866 }
8867 
8868 void RichEditorPattern::HandleCursorOnDragMoved(const RefPtr<NotifyDragEvent>& notifyDragEvent)
8869 {
8870     auto host = GetHost();
8871     CHECK_NULL_VOID(host);
8872     if (HasFocus()) {
8873         if (!isCursorAlwaysDisplayed_) {
8874             isCursorAlwaysDisplayed_ = true;
8875             StartTwinkling();
8876         }
8877         if (SystemProperties::GetDebugEnabled()) {
8878             TAG_LOGD(AceLogTag::ACE_TEXT_FIELD,
8879                 "In OnDragMoved, the cursor has always Displayed in the textField, id:%{public}d", host->GetId());
8880         }
8881         return;
8882     }
8883     TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
8884         "In OnDragMoved, the dragging node is moving in the richEditor, id:%{public}d", host->GetId());
8885     auto focusHub = GetFocusHub();
8886     CHECK_NULL_VOID(focusHub);
8887     isOnlyRequestFocus_ = true;
8888     focusHub->RequestFocusImmediately();
8889     if (focusHub->IsCurrentFocus()) {
8890         ShowCaretWithoutTwinkling();
8891     }
8892 };
8893 
8894 void RichEditorPattern::HandleCursorOnDragLeaved(const RefPtr<NotifyDragEvent>& notifyDragEvent)
8895 {
8896     auto host = GetHost();
8897     CHECK_NULL_VOID(host);
8898     TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
8899         "In OnDragLeaved, the dragging node has left from richEditor, id:%{public}d", host->GetId());
8900     auto focusHub = GetFocusHub();
8901     CHECK_NULL_VOID(focusHub);
8902     focusHub->LostFocusToViewRoot();
8903     StopTwinkling();
8904 };
8905 
8906 void RichEditorPattern::HandleCursorOnDragEnded(const RefPtr<NotifyDragEvent>& notifyDragEvent)
8907 {
8908     auto host = GetHost();
8909     CHECK_NULL_VOID(host);
8910     auto focusHub = GetFocusHub();
8911     CHECK_NULL_VOID(focusHub);
8912     StopAutoScroll();
8913     if (!isCursorAlwaysDisplayed_) {
8914         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "In OnDragEnded,"
8915             " the released location is not in the current richEditor, id:%{public}d", host->GetId());
8916         focusHub->LostFocus();
8917         StopTwinkling();
8918         return;
8919     }
8920     TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
8921         "In OnDragEnded, the released location is in the current richEditor, id:%{public}d", host->GetId());
8922     focusHub->LostFocusToViewRoot();
8923     isCursorAlwaysDisplayed_ = false;
8924     StopTwinkling();
8925 };
8926 
8927 void RichEditorPattern::HandleOnDragStatusCallback(
8928     const DragEventType& dragEventType, const RefPtr<NotifyDragEvent>& notifyDragEvent)
8929 {
8930     ScrollablePattern::HandleOnDragStatusCallback(dragEventType, notifyDragEvent);
8931     switch (dragEventType) {
8932         case DragEventType::MOVE:
8933             HandleCursorOnDragMoved(notifyDragEvent);
8934             break;
8935         case DragEventType::LEAVE:
8936             HandleCursorOnDragLeaved(notifyDragEvent);
8937             break;
8938         case DragEventType::DROP:
8939             HandleCursorOnDragEnded(notifyDragEvent);
8940             break;
8941         default:
8942             break;
8943     }
8944 }
8945 bool RichEditorPattern::CanStartAITask()
8946 {
8947     return TextPattern::CanStartAITask() && !isEditing_ && !isShowPlaceholder_;
8948 }
8949 
8950 bool RichEditorPattern::NeedShowAIDetect()
8951 {
8952     return TextPattern::NeedShowAIDetect() && !isEditing_ && !isShowPlaceholder_;
8953 }
8954 
8955 void RichEditorPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
8956 {
8957     /* no fixed attr below, just return */
8958     if (filter.IsFastFilter()) {
8959         return;
8960     }
8961     json->PutExtAttr("enableDataDetector", textDetectEnable_ ? "true" : "false", filter);
8962     json->PutExtAttr("dataDetectorConfig", dataDetectorAdapter_->textDetectConfigStr_.c_str(), filter);
8963     json->PutExtAttr("placeholder", GetPlaceHolderInJson().c_str(), filter);
8964     json->PutExtAttr("bindSelectionMenu", GetBindSelectionMenuInJson().c_str(), filter);
8965 }
8966 
8967 void RichEditorPattern::FillPreviewMenuInJson(const std::unique_ptr<JsonValue>& jsonValue) const
8968 {
8969     CHECK_NULL_VOID(jsonValue && oneStepDragParam_);
8970     auto jsonItem = JsonUtil::Create(true);
8971     jsonItem->Put("spanType", static_cast<int32_t>(TextSpanType::IMAGE));
8972     jsonItem->Put("responseType", static_cast<int32_t>(TextResponseType::LONG_PRESS));
8973     jsonItem->Put("menuType", static_cast<int32_t>(SelectionMenuType::PREVIEW_MENU));
8974     jsonValue->Put(jsonItem);
8975 }
8976 
8977 std::string RichEditorPattern::GetPlaceHolderInJson() const
8978 {
8979     auto host = GetHost();
8980     CHECK_NULL_RETURN(host, "");
8981     auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
8982     bool hasPlaceHolder = layoutProperty && layoutProperty->HasPlaceholder()
8983         && !layoutProperty->GetPlaceholder().value().empty();
8984     CHECK_NULL_RETURN(hasPlaceHolder, "");
8985     auto jsonValue = JsonUtil::Create(true);
8986     jsonValue->Put("value", layoutProperty->GetPlaceholderValue("").c_str());
8987     auto jsonFont = JsonUtil::Create(true);
8988     jsonFont->Put("size", GetFontSizeInJson(layoutProperty->GetPlaceholderFontSize()).c_str());
8989     jsonFont->Put("weight", GetFontWeightInJson(layoutProperty->GetPlaceholderFontWeight()).c_str());
8990     jsonFont->Put("family", GetFontFamilyInJson(layoutProperty->GetPlaceholderFontFamily()).c_str());
8991     jsonFont->Put("style", GetFontStyleInJson(layoutProperty->GetPlaceholderItalicFontStyle()).c_str());
8992     auto jsonStyle = JsonUtil::Create(true);
8993     jsonStyle->Put("font", jsonFont->ToString().c_str());
8994     jsonStyle->Put("fontColor", GetTextColorInJson(layoutProperty->GetPlaceholderTextColor()).c_str());
8995     jsonValue->Put("style", jsonStyle->ToString().c_str());
8996     return StringUtils::RestoreBackslash(jsonValue->ToString());
8997 }
8998 
8999 std::string RichEditorPattern::GetTextColorInJson(const std::optional<Color>& value) const
9000 {
9001     CHECK_NULL_RETURN(!value, value->ColorToString());
9002     auto host = GetHost();
9003     CHECK_NULL_RETURN(host, "");
9004     auto pipeline = host->GetContext();
9005     CHECK_NULL_RETURN(pipeline, "");
9006     auto richEditorTheme = pipeline->GetTheme<RichEditorTheme>();
9007     CHECK_NULL_RETURN(richEditorTheme, "");
9008     Color textColor = richEditorTheme->GetTextStyle().GetTextColor();
9009     return textColor.ColorToString();
9010 }
9011 
9012 void RichEditorPattern::GetCaretMetrics(CaretMetricsF& caretCaretMetric)
9013 {
9014     float caretHeight = 0.0f;
9015     OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight);
9016     auto host = GetHost();
9017     CHECK_NULL_VOID(host);
9018     auto offset = host->GetPaintRectOffset();
9019     caretOffset += offset;
9020     caretCaretMetric.offset = caretOffset;
9021     caretCaretMetric.height = caretHeight;
9022 }
9023 
9024 void RichEditorPattern::OnVirtualKeyboardAreaChanged()
9025 {
9026     CHECK_NULL_VOID(SelectOverlayIsOn());
9027     float selectLineHeight = 0.0f;
9028     textSelector_.selectionBaseOffset.SetX(
9029         CalcCursorOffsetByPosition(textSelector_.GetStart(), selectLineHeight).GetX());
9030     textSelector_.selectionDestinationOffset.SetX(
9031         CalcCursorOffsetByPosition(textSelector_.GetEnd(), selectLineHeight).GetX());
9032     CreateHandles();
9033 }
9034 
9035 void RichEditorPattern::ResetDragOption()
9036 {
9037     auto gestureEventHub = GetGestureEventHub();
9038     CHECK_NULL_VOID(gestureEventHub);
9039     if (gestureEventHub->GetIsTextDraggable()) {
9040         CloseSelectOverlay();
9041         ResetSelection();
9042     }
9043 }
9044 
9045 RectF RichEditorPattern::GetSelectArea()
9046 {
9047     RectF rect;
9048     auto paintOffset = selectOverlay_->GetPaintOffsetWithoutTransform();
9049     auto selectRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
9050     auto contentRect = contentRect_;
9051     contentRect.SetOffset(contentRect.GetOffset() + paintOffset);
9052     auto host = GetHost();
9053     CHECK_NULL_RETURN(host, rect);
9054     auto parent = host->GetAncestorNodeOfFrame();
9055     contentRect = GetVisibleContentRect(parent, contentRect);
9056     if (selectRects.empty()) {
9057         CHECK_NULL_RETURN(overlayMod_, rect);
9058         auto richEditorOverlay = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
9059         CHECK_NULL_RETURN(richEditorOverlay, rect);
9060         auto caretHeight = richEditorOverlay->GetCaretHeight();
9061         auto caretOffset = richEditorOverlay->GetCaretOffset();
9062         if (isShowPlaceholder_) {
9063             auto [offset, preferredHeight] = CalculateEmptyValueCaretRect();
9064             caretOffset = offset;
9065         }
9066         auto caretWidth = Dimension(1.5f, DimensionUnit::VP).ConvertToPx();
9067         auto selectRect = RectF(caretOffset + paintOffset, SizeF(caretWidth, caretHeight));
9068         return selectRect.IntersectRectT(contentRect);
9069     }
9070     auto frontRect = selectRects.front();
9071     auto backRect = selectRects.back();
9072     RectF res;
9073     if (GreatNotEqual(backRect.Bottom(), frontRect.Bottom())) {
9074         res.SetRect(contentRect_.GetX() + paintOffset.GetX(),
9075             frontRect.GetY() + richTextRect_.GetY() + paintOffset.GetY(), contentRect_.Width(),
9076             backRect.Bottom() - frontRect.Top());
9077     } else {
9078         res.SetRect(frontRect.GetX() + richTextRect_.GetX() + paintOffset.GetX(),
9079             frontRect.GetY() + richTextRect_.GetY() + paintOffset.GetY(), backRect.Right() - frontRect.Left(),
9080             backRect.Bottom() - frontRect.Top());
9081     }
9082     return res.IntersectRectT(contentRect);
9083 }
9084 
9085 bool RichEditorPattern::IsTouchInFrameArea(const PointF& touchPoint)
9086 {
9087     auto host = GetHost();
9088     CHECK_NULL_RETURN(host, false);
9089     auto viewPort = RectF(parentGlobalOffset_, frameRect_.GetSize());
9090     auto parent = host->GetAncestorNodeOfFrame();
9091     viewPort = GetVisibleContentRect(parent, viewPort);
9092     return viewPort.IsInRegion(touchPoint);
9093 }
9094 
9095 bool RichEditorPattern::SetPlaceholder(std::vector<std::list<RefPtr<SpanItem>>>& spanItemList)
9096 {
9097     if (!spans_.empty()) {
9098         isShowPlaceholder_ = false;
9099         return false;
9100     }
9101     auto host = GetHost();
9102     CHECK_NULL_RETURN(host, false);
9103     auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
9104     CHECK_NULL_RETURN(layoutProperty, false);
9105     if (!layoutProperty->HasPlaceholder() || layoutProperty->GetPlaceholder().value().empty()) {
9106         isShowPlaceholder_ = false;
9107         return false;
9108     }
9109     auto placeholderValue = layoutProperty->GetPlaceholder().value();
9110     auto* stack = ViewStackProcessor::GetInstance();
9111     CHECK_NULL_RETURN(stack, false);
9112     auto nodeId = stack->ClaimNodeId();
9113     auto placeholderNode = SpanNode::GetOrCreateSpanNode(nodeId);
9114     CHECK_NULL_RETURN(placeholderNode, false);
9115     if (layoutProperty->HasPlaceholderFontSize()) {
9116         placeholderNode->UpdateFontSize(layoutProperty->GetPlaceholderFontSize().value());
9117     }
9118     if (layoutProperty->HasPlaceholderFontWeight()) {
9119         placeholderNode->UpdateFontWeight(layoutProperty->GetPlaceholderFontWeight().value());
9120     }
9121     if (layoutProperty->HasPlaceholderFontFamily()) {
9122         placeholderNode->UpdateFontFamily(layoutProperty->GetPlaceholderFontFamily().value());
9123     }
9124     if (layoutProperty->HasPlaceholderItalicFontStyle()) {
9125         placeholderNode->UpdateItalicFontStyle(layoutProperty->GetPlaceholderItalicFontStyle().value());
9126     }
9127     if (layoutProperty->HasPlaceholderTextColor()) {
9128         placeholderNode->UpdateTextColor(layoutProperty->GetPlaceholderTextColor().value());
9129     } else {
9130         auto theme = GetTheme<RichEditorTheme>();
9131         placeholderNode->UpdateTextColor(theme ? theme->GetPlaceholderColor() : Color());
9132     }
9133 
9134     auto spanItem = placeholderNode->GetSpanItem();
9135     CHECK_NULL_RETURN(spanItem, false);
9136     spanItem->content = placeholderValue;
9137     spanItemList.clear();
9138     spanItemList.push_back({ { {spanItem} } });
9139     isShowPlaceholder_ = true;
9140     return true;
9141 }
9142 
9143 std::string RichEditorPattern::GetPlaceHolder() const
9144 {
9145     auto host = GetHost();
9146     CHECK_NULL_RETURN(host, "");
9147     auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
9148     CHECK_NULL_RETURN(layoutProperty, "");
9149     return layoutProperty->GetPlaceholderValue("");
9150 }
9151 
9152 Color RichEditorPattern::GetCaretColor()
9153 {
9154     if (caretColor_.has_value()) {
9155         return caretColor_.value();
9156     }
9157     auto host = GetHost();
9158     CHECK_NULL_RETURN(host, SYSTEM_CARET_COLOR);
9159     auto pipeline = host->GetContext();
9160     CHECK_NULL_RETURN(pipeline, SYSTEM_CARET_COLOR);
9161     auto richEditorTheme = pipeline->GetTheme<RichEditorTheme>();
9162     CHECK_NULL_RETURN(richEditorTheme, SYSTEM_CARET_COLOR);
9163     return richEditorTheme->GetCaretColor();
9164 }
9165 
9166 Color RichEditorPattern::GetSelectedBackgroundColor()
9167 {
9168     Color selectedBackgroundColor;
9169     if (selectedBackgroundColor_.has_value()) {
9170         selectedBackgroundColor = selectedBackgroundColor_.value();
9171     } else {
9172         auto host = GetHost();
9173         CHECK_NULL_RETURN(host, SYSTEM_SELECT_BACKGROUND_COLOR);
9174         auto pipeline = host->GetContext();
9175         CHECK_NULL_RETURN(pipeline, SYSTEM_SELECT_BACKGROUND_COLOR);
9176         auto richEditorTheme = pipeline->GetTheme<RichEditorTheme>();
9177         CHECK_NULL_RETURN(richEditorTheme, SYSTEM_SELECT_BACKGROUND_COLOR);
9178         selectedBackgroundColor = richEditorTheme->GetSelectedBackgroundColor();
9179     }
9180     // Alpha == 255 Means completely opaque
9181     if (selectedBackgroundColor.GetAlpha() == COLOR_OPAQUE) {
9182         selectedBackgroundColor = selectedBackgroundColor.ChangeOpacity(DEFAILT_OPACITY);
9183     }
9184     return selectedBackgroundColor;
9185 }
9186 
9187 void RichEditorPattern::HandleOnDragDropStyledString(const RefPtr<OHOS::Ace::DragEvent>& event)
9188 {
9189     CHECK_NULL_VOID(event);
9190     auto data = event->GetData();
9191     CHECK_NULL_VOID(data);
9192     auto arr = UdmfClient::GetInstance()->GetSpanStringRecord(data);
9193     if (!arr.empty()) {
9194         auto spanStr = SpanString::DecodeTlv(arr);
9195         if (!spanStr->GetSpanItems().empty()) {
9196             if (isSpanStringMode_) {
9197                 HandleOnDragInsertStyledString(spanStr);
9198                 return;
9199             }
9200             AddSpanByPasteData(spanStr);
9201             return;
9202         }
9203     }
9204 
9205     auto records = UdmfClient::GetInstance()->GetPlainTextRecords(data);
9206     if (records.empty()) {
9207         return;
9208     }
9209     std::string str;
9210     for (const auto& record : records) {
9211         str += record;
9212     }
9213     if (str.empty()) {
9214         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "text is empty.");
9215         return;
9216     }
9217     if (isSpanStringMode_) {
9218         InsertValueInStyledString(str);
9219     } else {
9220         HandleOnDragDropTextOperation(str, isDragSponsor_);
9221     }
9222 }
9223 
9224 void RichEditorPattern::HandleOnDragDrop(const RefPtr<OHOS::Ace::DragEvent>& event)
9225 {
9226     auto host = GetHost();
9227     CHECK_NULL_VOID(host);
9228     auto eventHub = host->GetEventHub<RichEditorEventHub>();
9229     CHECK_NULL_VOID(eventHub);
9230     TextCommonEvent textCommonEvent;
9231     if (textCommonEvent.IsPreventDefault()) {
9232         CloseSelectOverlay();
9233         ResetSelection();
9234         StartTwinkling();
9235         return;
9236     }
9237     HandleOnDragDropStyledString(event);
9238     if (textSelector_.IsValid()) {
9239         CloseSelectOverlay();
9240         ResetSelection();
9241     }
9242     auto focusHub = GetHost()->GetOrCreateFocusHub();
9243     CHECK_NULL_VOID(focusHub);
9244     if (focusHub->IsCurrentFocus()) {
9245         StartTwinkling();
9246     }
9247 }
9248 
9249 void RichEditorPattern::DeleteForward(int32_t currentPosition, int32_t length)
9250 {
9251     RichEditorDeleteValue info;
9252     info.SetOffset(currentPosition);
9253     info.SetRichEditorDeleteDirection(RichEditorDeleteDirection::FORWARD);
9254     info.SetLength(length);
9255     CalcDeleteValueObj(currentPosition, length, info);
9256     DeleteByDeleteValueInfo(info);
9257 }
9258 
9259 int32_t RichEditorPattern::HandleOnDragDeleteForward()
9260 {
9261     int32_t allDelLength = 0;
9262     SelectionInfo textSelectInfo = GetSpansInfo(dragRange_.first, dragRange_.second, GetSpansMethod::ONSELECT);
9263     std::list<ResultObject> dragResultObjects = textSelectInfo.GetSelection().resultObjects;
9264     for (auto ri = dragResultObjects.rbegin(); ri != dragResultObjects.rend(); ++ri) {
9265         if (SelectSpanType::TYPESPAN == ri->type || (SelectSpanType::TYPEIMAGE == ri->type && ri->valueString != " ")) {
9266             int32_t spanStart = ri->offsetInSpan[RichEditorSpanRange::RANGESTART];
9267             int32_t spanEnd = ri->offsetInSpan[RichEditorSpanRange::RANGEEND];
9268             int32_t reStart = ri->spanPosition.spanRange[RichEditorSpanRange::RANGESTART];
9269             int32_t delStart = reStart;
9270             if (spanStart > 0) {
9271                 delStart += spanStart;
9272             }
9273             int32_t delLength = spanEnd - spanStart;
9274             DeleteForward(delStart, delLength);
9275             allDelLength += delLength;
9276         }
9277     }
9278     return allDelLength;
9279 }
9280 
9281 void RichEditorPattern::HandleOnDragDropTextOperation(const std::string& insertValue, bool isDeleteSelect)
9282 {
9283     if (!isDeleteSelect) {
9284         InsertValueByOperationType(insertValue, OperationType::DRAG);
9285         return;
9286     }
9287     int32_t currentPosition = caretPosition_;
9288     int32_t strLength = static_cast<int32_t>(StringUtils::ToWstring(insertValue).length());
9289     OperationRecord record;
9290     record.addText = insertValue;
9291     record.beforeCaretPosition = dragRange_.first;
9292     RichEditorChangeValue changeValue;
9293     CHECK_NULL_VOID(BeforeChangeText(changeValue, record, RecordType::DRAG));
9294     if (currentPosition < dragRange_.first) {
9295         InsertValueByOperationType(insertValue, OperationType::DRAG);
9296         dragRange_.first += strLength;
9297         dragRange_.second += strLength;
9298         HandleOnDragDeleteForward();
9299     } else if (currentPosition > dragRange_.second) {
9300         InsertValueByOperationType(insertValue, OperationType::DRAG);
9301         int32_t delLength = HandleOnDragDeleteForward();
9302         caretPosition_ -= delLength;
9303     }
9304 
9305     AfterContentChange(changeValue);
9306 }
9307 
9308 void RichEditorPattern::UndoDrag(const OperationRecord& record)
9309 {
9310     if (!record.addText.has_value() || record.deleteCaretPostion == -1) {
9311         return;
9312     }
9313     const std::string& str = record.addText.value();
9314     int32_t length = static_cast<int32_t>(StringUtils::ToWstring(str).length());
9315     DeleteForward(record.beforeCaretPosition, length);
9316 
9317     caretPosition_ = record.deleteCaretPostion;
9318     InsertValueOperation(str, nullptr, OperationType::DEFAULT);
9319 }
9320 
9321 void RichEditorPattern::RedoDrag(const OperationRecord& record)
9322 {
9323     if (!record.addText.has_value() || record.deleteCaretPostion == -1) {
9324         return;
9325     }
9326     RichEditorChangeValue changeValue;
9327     CHECK_NULL_VOID(BeforeChangeText(changeValue, record, RecordType::REDO));
9328     const std::string& str = record.addText.value();
9329     int32_t length = static_cast<int32_t>(StringUtils::ToWstring(str).length());
9330     DeleteForward(record.deleteCaretPostion, length);
9331     caretPosition_ = record.beforeCaretPosition;
9332     InsertValueOperation(str, nullptr, OperationType::DRAG);
9333     AfterContentChange(changeValue);
9334 }
9335 
9336 void RichEditorPattern::HandleOnDragInsertValueOperation(const std::string& insertValue)
9337 {
9338     InsertValueByOperationType(insertValue, OperationType::DRAG);
9339 }
9340 
9341 void RichEditorPattern::HandleOnDragInsertValue(const std::string& insertValue)
9342 {
9343     OperationRecord record;
9344     record.beforeCaretPosition = caretPosition_ + moveLength_;
9345     if (textSelector_.IsValid()) {
9346         record.beforeCaretPosition = textSelector_.GetTextStart();
9347     }
9348     record.addText = insertValue;
9349     ClearRedoOperationRecords();
9350     InsertValueByOperationType(insertValue, OperationType::DRAG);
9351     int32_t length = dragRange_.second - dragRange_.first;
9352     record.afterCaretPosition = record.beforeCaretPosition + length;
9353     record.deleteCaretPostion = dragRange_.first;
9354     AddOperationRecord(record);
9355 }
9356 
9357 bool RichEditorPattern::IsEditing()
9358 {
9359     return isEditing_;
9360 }
9361 
9362 void RichEditorPattern::HandleOnEditChanged(bool isEditing)
9363 {
9364     if (isEditing_ == isEditing) {
9365         return;
9366     }
9367     auto host = GetHost();
9368     CHECK_NULL_VOID(host);
9369     auto eventHub = host->GetEventHub<RichEditorEventHub>();
9370     CHECK_NULL_VOID(eventHub);
9371     isEditing_ = isEditing;
9372     eventHub->FireOnEditingChange(isEditing);
9373     if (CanStartAITask()) {
9374         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "leave edit state, start AI task");
9375         dataDetectorAdapter_->StartAITask();
9376     } else {
9377         if (isEditing) {
9378             TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "enter edit state, reset previewLongPress_");
9379             previewLongPress_ = false;
9380         }
9381         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
9382     }
9383 }
9384 
9385 void RichEditorPattern::ResetKeyboardIfNeed()
9386 {
9387     bool needToResetKeyboard = false;
9388     auto currentAction = GetTextInputActionValue(GetDefaultTextInputAction());
9389     // When the enter key type changes, the keyboard needs to be reset.
9390     if (action_ != TextInputAction::UNSPECIFIED) {
9391         needToResetKeyboard = action_ != currentAction;
9392     }
9393     action_ = currentAction;
9394 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
9395     if (needToResetKeyboard) {
9396         // if keyboard attached or keyboard is shown, pull up keyboard again
9397         if (imeShown_ || isCustomKeyboardAttached_) {
9398             if (HasFocus()) {
9399                 RequestKeyboard(false, true, true);
9400             }
9401             return;
9402         }
9403 #if defined(ENABLE_STANDARD_INPUT)
9404         auto inputMethod = MiscServices::InputMethodController::GetInstance();
9405         CHECK_NULL_VOID(inputMethod);
9406         MiscServices::Configuration config;
9407         config.SetEnterKeyType(static_cast<MiscServices::EnterKeyType>(action_));
9408         config.SetTextInputType(static_cast<MiscServices::TextInputType>(keyboard_));
9409         inputMethod->OnConfigurationChange(config);
9410 #endif
9411     }
9412 #else
9413     if (needToResetKeyboard && HasConnection()) {
9414         CloseKeyboard(false);
9415         RequestKeyboard(false, true, true);
9416     }
9417 #endif
9418 }
9419 
9420 void RichEditorPattern::OnTextInputActionUpdate(TextInputAction value) {}
9421 
9422 void RichEditorPattern::PerformAction(TextInputAction action, bool forceCloseKeyboard)
9423 {
9424     auto host = GetHost();
9425     CHECK_NULL_VOID(host);
9426     // When the Enter key is triggered, perform a line feed operation.
9427     if (action == TextInputAction::NEW_LINE) {
9428         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "action=%{public}d, forceCloseKeyboard=%{public}d", action,
9429             forceCloseKeyboard);
9430         InsertValue("\n", true);
9431     }
9432     // Enter key type callback
9433     TextFieldCommonEvent event;
9434     auto eventHub = host->GetEventHub<RichEditorEventHub>();
9435     eventHub->FireOnSubmit(static_cast<int32_t>(action), event);
9436     // If the developer wants to keep editing, editing will not stop
9437     if (event.IsKeepEditable() || action == TextInputAction::NEW_LINE) {
9438         return;
9439     }
9440     // Exit the editing state
9441     StopEditing();
9442 }
9443 
9444 void RichEditorPattern::StopEditing()
9445 {
9446     CHECK_NULL_VOID(HasFocus());
9447     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "StopEditing");
9448 
9449     // The selection status disappears, the cursor is hidden, and the soft keyboard is exited
9450     HandleBlurEvent();
9451     // In order to avoid the physical keyboard being able to type, you need to make sure that you lose focus
9452     FocusHub::LostFocusToViewRoot();
9453 }
9454 
9455 TextInputAction RichEditorPattern::GetDefaultTextInputAction() const
9456 {
9457     // As with TextInput, it is a line break by default
9458     return TextInputAction::NEW_LINE;
9459 }
9460 
9461 void RichEditorPattern::GetChangeSpanStyle(RichEditorChangeValue& changeValue, std::optional<TextStyle>& spanTextStyle,
9462     std::optional<struct UpdateParagraphStyle>& spanParaStyle, const RefPtr<SpanNode>& spanNode, int32_t spanIndex)
9463 {
9464     auto originalSpans = changeValue.GetRichEditorOriginalSpans();
9465     if (spanIndex == 0 && originalSpans.size()) {
9466         const RichEditorAbstractSpanResult& firstInfo = originalSpans.front();
9467         const RichEditorAbstractSpanResult& lastInfo = originalSpans.back();
9468         int32_t firstLength = static_cast<int32_t>(StringUtils::ToWstring(firstInfo.GetValue()).length());
9469         int32_t lastLength = static_cast<int32_t>(StringUtils::ToWstring(lastInfo.GetValue()).length());
9470         if (firstInfo.GetEraseLength() == firstLength && lastInfo.GetEraseLength() == lastLength) {
9471             if (spans_.size() == originalSpans.size() ||
9472                 static_cast<int32_t>(spans_.size()) == (lastInfo.GetSpanIndex() + 1)) {
9473                 return; // all spanNode be deleted, set default style
9474             }
9475             spanIndex = lastInfo.GetSpanIndex() + 1;
9476         } else if (firstInfo.GetEraseLength() == firstLength) {
9477             spanIndex = lastInfo.GetSpanIndex();
9478         }
9479         auto it = spans_.begin();
9480         std::advance(it, spanIndex);
9481         if ((*it)->unicode != 0 || DynamicCast<PlaceholderSpanItem>(*it)) {
9482             return; // is not a textSpan(Image/Symbol/other)
9483         }
9484         spanTextStyle = (*it)->GetTextStyle();
9485         struct UpdateParagraphStyle paraStyle;
9486         paraStyle.textAlign = (*it)->textLineStyle->GetTextAlign();
9487         paraStyle.leadingMargin = (*it)->textLineStyle->GetLeadingMargin();
9488         paraStyle.wordBreak = (*it)->textLineStyle->GetWordBreak();
9489         paraStyle.lineBreakStrategy = (*it)->textLineStyle->GetLineBreakStrategy();
9490         spanParaStyle = paraStyle;
9491     } else if (spanNode && spanNode->GetSpanItem()) {
9492         spanTextStyle = spanNode->GetSpanItem()->GetTextStyle();
9493         struct UpdateParagraphStyle paraStyle;
9494         paraStyle.textAlign = spanNode->GetTextAlign();
9495         paraStyle.leadingMargin = spanNode->GetLeadingMarginValue({});
9496         paraStyle.wordBreak = spanNode->GetWordBreak();
9497         paraStyle.lineBreakStrategy = spanNode->GetLineBreakStrategy();
9498         spanParaStyle = paraStyle;
9499     }
9500 }
9501 
9502 void RichEditorPattern::GetReplacedSpan(RichEditorChangeValue& changeValue, int32_t& innerPosition,
9503     const std::string& insertValue, int32_t textIndex, std::optional<TextStyle> textStyle,
9504     std::optional<struct UpdateParagraphStyle> paraStyle, bool isCreate, bool fixDel)
9505 {
9506     std::string originalStr;
9507     int32_t originalPos = 0;
9508     RefPtr<SpanItem> spanItem = fixDel ? GetDelPartiallySpanItem(changeValue, originalStr, originalPos) : nullptr;
9509 
9510     TextInsertValueInfo info;
9511     CalcInsertValueObj(info, textIndex, isCreate);
9512     int32_t spanIndex = info.GetSpanIndex();
9513     int32_t offsetInSpan = info.GetOffsetInSpan();
9514     auto host = GetHost();
9515     CHECK_NULL_VOID(host);
9516     auto uiNode = host->GetChildAtIndex(spanIndex);
9517     RefPtr<SpanNode> spanNode = DynamicCast<SpanNode>(uiNode);
9518     if (!isCreate && textIndex && uiNode && uiNode->GetTag() != V2::SPAN_ETS_TAG) {
9519         spanNode = nullptr;
9520         ++spanIndex; // select/create a new span When the span is not a textSpan(Image/Symbol/other)
9521         offsetInSpan = 0;
9522         spanNode = DynamicCast<SpanNode>(host->GetChildAtIndex(spanIndex));
9523     }
9524 
9525     auto wInsertValue = StringUtils::ToWstring(insertValue);
9526     changeValue.SetRangeAfter({ innerPosition, innerPosition + wInsertValue.length()});
9527     std::wstring textTemp = wInsertValue;
9528     if (!textStyle && !isCreate && spanNode) {
9529         if (typingStyle_ && !HasSameTypingStyle(spanNode)) {
9530             textStyle = typingTextStyle_; // create a new span When have a different typingStyle
9531             bool insertInSpan = textIndex && offsetInSpan;
9532             spanIndex = insertInSpan ? spanIndex + 1 : spanIndex;
9533             offsetInSpan = 0;
9534         } else {
9535             textTemp = StringUtils::ToWstring(spanNode->GetSpanItem()->content);
9536             textTemp.insert(offsetInSpan, wInsertValue);
9537         }
9538     }
9539 
9540     auto it = textTemp.find(lineSeparator);
9541     bool containNextLine = it != std::wstring::npos;
9542     auto content = StringUtils::ToString(textTemp);
9543 
9544     if (textStyle || containNextLine) { // SpanNode Fission
9545         GetReplacedSpanFission(changeValue, innerPosition, content, spanIndex, offsetInSpan, textStyle, paraStyle);
9546     } else {
9547         std::optional<TextStyle> spanTextStyle = textStyle ? textStyle : typingTextStyle_;
9548         std::optional<struct UpdateParagraphStyle> spanParaStyle = paraStyle;
9549         GetChangeSpanStyle(changeValue, spanTextStyle, spanParaStyle, spanNode, spanIndex);
9550         CreateSpanResult(changeValue, innerPosition, spanIndex, offsetInSpan, offsetInSpan + wInsertValue.length(),
9551             content, spanTextStyle, spanParaStyle);
9552         innerPosition += wInsertValue.length();
9553     }
9554 
9555     if (spanItem) {
9556         spanItem->content = originalStr;
9557         spanItem->position = originalPos;
9558     }
9559 }
9560 
9561 void RichEditorPattern::GetReplacedSpanFission(RichEditorChangeValue& changeValue, int32_t& innerPosition,
9562     std::string& content, int32_t startSpanIndex, int32_t offsetInSpan, std::optional<TextStyle> textStyle,
9563     std::optional<struct UpdateParagraphStyle> paraStyle)
9564 {
9565     std::vector<RichEditorAbstractSpanResult> ret;
9566     int spanIndex = startSpanIndex;
9567     auto wContent = StringUtils::ToWstring(content);
9568 
9569     auto index = wContent.find(lineSeparator);
9570     while (index != std::wstring::npos) {
9571         auto textAfter = wContent.substr(index + 1);
9572         if (textAfter.empty()) {
9573             break;
9574         }
9575         auto textBefore = wContent.substr(0, index + 1);
9576         if (offsetInSpan != static_cast<int32_t>(textBefore.length())) {
9577             CreateSpanResult(changeValue, innerPosition, spanIndex, offsetInSpan, textBefore.length(),
9578                 StringUtils::ToString(textBefore), textStyle, paraStyle);
9579             innerPosition += textBefore.length() - offsetInSpan;
9580         }
9581         wContent = textAfter;
9582         index = wContent.find(lineSeparator);
9583         offsetInSpan = 0;
9584         ++spanIndex;
9585     }
9586     CreateSpanResult(changeValue, innerPosition, spanIndex, offsetInSpan, wContent.length(),
9587         StringUtils::ToString(wContent), textStyle, paraStyle);
9588     innerPosition += wContent.length();
9589 }
9590 
9591 void RichEditorPattern::CreateSpanResult(RichEditorChangeValue& changeValue, int32_t& innerPosition, int32_t spanIndex,
9592     int32_t offsetInSpan, int32_t endInSpan, std::string content, std::optional<TextStyle> textStyle,
9593     std::optional<struct UpdateParagraphStyle> paraStyle)
9594 {
9595     RichEditorAbstractSpanResult retInfo;
9596     if (textStyle) {
9597         SetTextStyleToRet(retInfo, *textStyle);
9598     } else {
9599         retInfo.SetFontColor((Color::BLACK).ColorToString());
9600         retInfo.SetFontSize(Dimension(16.0f, DimensionUnit::VP).ConvertToVp());
9601         retInfo.SetFontStyle(OHOS::Ace::FontStyle::NORMAL);
9602         retInfo.SetFontWeight(static_cast<int32_t>(FontWeight::NORMAL));
9603         retInfo.SetTextDecoration(TextDecoration::NONE);
9604         retInfo.SetColor((Color::BLACK).ColorToString());
9605         retInfo.SetFontFamily("HarmonyOS Sans");
9606     }
9607     retInfo.SetSpanIndex(spanIndex);
9608     if (!previewTextRecord_.newPreviewContent.empty()) {
9609         retInfo.SetPreviewText(previewTextRecord_.newPreviewContent);
9610     } else {
9611         retInfo.SetValue(content);
9612     }
9613     int32_t rangStart = std::max(0, innerPosition - offsetInSpan);
9614     retInfo.SetSpanRangeStart(rangStart);
9615     retInfo.SetSpanRangeEnd(rangStart + StringUtils::ToWstring(content).length());
9616     retInfo.SetOffsetInSpan(offsetInSpan);
9617     retInfo.SetEraseLength(endInSpan - offsetInSpan);
9618     if (paraStyle) {
9619         TextStyleResult textStyleResult = retInfo.GetTextStyle();
9620         textStyleResult.textAlign = static_cast<int32_t>(paraStyle->textAlign.value_or(TextAlign::START));
9621         if (paraStyle->leadingMargin) {
9622             textStyleResult.leadingMarginSize[0] = paraStyle->leadingMargin->size.Width().ToString();
9623             textStyleResult.leadingMarginSize[1] = paraStyle->leadingMargin->size.Height().ToString();
9624         }
9625         IF_TRUE(paraStyle->wordBreak, textStyleResult.wordBreak = static_cast<int32_t>(paraStyle->wordBreak.value()));
9626         IF_TRUE(paraStyle->lineBreakStrategy,
9627             textStyleResult.lineBreakStrategy = static_cast<int32_t>(paraStyle->lineBreakStrategy.value()));
9628         retInfo.SetTextStyle(textStyleResult);
9629     }
9630     changeValue.SetRichEditorReplacedSpans(retInfo);
9631 }
9632 
9633 void RichEditorPattern::SetTextStyleToRet(RichEditorAbstractSpanResult& retInfo, const TextStyle& textStyle)
9634 {
9635     retInfo.SetTextDecoration(textStyle.GetTextDecoration());
9636     retInfo.SetFontColor(textStyle.GetTextColor().ColorToString());
9637     retInfo.SetColor(textStyle.GetTextDecorationColor().ColorToString());
9638     retInfo.SetTextDecorationStyle(textStyle.GetTextDecorationStyle());
9639     retInfo.SetFontSize(textStyle.GetFontSize().ConvertToVp());
9640     retInfo.SetFontStyle(textStyle.GetFontStyle());
9641     TextStyleResult textStyleResult;
9642     textStyleResult.lineHeight = textStyle.GetLineHeight().ConvertToVp();
9643     textStyleResult.letterSpacing = textStyle.GetLetterSpacing().ConvertToVp();
9644     textStyleResult.textShadows = textStyle.GetTextShadows();
9645     retInfo.SetTextStyle(textStyleResult);
9646     retInfo.SetLineHeight(textStyle.GetLineHeight().ConvertToVp());
9647     retInfo.SetLetterspacing(textStyle.GetLetterSpacing().ConvertToVp());
9648     retInfo.SetFontFeature(textStyle.GetFontFeatures());
9649     std::string fontFamilyValue;
9650     auto fontFamily = textStyle.GetFontFamilies();
9651     for (const auto& str : fontFamily) {
9652         fontFamilyValue += str;
9653     }
9654     retInfo.SetFontFamily(fontFamilyValue);
9655     retInfo.SetFontWeight((int32_t)textStyle.GetFontWeight());
9656 }
9657 
9658 void RichEditorPattern::CalcInsertValueObj(TextInsertValueInfo& info, int textIndex, bool isCreate)
9659 {
9660     if (spans_.empty()) {
9661         info.SetSpanIndex(0);
9662         info.SetOffsetInSpan(0);
9663         return;
9664     }
9665     auto it = std::find_if(
9666         spans_.begin(), spans_.end(), [caretPosition = textIndex](const RefPtr<SpanItem>& spanItem) {
9667             auto spanLength = static_cast<int32_t>(StringUtils::ToWstring(spanItem->content).length());
9668             if (spanLength == 0) {
9669                 return spanItem->position == caretPosition;
9670             }
9671             return (spanItem->position - spanLength <= caretPosition) && (caretPosition <= spanItem->position);
9672         });
9673     if (it == spans_.end()) {
9674         info.SetSpanIndex(static_cast<int32_t>(spans_.size()) - 1);
9675         info.SetOffsetInSpan(StringUtils::ToWstring((*spans_.rbegin())->content).length());
9676         return;
9677     }
9678     if (textIndex && isCreate) {
9679         info.SetSpanIndex(std::distance(spans_.begin(), it) + 1);
9680         info.SetOffsetInSpan(0);
9681         return;
9682     }
9683     if ((*it)->content.back() == '\n' && (*it)->position == textIndex) { // next line/span begin
9684         info.SetSpanIndex(std::distance(spans_.begin(), it) + 1);
9685         info.SetOffsetInSpan(0);
9686     } else {
9687         info.SetSpanIndex(std::distance(spans_.begin(), it));
9688         int32_t spanStart = (*it)->position - StringUtils::ToWstring((*it)->content).length();
9689         info.SetOffsetInSpan(textIndex - spanStart);
9690     }
9691 }
9692 
9693 void RichEditorPattern::GetDeletedSpan(RichEditorChangeValue& changeValue, int32_t& innerPosition,
9694     int32_t length, RichEditorDeleteDirection direction, bool isResetSelection)
9695 {
9696     RichEditorDeleteValue info;
9697     if (!textSelector_.SelectNothing()) {
9698         length = textSelector_.GetTextEnd() - textSelector_.GetTextStart();
9699         innerPosition = std::min(textSelector_.GetStart(), textSelector_.GetEnd());
9700     } else if (!previewTextRecord_.previewContent.empty()) {
9701         length = previewTextRecord_.replacedRange.end - previewTextRecord_.replacedRange.start;
9702         innerPosition = previewTextRecord_.replacedRange.start;
9703     } else {
9704         int32_t emojiLength = CalculateDeleteLength(length, (direction == RichEditorDeleteDirection::BACKWARD));
9705         if (isResetSelection) {
9706             CloseSelectOverlay();
9707             ResetSelection();
9708         }
9709         if (direction == RichEditorDeleteDirection::BACKWARD) {
9710             innerPosition -= emojiLength;
9711         }
9712         if (length < emojiLength) {
9713             length = emojiLength;
9714         }
9715     }
9716 
9717     info.SetOffset(innerPosition);
9718     info.SetRichEditorDeleteDirection(direction);
9719     info.SetLength(length);
9720     if (!spans_.empty()) {
9721         CalcDeleteValueObj(innerPosition, length, info);
9722     }
9723     if (!spans_.empty() || isAPI14Plus) {
9724         changeValue.SetRangeBefore({ innerPosition, innerPosition + length });
9725         changeValue.SetRangeAfter({ innerPosition, innerPosition });
9726     }
9727     const std::list<RichEditorAbstractSpanResult>& resultList = info.GetRichEditorDeleteSpans();
9728     for (auto& it : resultList) {
9729         if (it.GetType() == SpanResultType::TEXT) {
9730             changeValue.SetRichEditorOriginalSpans(it);
9731         } else if (it.GetType() == SpanResultType::SYMBOL && textSelector_.SelectNothing() &&
9732             previewTextRecord_.previewContent.empty()) {
9733             int32_t symbolStart = it.GetSpanRangeStart();
9734             changeValue.SetRichEditorOriginalSpans(it);
9735             changeValue.SetRangeBefore({ symbolStart, symbolStart + SYMBOL_SPAN_LENGTH });
9736             changeValue.SetRangeAfter({ symbolStart, symbolStart });
9737         }
9738     }
9739 }
9740 
9741 RefPtr<SpanItem> RichEditorPattern::GetDelPartiallySpanItem(
9742     RichEditorChangeValue& changeValue, std::string& originalStr, int32_t& originalPos)
9743 {
9744     RefPtr<SpanItem> retItem = nullptr;
9745     if (changeValue.GetRichEditorOriginalSpans().size() == 0) {
9746         return retItem;
9747     }
9748     std::wstring textTemp;
9749     auto originalSpans = changeValue.GetRichEditorOriginalSpans();
9750     const RichEditorAbstractSpanResult& firstResult = originalSpans.front();
9751     auto it = spans_.begin();
9752     std::advance(it, firstResult.GetSpanIndex());
9753     retItem = *it;
9754     originalStr = retItem->content;
9755     originalPos = retItem->position;
9756     textTemp = StringUtils::ToWstring(originalStr).erase(firstResult.OffsetInSpan(), firstResult.GetEraseLength());
9757     retItem->content = StringUtils::ToString(textTemp);
9758     retItem->position -= firstResult.GetEraseLength();
9759     if (firstResult.GetEraseLength() != static_cast<int32_t>(StringUtils::ToWstring(firstResult.GetValue()).length())) {
9760         return retItem;
9761     }
9762 
9763     if (firstResult.GetSpanIndex() == 0) {
9764         int32_t spanIndex = 0;
9765         for (auto& orgIt : originalSpans) {
9766             spanIndex = orgIt.GetSpanIndex();
9767             if (orgIt.GetEraseLength() != static_cast<int32_t>(StringUtils::ToWstring(orgIt.GetValue()).length())) {
9768                 // find the deleted(Partially) spanItem
9769                 auto findIt = spans_.begin();
9770                 std::advance(findIt, spanIndex);
9771                 textTemp = StringUtils::ToWstring((*findIt)->content);
9772                 textTemp.erase(orgIt.OffsetInSpan(), orgIt.GetEraseLength());
9773                 retItem->content = StringUtils::ToString(textTemp);
9774                 retItem->position = textTemp.length();
9775                 return retItem;
9776             }
9777         }
9778         if (spans_.size() == originalSpans.size() || static_cast<int32_t>(spans_.size()) == (spanIndex + 1)) {
9779             return retItem; // all spanNode be deleted
9780         }
9781         auto nextIt = spans_.begin();
9782         std::advance(nextIt, spanIndex + 1);
9783         if ((*nextIt)->unicode != 0 || DynamicCast<PlaceholderSpanItem>(*nextIt)) {
9784             return retItem; // is not a textSpan(Image/Symbol/other)
9785         }
9786         retItem->content = (*nextIt)->content;
9787         retItem->position = StringUtils::ToWstring(retItem->content).length();
9788     }
9789     return retItem;
9790 }
9791 
9792 bool RichEditorPattern::BeforeChangeText(RichEditorChangeValue& changeValue, const TextSpanOptions& options)
9793 {
9794     auto eventHub = GetEventHub<RichEditorEventHub>();
9795     CHECK_NULL_RETURN(eventHub, false);
9796     if (!eventHub->HasOnWillChange() && !eventHub->HasOnDidChange()) {
9797         return true;
9798     }
9799     int32_t innerPosition = caretPosition_;
9800 
9801    // AddTextSpan
9802     std::optional<TextStyle> textStyle = std::nullopt;
9803     if (options.style.has_value()) {
9804         textStyle = options.style;
9805     }
9806     if (options.offset.has_value()) {
9807         if (spans_.empty() || options.offset.value() < 0) {
9808             innerPosition = 0;
9809         } else if (options.offset.value() > GetTextContentLength()) {
9810             innerPosition = GetTextContentLength();
9811         } else {
9812             innerPosition = options.offset.value();
9813         }
9814     } else {
9815         innerPosition = GetTextContentLength();
9816     }
9817     // only add, do not delete
9818     changeValue.SetRangeBefore({ innerPosition, innerPosition });
9819     GetReplacedSpan(changeValue, innerPosition, options.value, innerPosition, textStyle, options.paraStyle, true);
9820     CHECK_NULL_RETURN(eventHub->HasOnWillChange(), true);
9821     auto ret = eventHub->FireOnWillChange(changeValue);
9822     return ret;
9823 }
9824 
9825 bool RichEditorPattern::BeforeAddImage(RichEditorChangeValue& changeValue,
9826     const ImageSpanOptions& options, int32_t insertIndex)
9827 {
9828     auto eventHub = GetEventHub<RichEditorEventHub>();
9829     CHECK_NULL_RETURN(eventHub, false);
9830     if (!eventHub->HasOnWillChange() && !eventHub->HasOnDidChange()) {
9831         return true;
9832     }
9833     changeValue.SetRangeBefore({ insertIndex, insertIndex });
9834     changeValue.SetRangeAfter({ insertIndex, insertIndex + 1 });
9835     RichEditorAbstractSpanResult retInfo;
9836     TextInsertValueInfo info;
9837     CalcInsertValueObj(info, insertIndex, true);
9838     int32_t spanIndex = info.GetSpanIndex();
9839     retInfo.SetSpanIndex(spanIndex);
9840     if (options.image) {
9841         retInfo.SetValueResourceStr(*options.image);
9842     }
9843     if (options.imagePixelMap) {
9844         retInfo.SetValuePixelMap(*options.imagePixelMap);
9845     }
9846     if (options.imageAttribute.has_value()) {
9847         auto imgAttr = options.imageAttribute.value();
9848         if (imgAttr.size.has_value()) {
9849             retInfo.SetSizeWidth(imgAttr.size->width.value_or(CalcDimension()).ConvertToPx());
9850             retInfo.SetSizeHeight(imgAttr.size->height.value_or(CalcDimension()).ConvertToPx());
9851         }
9852         if (imgAttr.verticalAlign.has_value()) {
9853             retInfo.SetVerticalAlign(imgAttr.verticalAlign.value());
9854         }
9855         if (imgAttr.objectFit.has_value()) {
9856             retInfo.SetImageFit(imgAttr.objectFit.value());
9857         }
9858         if (imgAttr.marginProp.has_value()) {
9859             retInfo.SetMargin(imgAttr.marginProp.value().ToString());
9860         }
9861         if (imgAttr.borderRadius.has_value()) {
9862             retInfo.SetBorderRadius(imgAttr.borderRadius.value().ToString());
9863         }
9864     }
9865     retInfo.SetOffsetInSpan(0);
9866     retInfo.SetEraseLength(1);
9867     retInfo.SetSpanRangeStart(insertIndex);
9868     retInfo.SetSpanRangeEnd(insertIndex + 1);
9869     retInfo.SetSpanType(SpanResultType::IMAGE);
9870     changeValue.SetRichEditorReplacedImageSpans(retInfo);
9871     CHECK_NULL_RETURN(eventHub->HasOnWillChange(), true);
9872     auto ret = eventHub->FireOnWillChange(changeValue);
9873     return ret;
9874 }
9875 
9876 void RichEditorPattern::FixMoveDownChange(RichEditorChangeValue& changeValue, int32_t delLength)
9877 {
9878     int32_t delSpanCount = 0;
9879     for (auto& it : changeValue.GetRichEditorOriginalSpans()) {
9880         if (it.GetEraseLength() == static_cast<int32_t>(StringUtils::ToWstring(it.GetValue()).length())) {
9881             ++delSpanCount;
9882         }
9883     }
9884     for (auto& it : const_cast<std::vector<RichEditorAbstractSpanResult>&>(changeValue.GetRichEditorReplacedSpans())) {
9885         if (delSpanCount) {
9886             it.SetSpanIndex(it.GetSpanIndex() - delSpanCount);
9887         }
9888     }
9889 }
9890 
9891 void RichEditorPattern::BeforeUndo(
9892     RichEditorChangeValue& changeValue, int32_t& innerPosition, const OperationRecord& record)
9893 {
9894     innerPosition = record.afterCaretPosition;
9895     if (record.addText.has_value() && record.deleteCaretPostion != -1) { // UndoDrag
9896         GetDeletedSpan(changeValue, innerPosition, StringUtils::ToWstring(record.addText.value_or("")).length(),
9897             RichEditorDeleteDirection::FORWARD);
9898         innerPosition = record.deleteCaretPostion;
9899         GetReplacedSpan(changeValue, innerPosition, record.addText.value(), innerPosition, std::nullopt, std::nullopt);
9900     } else if (record.addText.has_value() && record.deleteText.has_value()) {
9901         GetDeletedSpan(changeValue, innerPosition, StringUtils::ToWstring(record.addText.value_or("")).length(),
9902             RichEditorDeleteDirection::BACKWARD);
9903         GetReplacedSpan(
9904             changeValue, innerPosition, record.deleteText.value(), innerPosition, std::nullopt, std::nullopt);
9905     } else if (record.deleteText.has_value()) {
9906         GetReplacedSpan(
9907             changeValue, innerPosition, record.deleteText.value(), innerPosition, std::nullopt, std::nullopt);
9908     } else if (record.addText.has_value()) {
9909         GetDeletedSpan(changeValue, innerPosition, StringUtils::ToWstring(record.addText.value_or("")).length(),
9910             RichEditorDeleteDirection::BACKWARD);
9911     }
9912 }
9913 
9914 void RichEditorPattern::BeforeRedo(
9915     RichEditorChangeValue& changeValue, int32_t& innerPosition, const OperationRecord& record)
9916 {
9917     innerPosition = record.beforeCaretPosition - StringUtils::ToWstring(record.addText.value_or("")).length();
9918     if (record.addText.has_value() && record.deleteCaretPostion != -1) { // RedoDrag
9919         innerPosition = record.deleteCaretPostion;
9920         GetDeletedSpan(changeValue, innerPosition, StringUtils::ToWstring(record.addText.value_or("")).length(),
9921             RichEditorDeleteDirection::FORWARD);
9922         innerPosition = record.beforeCaretPosition;
9923         GetReplacedSpan(changeValue, innerPosition, record.addText.value(), innerPosition, std::nullopt, std::nullopt);
9924     } else if (record.addText.has_value() && record.deleteText.has_value()) {
9925         GetDeletedSpan(changeValue, innerPosition, StringUtils::ToWstring(record.deleteText.value_or("")).length(),
9926             RichEditorDeleteDirection::FORWARD);
9927         GetReplacedSpan(changeValue, innerPosition, record.addText.value(), innerPosition, std::nullopt, std::nullopt);
9928     } else if (record.deleteText.has_value()) {
9929         innerPosition = record.beforeCaretPosition - StringUtils::ToWstring(record.deleteText.value_or("")).length();
9930         GetDeletedSpan(changeValue, innerPosition, StringUtils::ToWstring(record.deleteText.value_or("")).length(),
9931             RichEditorDeleteDirection::FORWARD);
9932     } else if (record.addText.has_value()) {
9933         innerPosition = std::min(innerPosition, record.afterCaretPosition);
9934         int32_t innerAddPosition = record.afterCaretPosition - static_cast<int32_t>(record.addText.value().length());
9935         if (changeValue.GetRichEditorOriginalSpans().empty()) {
9936             innerPosition = caretPosition_;
9937             innerAddPosition = caretPosition_;
9938         }
9939         GetReplacedSpan(changeValue, innerAddPosition, record.addText.value(), innerPosition,
9940             std::nullopt, std::nullopt);
9941     }
9942 }
9943 
9944 void RichEditorPattern::BeforeDrag(
9945     RichEditorChangeValue& changeValue, int32_t& innerPosition, const OperationRecord& record)
9946 {
9947     int length = StringUtils::ToWstring(record.addText.value_or("")).length();
9948     int32_t nowPosition = innerPosition;
9949     std::optional<TextStyle> style = std::nullopt;
9950     if (typingStyle_.has_value() && typingTextStyle_.has_value()) {
9951         style = typingTextStyle_.value();
9952     }
9953     if (!isDragSponsor_) { // drag from outside
9954         GetReplacedSpan(
9955             changeValue, innerPosition, record.addText.value(), innerPosition, style, std::nullopt, true, false);
9956     } else if (nowPosition < record.beforeCaretPosition + length) { // move up
9957         innerPosition = record.beforeCaretPosition;
9958         GetDeletedSpan(changeValue, innerPosition, length, RichEditorDeleteDirection::FORWARD);
9959         innerPosition = nowPosition;
9960         GetReplacedSpan(
9961             changeValue, innerPosition, record.addText.value(), nowPosition, style, std::nullopt, true, false);
9962     } else { // move down
9963         innerPosition = record.beforeCaretPosition;
9964         GetDeletedSpan(changeValue, innerPosition, length, RichEditorDeleteDirection::FORWARD);
9965         innerPosition = nowPosition - length;
9966         GetReplacedSpan(
9967             changeValue, innerPosition, record.addText.value(), nowPosition, style, std::nullopt, true, false);
9968         FixMoveDownChange(changeValue, length);
9969     }
9970 }
9971 
9972 bool RichEditorPattern::BeforeChangeText(
9973     RichEditorChangeValue& changeValue, const OperationRecord& record, RecordType type, int32_t delLength)
9974 {
9975     int32_t innerPosition = caretPosition_;
9976     auto eventHub = GetEventHub<RichEditorEventHub>();
9977     CHECK_NULL_RETURN(eventHub, false);
9978     if (!eventHub->HasOnWillChange() && !eventHub->HasOnDidChange()) {
9979         return true;
9980     }
9981 
9982     if (RecordType::INSERT == type) {
9983         if (textSelector_.IsValid()) {
9984             GetDeletedSpan(changeValue, innerPosition,
9985                 static_cast<int32_t>(textSelector_.GetTextEnd() - textSelector_.GetTextStart()));
9986         } else if (!previewTextRecord_.previewContent.empty()) {
9987             GetDeletedSpan(changeValue, innerPosition,
9988                 static_cast<int32_t>(previewTextRecord_.replacedRange.end - previewTextRecord_.replacedRange.start));
9989         }
9990         GetReplacedSpan(changeValue, innerPosition, record.addText.value(), innerPosition, std::nullopt, std::nullopt);
9991     }
9992     if (RecordType::DEL_FORWARD == type) {
9993         innerPosition = record.beforeCaretPosition;
9994         GetDeletedSpan(changeValue, innerPosition, delLength, RichEditorDeleteDirection::FORWARD, false);
9995     }
9996     if (RecordType::DEL_BACKWARD == type) {
9997         innerPosition = record.beforeCaretPosition;
9998         GetDeletedSpan(changeValue, innerPosition, delLength, RichEditorDeleteDirection::BACKWARD, false);
9999     }
10000     if (RecordType::UNDO == type) {
10001         BeforeUndo(changeValue, innerPosition, record);
10002     }
10003     if (RecordType::REDO == type) {
10004         BeforeRedo(changeValue, innerPosition, record);
10005     }
10006     if (RecordType::DRAG == type) {
10007         BeforeDrag(changeValue, innerPosition, record);
10008     }
10009     bool isDelete = RecordType::DEL_FORWARD == type || RecordType::DEL_BACKWARD == type;
10010     if (changeValue.GetRichEditorOriginalSpans().empty() && !isDelete) {
10011         // only add, do not delete
10012         changeValue.SetRangeBefore({ caretPosition_, caretPosition_ });
10013     }
10014 
10015     CHECK_NULL_RETURN(eventHub->HasOnWillChange(), true);
10016     auto ret = eventHub->FireOnWillChange(changeValue);
10017     return ret;
10018 }
10019 
10020 OffsetF RichEditorPattern::GetTextPaintOffset() const
10021 {
10022     if (selectOverlay_->HasRenderTransform()) {
10023         return selectOverlay_->GetPaintRectOffsetWithTransform();
10024     }
10025     return GetPaintRectGlobalOffset();
10026 }
10027 
10028 OffsetF RichEditorPattern::GetPaintRectGlobalOffset() const
10029 {
10030     auto host = GetHost();
10031     CHECK_NULL_RETURN(host, OffsetF(0.0f, 0.0f));
10032     auto pipeline = host->GetContextRefPtr();
10033     CHECK_NULL_RETURN(pipeline, OffsetF(0.0f, 0.0f));
10034     auto rootOffset = pipeline->GetRootRect().GetOffset();
10035     auto textPaintOffset = host->GetPaintRectOffset();
10036     return textPaintOffset - rootOffset;
10037 }
10038 
10039 void RichEditorPattern::HandlePointWithTransform(OffsetF& point)
10040 {
10041     auto host = GetHost();
10042     CHECK_NULL_VOID(host);
10043     PointF convertPoint = { point.GetX(), point.GetY() };
10044     auto parent = host;
10045     while (parent && (parent->GetTag() != V2::WINDOW_SCENE_ETS_TAG)) {
10046         auto renderContext = parent->GetRenderContext();
10047         CHECK_NULL_VOID(renderContext);
10048         auto paintOffset = renderContext->GetPaintRectWithoutTransform().GetOffset();
10049         if (parent != host) {
10050             convertPoint = convertPoint + paintOffset;
10051         }
10052         renderContext->GetPointTransform(convertPoint);
10053         parent = parent->GetAncestorNodeOfFrame(true);
10054     }
10055     point = { convertPoint.GetX(), convertPoint.GetY() };
10056 }
10057 
10058 const std::list<RefPtr<UINode>>& RichEditorPattern::GetAllChildren() const
10059 {
10060     childNodes_.clear();
10061     auto host = GetHost();
10062     CHECK_NULL_RETURN(host, childNodes_);
10063     auto children = host->GetChildren();
10064     for (const auto& child: children) {
10065         childNodes_.push_back(child);
10066     }
10067     return childNodes_;
10068 }
10069 
10070 CaretOffsetInfo RichEditorPattern::GetCaretOffsetInfoByPosition(int32_t position)
10071 {
10072     CaretOffsetInfo caretInfo;
10073     int32_t currrentPosition = 0;
10074     if (position == -1) {
10075         currrentPosition = caretPosition_;
10076     } else {
10077         currrentPosition = position;
10078     }
10079     caretInfo.caretOffsetUp = CalcCursorOffsetByPosition(currrentPosition, caretInfo.caretHeightUp, false, false);
10080     caretInfo.caretOffsetDown = CalcCursorOffsetByPosition(currrentPosition, caretInfo.caretHeightDown, true, false);
10081     caretInfo.caretOffsetLine = CalcCursorOffsetByPosition(currrentPosition, caretInfo.caretHeightLine);
10082     return caretInfo;
10083 }
10084 
10085 void RichEditorPattern::CalcLineSidesIndexByPosition(int32_t& startIndex, int32_t& endIndex)
10086 {
10087     Offset textStartOffset;
10088     Offset textEndOffset;
10089 
10090     CHECK_NULL_VOID(overlayMod_);
10091     auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
10092     auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize());
10093     float textOffsetY = richTextRect_.GetY() - (minDet / 2.0);
10094     auto currentCaretOffsetOverlay = overlayMod->GetCaretOffset();
10095     textStartOffset = Offset(0, currentCaretOffsetOverlay.GetY() - textOffsetY);
10096     textEndOffset = Offset(richTextRect_.Width(), currentCaretOffsetOverlay.GetY() - textOffsetY);
10097     startIndex = paragraphs_.GetIndex(textStartOffset);
10098     endIndex = paragraphs_.GetIndex(textEndOffset);
10099 }
10100 
10101 RectF RichEditorPattern::CalcLineInfoByPosition()
10102 {
10103     int32_t startIndex = 0;
10104     int32_t endIndex = 0;
10105 
10106     CalcLineSidesIndexByPosition(startIndex, endIndex);
10107     if (startIndex == endIndex) {
10108         endIndex += 1;
10109     }
10110     auto selectedRects = paragraphs_.GetRects(startIndex, endIndex);
10111     CHECK_NULL_RETURN(selectedRects.size(), {});
10112     return selectedRects.front();
10113 }
10114 
10115 int32_t RichEditorPattern::CalcMoveUpPos(float& leadingMarginOffset)
10116 {
10117     int32_t caretPosition;
10118     CaretOffsetInfo caretInfo;
10119     float textOffsetDownY = 0.0f;
10120     int32_t startIndex = 0;
10121     int32_t endIndex = 0;
10122     Offset textOffset;
10123 
10124     caretInfo = GetCaretOffsetInfoByPosition();
10125     auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize());
10126     CHECK_NULL_RETURN(overlayMod_, 0);
10127     auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
10128     auto caretOffsetOverlay = overlayMod->GetCaretOffset();
10129     auto caretOffsetWidth = overlayMod->GetCaretWidth();
10130     bool cursorNotAtLineStart = NearEqual(caretOffsetOverlay.GetX(), caretInfo.caretOffsetUp.GetX(), caretOffsetWidth);
10131     float textOffsetY = richTextRect_.GetY() + (minDet / 2.0); // 2.0 Cursor one half at the center position
10132     CalcLineSidesIndexByPosition(startIndex, endIndex);
10133     auto rectLineInfo = CalcLineInfoByPosition();
10134     leadingMarginOffset = rectLineInfo.GetX();
10135     if (cursorNotAtLineStart) {
10136         textOffsetDownY = caretInfo.caretOffsetLine.GetY() - textOffsetY;
10137         // lm mean leadingMargin abbr
10138         auto lmSizeOffset = (endIndex - startIndex <= 1 && NearEqual(rectLineInfo.Width(), richTextRect_.Width()))
10139                                 ? rectLineInfo.GetX()
10140                                 : 0;
10141         textOffset = Offset(caretInfo.caretOffsetLine.GetX() - richTextRect_.GetX() + lmSizeOffset, textOffsetDownY);
10142     } else {
10143         textOffsetDownY = caretInfo.caretOffsetLine.GetY() + caretInfo.caretHeightLine - textOffsetY;
10144         textOffset = Offset(caretOffsetOverlay.GetX() - richTextRect_.GetX(), textOffsetDownY);
10145     }
10146     caretPosition = paragraphs_.GetIndex(textOffset);
10147     return caretPosition;
10148 }
10149 
10150 int32_t RichEditorPattern::CalcMoveDownPos(float& leadingMarginOffset)
10151 {
10152     CaretOffsetInfo caretInfo;
10153     float textOffsetDownY = 0.0f;
10154     Offset textOffset;
10155     int32_t caretPositionEnd;
10156 
10157     caretInfo = GetCaretOffsetInfoByPosition();
10158     auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize());
10159     CHECK_NULL_RETURN(overlayMod_, 0);
10160     auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
10161     auto caretOffsetOverlay = overlayMod->GetCaretOffset();
10162     auto caretOffsetWidth = overlayMod->GetCaretWidth();
10163     float textOffsetX = richTextRect_.GetX();
10164     float textOffsetY = richTextRect_.GetY() - (minDet / 2.0);
10165     bool cursorNotAtLineStart = NearEqual(caretOffsetOverlay.GetX(), caretInfo.caretOffsetUp.GetX(), caretOffsetWidth);
10166     // midle or enter
10167     auto rectLineInfo = CalcLineInfoByPosition();
10168     leadingMarginOffset = rectLineInfo.GetX();
10169     auto lineHeightDis = rectLineInfo.Height();
10170     // midle or end, first line start position,end line end position
10171     textOffsetDownY = caretInfo.caretOffsetLine.GetY() + caretInfo.caretHeightLine - textOffsetY;
10172     if (cursorNotAtLineStart || caretPosition_ == 0) {
10173         textOffset = Offset(caretInfo.caretOffsetLine.GetX() - textOffsetX, textOffsetDownY);
10174     } else {
10175         textOffsetDownY += lineHeightDis;
10176         textOffset = Offset(caretOffsetOverlay.GetX() - textOffsetX, textOffsetDownY);
10177     }
10178     caretPositionEnd = paragraphs_.GetIndex(textOffset);
10179     return caretPositionEnd;
10180 }
10181 
10182 int32_t RichEditorPattern::CalcLineBeginPosition()
10183 {
10184     float caretHeight = 0.0f;
10185     OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight, false, false);
10186     auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
10187     auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize());
10188     auto textOffsetY = caretOffset.GetY() - textPaintOffset.GetY() + (minDet / 2.0);
10189     Offset textOffset = { 0, textOffsetY };
10190     auto newPos = paragraphs_.GetIndex(textOffset);
10191     return newPos;
10192 }
10193 
10194 float RichEditorPattern::GetTextThemeFontSize()
10195 {
10196     auto host = GetHost();
10197     CHECK_NULL_RETURN(host, 0.0f);
10198     auto context = host->GetContext();
10199     CHECK_NULL_RETURN(context, 0.0f);
10200     auto theme = context->GetTheme<TextTheme>();
10201     CHECK_NULL_RETURN(theme, 0.0f);
10202     auto textStyle = theme->GetTextStyle();
10203     return textStyle.GetFontSize().ConvertToPx();
10204 }
10205 
10206 int32_t RichEditorPattern::CalcLineEndPosition(int32_t index)
10207 {
10208     CaretOffsetInfo caretInfo;
10209     int32_t realCaretOffsetY = 0;
10210     int32_t realLastClickOffsetY = 0;
10211 
10212     caretInfo = GetCaretOffsetInfoByPosition(index);
10213     if (NearEqual(richTextRect_.GetY(), contentRect_.GetY())) {
10214         realLastClickOffsetY = lastClickOffset_.GetY();
10215         realCaretOffsetY = caretInfo.caretOffsetDown.GetY();
10216     } else {
10217         auto scrollOffset =
10218             caretInfo.caretOffsetDown.GetY() - caretInfo.caretOffsetUp.GetY() + caretInfo.caretOffsetLine.GetY();
10219         realLastClickOffsetY = lastClickOffset_.GetY() + std::abs(richTextRect_.GetY()) + contentRect_.GetY();
10220         realCaretOffsetY = scrollOffset + std::abs(richTextRect_.GetY()) + contentRect_.GetY();
10221     }
10222     auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
10223     auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize());
10224     Offset textOffset;
10225     auto rectLineInfo = CalcLineInfoByPosition();
10226     float textWidth = richTextRect_.Width() + rectLineInfo.GetX();
10227     float textPaintOffsetY = textPaintOffset.GetY() - (minDet / 2.0);
10228     float textOffsetClickY = realLastClickOffsetY - textPaintOffsetY;
10229     float textOffsetDownY = realCaretOffsetY - textPaintOffsetY;
10230     if (lastClickOffset_.NonNegative()) {
10231         textOffset = { textWidth, textOffsetClickY };
10232     } else {
10233         textOffset = { textWidth, textOffsetDownY };
10234     }
10235     auto position = paragraphs_.GetIndex(textOffset);
10236     return position;
10237 }
10238 
10239 bool RichEditorPattern::CursorMoveLineBegin()
10240 {
10241     int32_t currentPositionIndex = 0;
10242     if (textSelector_.SelectNothing()) {
10243         currentPositionIndex = caretPosition_;
10244     } else {
10245         currentPositionIndex = textSelector_.GetTextStart();
10246     }
10247     CloseSelectOverlay();
10248     ResetSelection();
10249     float caretHeightDown = 0.0f;
10250     Offset textOffset;
10251 
10252     if (0 == currentPositionIndex) {
10253         SetCaretPosition(currentPositionIndex);
10254         StartTwinkling();
10255         return false;
10256     }
10257     OffsetF caretOffsetDown = CalcCursorOffsetByPosition(currentPositionIndex, caretHeightDown, true, false);
10258     auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
10259     auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize());
10260     float textPaintOffsetY = textPaintOffset.GetY() - (minDet / 2.0);
10261     if (lastClickOffset_.NonNegative()) {
10262         textOffset = { 0, lastClickOffset_.GetY() - textPaintOffsetY };
10263     } else {
10264         textOffset = { 0, caretOffsetDown.GetY() - textPaintOffsetY };
10265     }
10266     auto position = paragraphs_.GetIndex(textOffset);
10267     AdjustCursorPosition(position);
10268     SetCaretPosition(position);
10269     MoveCaretToContentRect();
10270     StartTwinkling();
10271     auto host = GetHost();
10272     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
10273     return true;
10274 }
10275 
10276 bool RichEditorPattern::CursorMoveLineEnd()
10277 {
10278     int32_t position = 0;
10279     if (!textSelector_.SelectNothing()) {
10280         CaretOffsetInfo caretInfo = GetCaretOffsetInfoByPosition(textSelector_.GetTextEnd());
10281         bool cursorAtLineEnd = !NearEqual(caretInfo.caretOffsetUp.GetX(), caretInfo.caretOffsetDown.GetX(), 0.5f);
10282         if (cursorAtLineEnd) {
10283             position = textSelector_.GetTextEnd();
10284         } else {
10285             position = CalcLineEndPosition(textSelector_.GetTextEnd());
10286         }
10287     } else {
10288         position = CalcLineEndPosition();
10289     }
10290     CloseSelectOverlay();
10291     ResetSelection();
10292     float caretHeight = 0.0f;
10293     CHECK_NULL_RETURN(overlayMod_, false);
10294     auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
10295     SetCaretPosition(position);
10296     StartTwinkling();
10297     OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight, false, false);
10298     overlayMod->SetCaretOffsetAndHeight(caretOffset, caretHeight);
10299     SetLastClickOffset(caretOffset);
10300     caretAffinityPolicy_ = CaretAffinityPolicy::UPSTREAM_FIRST;
10301     MoveCaretToContentRect(caretOffset, caretHeight);
10302     auto host = GetHost();
10303     CHECK_NULL_RETURN(host, false);
10304     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
10305     return true;
10306 }
10307 
10308 void RichEditorPattern::HandleSelectFontStyle(KeyCode code)
10309 {
10310     if (textSelector_.SelectNothing() || isSpanStringMode_) {
10311         return;
10312     }
10313     auto host = GetHost();
10314     CHECK_NULL_VOID(host);
10315     UpdateSelectSpanStyle(textSelector_.GetTextStart(), textSelector_.GetTextEnd(), code);
10316     StopTwinkling();
10317     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
10318 }
10319 
10320 void RichEditorPattern::HandleOnShowMenu()
10321 {
10322     CHECK_NULL_VOID(overlayMod_);
10323     auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
10324     auto caretOffsetOverlay = overlayMod->GetCaretOffset();
10325     auto host = GetHost();
10326     CHECK_NULL_VOID(host);
10327     auto focusHub = host->GetOrCreateFocusHub();
10328     CHECK_NULL_VOID(focusHub);
10329     selectionMenuOffsetByMouse_ = OffsetF(
10330         parentGlobalOffset_.GetX() + caretOffsetOverlay.GetX(), parentGlobalOffset_.GetY() + caretOffsetOverlay.GetY());
10331     focusHub->RequestFocusImmediately();
10332     StartTwinkling();
10333     ShowSelectOverlay(RectF(), RectF(), IsSelectAll(), TextResponseType::RIGHT_CLICK);
10334 }
10335 
10336 PositionType RichEditorPattern::GetPositionTypeFromLine()
10337 {
10338     auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
10339     CHECK_NULL_RETURN(overlayMod, PositionType::DEFAULT);
10340     auto currentCaretOffsetOverlay = overlayMod->GetCaretOffset();
10341     auto caretOffsetWidth = overlayMod->GetCaretWidth();
10342     int32_t currentParagraphStart = GetParagraphBeginPosition(caretPosition_);
10343     bool isParagraphStart = caretPosition_ == currentParagraphStart;
10344     CHECK_NULL_RETURN(!isParagraphStart, PositionType::PARAGRAPH_START);
10345     int32_t currentParagraphEnd = GetParagraphEndPosition(caretPosition_);
10346     bool isParagraphEnd = caretPosition_ == currentParagraphEnd;
10347     CHECK_NULL_RETURN(!isParagraphEnd, PositionType::PARAGRAPH_END);
10348     CaretOffsetInfo caretInfo = GetCaretOffsetInfoByPosition();
10349     bool isCaretAtLineMiddle = NearEqual(caretInfo.caretOffsetDown.GetX(), caretInfo.caretOffsetUp.GetX(), 0.5f);
10350     CHECK_NULL_RETURN(!isCaretAtLineMiddle, PositionType::DEFAULT);
10351     bool isCaretAtLineEnd =
10352         NearEqual(currentCaretOffsetOverlay.GetX(), caretInfo.caretOffsetUp.GetX(), caretOffsetWidth);
10353     CHECK_NULL_RETURN(!isCaretAtLineEnd, PositionType::LINE_END);
10354     return PositionType::LINE_START;
10355 }
10356 
10357 int32_t RichEditorPattern::HandleSelectWrapper(CaretMoveIntent direction, int32_t fixedPos)
10358 {
10359     int32_t index = GetCaretPosition();
10360     switch (direction) {
10361         case CaretMoveIntent::Left:
10362             return CaretPositionSelectEmoji(CaretMoveIntent::Left);
10363         case CaretMoveIntent::Right:
10364             return CaretPositionSelectEmoji(CaretMoveIntent::Right);
10365         case CaretMoveIntent::Up:
10366             return HandleSelectPosition(true);
10367         case CaretMoveIntent::Down:
10368             return HandleSelectPosition(false);
10369         case CaretMoveIntent::LeftWord: {
10370             int32_t startPosition = 0;
10371             int32_t aiContentStart = 0;
10372             aiContentStart = std::clamp(index - RICH_DEFAULT_AI_WORD, 0, GetTextContentLength());
10373             AIDeleteComb(aiContentStart, index, startPosition, true);
10374             return startPosition;
10375         }
10376         case CaretMoveIntent::RightWord: {
10377             int32_t endPosition = 0;
10378             int32_t aiContentEnd = GetTextContentLength();
10379             aiContentEnd = std::clamp(index + RICH_DEFAULT_AI_WORD, 0, GetTextContentLength());
10380             AIDeleteComb(index, aiContentEnd, endPosition, false);
10381             return endPosition;
10382         }
10383         case CaretMoveIntent::ParagraghBegin:
10384             return HandleSelectParagraghPos(true);
10385         case CaretMoveIntent::ParagraghEnd:
10386             return HandleSelectParagraghPos(false);
10387         case CaretMoveIntent::LineBegin:
10388             return CalcLineBeginPosition();
10389         case CaretMoveIntent::LineEnd:
10390             return CalcLineEndPosition();
10391         default:
10392             return NONE_SELECT_TYPE;
10393     }
10394 }
10395 
10396 int32_t RichEditorPattern::HandleSelectPosition(bool isForward)
10397 {
10398     float caretHeight = 0.0f;
10399     float newCaretHeight = 0.0f;
10400     float careOffsetY = 0.0f;
10401     int32_t newPos;
10402     Offset textOffset;
10403     OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight);
10404     auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize()) / 2.0;
10405     auto positionType = GetPositionTypeFromLine();
10406     if (isForward) {
10407         float selectStartHeight = 0.0f;
10408         OffsetF selectStartOffset = CalcCursorOffsetByPosition(textSelector_.GetTextStart(), selectStartHeight);
10409         careOffsetY = caretOffset.GetY() - GetTextRect().GetY() - minDet;
10410         newPos = paragraphs_.GetIndex(Offset(caretOffset.GetX() - GetTextRect().GetX(), careOffsetY), true);
10411         OffsetF newCaretOffset = CalcCursorOffsetByPosition(newPos, newCaretHeight);
10412         if (!textSelector_.SelectNothing() && textSelector_.GetTextEnd() == caretPosition_ &&
10413             selectStartOffset.GetY() == newCaretOffset.GetY()) {
10414             return textSelector_.GetTextStart();
10415         }
10416     } else {
10417         float selectEndHeight = 0.0f;
10418         OffsetF selectEndOffset = CalcCursorOffsetByPosition(textSelector_.GetEnd(), selectEndHeight);
10419         careOffsetY = caretOffset.GetY() - GetTextRect().GetY() + caretHeight + (minDet / 2.0);
10420         if (positionType == PositionType::LINE_START) {
10421             auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
10422             CHECK_NULL_RETURN(overlayMod, 0);
10423             auto caretOffsetOverlay = overlayMod->GetCaretOffset();
10424             auto rectLineInfo = CalcLineInfoByPosition();
10425             careOffsetY += rectLineInfo.Height();
10426             textOffset = Offset(caretOffsetOverlay.GetX() - GetTextRect().GetX(), careOffsetY);
10427         } else {
10428             textOffset = Offset(caretOffset.GetX() - GetTextRect().GetX(), careOffsetY);
10429         }
10430         newPos = paragraphs_.GetIndex(textOffset, true);
10431         OffsetF newCaretOffset = CalcCursorOffsetByPosition(newPos, newCaretHeight);
10432         if (!textSelector_.SelectNothing() && textSelector_.GetTextStart() == caretPosition_ &&
10433             selectEndOffset.GetY() == newCaretOffset.GetY()) {
10434             return textSelector_.GetTextEnd();
10435         }
10436     }
10437     bool isParagraphStart = newPos == GetParagraphBeginPosition(newPos);
10438     if (isParagraphStart && newPos != GetTextContentLength() && newPos != 0) {
10439         return newPos - 1;
10440     }
10441     return newPos;
10442 }
10443 
10444 int32_t RichEditorPattern::HandleSelectParagraghPos(bool direction)
10445 {
10446     int32_t newPos = 0;
10447     CloseSelectOverlay();
10448     ResetSelection();
10449     if (direction) {
10450         newPos = GetParagraphBeginPosition(caretPosition_);
10451         if (newPos == caretPosition_ && caretPosition_ > 0) {
10452             newPos = GetParagraphBeginPosition(caretPosition_ - 1);
10453         }
10454     } else {
10455         newPos = GetParagraphEndPosition(caretPosition_);
10456         if (newPos == caretPosition_ && caretPosition_ < static_cast<int32_t>(GetTextContentLength())) {
10457             newPos = GetParagraphEndPosition(caretPosition_ + 1);
10458         }
10459     }
10460     return newPos;
10461 }
10462 
10463 void RichEditorPattern::HandleSelectFontStyleWrapper(KeyCode code, TextStyle& spanStyle)
10464 {
10465     switch (code) {
10466         case KeyCode::KEY_B:
10467             if (spanStyle.GetFontWeight() == Ace::FontWeight::BOLD) {
10468                 spanStyle.SetFontWeight(Ace::FontWeight::NORMAL);
10469             } else {
10470                 spanStyle.SetFontWeight(Ace::FontWeight::BOLD);
10471             }
10472             break;
10473         case KeyCode::KEY_I:
10474             if (spanStyle.GetFontStyle() == OHOS::Ace::FontStyle::ITALIC) {
10475                 spanStyle.SetFontStyle(OHOS::Ace::FontStyle::NORMAL);
10476             } else {
10477                 spanStyle.SetFontStyle(OHOS::Ace::FontStyle::ITALIC);
10478             }
10479             break;
10480         case KeyCode::KEY_U:
10481             if (spanStyle.GetTextDecoration() == TextDecoration::UNDERLINE) {
10482                 spanStyle.SetTextDecoration(TextDecoration::NONE);
10483             } else {
10484                 spanStyle.SetTextDecoration(TextDecoration::UNDERLINE);
10485             }
10486             break;
10487         default:
10488             LOGW("Unsupported select operation for HandleSelectFrontStyle");
10489             return;
10490     }
10491 }
10492 
10493 void RichEditorPattern::AIDeleteComb(int32_t start, int32_t end, int32_t& aiPosition, bool direction)
10494 {
10495     auto selectTextContent = GetContentBySpans();
10496     std::u16string u16Content = StringUtils::Str8ToStr16(selectTextContent);
10497     // get select content
10498     std::u16string selectData16 = u16Content.substr(static_cast<int32_t>(start), static_cast<int32_t>(end - start));
10499     std::string selectData = StringUtils::Str16ToStr8(selectData16);
10500     int32_t aiPosStart;
10501     int32_t aiPosEnd;
10502     int32_t caretPosition;
10503     int32_t size = 1;
10504 
10505     if (direction) {
10506         caretPosition = end - start - size;
10507         DataDetectorMgr::GetInstance().AdjustWordSelection(caretPosition, selectData, aiPosStart, aiPosEnd);
10508         aiPosition = aiPosStart + start;
10509     } else {
10510         caretPosition = 0;
10511         DataDetectorMgr::GetInstance().AdjustWordSelection(caretPosition, selectData, aiPosStart, aiPosEnd);
10512         aiPosition = aiPosEnd + start;
10513     }
10514     if (aiPosStart < 0 || aiPosEnd < 0) {
10515         aiPosition = GetCaretPosition();
10516     }
10517 }
10518 
10519 bool RichEditorPattern::HandleOnDeleteComb(bool backward)
10520 {
10521     CloseSelectOverlay();
10522     ResetSelection();
10523     int32_t startPosition = 0;
10524     int32_t endPosition = 0;
10525     int32_t index = GetCaretPosition();
10526     int32_t aiContentStart = 0;
10527     int32_t aiContentEnd = GetTextContentLength();
10528 
10529     if (backward) {
10530         int32_t currentCaretPosition = caretPosition_ - 1;
10531         AdjustSelector(currentCaretPosition, HandleType::FIRST);
10532         if (caretPosition_ - currentCaretPosition > 1) {
10533             DeleteBackward(1);
10534             return true;
10535         }
10536         aiContentStart = std::clamp(index - RICH_DEFAULT_AI_WORD, 0, GetTextContentLength());
10537         AIDeleteComb(aiContentStart, index, startPosition, backward);
10538         if (startPosition == caretPosition_) {
10539             return false;
10540         }
10541         DeleteBackward(caretPosition_ - startPosition);
10542         SetCaretPosition(startPosition);
10543     } else {
10544         aiContentEnd = std::clamp(index + RICH_DEFAULT_AI_WORD, 0, GetTextContentLength());
10545         AIDeleteComb(index, aiContentEnd, endPosition, backward);
10546         if (endPosition == caretPosition_) {
10547             return false;
10548         }
10549         DeleteForward(endPosition - caretPosition_);
10550     }
10551     MoveCaretToContentRect();
10552     StartTwinkling();
10553     auto host = GetHost();
10554     CHECK_NULL_RETURN(host, false);
10555     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
10556     return true;
10557 }
10558 
10559 void RichEditorPattern::HandleTripleClickEvent(OHOS::Ace::GestureEvent& info)
10560 {
10561     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleTripleClickEvent");
10562     CHECK_EQUAL_VOID(IsPreviewTextInputting(), true);
10563     CHECK_EQUAL_VOID(IsDragging(), true);
10564     auto focusHub = GetFocusHub();
10565     CHECK_NULL_VOID(focusHub);
10566     CHECK_EQUAL_VOID(focusHub->IsFocusable(), false);
10567     auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
10568     Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
10569         info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
10570     int32_t pos = paragraphs_.GetIndex(textOffset);
10571 
10572     int32_t start = 0;
10573     int32_t end = 0;
10574     auto& paragraphInfoList = paragraphs_.GetParagraphs();
10575     if (pos == paragraphInfoList.back().end) {
10576         start = paragraphInfoList.back().start;
10577         end = paragraphInfoList.back().end;
10578     } else {
10579         for (const auto& paragraph : paragraphInfoList) {
10580             if (pos >= paragraph.start && pos < paragraph.end) {
10581                 start = paragraph.start;
10582                 end = paragraph.end;
10583                 break;
10584             }
10585         }
10586     }
10587     if (paragraphInfoList.back().end != end) {
10588         --end;
10589     }
10590     end = std::min(GetTextContentLength(), end);
10591     start = std::min(GetTextContentLength(), start);
10592     CHECK_EQUAL_VOID(start > end, true);
10593     TripleClickSection(info, start, end, pos);
10594 }
10595 
10596 void RichEditorPattern::OnSelectionMenuOptionsUpdate(
10597     const NG::OnCreateMenuCallback&& onCreateMenuCallback, const NG::OnMenuItemClickCallback&& onMenuItemClick)
10598 {
10599     selectOverlay_->OnSelectionMenuOptionsUpdate(std::move(onCreateMenuCallback), std::move(onMenuItemClick));
10600 }
10601 
10602 bool RichEditorPattern::CheckTripClickEvent(GestureEvent& info)
10603 {
10604     clickInfo_.push_back(info.GetTimeStamp());
10605     if (clickInfo_.size() > MAX_CLICK) {
10606         clickInfo_.erase(clickInfo_.begin());
10607     }
10608     if (clickInfo_.size() == MAX_CLICK) {
10609         std::chrono::duration<float, std::ratio<1, InputAIChecker::SECONDS_TO_MILLISECONDS>>
10610             clickTimeIntervalOne = clickInfo_[1] - clickInfo_[0];
10611         std::chrono::duration<float, std::ratio<1, InputAIChecker::SECONDS_TO_MILLISECONDS>>
10612             clickTimeIntervalTwo = clickInfo_[2] - clickInfo_[1];
10613         if (clickTimeIntervalOne.count() < DOUBLE_CLICK_INTERVAL_MS
10614             && clickTimeIntervalTwo.count() < DOUBLE_CLICK_INTERVAL_MS) {
10615             return true;
10616         }
10617     }
10618     return false;
10619 }
10620 
10621 void RichEditorPattern::PreferredParagraph()
10622 {
10623     CHECK_NULL_VOID(typingTextStyle_.has_value());
10624     if (presetParagraph_) {
10625         presetParagraph_->Reset();
10626         presetParagraph_ = nullptr;
10627     }
10628     std::string textContent;
10629     textContent = "a";
10630     TextStyle textStyle;
10631     textStyle = typingTextStyle_.value();
10632     ParagraphStyle paraStyle {
10633         .align = textStyle.GetTextAlign(),
10634         .maxLines = textStyle.GetMaxLines(),
10635         .fontLocale = Localization::GetInstance()->GetFontLocale(),
10636         .wordBreak = textStyle.GetWordBreak(),
10637         .lineBreakStrategy = textStyle.GetLineBreakStrategy(),
10638         .textOverflow = textStyle.GetTextOverflow(),
10639         .fontSize = textStyle.GetFontSize().ConvertToPx() };
10640     presetParagraph_ = Paragraph::Create(paraStyle, FontCollection::Current());
10641     CHECK_NULL_VOID(presetParagraph_);
10642     presetParagraph_->PushStyle(textStyle);
10643     presetParagraph_->AddText(StringUtils::Str8ToStr16(textContent));
10644     presetParagraph_->Build();
10645     presetParagraph_->Layout(std::numeric_limits<double>::infinity());
10646 }
10647 
10648 void RichEditorPattern::ShowCaretNoTwinkling(const Offset& textOffset)
10649 {
10650     auto position = paragraphs_.GetIndex(textOffset);
10651     SetCaretPosition(position);
10652     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "show caret no twinkling at position=%{public}d", position);
10653     StopTwinkling();
10654     caretVisible_ = true;
10655     isMoveCaretAnywhere_ = true;
10656     auto host = GetHost();
10657     CHECK_NULL_VOID(host);
10658     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
10659 
10660     CHECK_NULL_VOID(overlayMod_);
10661     auto [lastClickOffset, caretHeight] = CalcAndRecordLastClickCaretInfo(textOffset);
10662     auto localOffset = OffsetF(textOffset.GetX(), lastClickOffset.GetY());
10663     DynamicCast<RichEditorOverlayModifier>(overlayMod_)->SetCaretOffsetAndHeight(localOffset, caretHeight);
10664 
10665     // select + long press, so cancel selection.
10666     if (textSelector_.IsValid()) {
10667         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "select + long press, so cancel selection");
10668         CloseSelectOverlay();
10669         ResetSelection();
10670     }
10671 }
10672 
10673 void RichEditorPattern::UpdateSelectionByTouchMove(const Offset& touchOffset)
10674 {
10675     // While previewing + long press and move, then shall select content.
10676     auto host = GetHost();
10677     CHECK_NULL_VOID(host);
10678 
10679     Offset textOffset = ConvertTouchOffsetToTextOffset(touchOffset);
10680     auto positionWithAffinity = paragraphs_.GetGlyphPositionAtCoordinate(textOffset);
10681     SetCaretPositionWithAffinity(positionWithAffinity);
10682     MoveCaretToContentRect();
10683     int32_t currentPosition = GreatNotEqual(textOffset.GetY(), paragraphs_.GetHeight())
10684                                 ? GetTextContentLength()
10685                                 : caretPosition_;
10686     auto localOffset = OffsetF(touchOffset.GetX(), touchOffset.GetY());
10687     if (magnifierController_ && GetTextContentLength() > 0) {
10688         magnifierController_->SetLocalOffset(localOffset);
10689     }
10690     auto [initSelectStart, initSelectEnd] = initSelector_;
10691     int32_t start = std::min(initSelectStart, currentPosition);
10692     int32_t end = std::max(initSelectEnd, currentPosition);
10693     if (start == textSelector_.GetTextStart()) {
10694         StartVibratorByIndexChange(end, textSelector_.GetTextEnd());
10695     } else {
10696         StartVibratorByIndexChange(start, textSelector_.GetTextStart());
10697     }
10698     HandleSelectionChange(start, end);
10699     TriggerAvoidOnCaretChange();
10700     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
10701 }
10702 
10703 void RichEditorPattern::MoveCaretAnywhere(const Offset& offset)
10704 {
10705     // While editing + long press and move, then shall move caret:caret show anywhere on the fonts.
10706     CHECK_NULL_VOID(isMoveCaretAnywhere_);
10707     Offset textOffset = ConvertTouchOffsetToTextOffset(offset);
10708     auto position = paragraphs_.GetIndex(textOffset);
10709     AdjustCursorPosition(position);
10710     SetCaretPosition(position);
10711     auto [caretOffset, caretHeight] = CalcAndRecordLastClickCaretInfo(textOffset);
10712     CHECK_NULL_VOID(overlayMod_);
10713     auto localOffset = OffsetF(offset.GetX(), caretOffset.GetY());
10714 
10715     auto host = GetHost();
10716     CHECK_NULL_VOID(host);
10717     auto geometryNode = host->GetGeometryNode();
10718     CHECK_NULL_VOID(geometryNode);
10719     auto frameSize = geometryNode->GetFrameSize();
10720     // make sure the caret is display in the range of frame.
10721     localOffset.SetX(std::clamp(localOffset.GetX(), 0.0f, frameSize.Width()));
10722     DynamicCast<RichEditorOverlayModifier>(overlayMod_)->SetCaretOffsetAndHeight(localOffset, caretHeight);
10723     MoveCaretToContentRect(localOffset, caretHeight);
10724 }
10725 
10726 void RichEditorPattern::TripleClickSection(GestureEvent& info, int32_t start, int32_t end, int32_t pos)
10727 {
10728     auto host = GetHost();
10729     CHECK_NULL_VOID(host);
10730     textSelector_.Update(start, end);
10731     if (info.GetSourceTool() == SourceTool::FINGER) {
10732         showSelect_ = true;
10733         RequestKeyboard(false, true, true);
10734         HandleOnEditChanged(true);
10735         SetCaretPositionWithAffinity({ end, TextAffinity::UPSTREAM });
10736         MoveCaretToContentRect();
10737         CalculateHandleOffsetAndShowOverlay();
10738         selectOverlay_->ProcessOverlay({ .menuIsShow = !selectOverlay_->GetIsHandleMoving(), .animation = true });
10739     }
10740     if (info.GetSourceTool() == SourceTool::FINGER && start == end) {
10741         selectOverlay_->SetIsSingleHandle(true);
10742     }
10743     if (textSelector_.SelectNothing()) {
10744         textSelector_.Update(pos, pos);
10745     } else {
10746         StopTwinkling();
10747     }
10748     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
10749 }
10750 
10751 void RichEditorPattern::RequestKeyboardToEdit()
10752 {
10753     CHECK_NULL_VOID(!isEditing_ && HasFocus());
10754     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "request keyboard and enter edit");
10755     RequestKeyboard(false, true, true);
10756     HandleOnEditChanged(true);
10757 }
10758 
10759 bool RichEditorPattern::IsResponseRegionExpandingNeededForStylus(const TouchEvent& touchEvent) const
10760 {
10761     if (touchEvent.sourceTool != SourceTool::PEN || touchEvent.type != TouchType::DOWN) {
10762         return false;
10763     }
10764     auto host = GetHost();
10765     CHECK_NULL_RETURN(host, false);
10766     auto focusHub = host->GetFocusHub();
10767     CHECK_NULL_RETURN(focusHub, false);
10768     if (!focusHub->IsFocusable() || !host->IsVisible()) {
10769         return false;
10770     }
10771     auto renderContext = host->GetRenderContext();
10772     CHECK_NULL_RETURN(renderContext, false);
10773     auto opacity = renderContext->GetOpacity();
10774     // if opacity is 0.0f, no need to hit frameNode.
10775     if (NearZero(opacity.value_or(1.0f))) {
10776         return false;
10777     }
10778     return true;
10779 }
10780 
10781 RectF RichEditorPattern::ExpandDefaultResponseRegion(RectF& rect)
10782 {
10783     return rect + NG::SizeF(0, OHOS::Ace::HOT_AREA_ADJUST_SIZE.ConvertToPx() * OHOS::Ace::HOT_AREA_EXPAND_TIME) +
10784            NG::OffsetF(0, -OHOS::Ace::HOT_AREA_ADJUST_SIZE.ConvertToPx());
10785 }
10786 
10787 bool RichEditorPattern::InsertOrDeleteSpace(int32_t index)
10788 {
10789     // delete or insert space
10790     if (index < 0 || index >= GetTextContentLength()) {
10791         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "index is invalid, index=%{public}d", index);
10792         return false;
10793     }
10794     bool success = SetCaretPosition(index);
10795     CHECK_NULL_RETURN(success, false);
10796     CloseSelectOverlay();
10797     ResetSelection();
10798 
10799     auto curIt = GetSpanIter(index);
10800     if (curIt != spans_.end()) {
10801         std::wstring curText = StringUtils::ToWstring((*curIt)->content);
10802         if ((*curIt)->spanItemType == SpanItemType::NORMAL
10803             && index >= (*curIt)->rangeStart && curText[index - (*curIt)->rangeStart] == L' ') {
10804             TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "delete forward");
10805             DeleteForward(1);
10806             return true;
10807         }
10808     }
10809 
10810     auto preIt = GetSpanIter(index - 1);
10811     if (preIt != spans_.end()) {
10812         std::wstring preText = StringUtils::ToWstring((*preIt)->content);
10813         if ((*preIt)->spanItemType == SpanItemType::NORMAL
10814             && index - 1 >= (*preIt)->rangeStart && preText[index - 1 - (*preIt)->rangeStart] == L' ') {
10815             TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "delete backward");
10816             DeleteBackward(1);
10817             return true;
10818         }
10819     }
10820 
10821     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "insert value");
10822     InsertValue(" ", true);
10823     return true;
10824 }
10825 
10826 void RichEditorPattern::DeleteRange(int32_t start, int32_t end)
10827 {
10828     if (start > end) {
10829         std::swap(start, end);
10830     }
10831     start = std::max(0, start);
10832     end = std::min(GetTextContentLength(), end);
10833     if (start > GetTextContentLength() || end < 0 || start == end) {
10834         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "start=%{public}d, end=%{public}d, not in the range", start, end);
10835         return;
10836     }
10837     CHECK_NULL_VOID(!IsPreviewTextInputting());
10838     SetCaretPosition(start);
10839     auto length = end - start;
10840     if (isSpanStringMode_) {
10841         DeleteValueInStyledString(start, length, true, false);
10842         return;
10843     }
10844     OperationRecord record;
10845     record.beforeCaretPosition = caretPosition_;
10846     RichEditorChangeValue changeValue;
10847     CHECK_NULL_VOID(BeforeChangeText(changeValue, record, RecordType::DEL_FORWARD, length));
10848     std::wstringstream wss;
10849     for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
10850         wss << StringUtils::ToWstring((*iter)->content);
10851     }
10852     auto textContent = wss.str();
10853     auto realEnd = std::clamp(caretPosition_ + length, 0, static_cast<int32_t>(textContent.length()));
10854     std::wstring deleteText = textContent.substr(
10855         static_cast<uint32_t>(std::clamp(caretPosition_, 0, static_cast<int32_t>(textContent.length()))),
10856         static_cast<uint32_t>(realEnd - caretPosition_));
10857     if (caretPosition_ != GetTextContentLength()) {
10858         RichEditorDeleteValue info;
10859         info.SetOffset(caretPosition_);
10860         info.SetRichEditorDeleteDirection(RichEditorDeleteDirection::FORWARD);
10861         info.SetLength(length);
10862         int32_t currentPosition = caretPosition_;
10863         if (!spans_.empty()) {
10864             CalcDeleteValueObj(currentPosition, length, info);
10865             bool doDelete = DoDeleteActions(currentPosition, length, info);
10866             CHECK_NULL_VOID(doDelete);
10867         }
10868     }
10869     CHECK_NULL_VOID(deleteText.length() != 0);
10870     ClearRedoOperationRecords();
10871     record.deleteText = StringUtils::ToString(deleteText);
10872     record.afterCaretPosition = caretPosition_;
10873     AddOperationRecord(record);
10874     AfterContentChange(changeValue);
10875 }
10876 
10877 TextStyle RichEditorPattern::GetDefaultTextStyle()
10878 {
10879     auto theme = GetTheme<RichEditorTheme>();
10880     TextStyle style = theme ? theme->GetTextStyle() : TextStyle();
10881     style.SetFontSize(Dimension(16.0f, DimensionUnit::VP));
10882     style.SetFontFeatures(ParseFontFeatureSettings("\"pnum\" 1"));
10883     style.SetFontFamilies({ "HarmonyOS Sans" });
10884     return style;
10885 }
10886 
10887 bool RichEditorPattern::IsTextEditableForStylus() const
10888 {
10889     auto host = GetHost();
10890     CHECK_NULL_RETURN(host, false);
10891     auto focusHub = host->GetFocusHub();
10892     CHECK_NULL_RETURN(focusHub, false);
10893     if (!focusHub->IsFocusable() || !host->IsVisible()) {
10894         return false;
10895     }
10896     auto renderContext = host->GetRenderContext();
10897     CHECK_NULL_RETURN(renderContext, false);
10898     auto opacity = renderContext->GetOpacity();
10899     // if opacity is 0.0f, no need to hit frameNode.
10900     if (NearZero(opacity.value_or(1.0f))) {
10901         return false;
10902     }
10903     return true;
10904 }
10905 
10906 bool RichEditorPattern::IsShowAIWrite()
10907 {
10908     CHECK_NULL_RETURN(!textSelector_.SelectNothing(), false);
10909     auto container = Container::Current();
10910     if (container && container->IsScenceBoardWindow()) {
10911         return false;
10912     }
10913 
10914     if (copyOption_ == CopyOptions::None) {
10915         return false;
10916     }
10917     auto theme = GetTheme<RichEditorTheme>();
10918     CHECK_NULL_RETURN(theme, false);
10919     auto bundleName = theme->GetAIWriteBundleName();
10920     auto abilityName = theme->GetAIWriteAbilityName();
10921     if (bundleName.empty() || abilityName.empty()) {
10922         return false;
10923     }
10924     aiWriteAdapter_->SetBundleName(bundleName);
10925     aiWriteAdapter_->SetAbilityName(abilityName);
10926     TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
10927         "BundleName: %{public}s, abilityName: %{public}s", bundleName.c_str(), abilityName.c_str());
10928 
10929     auto isAISupport = false;
10930     if (theme->GetAIWriteIsSupport() == "true") {
10931         isAISupport = true;
10932     }
10933     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "isAISupport: %{public}d", isAISupport);
10934     return isAISupport;
10935 }
10936 
10937 void RichEditorPattern::GetAIWriteInfo(AIWriteInfo& info)
10938 {
10939     CHECK_NULL_VOID(!textSelector_.SelectNothing());
10940     info.firstHandle = textSelector_.firstHandle.ToString();
10941     info.secondHandle = textSelector_.secondHandle.ToString();
10942     info.selectStart = textSelector_.GetTextStart();
10943     info.selectEnd = textSelector_.GetTextEnd();
10944     auto host = GetHost();
10945     CHECK_NULL_VOID(host);
10946     info.componentType = host->GetTag();
10947 
10948     // serialize the sentenced-level text
10949     auto textSize = static_cast<int32_t>(GetWideText().length()) + placeholderCount_;
10950     RefPtr<SpanString> spanString = ToStyledString(0, textSize);
10951     auto contentAll = spanString->GetWideString();
10952     auto sentenceStart = 0;
10953     auto sentenceEnd = textSize;
10954     for (int32_t i = info.selectStart; i >= 0; --i) {
10955         if (aiWriteAdapter_->IsSentenceBoundary(contentAll[i])) {
10956             sentenceStart = i + 1;
10957             break;
10958         }
10959     }
10960     for (int32_t i = info.selectEnd; i < textSize; i++) {
10961         if (aiWriteAdapter_->IsSentenceBoundary(contentAll[i])) {
10962             sentenceEnd = i;
10963             break;
10964         }
10965     }
10966     info.start = info.selectStart - sentenceStart;
10967     info.end = info.selectEnd - sentenceStart;
10968     spanString = ToStyledString(sentenceStart, sentenceEnd);
10969     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "Sentence range=[%{public}d--%{public}d], content = %{private}s",
10970         sentenceStart, sentenceEnd, spanString->GetString().c_str());
10971     spanString->EncodeTlv(info.sentenceBuffer);
10972 
10973     // serialize the selected text
10974     spanString = ToStyledString(info.selectStart, info.selectEnd);
10975     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "Selected range=[%{public}d--%{public}d], content = %{private}s",
10976         info.selectStart, info.selectEnd, spanString->GetString().c_str());
10977     spanString->EncodeTlv(info.selectBuffer);
10978     info.selectLength = static_cast<int32_t>(aiWriteAdapter_->GetSelectLengthOnlyText(spanString->GetWideString()));
10979 }
10980 
10981 void RichEditorPattern::HandleOnAIWrite()
10982 {
10983     aiWriteAdapter_->SetAIWrite(true);
10984     AIWriteInfo info;
10985     GetAIWriteInfo(info);
10986     CloseSelectOverlay();
10987     CloseKeyboard(true);
10988 
10989     auto callback = [weak = WeakClaim(this), info](std::vector<uint8_t>& buffer) {
10990         auto pattern = weak.Upgrade();
10991         CHECK_NULL_VOID(pattern);
10992         pattern->HandleAIWriteResult(info.selectStart, info.selectEnd, buffer);
10993         auto aiWriteAdapter = pattern->aiWriteAdapter_;
10994         CHECK_NULL_VOID(aiWriteAdapter);
10995         aiWriteAdapter->CloseModalUIExtension();
10996     };
10997     auto host = GetHost();
10998     CHECK_NULL_VOID(host);
10999     auto pipeline = host->GetContext();
11000     CHECK_NULL_VOID(pipeline);
11001     aiWriteAdapter_->SetPipelineContext(WeakClaim(pipeline));
11002     aiWriteAdapter_->ShowModalUIExtension(info, callback);
11003 }
11004 
11005 SymbolSpanOptions RichEditorPattern::GetSymbolSpanOptions(const RefPtr<SpanItem>& spanItem)
11006 {
11007     CHECK_NULL_RETURN(spanItem, {});
11008     TextStyle textStyle = GetDefaultTextStyle();
11009     UseSelfStyle(spanItem->fontStyle, spanItem->textLineStyle, textStyle);
11010     SymbolSpanOptions options;
11011     options.style = textStyle;
11012     options.offset = caretPosition_;
11013     options.resourceObject = spanItem->GetResourceObject();
11014     options.symbolId = spanItem->GetSymbolId();
11015     return options;
11016 }
11017 
11018 void RichEditorPattern::ReplacePlaceholderWithCustomSpan(
11019     const RefPtr<SpanItem>& spanItem, size_t& index, size_t& textIndex)
11020 {
11021     if (isSpanStringMode_) {
11022         auto customSpanItem = DynamicCast<CustomSpanItem>(spanItem);
11023         auto customSpan = MakeRefPtr<CustomSpan>();
11024         if (customSpanItem->onMeasure.has_value()) {
11025             customSpan->SetOnMeasure(customSpanItem->onMeasure.value());
11026         }
11027         if (customSpanItem->onDraw.has_value()) {
11028             customSpan->SetOnDraw(customSpanItem->onDraw.value());
11029         }
11030         auto spanString = MakeRefPtr<MutableSpanString>(customSpan);
11031         InsertStyledStringByPaste(spanString);
11032     } else {
11033         auto customSpanItem = DynamicCast<PlaceholderSpanItem>(spanItem);
11034         CHECK_NULL_VOID(customSpanItem);
11035         auto customNode = customSpanItem->GetCustomNode();
11036         SpanOptionBase options;
11037         options.offset = caretPosition_;
11038         AddPlaceholderSpan(customNode, options);
11039     }
11040     textIndex = index + PLACEHOLDER_LENGTH;
11041 }
11042 
11043 void RichEditorPattern::ReplacePlaceholderWithSymbolSpan(
11044     const RefPtr<SpanItem>& spanItem, size_t& index, size_t& textIndex)
11045 {
11046     auto options = GetSymbolSpanOptions(spanItem);
11047     options.offset = caretPosition_;
11048     AddSymbolSpan(options, false, caretPosition_);
11049     textIndex = index + PLACEHOLDER_LENGTH;
11050 }
11051 
11052 void RichEditorPattern::ReplacePlaceholderWithImageSpan(
11053     const RefPtr<SpanItem>& spanItem, size_t& index, size_t& textIndex)
11054 {
11055     auto imageSpanItem = DynamicCast<ImageSpanItem>(spanItem);
11056     CHECK_NULL_VOID(imageSpanItem);
11057     auto options = imageSpanItem->options;
11058     options.offset = caretPosition_;
11059     if (isSpanStringMode_) {
11060         auto spanString = MakeRefPtr<SpanString>(options);
11061         InsertStyledStringByPaste(spanString);
11062     } else {
11063         AddImageSpan(options, true, caretPosition_, true);
11064     }
11065     textIndex = index + PLACEHOLDER_LENGTH;
11066 }
11067 
11068 void RichEditorPattern::ReplacePlaceholderWithRawSpans(
11069     const RefPtr<SpanItem>& spanItem, size_t& index, size_t& textIndex)
11070 {
11071     switch (spanItem->spanItemType) {
11072         case SpanItemType::SYMBOL:
11073             ReplacePlaceholderWithSymbolSpan(spanItem, index, textIndex);
11074             return;
11075         case SpanItemType::CustomSpan:
11076             ReplacePlaceholderWithCustomSpan(spanItem, index, textIndex);
11077             return;
11078         case SpanItemType::IMAGE:
11079             ReplacePlaceholderWithImageSpan(spanItem, index, textIndex);
11080             return;
11081         default:
11082             return;
11083     }
11084 }
11085 
11086 void RichEditorPattern::AddSpansAndReplacePlaceholder(RefPtr<SpanString>& spanString)
11087 {
11088     auto content = spanString->GetWideString();
11089     size_t textIndex = 0;
11090     size_t index = content.find(PLACEHOLDER_MARK);
11091 
11092     while (index != std::string::npos) {
11093         if (textIndex < index) {
11094             auto subSpan = spanString->GetSubSpanString(textIndex, index - textIndex);
11095             AddSpanByPasteData(subSpan);
11096         }
11097         auto key = StringUtils::ToString(content.substr(index, PLACEHOLDER_LENGTH));
11098         if (placeholderSpansMap_.find(key) == placeholderSpansMap_.end()) {
11099             index = content.find(PLACEHOLDER_MARK, index + 1);
11100             continue;
11101         }
11102         auto spanItem = placeholderSpansMap_[key];
11103         if (!spanItem) {
11104             index = content.find(PLACEHOLDER_MARK, index + 1);
11105             continue;
11106         }
11107         ReplacePlaceholderWithRawSpans(spanItem, index, textIndex);
11108         index = content.find(PLACEHOLDER_MARK, index + 1);
11109     }
11110     if (textIndex < content.length()) {
11111         auto subSpan = spanString->GetSubSpanString(textIndex, content.length() - textIndex);
11112         AddSpanByPasteData(subSpan);
11113     }
11114 }
11115 
11116 void RichEditorPattern::InsertSpanByBackData(RefPtr<SpanString>& spanString)
11117 {
11118     CHECK_NULL_VOID(spanString);
11119     if (textSelector_.IsValid()) {
11120         SetCaretPosition(textSelector_.GetTextStart());
11121         DeleteForward(textSelector_.GetTextStart(), textSelector_.GetTextEnd() - textSelector_.GetTextStart());
11122         ResetSelection();
11123     }
11124     if (placeholderSpansMap_.empty()) {
11125         AddSpanByPasteData(spanString);
11126     } else {
11127         AddSpansAndReplacePlaceholder(spanString);
11128     }
11129     StartTwinkling();
11130     auto host = GetHost();
11131     CHECK_NULL_VOID(host);
11132     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
11133     host->MarkModifyDone();
11134 }
11135 
11136 void RichEditorPattern::HandleAIWriteResult(int32_t start, int32_t end, std::vector<uint8_t>& buffer)
11137 {
11138     RefPtr<SpanString> spanString = SpanString::DecodeTlv(buffer);
11139     if (spanString->GetSpanItems().empty()) {
11140         return;
11141     }
11142     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "Backfilling results range=[%{public}d--%{public}d], content = %{private}s",
11143         start, end, spanString->GetString().c_str());
11144     textSelector_.Update(start, end);
11145     auto length = end - start;
11146     CHECK_NULL_VOID(length > 0);
11147     DeleteBackward(length);
11148     InsertSpanByBackData(spanString);
11149     BeforeIMEInsertValue(spanString->GetString());
11150     InsertValue("");
11151 }
11152 } // namespace OHOS::Ace::NG
11153