• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #define NAPI_VERSION 8
17 
18 #include "core/components_ng/pattern/rich_editor/rich_editor_pattern.h"
19 
20 #include <algorithm>
21 #include <chrono>
22 #include <cstddef>
23 #include <cstdint>
24 #include <functional>
25 #include <future>
26 #include <iterator>
27 #include <sstream>
28 #include <string>
29 #include <utility>
30 
31 #include "interfaces/inner_api/ui_session/ui_session_manager.h"
32 #ifdef ANDROID_PLATFORM
33 #include "adapter/android/capability/java/jni/editing/text_input_client_handler.h"
34 #endif
35 #ifdef IOS_PLATFORM
36 #include "adapter/ios/capability/editing/text_input_client_handler.h"
37 #endif
38 #include "adapter/ohos/capability/clipboard/clipboard_impl.h"
39 #include "base/geometry/offset.h"
40 #include "base/i18n/localization.h"
41 #include "base/log/ace_trace.h"
42 #include "base/log/dump_log.h"
43 #include "base/log/log_wrapper.h"
44 #include "base/memory/ace_type.h"
45 #include "base/utils/string_utils.h"
46 #include "base/utils/utf_helper.h"
47 #include "base/utils/utils.h"
48 #include "core/common/ai/data_detector_mgr.h"
49 #include "core/common/clipboard/paste_data.h"
50 #include "core/common/container.h"
51 #include "core/common/container_scope.h"
52 #include "core/common/ime/text_input_client.h"
53 #include "core/common/share/text_share_adapter.h"
54 #include "core/common/stylus/stylus_detector_mgr.h"
55 #include "core/common/vibrator/vibrator_utils.h"
56 #include "core/components/common/layout/constants.h"
57 #include "core/components/common/properties/text_style_parser.h"
58 #include "core/components_ng/base/inspector_filter.h"
59 #include "core/components_ng/base/observer_handler.h"
60 #include "core/components_ng/base/view_stack_processor.h"
61 #include "core/components_ng/event/event_hub.h"
62 #include "core/components_ng/event/gesture_event_hub.h"
63 #include "core/components_ng/event/long_press_event.h"
64 #include "core/components_ng/pattern/image/image_pattern.h"
65 #include "core/components_ng/pattern/overlay/keyboard_base_pattern.h"
66 #include "core/components_ng/pattern/rich_editor/color_mode_processor.h"
67 #include "core/components_ng/pattern/rich_editor/one_step_drag_controller.h"
68 #include "core/components_ng/pattern/rich_editor/rich_editor_content_pattern.h"
69 #include "core/components_ng/pattern/rich_editor/rich_editor_event_hub.h"
70 #include "core/components_ng/pattern/rich_editor/rich_editor_layout_property.h"
71 #include "core/components_ng/pattern/rich_editor/rich_editor_model.h"
72 #include "core/components_ng/pattern/rich_editor/rich_editor_undo_manager.h"
73 #include "core/components_ng/pattern/rich_editor/rich_editor_utils.h"
74 #include "core/components_ng/pattern/rich_editor/style_manager.h"
75 #include "core/components_ng/pattern/rich_editor_drag/rich_editor_drag_pattern.h"
76 #include "core/components_ng/pattern/text_field/text_field_manager.h"
77 #include "core/components_ng/pattern/text_field/text_input_ai_checker.h"
78 #include "core/text/html_utils.h"
79 
80 #ifndef ACE_UNITTEST
81 #ifdef ENABLE_STANDARD_INPUT
82 #include "refbase.h"
83 #include "adapter/ohos/entrance/ace_container.h"
84 #include "core/components_ng/pattern/text_field/on_text_changed_listener_impl.h"
85 #endif
86 #endif
87 
88 #include "core/common/udmf/udmf_client.h"
89 
90 #ifdef WINDOW_SCENE_SUPPORTED
91 #include "core/components_ng/pattern/window_scene/helper/window_scene_helper.h"
92 #endif
93 
94 #ifdef ENABLE_ROSEN_BACKEND
95 #include "core/components/custom_paint/rosen_render_custom_paint.h"
96 #endif
97 
98 namespace OHOS::Ace::NG {
99 
100 namespace {
101 #if defined(ENABLE_STANDARD_INPUT)
102 // should be moved to theme
103 constexpr float DEFAULT_CARET_HEIGHT = 18.5f;
104 constexpr Dimension KEYBOARD_AVOID_OFFSET = 24.0_vp;
105 constexpr size_t MAX_PLACEHOLDER_SIZE = 255;
106 constexpr size_t MAX_ABILITY_NAME_SIZE = 127;
107 #endif
108 constexpr Dimension CARET_WIDTH = 2.0_vp;
109 constexpr int32_t IMAGE_SPAN_LENGTH = 1;
110 constexpr int32_t SYMBOL_SPAN_LENGTH = 2;
111 constexpr uint32_t RICH_EDITOR_TWINKLING_INTERVAL_MS = 500;
112 constexpr uint32_t RICH_EDITOR_TWINKLING_INTERVAL_MS_DEBUG = 3000;
113 constexpr int32_t AUTO_SCROLL_INTERVAL = 15;
114 constexpr Dimension CARET_BOTTOM_DISTANCE = 16.0_vp;
115 constexpr Dimension AUTO_SCROLL_EDGE_DISTANCE = 15.0_vp;
116 constexpr Dimension AUTO_SCROLL_DRAG_EDGE_DISTANCE = 58.0_vp;
117 constexpr float MAX_DRAG_SCROLL_SPEED = 2400.0f;
118 constexpr float TIME_UNIT = 1000.0f;
119 constexpr float DOUBLE_CLICK_INTERVAL_MS = 300.0f;
120 constexpr uint32_t RECORD_MAX_LENGTH = 20;
121 constexpr float DEFAILT_OPACITY = 0.2f;
122 constexpr int64_t COLOR_OPAQUE = 255;
123 constexpr int32_t MAX_CLICK = 3;
124 
125 constexpr Color SYSTEM_CARET_COLOR = Color(0xff007dff);
126 constexpr Color SYSTEM_SELECT_BACKGROUND_COLOR = Color(0x33007dff);
127 
128 const auto MAGNIFIER_ANIMATION_CURVE = AceType::MakeRefPtr<InterpolatingSpring>(0.0f, 1.0f, 228.0f, 30.0f);
129 constexpr int32_t MAGNIFIER_ANIMATION_DURATION = 100;
130 
131 constexpr int32_t ERROR_BAD_PARAMETERS = -1;
132 constexpr char PREVIEW_STYLE_NORMAL[] = "normal";
133 constexpr char PREVIEW_STYLE_UNDERLINE[] = "underline";
134 const std::u16string LINE_SEPARATOR = u"\n";
135 // hen do ai anaylsis, we should limit the left an right limit of the string
136 constexpr static int32_t AI_TEXT_RANGE_LEFT = 50;
137 constexpr static int32_t AI_TEXT_RANGE_RIGHT = 50;
138 constexpr static int32_t NONE_SELECT_TYPE = -1;
139 
140 constexpr float RICH_DEFAULT_SHADOW_COLOR = 0x33000000;
141 constexpr float RICH_DEFAULT_ELEVATION = 120.0f;
142 constexpr int32_t CUSTOM_CONTENT_LENGTH = 1;
143 constexpr int32_t SYMBOL_CONTENT_LENGTH = 2;
144 constexpr int32_t PLACEHOLDER_LENGTH = 6;
145 const std::u16string PLACEHOLDER_MARK = u"![id";
146 const std::string SPACE_CHARS = "^\\s+|\\s+$";
147 const std::string RICHEDITOR = "RichEditor.";
148 const std::string EVENT = "event";
149 const static std::regex REMOVE_SPACE_CHARS{SPACE_CHARS};
__anon211d89bc0202(const RefPtr<SpanItem>& span)150 const auto URL_SPAN_FILTER = [](const RefPtr<SpanItem>& span){ return (span->urlOnRelease); };
151 } // namespace
152 
RichEditorPattern(bool isStyledStringMode)153 RichEditorPattern::RichEditorPattern(bool isStyledStringMode) :
154 #ifndef ACE_UNITTEST
155     isAPI14Plus(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_FOURTEEN)),
156 #else
157     isAPI14Plus(true),
158 #endif
159     isAPI16Plus(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_SIXTEEN)),
160     isAPI18Plus(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)),
161     isAPI20Plus(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWENTY))
162 {
163     SetSpanStringMode(isStyledStringMode);
164     selectOverlay_ = AceType::MakeRefPtr<RichEditorSelectOverlay>(WeakClaim(this));
165     magnifierController_ = MakeRefPtr<MagnifierController>(WeakClaim(this));
166     styledString_ = MakeRefPtr<MutableSpanString>(u"");
167     styledString_->SetSpanWatcher(WeakClaim(this));
168     twinklingInterval_ = SystemProperties::GetDebugEnabled()
169         ? RICH_EDITOR_TWINKLING_INTERVAL_MS_DEBUG : RICH_EDITOR_TWINKLING_INTERVAL_MS;
170     floatingCaretState_.UpdateOriginCaretColor(GetDisplayColorMode());
171     undoManager_ = RichEditorUndoManager::Create(isSpanStringMode_, WeakClaim(this));
172     styleManager_ = std::make_unique<StyleManager>(WeakClaim(this));
173     if (!dataDetectorAdapter_) {
174         dataDetectorAdapter_ = MakeRefPtr<DataDetectorAdapter>();
175     }
176 }
177 
~RichEditorPattern()178 RichEditorPattern::~RichEditorPattern()
179 {
180     if (isCustomKeyboardAttached_) {
181         CloseCustomKeyboard();
182     }
183 #ifdef CROSS_PLATFORM
184     if (HasConnection()) {
185         connection_->Close(GetInstanceId());
186         connection_ = nullptr;
187     }
188 #endif
189 }
190 
SetStyledString(const RefPtr<SpanString> & value)191 void RichEditorPattern::SetStyledString(const RefPtr<SpanString>& value)
192 {
193     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "SetStyledString, len=%{public}d", value->GetLength());
194     SEC_TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "SetStyledString, str=%{public}s", value->GetString().c_str());
195     if (GetTextContentLength() > maxLength_.value_or(INT_MAX)) {
196         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "SetStyledString: Reach the maxLength. maxLength=%{public}d", maxLength_.value_or(INT_MAX));
197         return;
198     }
199     CHECK_NULL_VOID(value && styledString_);
200     auto subValue = value;
201     if (value->GetLength() != styledString_->GetLength() && value->GetLength() > maxLength_.value_or(INT_MAX)) {
202         auto subLength = CalculateTruncationLength(value->GetU16string(), maxLength_.value_or(INT_MAX));
203         if (subLength == 0) {
204             IF_TRUE(IsPreviewTextInputting() && !previewTextRecord_.previewTextExiting, NotifyExitTextPreview(true));
205             return;
206         }
207         subValue = value->GetSubSpanString(0, subLength);
208     }
209     IF_TRUE(IsPreviewTextInputting() && !previewTextRecord_.previewTextExiting, NotifyExitTextPreview(true));
210     auto length = styledString_->GetLength();
211     UndoRedoRecord record;
212     undoManager_->ApplyOperationToRecord(0, length, subValue, record);
213     CloseSelectOverlay();
214     ResetSelection();
215     styledString_->RemoveCustomSpan();
216     styledString_->ReplaceSpanString(0, length, subValue);
217     SetCaretPosition(styledString_->GetLength());
218     SetNeedMoveCaretToContentRect();
219     auto host = GetContentHost();
220     CHECK_NULL_VOID(host);
221     styledString_->AddCustomSpan();
222     styledString_->SetFramNode(host);
223     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
224     ForceTriggerAvoidOnCaretChange();
225     undoManager_->RecordOperation(record);
226     ReportAfterContentChangeEvent();
227 }
228 
UpdateSpanItems(const std::list<RefPtr<NG::SpanItem>> & spanItems)229 void RichEditorPattern::UpdateSpanItems(const std::list<RefPtr<NG::SpanItem>>& spanItems)
230 {
231     SetSpanItemChildren(spanItems);
232     ProcessStyledString();
233 }
234 
ProcessStyledString()235 void RichEditorPattern::ProcessStyledString()
236 {
237     auto host = GetContentHost();
238     CHECK_NULL_VOID(host);
239     std::u16string textCache = textForDisplay_;
240     textForDisplay_.clear();
241     dataDetectorAdapter_->textForAI_.clear();
242     host->Clean();
243     RemoveEmptySpanItems();
244     hasUrlSpan_ = false;
245     for (const auto& span : spans_) {
246         if (!span) {
247             continue;
248         }
249         auto imageSpan = DynamicCast<ImageSpanItem>(span);
250         if (imageSpan) {
251             MountImageNode(imageSpan);
252             dataDetectorAdapter_->textForAI_ += u'\n';
253         } else {
254             dataDetectorAdapter_->textForAI_ += span->content;
255         }
256         textForDisplay_ += span->content;
257         auto [spanStart, spanEnd] = span->interval;
258         span->rangeStart = spanStart;
259         span->position = spanEnd;
260 
261         if (span->urlOnRelease) {
262             hasUrlSpan_ = true;
263         }
264     }
265     if (textForDisplay_ != textCache) {
266         dataDetectorAdapter_->aiDetectInitialized_ = false;
267     }
268     if (CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
269         dataDetectorAdapter_->StartAITask();
270     }
271 }
272 
MountImageNode(const RefPtr<ImageSpanItem> & imageItem)273 void RichEditorPattern::MountImageNode(const RefPtr<ImageSpanItem>& imageItem)
274 {
275     auto host = GetContentHost();
276     CHECK_NULL_VOID(host);
277     CHECK_NULL_VOID(imageItem);
278     auto options = imageItem->options;
279     auto imageNode = ImageSpanNode::GetOrCreateSpanNode(V2::IMAGE_ETS_TAG,
280         ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<ImagePattern>(); });
281     auto pattern = imageNode->GetPattern<ImagePattern>();
282     CHECK_NULL_VOID(pattern);
283     if (options.imagePixelMap.has_value()) {
284         pattern->SetSyncLoad(true);
285     } else if (options.imageAttribute.has_value()) {
286         pattern->SetSyncLoad(options.imageAttribute.value().syncLoad);
287     }
288     auto index = host->GetChildren().size();
289     imageNodes.push_back(imageNode);
290     imageNode->MountToParent(host, index);
291     HandleImageDrag(imageNode);
292     SetImageLayoutProperty(imageNode, options);
293     imageItem->nodeId_ = imageNode->GetId();
294     imageNode->SetImageItem(imageItem);
295 }
296 
SetImageLayoutProperty(RefPtr<ImageSpanNode> imageNode,const ImageSpanOptions & options)297 void RichEditorPattern::SetImageLayoutProperty(RefPtr<ImageSpanNode> imageNode, const ImageSpanOptions& options)
298 {
299     CHECK_NULL_VOID(imageNode);
300     auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
301     CHECK_NULL_VOID(imageLayoutProperty);
302     std::function<ImageSourceInfo()> createSourceInfoFunc = CreateImageSourceInfo(options);
303     imageLayoutProperty->UpdateImageSourceInfo(createSourceInfoFunc());
304     if (options.imageAttribute.has_value()) {
305         auto imgAttr = options.imageAttribute.value();
306         if (imgAttr.size.has_value()) {
307             imageLayoutProperty->UpdateUserDefinedIdealSize(imgAttr.size->GetSize());
308         }
309         if (imgAttr.verticalAlign.has_value()) {
310             imageLayoutProperty->UpdateVerticalAlign(imgAttr.verticalAlign.value());
311         }
312         if (imgAttr.objectFit.has_value()) {
313             imageLayoutProperty->UpdateImageFit(imgAttr.objectFit.value());
314         }
315         if (imgAttr.marginProp.has_value()) {
316             imageLayoutProperty->UpdateMargin(imgAttr.marginProp.value());
317         }
318         if (imgAttr.paddingProp.has_value()) {
319             imageLayoutProperty->UpdatePadding(imgAttr.paddingProp.value());
320         }
321         if (imgAttr.borderRadius.has_value()) {
322             auto imageRenderCtx = imageNode->GetRenderContext();
323             imageRenderCtx->UpdateBorderRadius(imgAttr.borderRadius.value());
324             imageRenderCtx->SetClipToBounds(true);
325         }
326         auto paintProperty = imageNode->GetPaintProperty<ImageRenderProperty>();
327         if (imgAttr.colorFilterMatrix.has_value() && paintProperty) {
328             paintProperty->UpdateColorFilter(imgAttr.colorFilterMatrix.value());
329             paintProperty->ResetDrawingColorFilter();
330         } else if (imgAttr.drawingColorFilter.has_value() && paintProperty) {
331             paintProperty->UpdateDrawingColorFilter(imgAttr.drawingColorFilter.value());
332             paintProperty->ResetColorFilter();
333         }
334     }
335     imageNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
336     imageNode->MarkModifyDone();
337     IF_PRESENT(oneStepDragController_, MarkDirtyNode(WeakClaim(RawPtr(imageNode))));
338 }
339 
HandleStyledStringInsertion(RefPtr<SpanString> insertStyledString,const UndoRedoRecord & record,std::u16string & subValue,bool needReplaceInTextPreview,bool shouldCommitInput)340 void RichEditorPattern::HandleStyledStringInsertion(RefPtr<SpanString> insertStyledString, const UndoRedoRecord& record,
341     std::u16string& subValue, bool needReplaceInTextPreview, bool shouldCommitInput)
342 {
343     auto changeStart = record.rangeBefore.start;
344     auto changeLength = record.rangeBefore.GetLength();
345     if (changeLength > 0 && (subValue.length() > 0 || !shouldCommitInput)) {
346         auto start = needReplaceInTextPreview ? previewTextRecord_.replacedRange.start : caretPosition_;
347         auto isUpdateCaret = !needReplaceInTextPreview;
348         DeleteValueInStyledString(start, changeLength, false, isUpdateCaret);
349     }
350     bool isSingleHandleMoving = selectOverlay_->IsSingleHandleMoving();
351     if (textSelector_.IsValid()) {
352         ResetSelection();
353     }
354     CloseSelectOverlay();
355     if (insertStyledString) {
356         styledString_->InsertSpanString(changeStart, insertStyledString);
357     } else {
358         styledString_->InsertString(changeStart, subValue);
359     }
360     SetCaretPosition(changeStart + static_cast<int32_t>(subValue.length()), !needReplaceInTextPreview);
361     IF_TRUE((!caretVisible_ || isSingleHandleMoving) && HasFocus(), StartTwinkling());
362     auto host = GetContentHost();
363     CHECK_NULL_VOID(host);
364     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
365     host->MarkModifyDone();
366 }
367 
InsertValueInStyledString(const std::u16string & insertValue,bool shouldCommitInput,bool isPaste)368 void RichEditorPattern::InsertValueInStyledString(
369     const std::u16string& insertValue, bool shouldCommitInput, bool isPaste)
370 {
371     CHECK_NULL_VOID(styledString_);
372     IF_TRUE(shouldCommitInput && previewTextRecord_.IsValid(), FinishTextPreviewInner());
373     int32_t changeStart = caretPosition_;
374     int32_t changeLength = 0;
375     if (textSelector_.IsValid()) {
376         changeStart = textSelector_.GetTextStart();
377         changeLength = textSelector_.GetTextEnd() - changeStart;
378         IF_TRUE(!shouldCommitInput, undoManager_->RecordPreviewInputtingStart(changeStart, changeLength));
379     }
380     auto subValue = insertValue;
381     if (!ProcessTextTruncationOperation(subValue, shouldCommitInput)) {
382         return;
383     }
384     auto needReplaceInTextPreview = (previewTextRecord_.needReplacePreviewText || previewTextRecord_.needReplaceText) &&
385                                previewTextRecord_.replacedRange.end - previewTextRecord_.replacedRange.start > 0;
386     if (needReplaceInTextPreview) {
387         changeStart= previewTextRecord_.replacedRange.start;
388         changeLength = previewTextRecord_.replacedRange.end - previewTextRecord_.replacedRange.start;
389     }
390     UndoRedoRecord record;
391     bool isPreventChange = false;
392     auto insertStyledString = styleManager_->CreateStyledStringByTypingStyle(subValue, styledString_, changeStart, changeLength);
393     if (insertStyledString) {
394         undoManager_->ApplyOperationToRecord(changeStart, changeLength, insertStyledString, record);
395     } else {
396         undoManager_->ApplyOperationToRecord(changeStart, changeLength, subValue, record);
397     }
398     isPreventChange = !BeforeStyledStringChange(record);
399     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "InsertValueInSS isPreventChange=%{public}d, needReplacePreviewText=%{public}d",
400         isPreventChange, previewTextRecord_.needReplacePreviewText);
401     if (isPreventChange && !previewTextRecord_.needReplacePreviewText) {
402         IF_TRUE(shouldCommitInput, undoManager_->ClearPreviewInputRecord());
403         return;
404     }
405     HandleStyledStringInsertion(insertStyledString, record, subValue, needReplaceInTextPreview, shouldCommitInput);
406     IF_TRUE(shouldCommitInput, undoManager_->RecordInsertOperation(record));
407     AfterStyledStringChange(record);
408     IF_TRUE(!isPaste, OnReportRichEditorEvent("onIMEInputComplete"));
409 }
410 
DeleteBackwardInStyledString(int32_t length)411 void RichEditorPattern::DeleteBackwardInStyledString(int32_t length)
412 {
413     DeleteValueInStyledString(caretPosition_ - length, length);
414 }
415 
DeleteForwardInStyledString(int32_t length,bool isIME)416 void RichEditorPattern::DeleteForwardInStyledString(int32_t length, bool isIME)
417 {
418     DeleteValueInStyledString(caretPosition_, length, isIME);
419 }
420 
DeleteValueInStyledString(int32_t start,int32_t length,bool isIME,bool isUpdateCaret)421 void RichEditorPattern::DeleteValueInStyledString(int32_t start, int32_t length, bool isIME, bool isUpdateCaret)
422 {
423     CHECK_NULL_VOID(styledString_);
424     if (!textSelector_.SelectNothing()) {
425         start = textSelector_.GetTextStart();
426         length = textSelector_.GetTextEnd() - textSelector_.GetTextStart();
427     }
428     auto range = TextEmojiProcessor::CalSubU16stringRange(start, length, styledString_->GetU16string(), true, true);
429     start = range.startIndex;
430     length = range.endIndex - range.startIndex;
431     UndoRedoRecord record;
432     undoManager_->ApplyOperationToRecord(start, length, u"", record);
433     bool isPreventChange = isIME && !BeforeStyledStringChange(record);
434     TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
435         "deleteInSS, start=%{public}d, length=%{public}d, isPreventChange=%{public}d, "
436         "isPreviewTextInputting=%{public}d",
437         start, length, isPreventChange, IsPreviewTextInputting());
438     CHECK_NULL_VOID(!isPreventChange || IsPreviewTextInputting());
439     IF_TRUE(isIME && !IsPreviewTextInputting(), undoManager_->RecordOperation(record));
440     bool isSingleHandleMoving = selectOverlay_->IsSingleHandleMoving();
441     if (textSelector_.IsValid()) {
442         CloseSelectOverlay();
443         ResetSelection();
444     }
445     styledString_->RemoveString(start, length);
446     if (isUpdateCaret) {
447         SetCaretPosition(start, !isModifyingContent_);
448     }
449     if ((!caretVisible_ || isSingleHandleMoving) && HasFocus()) {
450         StartTwinkling();
451         if (!isEditing_ && isIME) {
452             TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "previewLongPress_ is true, before RequestKeyboard");
453             RequestKeyboard(false, true, true);
454             HandleOnEditChanged(true);
455             previewLongPress_ = false;
456         }
457     }
458     if (isIME) {
459         AfterStyledStringChange(record);
460     }
461     OnReportRichEditorEvent("onDeleteComplete");
462     auto host = GetHost();
463     CHECK_NULL_VOID(host);
464     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
465     host->MarkModifyDone();
466 }
467 
CreateStyledStringByStyleBefore(int32_t start,const std::u16string & string)468 RefPtr<SpanString> RichEditorPattern::CreateStyledStringByStyleBefore(int32_t start, const std::u16string& string)
469 {
470     auto styledString = AceType::MakeRefPtr<SpanString>(string);
471     CHECK_NULL_RETURN(styledString_, styledString);
472     auto stringLength = styledString->GetLength();
473     CHECK_NULL_RETURN(stringLength != 0, styledString);
474     auto lastStyles = styledString_->GetSpans(start - 1, 1);
475     for (auto && style : lastStyles) {
476         CHECK_NULL_CONTINUE(style);
477         auto spanType = style->GetSpanType();
478         CHECK_NULL_CONTINUE(spanType != SpanType::Image && spanType != SpanType::CustomSpan);
479         auto span = style->GetSubSpan(0, stringLength);
480         styledString->AddSpan(span);
481     }
482     return styledString;
483 }
484 
BeforeStyledStringChange(const UndoRedoRecord & record,bool isUndo)485 bool RichEditorPattern::BeforeStyledStringChange(const UndoRedoRecord& record, bool isUndo)
486 {
487     CHECK_NULL_RETURN(!record.isOnlyStyleChange, true);
488     auto changeStart = isUndo ? record.rangeAfter.start : record.rangeBefore.start;
489     auto changeLength = isUndo ? record.rangeAfter.GetLength() : record.rangeBefore.GetLength();
490     auto styledString = isUndo ? record.styledStringBefore : record.styledStringAfter;
491     return BeforeStyledStringChange(changeStart, changeLength, styledString);
492 }
493 
BeforeStyledStringChange(int32_t start,int32_t length,const std::u16string & string)494 bool RichEditorPattern::BeforeStyledStringChange(int32_t start, int32_t length, const std::u16string& string)
495 {
496     auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
497     CHECK_NULL_RETURN(eventHub, true);
498     CHECK_NULL_RETURN(eventHub->HasOnStyledStringWillChange(), true);
499     auto changeStart = std::clamp(start, 0, GetTextContentLength());
500     auto styledString = CreateStyledStringByStyleBefore(changeStart, string);
501     return BeforeStyledStringChange(changeStart, length, styledString);
502 }
503 
BeforeStyledStringChange(int32_t start,int32_t length,const RefPtr<SpanString> & styledString)504 bool RichEditorPattern::BeforeStyledStringChange(int32_t start, int32_t length, const RefPtr<SpanString>& styledString)
505 {
506     auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
507     CHECK_NULL_RETURN(eventHub, true);
508     CHECK_NULL_RETURN(eventHub->HasOnStyledStringWillChange(), true);
509     auto replaceMentString = AceType::MakeRefPtr<MutableSpanString>(u"");
510     replaceMentString->AppendSpanString(styledString);
511     StyledStringChangeValue changeValue;
512     auto changeStart = std::clamp(start, 0, GetTextContentLength());
513     auto changeEnd = std::clamp(changeStart + length, 0, GetTextContentLength());
514     changeValue.SetRangeBefore({ changeStart, changeEnd });
515     changeValue.SetReplacementString(replaceMentString);
516     if (!previewTextRecord_.newPreviewContent.empty()) {
517         auto previewTextStyledString = AceType::MakeRefPtr<MutableSpanString>(previewTextRecord_.newPreviewContent);
518         changeValue.SetPreviewText(previewTextStyledString);
519     }
520     return eventHub->FireOnStyledStringWillChange(changeValue);
521 }
522 
AfterStyledStringChange(int32_t start,int32_t length,const std::u16string & string)523 void RichEditorPattern::AfterStyledStringChange(int32_t start, int32_t length, const std::u16string& string)
524 {
525     auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
526     CHECK_NULL_VOID(eventHub);
527     if (eventHub->HasOnStyledStringDidChange()) {
528         StyledStringChangeValue changeValue;
529         auto changeStart = std::clamp(start, 0, GetTextContentLength());
530         auto changeEnd = changeStart + length;
531         auto stringLength = static_cast<int32_t>(string.length());
532         auto stringEnd = changeStart + stringLength;
533         changeValue.SetRangeBefore({ changeStart, changeEnd });
534         changeValue.SetRangeAfter({ changeStart, stringEnd });
535         eventHub->FireOnStyledStringDidChange(changeValue);
536     }
537     ForceTriggerAvoidOnCaretChange();
538     ReportAfterContentChangeEvent();
539 }
540 
AfterStyledStringChange(const UndoRedoRecord & record,bool isUndo)541 void RichEditorPattern::AfterStyledStringChange(const UndoRedoRecord& record, bool isUndo)
542 {
543     CHECK_NULL_VOID(!record.isOnlyStyleChange);
544     auto start = isUndo ? record.rangeAfter.start : record.rangeBefore.start;
545     auto length = isUndo ? record.rangeAfter.GetLength() : record.rangeBefore.GetLength();
546     auto styledString = isUndo ? record.styledStringBefore : record.styledStringAfter;
547     CHECK_NULL_VOID(styledString);
548     AfterStyledStringChange(start, length, styledString->GetU16string());
549 }
550 
ProcessStyledUndo(const UndoRedoRecord & record)551 void RichEditorPattern::ProcessStyledUndo(const UndoRedoRecord& record)
552 {
553     CHECK_NULL_VOID(IsSupportStyledUndo());
554     auto host = GetHost();
555     CHECK_NULL_VOID(host);
556     IF_PRESENT(magnifierController_, RemoveMagnifierFrameNode());
557     ResetTouchAndMoveCaretState(false);
558     auto undoRecord = record;
559     undoRecord.Reverse();
560     isSpanStringMode_ ? ApplyRecordInStyledString(undoRecord) : ApplyRecordInSpans(undoRecord, true);
561     if (record.selectionBefore.GetLength() == 0) {
562         IF_TRUE(isEditing_, StartTwinkling());
563     } else {
564         HandleSelectionChange(undoRecord.selectionBefore.start, undoRecord.selectionBefore.end);
565         FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
566     }
567     SetCaretPosition(undoRecord.selectionBefore.end);
568     caretAffinityPolicy_ = undoRecord.caretAffinityBefore;
569     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
570     host->MarkModifyDone();
571 }
572 
ProcessStyledRedo(const UndoRedoRecord & record)573 void RichEditorPattern::ProcessStyledRedo(const UndoRedoRecord& record)
574 {
575     CHECK_NULL_VOID(IsSupportStyledUndo());
576     auto host = GetHost();
577     CHECK_NULL_VOID(host);
578     isSpanStringMode_ ? ApplyRecordInStyledString(record) : ApplyRecordInSpans(record, false);
579     SetCaretPosition(record.rangeAfter.end);
580     IF_TRUE(isEditing_, StartTwinkling());
581     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
582     host->MarkModifyDone();
583 }
584 
ApplyRecordInStyledString(const UndoRedoRecord & record)585 void RichEditorPattern::ApplyRecordInStyledString(const UndoRedoRecord& record)
586 {
587     CHECK_NULL_VOID(styledString_);
588     auto start = record.rangeBefore.start;
589     auto length = record.rangeBefore.GetLength();
590     auto styledString = record.styledStringAfter;
591     auto startBefore = record.rangeAfter.start;
592     auto lengthBefore = record.rangeAfter.GetLength();
593     CloseSelectOverlay();
594     ResetSelection();
595     if (record.isOnlyStyleChange) {
596         std::vector<RefPtr<SpanBase>> updateSpans;
597         for (const auto& spanType : record.updateSpanTypes) {
598             styledString_->RemoveSpan(start, length, spanType);
599             auto spansBefore = styledString->GetSpans(0, lengthBefore, spanType);
600             for (auto& span : spansBefore) {
601                 CHECK_NULL_CONTINUE(span);
602                 auto spanStart = span->GetStartIndex() + startBefore;
603                 auto spanEnd = span->GetEndIndex() + startBefore;
604                 updateSpans.push_back(span->GetSubSpan(spanStart, spanEnd));
605             }
606         }
607         paragraphCache_.Clear();
608         styledString_->BindWithSpans(updateSpans);
609         styledString_->NotifySpanWatcher();
610     } else {
611         styledString_->ReplaceSpanString(start, length, styledString);
612     }
613 }
614 
ApplyRecordInSpans(const UndoRedoRecord & record,bool isUndo)615 void RichEditorPattern::ApplyRecordInSpans(const UndoRedoRecord& record, bool isUndo)
616 {
617     CloseSelectOverlay();
618     ResetSelection();
619     StopTwinkling();
620     DeleteForward(record.rangeBefore.start, record.rangeBefore.GetLength());
621     ApplyOptions(record.optionsListAfter.value_or(OptionsList{}), record.IsRestoreBuilderSpan(), isUndo);
622 }
623 
ApplyOptions(const OptionsList & optionsList,bool restoreBuilderSpan,bool isUndo)624 void RichEditorPattern::ApplyOptions(const OptionsList& optionsList, bool restoreBuilderSpan, bool isUndo)
625 {
626     int32_t optionsLength = 0;
627     for (const auto& option : optionsList) {
628         std::visit([weak = WeakClaim(this), restoreBuilderSpan, &optionsLength, isUndo](const auto& specificOption) {
629             using T = std::decay_t<decltype(specificOption)>;
630             auto pattern = weak.Upgrade();
631             CHECK_NULL_VOID(pattern);
632             auto reason = isUndo ? TextChangeReason::UNDO : TextChangeReason::REDO;
633             if constexpr (std::is_same_v<T, ImageSpanOptions>) {
634                 ImageSpanOptions options = specificOption;
635                 options.optionSource = OptionSource::UNDO_REDO;
636                 pattern->AddImageSpan(options, reason, false, pattern->GetCaretIndex(), false);
637                 optionsLength++;
638             } else if constexpr (std::is_same_v<T, TextSpanOptions>) {
639                 TextSpanOptions options = specificOption;
640                 options.optionSource = OptionSource::UNDO_REDO;
641                 pattern->AddTextSpan(options, reason, false, pattern->GetCaretIndex());
642                 optionsLength += static_cast<int32_t>(options.value.length());
643             } else if constexpr (std::is_same_v<T, SymbolSpanOptions>) {
644                 SymbolSpanOptions options = specificOption;
645                 options.optionSource = OptionSource::UNDO_REDO;
646                 pattern->AddSymbolSpan(options, reason, false, pattern->GetCaretIndex());
647                 optionsLength += SYMBOL_SPAN_LENGTH;
648             } else if constexpr (std::is_same_v<T, BuilderSpanOptions>) {
649                 pattern->AddPlaceholderSpan(specificOption, restoreBuilderSpan, reason);
650                 optionsLength++;
651             }
652         }, option);
653     }
654     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "ApplyOptions length=%{public}d", optionsLength);
655 }
656 
AddPlaceholderSpan(const BuilderSpanOptions & options,bool restoreBuilderSpan,TextChangeReason reason)657 void RichEditorPattern::AddPlaceholderSpan(const BuilderSpanOptions& options, bool restoreBuilderSpan,
658     TextChangeReason reason)
659 {
660     if (!restoreBuilderSpan || !options.customNode) {
661         auto textOptions = TextSpanOptions{ .offset = options.offset, .value = u" " };
662         textOptions.optionSource = OptionSource::UNDO_REDO;
663         AddTextSpan(textOptions, reason, false, caretPosition_);
664         undoManager_->RemoveBuilderSpanOptions(options.customNode);
665         return;
666     }
667     auto baseOption = SpanOptionBase { .offset = options.offset, .optionSource = OptionSource::UNDO_REDO };
668     AddPlaceholderSpan(options.customNode, baseOption, reason);
669 }
670 
OnModifyDone()671 void RichEditorPattern::OnModifyDone()
672 {
673     Pattern::CheckLocalized();
674     auto host = GetHost();
675     CHECK_NULL_VOID(host);
676     auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
677     copyOption_ = layoutProperty->GetCopyOption().value_or(CopyOptions::Local);
678     auto context = host->GetContext();
679     CHECK_NULL_VOID(context);
680     ResetKeyboardIfNeed();
681     context->AddOnAreaChangeNode(host->GetId());
682     if (!clipboard_ && context) {
683         clipboard_ = ClipboardProxy::GetInstance()->GetClipboard(context->GetTaskExecutor());
684     }
685     instanceId_ = context->GetInstanceId();
686     InitMouseEvent();
687     InitAISpanHoverEvent();
688     auto focusHub = host->GetOrCreateFocusHub();
689     CHECK_NULL_VOID(focusHub);
690     InitFocusEvent(focusHub);
691     auto gestureEventHub = host->GetOrCreateGestureEventHub();
692     InitClickEvent(gestureEventHub);
693     InitLongPressEvent(gestureEventHub);
694     InitTouchEvent();
695     InitPanEvent();
696     HandleEnabled();
697     ProcessInnerPadding();
698     InitScrollablePattern();
699     SetAccessibilityAction();
700     selectOverlay_->SetMenuTranslateIsSupport(IsShowTranslate());
701     selectOverlay_->SetIsSupportMenuSearch(IsShowSearch());
702     if (host->IsDraggable()) {
703         InitDragDropEvent();
704     } else {
705         ClearDragDropEvent();
706     }
707     Register2DragDropManager();
708     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
709 
710     auto eventHub = host->GetOrCreateEventHub<EventHub>();
711     CHECK_NULL_VOID(eventHub);
712     bool enabledCache = eventHub->IsEnabled();
713     if (textDetectEnable_ && enabledCache != enabled_) {
714         enabled_ = enabledCache;
715         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
716     }
717     SetIsEnableSubWindowMenu();
718 }
719 
HandleEnabled()720 void RichEditorPattern::HandleEnabled()
721 {
722     auto host = GetHost();
723     CHECK_NULL_VOID(host);
724     auto renderContext = host->GetRenderContext();
725     CHECK_NULL_VOID(renderContext);
726     if (IsDisabled()) {
727         auto richEditorTheme = GetTheme<RichEditorTheme>();
728         CHECK_NULL_VOID(richEditorTheme);
729         auto disabledAlpha = richEditorTheme->GetDisabledAlpha();
730         renderContext->OnOpacityUpdate(disabledAlpha);
731     } else {
732         auto opacity = renderContext->GetOpacity().value_or(1.0);
733         renderContext->OnOpacityUpdate(opacity);
734     }
735 }
736 
BeforeCreateLayoutWrapper()737 void RichEditorPattern::BeforeCreateLayoutWrapper()
738 {
739     ACE_SCOPED_TRACE("RichEditorBeforeCreateLayoutWrapper");
740     if (!isSpanStringMode_) {
741         TextPattern::PreCreateLayoutWrapper();
742         hasUrlSpan_ = std::any_of(spans_.begin(), spans_.end(), URL_SPAN_FILTER);
743     } else if (contentMod_) {
744         contentMod_->ContentChange();
745     }
746 }
747 
UpdateMagnifierStateAfterLayout(bool frameSizeChange)748 void RichEditorPattern::UpdateMagnifierStateAfterLayout(bool frameSizeChange)
749 {
750     CHECK_NULL_VOID(!selectOverlay_->GetIsHandleMoving());
751     if (frameSizeChange && magnifierController_ && magnifierController_->GetMagnifierNodeExist()) {
752         ResetTouchSelectState();
753         ResetTouchAndMoveCaretState();
754         magnifierController_->RemoveMagnifierFrameNode();
755     }
756 }
757 
UpdateGestureHotZone(const RefPtr<LayoutWrapper> & dirty)758 void RichEditorPattern::UpdateGestureHotZone(const RefPtr<LayoutWrapper>& dirty)
759 {
760     const auto& geometryNode = dirty->GetGeometryNode();
761     auto paddingSize = geometryNode->GetPaddingSize();
762     auto paddingOffset = geometryNode->GetPaddingOffset() - geometryNode->GetFrameOffset();
763 
764     auto hotZoneWidth = Dimension(paddingSize.Width());
765     auto hotZoneHeight = Dimension(paddingSize.Height());
766     auto hotZoneOffset = DimensionOffset(Offset(paddingOffset.GetX(), paddingOffset.GetY()));
767 
768     auto gestureHub = GetGestureEventHub();
769     CHECK_NULL_VOID(gestureHub);
770     gestureHub->SetResponseRegion({ { hotZoneWidth, hotZoneHeight, hotZoneOffset } });
771 }
772 
ClearOnFocusTextField(FrameNode * node)773 void RichEditorPattern::ClearOnFocusTextField(FrameNode* node)
774 {
775     CHECK_NULL_VOID(isAPI14Plus && node);
776     auto context = node->GetContextRefPtr();
777     CHECK_NULL_VOID(context);
778     auto textFieldManager = DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
779     IF_PRESENT(textFieldManager, ClearOnFocusTextField(node->GetId()));
780 }
781 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)782 bool RichEditorPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
783 {
784     CHECK_NULL_RETURN(!config.skipMeasure && !dirty->SkipMeasureContent(), false);
785     auto originalFrameRect = frameRect_;
786     frameRect_ = dirty->GetGeometryNode()->GetFrameRect();
787     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
788     CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
789     auto layoutAlgorithm = DynamicCast<RichEditorLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
790     CHECK_NULL_RETURN(layoutAlgorithm, false);
791     UpdateParentOffsetAndOverlay();
792     const auto& richTextRectOpt = layoutAlgorithm->GetTextRect();
793     IF_TRUE(richTextRectOpt.has_value(), richTextRect_ = richTextRectOpt.value());
794     UpdateTextFieldManager(Offset(parentGlobalOffset_.GetX(), parentGlobalOffset_.GetY()), frameRect_.Height());
795     bool ret = TextPattern::OnDirtyLayoutWrapperSwap(dirty, config);
796     UpdateScrollStateAfterLayout(config.frameSizeChange);
797     UpdateMagnifierStateAfterLayout(config.frameSizeChange);
798     IF_TRUE(!isRichEditorInit_, FireOnReady());
799     MoveCaretOnLayoutSwap();
800     HandleTasksOnLayoutSwap();
801     HandleSelectOverlayOnLayoutSwap();
802     IF_TRUE(originalFrameRect.GetSize() != frameRect_.GetSize(), {
803         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "frame size change");
804         TriggerAvoidOnCaretChangeNextFrame();
805     });
806     IF_TRUE(!isModifyingContent_, UpdateCaretInfoToController());
807     auto host = GetHost();
808     CHECK_NULL_RETURN(host, ret);
809     auto context = host->GetRenderContext();
810     CHECK_NULL_RETURN(context, ret);
811     if (context->GetClipEdge().has_value()) {
812         auto geometryNode = host->GetGeometryNode();
813         auto frameOffset = geometryNode->GetFrameOffset();
814         auto frameSize = geometryNode->GetFrameSize();
815         auto height = static_cast<float>(paragraphs_.GetHeight() + std::fabs(baselineOffset_));
816         if (!context->GetClipEdge().value() && LessNotEqual(frameSize.Height(), height)) {
817             RectF boundsRect(frameOffset.GetX(), frameOffset.GetY(), frameSize.Width(), height);
818             CHECK_NULL_RETURN(overlayMod_, ret);
819             overlayMod_->SetBoundsRect(boundsRect);
820         }
821     }
822     caretUpdateType_ = CaretUpdateType::NONE;
823     IF_PRESENT(oneStepDragController_, HandleDirtyNodes());
824     UpdateGestureHotZone(dirty);
825     if (afterDragSelect_) {
826         UpdateSelectionAndHandleVisibility();
827         afterDragSelect_ = false;
828     }
829     releaseInDrop_ = false;
830     return ret;
831 }
832 
UpdateSelectionAndHandleVisibility()833 void RichEditorPattern::UpdateSelectionAndHandleVisibility()
834 {
835     auto start = recoverStart_;
836     auto end = recoverEnd_;
837     if (textSelector_.GetStart() == start && textSelector_.GetEnd() == end) {
838         return;
839     }
840     auto host = GetHost();
841     CHECK_NULL_VOID(host);
842     if (isMouseOrTouchPad(sourceTool_) && releaseInDrop_) {
843         if (isSpanStringMode_) {
844             start = caretPosition_ - insertValueLength_;
845             end = caretPosition_;
846         } else {
847             start = lastCaretPosition_;
848             end = insertValueLength_ + lastCaretPosition_;
849         }
850     }
851     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "UpdateSelectionAndHandleVisibility range=[%{public}d--%{public}d]", start, end);
852     textSelector_.Update(start, end);
853 
854     if (!isMouseOrTouchPad(sourceTool_)) {
855         if (!selectOverlay_->IsBothHandlesShow() && !selectOverlay_->SelectOverlayIsCreating()) {
856             showSelect_ = true;
857             host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
858             CalculateHandleOffsetAndShowOverlay();
859             selectOverlay_->ProcessOverlay({.menuIsShow = false, .animation = false});
860         }
861     }
862     FireOnSelectionChange(start, end, true);
863 }
864 
HandleSelectOverlayOnLayoutSwap()865 void RichEditorPattern::HandleSelectOverlayOnLayoutSwap()
866 {
867     bool needToRefreshSelectOverlay = textSelector_.IsValid() && SelectOverlayIsOn() && !IsPreviewTextInputting();
868     CHECK_NULL_VOID(needToRefreshSelectOverlay);
869     auto overlayTask = [weak = WeakClaim(this)]() {
870         auto pattern = weak.Upgrade();
871         CHECK_NULL_VOID(pattern);
872         auto selectOverlay = pattern->selectOverlay_;
873         IF_PRESENT(selectOverlay, UpdateSelectOverlayOnAreaChanged());
874     };
875     if (AnimationUtils::IsImplicitAnimationOpen()) {
876         auto pipeline = GetContext();
877         CHECK_NULL_VOID(pipeline);
878         pipeline->AddAfterRenderTask(overlayTask);
879     } else {
880         overlayTask();
881     }
882 }
883 
FireOnReady()884 void RichEditorPattern::FireOnReady()
885 {
886     auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
887     CHECK_NULL_VOID(eventHub);
888     eventHub->FireOnReady();
889     ClearOperationRecords();
890     isFirstCallOnReady_ = true;
891     isRichEditorInit_ = true;
892 }
893 
MoveCaretOnLayoutSwap()894 void RichEditorPattern::MoveCaretOnLayoutSwap()
895 {
896     MoveCaretAfterTextChange();
897     if (needMoveCaretToContentRect_ || isEditing_) {
898         MoveCaretToContentRect();
899         needMoveCaretToContentRect_ = false;
900     }
901 }
902 
CreateImageSourceInfo(const ImageSpanOptions & options)903 std::function<ImageSourceInfo()> RichEditorPattern::CreateImageSourceInfo(const ImageSpanOptions& options)
904 {
905     std::string src;
906     RefPtr<PixelMap> pixMap = nullptr;
907     std::string bundleName;
908     std::string moduleName;
909     if (options.image.has_value()) {
910         src = options.image.value();
911     }
912     if (options.imagePixelMap.has_value()) {
913         pixMap = options.imagePixelMap.value();
914     }
915     if (options.bundleName.has_value()) {
916         bundleName = options.bundleName.value();
917     }
918     if (options.moduleName.has_value()) {
919         moduleName = options.moduleName.value();
920     }
921     auto createSourceInfoFunc = [src, noPixMap = !options.imagePixelMap.has_value(),
922                                     isUriPureNumber = options.isUriPureNumber.value_or(false), pixMap, bundleName,
923                                     moduleName]() -> ImageSourceInfo {
924         ImageSourceInfo info;
925 #if defined(PIXEL_MAP_SUPPORTED)
926         if (noPixMap) {
927             info = ImageSourceInfo { src, bundleName, moduleName };
928         } else {
929             info = ImageSourceInfo(pixMap);
930         }
931 #else
932         info = ImageSourceInfo { src, bundleName, moduleName };
933 #endif
934         info.SetIsUriPureNumber(isUriPureNumber);
935         return info;
936     };
937     return std::move(createSourceInfoFunc);
938 }
939 
GetTextContentLength()940 int32_t RichEditorPattern::GetTextContentLength()
941 {
942     if (isSpanStringMode_ && styledString_) {
943         return styledString_->GetLength();
944     }
945     if (!spans_.empty()) {
946         auto it = spans_.rbegin();
947         return (*it)->position;
948     }
949     return 0;
950 }
951 
SetPreviewMenuParam(TextSpanType spanType,std::function<void ()> & builder,const SelectMenuParam & menuParam)952 void RichEditorPattern::SetPreviewMenuParam(TextSpanType spanType, std::function<void()>& builder, const SelectMenuParam& menuParam)
953 {
954     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "SetPreviewMenuParam, spanType=%{public}d, builder=%{public}d",
955         spanType, !!builder);
956     if (!oneStepDragController_) {
957         oneStepDragController_ = std::make_unique<OneStepDragController>(WeakClaim(this));
958     }
959     oneStepDragController_->SetMenuParam(spanType, builder, menuParam);
960     oneStepDragController_->SetEnableEventResponse(textSelector_, imageNodes, builderNodes);
961 }
962 
HandleImageDrag(const RefPtr<ImageSpanNode> & imageNode)963 void RichEditorPattern::HandleImageDrag(const RefPtr<ImageSpanNode>& imageNode)
964 {
965     DisableDrag(imageNode);
966     IF_PRESENT(oneStepDragController_, EnableOneStepDrag(TextSpanType::IMAGE, imageNode));
967 }
968 
DisableDrag(const RefPtr<ImageSpanNode> & imageNode)969 void RichEditorPattern::DisableDrag(const RefPtr<ImageSpanNode>& imageNode)
970 {
971     // Disable the image itself event
972     imageNode->SetDraggable(false);
973     auto gesture = imageNode->GetOrCreateGestureEventHub();
974     CHECK_NULL_VOID(gesture);
975     gesture->InitDragDropEvent();
976     gesture->SetDragEvent(nullptr, { PanDirection::DOWN }, 0, Dimension(0));
977 }
978 
SetGestureOptions(UserGestureOptions options,RefPtr<SpanItem> spanItem)979 void RichEditorPattern::SetGestureOptions(UserGestureOptions options, RefPtr<SpanItem> spanItem)
980 {
981     IF_TRUE(options.onClick, spanItem->SetOnClickEvent(std::move(options.onClick)));
982     IF_TRUE(options.onLongPress, spanItem->SetLongPressEvent(std::move(options.onLongPress)));
983     IF_TRUE(options.onDoubleClick, spanItem->SetDoubleClickEvent(std::move(options.onDoubleClick)));
984 }
985 
AddSpanHoverEvent(RefPtr<SpanItem> spanItem,const RefPtr<FrameNode> & frameNode,const SpanOptionBase & options)986 void RichEditorPattern::AddSpanHoverEvent(
987     RefPtr<SpanItem> spanItem, const RefPtr<FrameNode>& frameNode, const SpanOptionBase& options)
988 {
989     auto onHoverFunc = options.userMouseOption.onHover;
990     CHECK_NULL_VOID(spanItem && frameNode && onHoverFunc);
991     auto tag = frameNode->GetTag();
992     spanItem->SetHoverEvent(std::move(onHoverFunc));
993     auto eventHub = frameNode->GetOrCreateEventHub<EventHub>();
994     CHECK_NULL_VOID(eventHub);
995     auto inputHub = eventHub->GetOrCreateInputEventHub();
996     CHECK_NULL_VOID(inputHub);
997     auto hoverTask = [weak = WeakClaim(Referenced::RawPtr(spanItem)), tag](bool isHover, HoverInfo& info) {
998         auto item = weak.Upgrade();
999         if (item && item->onHover) {
1000             TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "[%{public}s] onHover status :[%{public}d]", tag.c_str(), isHover);
1001             item->onHover(isHover, info);
1002         }
1003     };
1004     auto hoverEvent = MakeRefPtr<InputEvent>(std::move(hoverTask));
1005     inputHub->AddOnHoverEvent(hoverEvent);
1006 }
1007 
AddImageSpanFromCollaboration(const ImageSpanOptions & options,bool updateCaret)1008 int32_t RichEditorPattern::AddImageSpanFromCollaboration(const ImageSpanOptions& options, bool updateCaret)
1009 {
1010     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AddImageSpanFromCollaboration, updateCaret=%{public}d, ssMode=%{public}d",
1011         updateCaret, !!isSpanStringMode_);
1012     if (isSpanStringMode_) {
1013         auto insertStyledString = MakeRefPtr<SpanString>(options);
1014         auto index = options.offset.value_or(caretPosition_);
1015         InsertStyledString(insertStyledString, index, updateCaret);
1016         return 0;
1017     }
1018     return AddImageSpan(options, TextChangeReason::COLLABORATION, false, 0, updateCaret);
1019 }
1020 
AddImageSpan(const ImageSpanOptions & options,TextChangeReason reason,bool isPaste,int32_t index,bool updateCaret)1021 int32_t RichEditorPattern::AddImageSpan(const ImageSpanOptions& options, TextChangeReason reason, bool isPaste,
1022     int32_t index, bool updateCaret)
1023 {
1024     if (GetTextContentLength() >= maxLength_.value_or(INT_MAX)) {
1025         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "AddImageSpan: Reach the maxLength. maxLength=%{public}d", maxLength_.value_or(INT_MAX));
1026         return 0;
1027     }
1028     auto host = GetContentHost();
1029     CHECK_NULL_RETURN(host, -1);
1030     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AddImageSpan, opts=%{public}s, updateCaret=%{public}d",
1031         options.ToString().c_str(), updateCaret);
1032     NotifyExitTextPreview(false);
1033     auto imageNode = ImageSpanNode::GetOrCreateSpanNode(V2::IMAGE_ETS_TAG,
1034         ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<ImagePattern>(); });
1035     auto pattern = imageNode->GetPattern<ImagePattern>();
1036     CHECK_NULL_RETURN(pattern, -1);
1037     pattern->SetSyncLoad(true);
1038     int32_t insertIndex = options.offset.value_or(GetTextContentLength());
1039     insertIndex = std::min(insertIndex, GetTextContentLength());
1040     AdjustSelectorForSymbol(insertIndex, HandleType::SECOND, SelectorAdjustPolicy::EXCLUDE);
1041     RichEditorChangeValue changeValue(reason);
1042     bool isUndoRedo = options.optionSource == OptionSource::UNDO_REDO;
1043     CHECK_NULL_RETURN(isUndoRedo || BeforeAddImage(changeValue, options, insertIndex), -1);
1044 
1045     HandleImageDrag(imageNode);
1046     AddOprationWhenAddImage(insertIndex);
1047     int32_t spanIndex = TextSpanSplit(insertIndex);
1048     IF_TRUE(spanIndex == -1, spanIndex = static_cast<int32_t>(host->GetChildren().size()));
1049 
1050     imageNodes.push_back(imageNode);
1051     imageNode->MountToParent(host, spanIndex);
1052     auto renderContext = imageNode->GetRenderContext();
1053     IF_PRESENT(renderContext, SetNeedAnimateFlag(false));
1054     SetImageLayoutProperty(imageNode, options);
1055     auto spanItem = imageNode->GetSpanItem();
1056     // The length of the imageSpan defaults to the length of a character to calculate the position
1057     spanItem->content = u" ";
1058     spanItem->SetImageSpanOptions(options);
1059     spanItem->spanItemType = SpanItemType::IMAGE;
1060     AddSpanItem(spanItem, spanIndex);
1061     SetGestureOptions(options.userGestureOption, spanItem);
1062     auto userMouseOption = options.userMouseOption;
1063     if (userMouseOption.onHover) {
1064         spanItem->onHover_ = std::move(userMouseOption.onHover);
1065         hoverableNodes.push_back(imageNode);
1066     }
1067     placeholderCount_++;
1068     IF_TRUE(!isUndoRedo, undoManager_->RecordAddSpanOperation(spanItem, SpanOptionsType::IMAGE));
1069     if (updateCaret) {
1070         SetCaretPosition(insertIndex + spanItem->content.length());
1071         SetNeedMoveCaretToContentRect();
1072     }
1073     ResetSelectionAfterAddSpan(isPaste);
1074     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1075     host->MarkModifyDone();
1076     IF_TRUE(!isUndoRedo, AfterContentChange(changeValue));
1077     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "end");
1078     return spanIndex;
1079 }
1080 
CreateHoverInfo(const MouseInfo & info)1081 HoverInfo RichEditorPattern::CreateHoverInfo(const MouseInfo& info)
1082 {
1083     HoverInfo hoverInfo;
1084     hoverInfo.SetType(info.GetType());
1085     hoverInfo.SetTimeStamp(info.GetTimeStamp());
1086     hoverInfo.SetTarget(info.GetTarget());
1087     hoverInfo.SetSourceDevice(info.GetSourceDevice());
1088     hoverInfo.SetForce(info.GetForce());
1089     IF_TRUE(info.GetTiltX(), hoverInfo.SetTiltX(info.GetTiltX().value()));
1090     IF_TRUE(info.GetTiltY(), hoverInfo.SetTiltY(info.GetTiltY().value()));
1091     hoverInfo.SetSourceTool(info.GetSourceTool());
1092     hoverInfo.SetDeviceId(info.GetDeviceId());
1093     hoverInfo.SetTargetDisplayId(info.GetTargetDisplayId());
1094     hoverInfo.SetStopPropagation(info.IsStopPropagation());
1095     hoverInfo.SetPreventDefault(info.IsPreventDefault());
1096     hoverInfo.SetPatternName(info.GetPatternName());
1097     hoverInfo.SetPressedKeyCodes(info.GetPressedKeyCodes());
1098     hoverInfo.SetIsPostEventResult(info.GetIsPostEventResult());
1099     hoverInfo.SetPostEventNodeId(info.GetPostEventNodeId());
1100     return hoverInfo;
1101 }
1102 
HandleImageHoverEvent(const MouseInfo & mouseInfo)1103 void RichEditorPattern::HandleImageHoverEvent(const MouseInfo& mouseInfo)
1104 {
1105     CHECK_NULL_VOID(mouseInfo.GetAction() == MouseAction::MOVE && !isMousePressed_);
1106     ACE_SCOPED_TRACE("RichEditorHandleImageHoverEvent");
1107     PointF mouseOffset = { mouseInfo.GetLocalLocation().GetX(), mouseInfo.GetLocalLocation().GetY() };
1108     HoverInfo info = CreateHoverInfo(mouseInfo);
1109     for (auto it = hoverableNodes.begin(); it != hoverableNodes.end();) {
1110         auto spanNode = it->Upgrade();
1111         if (!spanNode) {
1112             it = hoverableNodes.erase(it);
1113             continue;
1114         }
1115         const auto& imageSpanItem = spanNode->GetSpanItem();
1116         if (!imageSpanItem || !imageSpanItem->onHover_) {
1117             it = hoverableNodes.erase(it);
1118             continue;
1119         }
1120         const auto& geoNode = spanNode->GetGeometryNode();
1121         CHECK_NULL_CONTINUE(geoNode);
1122         const auto& imageRect = geoNode->GetFrameRect();
1123         if (!imageRect.IsInRegion(mouseOffset)) {
1124             ++it;
1125             continue;
1126         }
1127         if (!lastHoverSpanItem_) {
1128             imageSpanItem->onHover_(true, info);
1129             lastHoverSpanItem_ = imageSpanItem;
1130             lastHoverInfo_ = info;
1131             return;
1132         }
1133         CHECK_NULL_VOID(Referenced::RawPtr(lastHoverSpanItem_) != Referenced::RawPtr(imageSpanItem));
1134         imageSpanItem->onHover_(true, info);
1135         lastHoverSpanItem_->onHover_(false, info);
1136         lastHoverSpanItem_ = imageSpanItem;
1137         lastHoverInfo_ = info;
1138         return;
1139     }
1140 
1141     if (lastHoverSpanItem_) {
1142         lastHoverSpanItem_->onHover_(false, info);
1143         lastHoverSpanItem_.Reset();
1144     }
1145 }
1146 
AddOprationWhenAddImage(int32_t beforeCaretPos)1147 void RichEditorPattern::AddOprationWhenAddImage(int32_t beforeCaretPos)
1148 {
1149     OperationRecord record;
1150     record.beforeCaretPosition = beforeCaretPos;
1151     record.addText = u" ";
1152     ClearRedoOperationRecords();
1153     record.afterCaretPosition = record.beforeCaretPosition + 1;
1154     AddOperationRecord(record);
1155 }
1156 
ResetSelectionAfterAddSpan(bool isPaste)1157 void RichEditorPattern::ResetSelectionAfterAddSpan(bool isPaste)
1158 {
1159     if (isPaste || !textSelector_.IsValid()) {
1160         return;
1161     }
1162     CloseSelectOverlay();
1163     ResetSelection();
1164     if (isEditing_ && !caretVisible_) {
1165         StartTwinkling();
1166     }
1167 }
1168 
AddSpanItem(const RefPtr<SpanItem> & item,int32_t offset)1169 void RichEditorPattern::AddSpanItem(const RefPtr<SpanItem>& item, int32_t offset)
1170 {
1171     auto host = GetContentHost();
1172     CHECK_NULL_VOID(host);
1173     if (offset == -1) {
1174         offset = static_cast<int32_t>(host->GetChildren().size());
1175     }
1176     offset = std::clamp(offset, 0, static_cast<int32_t>(host->GetChildren().size()) - 1);
1177     auto it = spans_.begin();
1178     std::advance(it, offset);
1179     spans_.insert(it, item);
1180     UpdateSpanPosition();
1181     ReportAfterContentChangeEvent();
1182 }
1183 
SetContentPattern(const RefPtr<RichEditorContentPattern> & contentPattern)1184 void RichEditorPattern::SetContentPattern(const RefPtr<RichEditorContentPattern>& contentPattern)
1185 {
1186     contentPattern_ = contentPattern;
1187 }
1188 
GetContentHost() const1189 RefPtr<FrameNode> RichEditorPattern::GetContentHost() const
1190 {
1191     if (!contentPattern_) {
1192         TAG_LOGE(AceLogTag::ACE_RICH_TEXT, "GetContentHost, contentPattern is null");
1193         return nullptr;
1194     }
1195     return contentPattern_->GetHost();
1196 }
1197 
OnAttachToFrameNode()1198 void RichEditorPattern::OnAttachToFrameNode()
1199 {
1200     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "OnAttachToFrameNode");
1201     TextPattern::OnAttachToFrameNode();
1202     richEditorInstanceId_ = Container::CurrentIdSafely();
1203     auto frameNode = GetHost();
1204     CHECK_NULL_VOID(frameNode);
1205     frameId_ = frameNode->GetId();
1206     StylusDetectorMgr::GetInstance()->AddTextFieldFrameNode(frameNode, WeakClaim(this));
1207     auto context = GetContext();
1208     CHECK_NULL_VOID(context);
1209     context->AddWindowSizeChangeCallback(frameId_);
1210 
1211     auto patternCreator = [weak = WeakClaim(this)]() { return AceType::MakeRefPtr<RichEditorContentPattern>(weak); };
1212     auto nodeId = ElementRegister::GetInstance()->MakeUniqueId();
1213     auto contentNode = FrameNode::GetOrCreateFrameNode(V2::RICH_EDITOR_CONTENT_ETS_TAG, nodeId, patternCreator);
1214     frameNode->AddChild(contentNode);
1215     SetContentPattern(contentNode->GetPattern<RichEditorContentPattern>());
1216 }
1217 
OnDetachFromFrameNode(FrameNode * node)1218 void RichEditorPattern::OnDetachFromFrameNode(FrameNode* node)
1219 {
1220     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "OnAttachToFrameNode");
1221     TextPattern::OnDetachFromFrameNode(node);
1222     ScrollablePattern::OnDetachFromFrameNode(node);
1223     ClearOnFocusTextField(node);
1224     auto context = pipeline_.Upgrade();
1225     IF_PRESENT(context, RemoveWindowSizeChangeCallback(frameId_));
1226 }
1227 
AddPlaceholderSpan(const RefPtr<UINode> & customNode,const SpanOptionBase & options,TextChangeReason reason)1228 int32_t RichEditorPattern::AddPlaceholderSpan(const RefPtr<UINode>& customNode, const SpanOptionBase& options,
1229     TextChangeReason reason)
1230 {
1231     if (GetTextContentLength() >= maxLength_.value_or(INT_MAX)) {
1232         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "AddPlaceholderSpan: Reach the maxLength. maxLength=%{public}d", maxLength_.value_or(INT_MAX));
1233         return 0;
1234     }
1235     CHECK_NULL_RETURN(customNode, 0);
1236     auto host = GetContentHost();
1237     CHECK_NULL_RETURN(host, 0);
1238     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AddPlaceholderSpan");
1239     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "options=%{public}s", options.ToString().c_str());
1240     NotifyExitTextPreview(false);
1241     auto placeholderSpanNode = PlaceholderSpanNode::GetOrCreateSpanNode(V2::PLACEHOLDER_SPAN_ETS_TAG,
1242         ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<PlaceholderSpanPattern>(); });
1243     CHECK_NULL_RETURN(placeholderSpanNode, 0);
1244     customNode->MountToParent(placeholderSpanNode);
1245     SetSelfAndChildDraggableFalse(customNode);
1246     IF_PRESENT(oneStepDragController_, EnableOneStepDrag(TextSpanType::BUILDER, placeholderSpanNode));
1247     auto focusHub = placeholderSpanNode->GetOrCreateFocusHub();
1248     focusHub->SetFocusable(false);
1249     int32_t insertIndex = options.offset.value_or(GetTextContentLength());
1250     AdjustSelectorForSymbol(insertIndex, HandleType::SECOND, SelectorAdjustPolicy::EXCLUDE);
1251     int32_t spanIndex = TextSpanSplit(insertIndex);
1252     if (spanIndex == -1) {
1253         spanIndex = static_cast<int32_t>(host->GetChildren().size());
1254     }
1255     builderNodes.push_back(placeholderSpanNode);
1256     placeholderSpanNode->MountToParent(host, spanIndex);
1257     auto renderContext = placeholderSpanNode->GetRenderContext();
1258     IF_PRESENT(renderContext, SetNeedAnimateFlag(false));
1259     auto spanItem = placeholderSpanNode->GetSpanItem();
1260     spanItem->content = u" ";
1261     spanItem->SetCustomNode(customNode);
1262     spanItem->dragBackgroundColor_ = options.dragBackgroundColor;
1263     spanItem->isDragShadowNeeded_ = options.isDragShadowNeeded;
1264     AddSpanItem(spanItem, spanIndex);
1265     IF_TRUE(options.optionSource != OptionSource::UNDO_REDO,
1266         undoManager_->RecordAddSpanOperation(spanItem, SpanOptionsType::BUILDER));
1267     placeholderCount_++;
1268     SetCaretPosition(insertIndex + spanItem->content.length());
1269     ResetSelectionAfterAddSpan(false);
1270     auto placeholderPipelineContext = placeholderSpanNode->GetContext();
1271     IF_PRESENT(placeholderPipelineContext, SetDoKeyboardAvoidAnimate(false));
1272     SetNeedMoveCaretToContentRect();
1273     AddOnPlaceholderHoverEvent(placeholderSpanNode);
1274     placeholderSpanNode->MarkModifyDone();
1275     placeholderSpanNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1276     host->MarkModifyDone();
1277     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1278     return spanIndex;
1279 }
1280 
AddOnPlaceholderHoverEvent(const RefPtr<PlaceholderSpanNode> & placeholderSpanNode)1281 void RichEditorPattern::AddOnPlaceholderHoverEvent(const RefPtr<PlaceholderSpanNode>& placeholderSpanNode)
1282 {
1283     CHECK_NULL_VOID(placeholderSpanNode);
1284     auto inputHub = placeholderSpanNode->GetOrCreateInputEventHub();
1285     CHECK_NULL_VOID(inputHub);
1286     auto hoverTask = [weak = WeakClaim(this)](bool isHover) {
1287         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "placeholder, on hover event isHover=%{public}d", isHover);
1288         auto pattern = weak.Upgrade();
1289         CHECK_NULL_VOID(pattern);
1290         pattern->OnPlaceholderHover(isHover);
1291     };
1292     auto hoverEvent = MakeRefPtr<InputEvent>(std::move(hoverTask));
1293     inputHub->AddOnHoverEvent(hoverEvent);
1294 }
1295 
OnPlaceholderHover(bool isHover)1296 void RichEditorPattern::OnPlaceholderHover(bool isHover)
1297 {
1298     auto host = GetHost();
1299     CHECK_NULL_VOID(host);
1300     auto pipeline = GetContext();
1301     CHECK_NULL_VOID(pipeline);
1302     auto nodeId = host->GetId();
1303     if (isHover) {
1304         pipeline->FreeMouseStyleHoldNode(nodeId);
1305     } else {
1306         pipeline->FreeMouseStyleHoldNode();
1307         ChangeMouseStyle(MouseFormat::TEXT_CURSOR);
1308     }
1309 }
1310 
SetSelfAndChildDraggableFalse(const RefPtr<UINode> & customNode)1311 void RichEditorPattern::SetSelfAndChildDraggableFalse(const RefPtr<UINode>& customNode)
1312 {
1313     CHECK_NULL_VOID(customNode);
1314     auto frameNode = DynamicCast<FrameNode>(customNode);
1315     if (frameNode) {
1316         auto gestureEventHub = frameNode->GetOrCreateGestureEventHub();
1317         IF_PRESENT(gestureEventHub, SetDragForbiddenForcely(true));
1318     }
1319     for (const auto& child : customNode->GetChildren()) {
1320         SetSelfAndChildDraggableFalse(child);
1321     }
1322 }
1323 
AddTextSpan(TextSpanOptions options,TextChangeReason reason,bool isPaste,int32_t index)1324 int32_t RichEditorPattern::AddTextSpan(TextSpanOptions options, TextChangeReason reason, bool isPaste, int32_t index)
1325 {
1326     if (GetTextContentLength() >= maxLength_.value_or(INT_MAX)) {
1327         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "AddTextSpan: Reach the maxLength. maxLength=%{public}d", maxLength_.value_or(INT_MAX));
1328         return 0;
1329     }
1330     auto length = CalculateTruncationLength(options.value, maxLength_.value_or(INT_MAX) - GetTextContentLength());
1331     if (length == 0) {
1332         return -1;
1333     }
1334     options.value = options.value.substr(0, length);
1335     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AddTextSpan, opts=%{public}s", ToBriefString(options).c_str());
1336     SEC_TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AddTextSpan, opts=%{public}s", options.ToString().c_str());
1337     AdjustAddPosition(options);
1338     NotifyExitTextPreview();
1339     OperationRecord record;
1340     auto textContentLength = GetTextContentLength();
1341     if (options.offset.has_value()) {
1342         options.offset = std::clamp(options.offset.value(), 0, textContentLength);
1343         AdjustSelectorForSymbol(options.offset.value(), HandleType::SECOND, SelectorAdjustPolicy::EXCLUDE);
1344     }
1345     record.beforeCaretPosition = std::clamp(options.offset.value_or(textContentLength), 0, textContentLength);
1346     record.addText = options.value;
1347     RichEditorChangeValue changeValue(reason);
1348     bool isUndoRedo = options.optionSource == OptionSource::UNDO_REDO;
1349     CHECK_NULL_RETURN(isUndoRedo || BeforeChangeText(changeValue, options), -1);
1350     ClearRedoOperationRecords();
1351     record.afterCaretPosition = record.beforeCaretPosition + static_cast<int32_t>(options.value.length());
1352     AddOperationRecord(record);
1353     auto ret = AddTextSpanOperation(options, isPaste, index, false);
1354     SetNeedMoveCaretToContentRect();
1355     if (!previewTextRecord_.IsValid() && !isUndoRedo) {
1356         AfterContentChange(changeValue);
1357     }
1358     return ret;
1359 }
1360 
AdjustAddPosition(TextSpanOptions & options)1361 void RichEditorPattern::AdjustAddPosition(TextSpanOptions& options)
1362 {
1363     CHECK_NULL_VOID(IsPreviewTextInputting() && options.offset.has_value());
1364     auto& offset = options.offset.value();
1365     auto delta = offset - previewTextRecord_.startOffset;
1366     offset -= std::min(std::max(0, delta), previewTextRecord_.endOffset - previewTextRecord_.startOffset);
1367 }
1368 
AddTextSpanOperation(const TextSpanOptions & options,bool isPaste,int32_t index,bool needLeadingMargin,bool updateCaretPosition)1369 int32_t RichEditorPattern::AddTextSpanOperation(
1370     const TextSpanOptions& options, bool isPaste, int32_t index, bool needLeadingMargin, bool updateCaretPosition)
1371 {
1372     auto host = GetContentHost();
1373     CHECK_NULL_RETURN(host, -1);
1374 
1375     auto spanNode = SpanNode::GetOrCreateSpanNode(ElementRegister::GetInstance()->MakeUniqueId());
1376 
1377     int32_t spanIndex = 0;
1378     int32_t offset = -1;
1379     if (options.offset.has_value()) {
1380         offset = TextSpanSplit(options.offset.value(), needLeadingMargin);
1381         if (offset == -1) {
1382             spanIndex = static_cast<int32_t>(host->GetChildren().size());
1383         } else {
1384             spanIndex = offset;
1385         }
1386         spanNode->MountToParent(host, offset);
1387     } else if (index != -1) {
1388         spanNode->MountToParent(host, index);
1389         spanIndex = index;
1390     } else {
1391         spanIndex = static_cast<int32_t>(host->GetChildren().size());
1392         spanNode->MountToParent(host);
1393     }
1394     auto textStyle = options.style;
1395     if (options.urlAddress.has_value() && options.useThemeFontColor && textStyle.has_value()) {
1396         textStyle.value().SetTextColor(GetUrlSpanColor());
1397     }
1398     auto spanItem = spanNode->GetSpanItem();
1399     spanItem->SetTextStyle(textStyle);
1400     spanItem->useThemeFontColor = options.useThemeFontColor;
1401     spanItem->useThemeDecorationColor = options.useThemeDecorationColor;
1402     UpdateSpanNode(spanNode, options);
1403     AddSpanItem(spanItem, offset);
1404     if (!options.style.has_value()) {
1405         SetDefaultColor(spanNode);
1406     }
1407     if (options.paraStyle) {
1408         UpdateParagraphStyle(spanNode, *options.paraStyle);
1409     }
1410     SetGestureOptions(options.userGestureOption, spanItem);
1411     bool needRecord =
1412         options.optionSource != OptionSource::UNDO_REDO && options.optionSource != OptionSource::IME_INSERT;
1413     IF_TRUE(needRecord, undoManager_->RecordAddSpanOperation(spanItem, SpanOptionsType::TEXT));
1414     if (updateCaretPosition && !previewTextRecord_.IsValid()) {
1415         if (options.offset.has_value()) {
1416             SetCaretPosition(options.offset.value() + options.value.length());
1417         } else {
1418             SetCaretPosition(GetTextContentLength());
1419         }
1420     }
1421     ResetSelectionAfterAddSpan(isPaste);
1422     SpanNodeFission(spanNode);
1423     return spanIndex;
1424 }
1425 
UpdateSpanNode(RefPtr<SpanNode> spanNode,const TextSpanOptions & options)1426 void RichEditorPattern::UpdateSpanNode(RefPtr<SpanNode> spanNode, const TextSpanOptions& options)
1427 {
1428     spanNode->UpdateContent(options.value);
1429     if (options.style.has_value()) {
1430         const TextStyle& textStyle = options.style.value();
1431         spanNode->UpdateTextColorWithoutCheck(textStyle.GetTextColor());
1432         spanNode->UpdateFontSize(textStyle.GetFontSize());
1433         spanNode->UpdateItalicFontStyle(textStyle.GetFontStyle());
1434         spanNode->UpdateFontWeight(textStyle.GetFontWeight());
1435         spanNode->UpdateFontFamily(textStyle.GetFontFamilies());
1436         spanNode->UpdateTextDecoration(std::vector<TextDecoration> { textStyle.GetTextDecorationFirst() });
1437         spanNode->UpdateTextDecorationColorWithoutCheck(textStyle.GetTextDecorationColor());
1438         spanNode->UpdateTextDecorationStyle(textStyle.GetTextDecorationStyle());
1439         spanNode->UpdateLineThicknessScale(textStyle.GetLineThicknessScale());
1440         spanNode->UpdateTextShadow(textStyle.GetTextShadows());
1441         spanNode->UpdateHalfLeading(textStyle.GetHalfLeading());
1442         spanNode->UpdateLineHeight(textStyle.GetLineHeight());
1443         spanNode->UpdateLetterSpacing(textStyle.GetLetterSpacing());
1444         spanNode->UpdateFontFeature(textStyle.GetFontFeatures());
1445         UpdateTextBackgroundStyle(spanNode, textStyle.GetTextBackgroundStyle());
1446     }
1447     UpdateUrlStyle(spanNode, options.urlAddress);
1448 }
1449 
UpdateTextBackgroundStyle(RefPtr<SpanNode> & spanNode,const std::optional<TextBackgroundStyle> & style)1450 void RichEditorPattern::UpdateTextBackgroundStyle(
1451     RefPtr<SpanNode>& spanNode, const std::optional<TextBackgroundStyle>& style)
1452 {
1453     CHECK_NULL_VOID(style.has_value());
1454     TextBackgroundStyle backgroundStyle = style.value();
1455     backgroundStyle.needCompareGroupId = false;
1456     if (backgroundStyle == spanNode->GetTextBackgroundStyle()) {
1457         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "text background style is same");
1458         return;
1459     }
1460     backgroundStyle.needCompareGroupId = true;
1461     spanNode->SetTextBackgroundStyle(backgroundStyle);
1462 }
1463 
UpdateUrlStyle(RefPtr<SpanNode> & spanNode,const std::optional<std::u16string> & urlAddressOpt)1464 void RichEditorPattern::UpdateUrlStyle(RefPtr<SpanNode>& spanNode, const std::optional<std::u16string>& urlAddressOpt)
1465 {
1466     CHECK_NULL_VOID(spanNode && urlAddressOpt.has_value());
1467     auto& spanItem = spanNode->GetSpanItem();
1468     CHECK_NULL_VOID(spanItem);
1469     auto& urlAddress = urlAddressOpt.value();
1470 
1471     // handle url span callback
1472     std::function<void()> urlOnRelease;
1473     if (!urlAddress.empty()) {
1474         urlOnRelease = [add = UtfUtils::Str16ToStr8(urlAddress)]() {
1475             auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
1476             CHECK_NULL_VOID(pipelineContext);
1477             pipelineContext->HyperlinkStartAbility(add);
1478         };
1479     }
1480     spanItem->SetUrlOnReleaseEvent(std::move(urlOnRelease));
1481     spanItem->urlAddress = urlAddress;
1482 
1483     // handle url span color
1484     CHECK_NULL_VOID(spanItem->useThemeFontColor);
1485     if (urlAddress.empty()) {
1486         auto theme = GetTheme<RichEditorTheme>();
1487         CHECK_NULL_VOID(theme);
1488         const auto& themeTextStyle = theme->GetTextStyle();
1489         const auto& textColor = themeTextStyle.GetTextColor();
1490         spanNode->UpdateTextColor(textColor);
1491         IF_TRUE(spanItem->useThemeDecorationColor, spanNode->UpdateTextDecorationColor(textColor));
1492     } else {
1493         const auto& urlSpanColor = GetUrlSpanColor();
1494         spanNode->UpdateTextColor(urlSpanColor);
1495         IF_TRUE(spanItem->useThemeDecorationColor, spanNode->UpdateTextDecorationColor(urlSpanColor));
1496     }
1497 }
1498 
AddSymbolSpan(SymbolSpanOptions options,TextChangeReason reason,bool isPaste,int32_t index)1499 int32_t RichEditorPattern::AddSymbolSpan(SymbolSpanOptions options, TextChangeReason reason, bool isPaste, int32_t index)
1500 {
1501     if (GetTextContentLength() >= maxLength_.value_or(INT_MAX) - 1) {
1502         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "AddSymbolSpan: Reach the maxLength. maxLength=%{public}d", maxLength_.value_or(INT_MAX));
1503         return 0;
1504     }
1505     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AddSymbolSpan, opts=%{public}s isPaste=%{public}d, index=%{public}d",
1506         ToBriefString(options).c_str(), isPaste, index);
1507     SEC_TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AddSymbolSpan, opts=%{public}s", options.ToString().c_str());
1508 
1509     NotifyExitTextPreview(false);
1510     if (options.offset.has_value()) {
1511         options.offset = std::clamp(options.offset.value(), 0, GetTextContentLength());
1512         AdjustSelectorForSymbol(options.offset.value(), HandleType::SECOND, SelectorAdjustPolicy::EXCLUDE);
1513     }
1514     RichEditorChangeValue changeValue(reason);
1515     bool isUndoRedo = options.optionSource == OptionSource::UNDO_REDO;
1516     CHECK_NULL_RETURN(isUndoRedo || BeforeAddSymbol(changeValue, options), -1);
1517     OperationRecord record;
1518     record.beforeCaretPosition = options.offset.value_or(static_cast<int32_t>(GetTextContentLength()));
1519     record.addText = u" ";
1520     ClearRedoOperationRecords();
1521     record.afterCaretPosition = record.beforeCaretPosition + 1;
1522     AddOperationRecord(record);
1523     auto ret = AddSymbolSpanOperation(options, isPaste, index);
1524     SetNeedMoveCaretToContentRect();
1525     IF_TRUE(!isUndoRedo, AfterContentChange(changeValue));
1526     return ret;
1527 }
1528 
AddSymbolSpanOperation(const SymbolSpanOptions & options,bool isPaste,int32_t index)1529 int32_t RichEditorPattern::AddSymbolSpanOperation(const SymbolSpanOptions& options, bool isPaste, int32_t index)
1530 {
1531     auto host = GetContentHost();
1532     CHECK_NULL_RETURN(host, -1);
1533 
1534     auto spanNode = SpanNode::GetOrCreateSpanNode(V2::SYMBOL_SPAN_ETS_TAG,
1535         ElementRegister::GetInstance()->MakeUniqueId());
1536 
1537     int32_t insertIndex = options.offset.value_or(GetTextContentLength());
1538     int32_t spanIndex = TextSpanSplit(insertIndex);
1539     if (spanIndex == -1) {
1540         spanIndex = static_cast<int32_t>(host->GetChildren().size());
1541     }
1542     spanNode->MountToParent(host, spanIndex);
1543     spanNode->UpdateContent(options.symbolId);
1544     if (options.style.has_value()) {
1545         spanNode->UpdateFontSize(options.style.value().GetFontSize());
1546         spanNode->UpdateFontWeight(options.style.value().GetFontWeight());
1547         spanNode->UpdateSymbolColorList(options.style.value().GetSymbolColorList());
1548         spanNode->UpdateSymbolRenderingStrategy(options.style.value().GetRenderStrategy());
1549         spanNode->UpdateSymbolEffectStrategy(options.style.value().GetEffectStrategy());
1550         spanNode->UpdateSymbolType(options.style.value().GetSymbolType());
1551         spanNode->UpdateFontFamily(options.style.value().GetFontFamilies());
1552     }
1553     bool isUndoRedo = (options.optionSource == OptionSource::UNDO_REDO);
1554     IF_TRUE(isUndoRedo && options.paraStyle.has_value(), UpdateParagraphStyle(spanNode, options.paraStyle.value()));
1555     auto spanItem = spanNode->GetSpanItem();
1556     spanItem->content = u"  ";
1557     spanItem->spanItemType = SpanItemType::SYMBOL;
1558     spanItem->SetSymbolId(options.symbolId);
1559     spanItem->SetTextStyle(options.style);
1560     spanItem->SetResourceObject(options.resourceObject);
1561     AddSpanItem(spanItem, spanIndex);
1562     IF_TRUE(!isUndoRedo, undoManager_->RecordAddSpanOperation(spanItem, SpanOptionsType::SYMBOL));
1563     SetCaretPosition(insertIndex + spanItem->content.length());
1564     ResetSelectionAfterAddSpan(false);
1565     SpanNodeFission(spanNode);
1566     return spanIndex;
1567 }
1568 
BeforeAddSymbol(RichEditorChangeValue & changeValue,const SymbolSpanOptions & options)1569 bool RichEditorPattern::BeforeAddSymbol(RichEditorChangeValue& changeValue, const SymbolSpanOptions& options)
1570 {
1571     auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
1572     CHECK_NULL_RETURN(eventHub, false);
1573     CHECK_NULL_RETURN(eventHub->HasOnWillChange(), true);
1574 
1575     int32_t contentLength = GetTextContentLength();
1576     int32_t insertIndex = options.offset.value_or(contentLength);
1577     insertIndex = std::clamp(insertIndex, 0, contentLength);
1578 
1579     changeValue.SetRangeBefore({ insertIndex, insertIndex });
1580     changeValue.SetRangeAfter({ insertIndex, insertIndex + SYMBOL_SPAN_LENGTH });
1581     RichEditorAbstractSpanResult retInfo;
1582     TextInsertValueInfo info;
1583     CalcInsertValueObj(info, insertIndex, true);
1584     int32_t spanIndex = info.GetSpanIndex();
1585     retInfo.SetSpanIndex(spanIndex);
1586     retInfo.SetOffsetInSpan(0);
1587     retInfo.SetValueString(std::to_string(options.symbolId));
1588     retInfo.SetEraseLength(SYMBOL_SPAN_LENGTH);
1589     retInfo.SetSpanRangeStart(insertIndex);
1590     retInfo.SetSpanRangeEnd(insertIndex + SYMBOL_SPAN_LENGTH);
1591     retInfo.SetSpanType(SpanResultType::SYMBOL);
1592 
1593     TextStyle style = options.style.value_or(TextStyle());
1594     retInfo.SetSymbolSpanStyle(SymbolSpanStyle(style));
1595     retInfo.SetValueResource(options.resourceObject);
1596 
1597     changeValue.SetRichEditorReplacedSymbolSpans(retInfo);
1598     auto ret = eventHub->FireOnWillChange(changeValue);
1599     return ret;
1600 }
1601 
AfterContentChange(RichEditorChangeValue & changeValue)1602 void RichEditorPattern::AfterContentChange(RichEditorChangeValue& changeValue)
1603 {
1604     auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
1605     if (eventHub && eventHub->HasOnDidChange()) {
1606         eventHub->FireOnDidChange(changeValue);
1607     }
1608     ForceTriggerAvoidOnCaretChange();
1609     ReportAfterContentChangeEvent();
1610 }
1611 
ReportAfterContentChangeEvent()1612 void RichEditorPattern::ReportAfterContentChangeEvent()
1613 {
1614     std::string currentContent;
1615     if (isSpanStringMode_) {
1616         IF_TRUE(styledString_, currentContent = styledString_->GetString());
1617     } else {
1618         std::u16string u16Str;
1619         GetContentBySpans(u16Str);
1620         currentContent = UtfUtils::Str16DebugToStr8(u16Str);
1621     }
1622 
1623     auto weakThis = AceType::WeakClaim(this);
1624     auto callback = [weakThis](const std::string& type, const std::string& content) {
1625         auto strongThis = weakThis.Upgrade();
1626         CHECK_NULL_VOID(strongThis);
1627         strongThis->OnAccessibilityEventTextChange(type, content);
1628     };
1629 
1630     ProcessAccessibilityTextChange(
1631         currentContent,
1632         std::move(callback),
1633         AceLogTag::ACE_RICH_TEXT
1634     );
1635 }
1636 
SpanNodeFission(RefPtr<SpanNode> & spanNode,bool needLeadingMargin)1637 void RichEditorPattern::SpanNodeFission(RefPtr<SpanNode>& spanNode, bool needLeadingMargin)
1638 {
1639     auto spanItem = spanNode->GetSpanItem();
1640     auto wContent = spanItem->content;
1641     auto spanStart = spanItem->position - wContent.length();
1642     for (size_t i = 0; i < wContent.length(); i++) {
1643         if (wContent[i] == '\n') {
1644             TextSpanSplit(static_cast<int32_t>(spanStart + i + 1), needLeadingMargin);
1645         }
1646     }
1647     UpdateSpanPosition();
1648 }
1649 
DeleteSpans(const RangeOptions & options,TextChangeReason reason)1650 void RichEditorPattern::DeleteSpans(const RangeOptions& options, TextChangeReason reason)
1651 {
1652     IF_TRUE(previewTextRecord_.previewTextExiting, TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "exiting preview"));
1653     IF_TRUE(IsPreviewTextInputting() && !previewTextRecord_.previewTextExiting, NotifyExitTextPreview());
1654     auto length = GetTextContentLength();
1655     int32_t start = options.start.value_or(0);
1656     int32_t end = options.end.value_or(length);
1657     if (start > end) {
1658         std::swap(start, end);
1659     }
1660     start = std::max(0, start);
1661     end = std::min(length, end);
1662     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "delete spans range=[%{public}d, %{public}d]", start, end);
1663     if (start > length || end < 0 || start == end) {
1664         return;
1665     }
1666     AdjustSelector(start, end);
1667     OperationRecord record;
1668     record.beforeCaretPosition = start;
1669     std::u16string u16valueString;
1670     GetContentBySpans(u16valueString);
1671     record.deleteText = u16valueString.substr(start, end - start);
1672     RichEditorChangeValue changeValue(reason);
1673     changeValue.SetRangeBefore({ start, end });
1674     changeValue.SetRangeAfter({ start, start });
1675     if (auto eventHub = GetOrCreateEventHub<RichEditorEventHub>(); eventHub) {
1676         CHECK_NULL_VOID(eventHub->FireOnWillChange(changeValue));
1677     }
1678     ClearRedoOperationRecords();
1679     record.afterCaretPosition = start;
1680     AddOperationRecord(record);
1681     UndoRedoRecord styledRecord;
1682     undoManager_->UpdateRecordBeforeChange(start, end - start, styledRecord);
1683     DeleteSpansOperation(start, end);
1684     undoManager_->RecordOperationAfterChange(start, 0, styledRecord);
1685     AfterContentChange(changeValue);
1686 }
1687 
DeleteSpansOperation(int32_t start,int32_t end)1688 void RichEditorPattern::DeleteSpansOperation(int32_t start, int32_t end)
1689 {
1690     auto startInfo = GetSpanPositionInfo(start);
1691     auto endInfo = GetSpanPositionInfo(end - 1);
1692     if (startInfo.spanIndex_ == endInfo.spanIndex_) {
1693         DeleteSpanByRange(start, end, startInfo);
1694     } else {
1695         DeleteSpansByRange(start, end, startInfo, endInfo);
1696     }
1697     RemoveEmptySpanItems();
1698     if (textSelector_.IsValid()) {
1699         SetCaretPosition(textSelector_.GetTextStart());
1700         CloseSelectOverlay();
1701         ResetSelection();
1702     }
1703     SetCaretOffset(start);
1704     auto host = GetContentHost();
1705     CHECK_NULL_VOID(host);
1706     if (host->GetChildren().empty() || GetTextContentLength() == 0) {
1707         SetCaretPosition(0);
1708         textForDisplay_.clear();
1709     }
1710     UpdateSpanPosition();
1711 }
1712 
RemoveEmptySpanItems()1713 void RichEditorPattern::RemoveEmptySpanItems()
1714 {
1715     for (auto it = spans_.begin(); it != spans_.end();) {
1716         if ((*it)->content.empty()) {
1717             it = spans_.erase(it);
1718         } else {
1719             ++it;
1720         }
1721     }
1722 }
1723 
RemoveEmptySpanNodes()1724 void RichEditorPattern::RemoveEmptySpanNodes()
1725 {
1726     auto host = GetContentHost();
1727     CHECK_NULL_VOID(host);
1728     auto& spanNodes = host->GetChildren();
1729     for (auto it = spanNodes.begin(); it != spanNodes.end();) {
1730         auto spanNode = AceType::DynamicCast<SpanNode>(*it);
1731         if (!spanNode) {
1732             ++it;
1733             continue;
1734         }
1735         if (spanNode->GetSpanItem()->content.empty()) {
1736             it = host->RemoveChild(spanNode);
1737         } else {
1738             ++it;
1739         }
1740     }
1741 }
1742 
RemoveEmptySpans()1743 void RichEditorPattern::RemoveEmptySpans()
1744 {
1745     RemoveEmptySpanItems();
1746     RemoveEmptySpanNodes();
1747 }
1748 
DeleteSpanByRange(int32_t start,int32_t end,SpanPositionInfo info)1749 void RichEditorPattern::DeleteSpanByRange(int32_t start, int32_t end, SpanPositionInfo info)
1750 {
1751     auto host = GetContentHost();
1752     CHECK_NULL_VOID(host);
1753     auto childrens = host->GetChildren();
1754     auto it = childrens.begin();
1755     std::advance(it, info.spanIndex_);
1756     CHECK_NULL_VOID(it != childrens.end());
1757     if (start == info.spanStart_ && end == info.spanEnd_) {
1758         ClearContent(*it);
1759         host->RemoveChild(*it);
1760     } else {
1761         auto spanNode = DynamicCast<SpanNode>(*it);
1762         CHECK_NULL_VOID(spanNode);
1763         auto spanItem = spanNode->GetSpanItem();
1764         auto beforStr = spanItem->content.substr(0, start - info.spanStart_);
1765         auto endStr = spanItem->content.substr(end - info.spanStart_);
1766         spanNode->UpdateContent(beforStr + endStr);
1767     }
1768     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1769     host->MarkModifyDone();
1770 }
1771 
DeleteSpansByRange(int32_t start,int32_t end,SpanPositionInfo startInfo,SpanPositionInfo endInfo)1772 void RichEditorPattern::DeleteSpansByRange(
1773     int32_t start, int32_t end, SpanPositionInfo startInfo, SpanPositionInfo endInfo)
1774 {
1775     auto host = GetContentHost();
1776     CHECK_NULL_VOID(host);
1777     auto childrens = host->GetChildren();
1778     CHECK_NULL_VOID(!childrens.empty());
1779 
1780     auto itStart = childrens.begin();
1781     if (startInfo.spanIndex_ >= static_cast<int32_t>(childrens.size())) {
1782         std::advance(itStart, static_cast<int32_t>(childrens.size()) - 1);
1783         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "startInfo.spanIndex_ is larger than childrens size");
1784     } else {
1785         std::advance(itStart, startInfo.spanIndex_);
1786     }
1787     CHECK_NULL_VOID(itStart != childrens.end());
1788     auto saveStartSpan = (start == startInfo.spanStart_) ? 0 : 1;
1789     if (saveStartSpan) {
1790         auto spanNodeStart = DynamicCast<SpanNode>(*itStart);
1791         CHECK_NULL_VOID(spanNodeStart);
1792         auto spanItemStart = spanNodeStart->GetSpanItem();
1793         auto beforStr = spanItemStart->content.substr(0, start - startInfo.spanStart_);
1794         spanNodeStart->UpdateContent(beforStr);
1795     }
1796     auto itEnd = childrens.begin();
1797     std::advance(itEnd, endInfo.spanIndex_);
1798     auto delEndSpan = (end == endInfo.spanEnd_) ? 1 : 0;
1799     if (!delEndSpan) {
1800         auto spanNodeEnd = DynamicCast<SpanNode>(*itEnd);
1801         CHECK_NULL_VOID(spanNodeEnd);
1802         auto spanItemEnd = spanNodeEnd->GetSpanItem();
1803         auto endStr = spanItemEnd->content.substr(end - endInfo.spanStart_, endInfo.spanEnd_ - end);
1804         spanNodeEnd->UpdateContent(endStr);
1805     }
1806     auto startIter = childrens.begin();
1807     std::advance(startIter, startInfo.spanIndex_ + saveStartSpan);
1808     auto endIter = childrens.begin();
1809     std::advance(endIter, endInfo.spanIndex_);
1810     for (auto iter = startIter; iter != endIter; ++iter) {
1811         ClearContent(*iter);
1812         host->RemoveChild(*iter);
1813     }
1814     if (endIter != childrens.end() && delEndSpan) {
1815         ClearContent(*endIter);
1816         host->RemoveChild(*endIter);
1817     }
1818     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1819     host->MarkModifyDone();
1820 }
1821 
GetLeftTextOfCursor(int32_t number)1822 std::u16string RichEditorPattern::GetLeftTextOfCursor(int32_t number)
1823 {
1824     if (number > caretPosition_) {
1825         number = caretPosition_;
1826     }
1827     auto start = caretPosition_;
1828     if (IsSelected()) {
1829         start = std::min(textSelector_.GetStart(), textSelector_.GetEnd());
1830     }
1831     return GetSelectedText(start - number, start);
1832 }
1833 
GetRightTextOfCursor(int32_t number)1834 std::u16string RichEditorPattern::GetRightTextOfCursor(int32_t number)
1835 {
1836     auto end = caretPosition_;
1837     if (IsSelected()) {
1838         end = std::max(textSelector_.GetStart(), textSelector_.GetEnd());
1839     }
1840     return GetSelectedText(end, end + number);
1841 }
1842 
GetTextIndexAtCursor()1843 int32_t RichEditorPattern::GetTextIndexAtCursor()
1844 {
1845     return caretPosition_;
1846 }
1847 
ClearContent(const RefPtr<UINode> & child)1848 void RichEditorPattern::ClearContent(const RefPtr<UINode>& child)
1849 {
1850     CHECK_NULL_VOID(child);
1851     if (child->GetTag() == V2::SPAN_ETS_TAG) {
1852         auto spanNode = DynamicCast<SpanNode>(child);
1853         if (spanNode) {
1854             spanNode->UpdateContent(u"");
1855             spanNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1856         }
1857         return;
1858     }
1859     auto imageSpanNode = DynamicCast<ImageSpanNode>(child);
1860     if (imageSpanNode) {
1861         imageSpanNode->GetSpanItem()->content.clear();
1862         imageSpanNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1863         return;
1864     }
1865     auto placeholderSpanNode = DynamicCast<PlaceholderSpanNode>(child);
1866     if (placeholderSpanNode) {
1867         placeholderSpanNode->GetSpanItem()->content.clear();
1868         placeholderSpanNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1869     }
1870 }
1871 
GetSpanPositionInfo(int32_t position)1872 SpanPositionInfo RichEditorPattern::GetSpanPositionInfo(int32_t position)
1873 {
1874     SpanPositionInfo spanPositionInfo(-1, -1, -1, -1);
1875     CHECK_NULL_RETURN(!spans_.empty(), spanPositionInfo);
1876     position = std::clamp(position, 0, GetTextContentLength());
1877     // find the spanItem where the position is
1878     auto it = std::find_if(spans_.begin(), spans_.end(), [position](const RefPtr<SpanItem>& spanItem) {
1879         return (spanItem->position - static_cast<int32_t>(spanItem->content.length()) <= position) &&
1880                (position < spanItem->position);
1881     });
1882     if (it != spans_.end() && (*it)->unicode != 0 && (*it)->position - caretPosition_ + moveLength_ == 1) {
1883         it++;
1884         moveLength_++;
1885         position++;
1886     }
1887 
1888     // the position is at the end
1889     if (it == spans_.end()) {
1890         return spanPositionInfo;
1891     }
1892 
1893     spanPositionInfo.spanIndex_ = std::distance(spans_.begin(), it);
1894     int32_t contentLen = static_cast<int32_t>((*it)->content.length());
1895     spanPositionInfo.spanStart_ = (*it)->position - contentLen;
1896     spanPositionInfo.spanEnd_ = (*it)->position;
1897     spanPositionInfo.spanOffset_ = position - spanPositionInfo.spanStart_;
1898     return spanPositionInfo;
1899 }
1900 
CopyTextSpanStyle(RefPtr<SpanNode> & source,RefPtr<SpanNode> & target,bool needLeadingMargin)1901 void RichEditorPattern::CopyTextSpanStyle(RefPtr<SpanNode>& source, RefPtr<SpanNode>& target, bool needLeadingMargin)
1902 {
1903     CopyTextSpanFontStyle(source, target);
1904     CopyTextSpanLineStyle(source, target, needLeadingMargin);
1905     CopyTextSpanUrlStyle(source, target);
1906 }
1907 
CopyTextSpanFontStyle(RefPtr<SpanNode> & source,RefPtr<SpanNode> & target)1908 void RichEditorPattern::CopyTextSpanFontStyle(RefPtr<SpanNode>& source, RefPtr<SpanNode>& target)
1909 {
1910     CHECK_NULL_VOID(source);
1911     CHECK_NULL_VOID(source->GetTag() == V2::SPAN_ETS_TAG);
1912     CHECK_NULL_VOID(target);
1913     COPY_SPAN_STYLE_IF_PRESENT(source, target, FontSize);
1914     COPY_SPAN_STYLE_IF_PRESENT(source, target, TextColor);
1915     COPY_SPAN_STYLE_IF_PRESENT(source, target, ItalicFontStyle);
1916     COPY_SPAN_STYLE_IF_PRESENT(source, target, FontWeight);
1917     COPY_SPAN_STYLE_IF_PRESENT(source, target, FontFamily);
1918     COPY_SPAN_STYLE_IF_PRESENT(source, target, TextDecoration);
1919     COPY_SPAN_STYLE_IF_PRESENT(source, target, TextDecorationColor);
1920     COPY_SPAN_STYLE_IF_PRESENT(source, target, TextDecorationStyle);
1921     COPY_SPAN_STYLE_IF_PRESENT(source, target, LineThicknessScale);
1922     COPY_SPAN_STYLE_IF_PRESENT(source, target, TextCase);
1923     COPY_SPAN_STYLE_IF_PRESENT(source, target, LineHeight);
1924     COPY_SPAN_STYLE_IF_PRESENT(source, target, HalfLeading);
1925     COPY_SPAN_STYLE_IF_PRESENT(source, target, LetterSpacing);
1926     COPY_SPAN_STYLE_IF_PRESENT(source, target, FontFeature);
1927     COPY_SPAN_STYLE_IF_PRESENT(source, target, TextShadow);
1928     target->GetSpanItem()->useThemeFontColor = source->GetSpanItem()->useThemeFontColor;
1929     target->GetSpanItem()->useThemeDecorationColor = source->GetSpanItem()->useThemeDecorationColor;
1930     UpdateTextBackgroundStyle(target, source->GetTextBackgroundStyle());
1931 }
1932 
CopyTextSpanLineStyle(RefPtr<SpanNode> & source,RefPtr<SpanNode> & target,bool needLeadingMargin)1933 void RichEditorPattern::CopyTextSpanLineStyle(
1934     RefPtr<SpanNode>& source, RefPtr<SpanNode>& target, bool needLeadingMargin)
1935 {
1936     CHECK_NULL_VOID(source);
1937     CHECK_NULL_VOID(target);
1938     COPY_SPAN_STYLE_IF_PRESENT(source, target, TextAlign);
1939     COPY_SPAN_STYLE_IF_PRESENT(source, target, WordBreak);
1940     COPY_SPAN_STYLE_IF_PRESENT(source, target, LineBreakStrategy);
1941     COPY_SPAN_STYLE_IF_PRESENT(source, target, ParagraphSpacing);
1942     COPY_SPAN_STYLE_IF_PRESENT(source, target, TextVerticalAlign);
1943     if (source->HasLeadingMargin()) {
1944         auto leadingMargin = source->GetLeadingMarginValue({});
1945         if (!needLeadingMargin) {
1946             leadingMargin.pixmap.Reset();
1947         }
1948         target->UpdateLeadingMargin(leadingMargin);
1949     }
1950 }
1951 
CopyTextSpanUrlStyle(RefPtr<SpanNode> & source,RefPtr<SpanNode> & target)1952 void RichEditorPattern::CopyTextSpanUrlStyle(RefPtr<SpanNode>& source, RefPtr<SpanNode>& target)
1953 {
1954     CHECK_NULL_VOID(source && source->GetTag() == V2::SPAN_ETS_TAG);
1955     CHECK_NULL_VOID(target && target->GetTag() == V2::SPAN_ETS_TAG);
1956     const auto& sourceSpanItem = source->GetSpanItem();
1957     const auto& targetSpanItem = target->GetSpanItem();
1958     CHECK_NULL_VOID(sourceSpanItem && targetSpanItem);
1959     targetSpanItem->urlOnRelease = sourceSpanItem->urlOnRelease;
1960     targetSpanItem->urlAddress = sourceSpanItem->urlAddress;
1961 }
1962 
CopyGestureOption(const RefPtr<SpanNode> & source,RefPtr<SpanNode> & target)1963 void RichEditorPattern::CopyGestureOption(const RefPtr<SpanNode>& source, RefPtr<SpanNode>& target)
1964 {
1965     CHECK_NULL_VOID(source);
1966     CHECK_NULL_VOID(target);
1967     auto sourceItem = source->GetSpanItem();
1968     CHECK_NULL_VOID(sourceItem);
1969     auto targetItem = target->GetSpanItem();
1970     CHECK_NULL_VOID(targetItem);
1971 
1972     if (sourceItem->onClick) {
1973         auto tmpClickFunc = sourceItem->onClick;
1974         targetItem->SetOnClickEvent(std::move(tmpClickFunc));
1975     }
1976     if (sourceItem->onLongPress) {
1977         auto tmpLongPressFunc = sourceItem->onLongPress;
1978         targetItem->SetLongPressEvent(std::move(tmpLongPressFunc));
1979     }
1980     if (sourceItem->onDoubleClick) {
1981         auto tmpDoubleClickFunc = sourceItem->onDoubleClick;
1982         targetItem->SetDoubleClickEvent(std::move(tmpDoubleClickFunc));
1983     }
1984     if (sourceItem->onHover) {
1985         auto tmpHoverFunc = sourceItem->onHover;
1986         targetItem->SetHoverEvent(std::move(tmpHoverFunc));
1987     }
1988     if (sourceItem->onTouch) {
1989         auto tmpTouchFunc = sourceItem->onTouch;
1990         targetItem->SetTouchEvent(std::move(tmpTouchFunc));
1991     }
1992 }
1993 
HandleUserTouchEvent(TouchEventInfo & info)1994 void RichEditorPattern::HandleUserTouchEvent(TouchEventInfo& info)
1995 {
1996     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleUserTouchEvent");
1997     TextPattern::HandleSpanStringTouchEvent(info);
1998 }
1999 
TextSpanSplit(int32_t position,bool needLeadingMargin)2000 int32_t RichEditorPattern::TextSpanSplit(int32_t position, bool needLeadingMargin)
2001 {
2002     CHECK_NULL_RETURN(!spans_.empty(), -1);
2003 
2004     SpanPositionInfo positionInfo = GetSpanPositionInfo(position);
2005     int32_t spanIndex = positionInfo.spanIndex_;
2006     int32_t spanStart = positionInfo.spanStart_;
2007     int32_t spanEnd = positionInfo.spanEnd_;
2008     int32_t offsetInSpan = positionInfo.spanOffset_;
2009     TAG_LOGD(AceLogTag::ACE_RICH_TEXT,
2010         "position=%{public}d, spanIndex=%{public}d, spanRange=[%{public}d,%{public}d], offsetInSpan=%{public}d",
2011         position, spanIndex, spanStart, spanEnd, offsetInSpan);
2012 
2013     CHECK_NULL_RETURN((offsetInSpan > 0), spanIndex);
2014 
2015     auto host = GetContentHost();
2016     CHECK_NULL_RETURN(host, -1);
2017     auto spanNode = DynamicCast<SpanNode>(host->GetChildAtIndex(spanIndex));
2018     CHECK_NULL_RETURN(spanNode, -1);
2019 
2020     auto spanItem = spanNode->GetSpanItem();
2021     auto spanItemContent = spanItem->content;
2022     offsetInSpan = std::min(offsetInSpan, static_cast<int32_t>(spanItemContent.length()));
2023 
2024     spanNode->UpdateContent(spanItemContent.substr(0, offsetInSpan));
2025     spanItem->position = spanStart + offsetInSpan;
2026 
2027     auto newSpanNode = SpanNode::GetOrCreateSpanNode(ElementRegister::GetInstance()->MakeUniqueId());
2028     CHECK_NULL_RETURN(newSpanNode, -1);
2029 
2030     CopyTextSpanStyle(spanNode, newSpanNode, needLeadingMargin);
2031     CopyGestureOption(spanNode, newSpanNode);
2032     newSpanNode->UpdateContent(spanItemContent.substr(offsetInSpan));
2033     newSpanNode->MountToParent(host, spanIndex + 1);
2034 
2035     auto newSpanItem = newSpanNode->GetSpanItem();
2036     newSpanItem->rangeStart = spanStart + offsetInSpan;
2037     newSpanItem->position = spanEnd;
2038 
2039     auto spanIter = spans_.begin();
2040     std::advance(spanIter, spanIndex + 1);
2041     spans_.insert(spanIter, newSpanItem);
2042 
2043     return spanIndex + 1;
2044 }
2045 
GetCaretPosition()2046 int32_t RichEditorPattern::GetCaretPosition()
2047 {
2048     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "GetCaretPosition");
2049     return caretPosition_;
2050 }
2051 
SetCaretOffset(int32_t caretPosition)2052 bool RichEditorPattern::SetCaretOffset(int32_t caretPosition)
2053 {
2054     if (IsPreviewTextInputting()) {
2055         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "intercept operation in previewText state");
2056         return false;
2057     }
2058     int32_t inputCaretPosition = caretPosition;
2059     AdjustSelector(caretPosition, HandleType::SECOND);
2060     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "setCaretOffset, in=%{public}d, afterAdjust=%{public}d",
2061         inputCaretPosition, caretPosition);
2062     bool success = SetCaretPosition(caretPosition);
2063     auto host = GetHost();
2064     CHECK_NULL_RETURN(host, false);
2065     auto focusHub = host->GetOrCreateFocusHub();
2066     CHECK_NULL_RETURN(focusHub, false);
2067     if (focusHub->IsCurrentFocus()) {
2068         isCursorAlwaysDisplayed_ = false;
2069         StartTwinkling();
2070     }
2071     CloseSelectOverlay();
2072     ResetSelection();
2073     return success;
2074 }
2075 
CalcCursorOffsetByPosition(int32_t position,float & selectLineHeight,bool downStreamFirst,bool needLineHighest)2076 OffsetF RichEditorPattern::CalcCursorOffsetByPosition(
2077     int32_t position, float& selectLineHeight, bool downStreamFirst, bool needLineHighest)
2078 {
2079     selectLineHeight = 0.0f;
2080     auto host = GetHost();
2081     auto contentHost = GetContentHost();
2082     CHECK_NULL_RETURN(host && contentHost, OffsetF(0, 0));
2083     auto pipeline = host->GetContext();
2084     CHECK_NULL_RETURN(pipeline, OffsetF(0, 0));
2085     auto rootOffset = pipeline->GetRootRect().GetOffset();
2086     auto textPaintOffset = richTextRect_.GetOffset();
2087     needLineHighest |= IsCustomSpanInCaretPos(position, downStreamFirst);
2088     auto startOffset = paragraphs_.ComputeCursorOffset(position, selectLineHeight, downStreamFirst, needLineHighest);
2089     auto children = contentHost->GetChildren();
2090     if (NearZero(selectLineHeight)) {
2091         if (children.empty() || GetTextContentLength() == 0) {
2092             return textPaintOffset - rootOffset;
2093         }
2094         if (std::all_of(children.begin(), children.end(), [](RefPtr<UINode>& node) {
2095                 CHECK_NULL_RETURN(node, false);
2096                 return (node->GetTag() == V2::IMAGE_ETS_TAG || node->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG);
2097             })) {
2098             bool isTail = false;
2099             auto it = children.begin();
2100             if (position >= static_cast<int32_t>(children.size())) {
2101                 std::advance(it, (static_cast<int32_t>(children.size()) - 1));
2102                 isTail = true;
2103             } else {
2104                 std::advance(it, position);
2105             }
2106             if (it == children.end()) {
2107                 return startOffset;
2108             }
2109             auto imageNode = DynamicCast<FrameNode>(*it);
2110             if (imageNode) {
2111                 auto geometryNode = imageNode->GetGeometryNode();
2112                 CHECK_NULL_RETURN(geometryNode, OffsetF(0.0f, 0.0f));
2113                 startOffset = geometryNode->GetMarginFrameOffset();
2114                 selectLineHeight = geometryNode->GetMarginFrameSize().Height();
2115                 startOffset += isTail ? OffsetF(geometryNode->GetMarginFrameSize().Width(), 0.0f) : OffsetF(0.0f, 0.0f);
2116             }
2117             return startOffset;
2118         }
2119     }
2120     auto caretOffset = startOffset + textPaintOffset + rootOffset;
2121     CHECK_NULL_RETURN(overlayMod_, caretOffset);
2122     caretOffset.SetX(std::clamp(caretOffset.GetX(), 0.0f, richTextRect_.Right()));
2123     return caretOffset;
2124 }
2125 
HandleCurrentPositionParagraphInfo(float & lastLineTop,float & paragraphSpacing)2126 void RichEditorPattern::HandleCurrentPositionParagraphInfo(float& lastLineTop, float& paragraphSpacing)
2127 {
2128     auto paragraphInfo = paragraphs_.GetParagraphInfo(caretPosition_);
2129     paragraphSpacing = paragraphInfo.paragraphStyle.paragraphSpacing.ConvertToPx();
2130     float lastLineHeight = 0.0f;
2131     CHECK_EQUAL_VOID(paragraphInfo.end - 1 >= paragraphInfo.start, false);
2132     lastLineTop = CalcCursorOffsetByPosition(paragraphInfo.end -1, lastLineHeight, true, true).GetY();
2133 }
2134 
IsCustomSpanInCaretPos(int32_t position,bool downStreamFirst)2135 bool RichEditorPattern::IsCustomSpanInCaretPos(int32_t position, bool downStreamFirst)
2136 {
2137     CHECK_NULL_RETURN((isSpanStringMode_ && styledString_), false);
2138     auto start = downStreamFirst ? position : position - 1;
2139     start = std::clamp(start, 0, GetTextContentLength());
2140     auto lastStyles = styledString_->GetSpans(start, 1);
2141     for (auto& style : lastStyles) {
2142         if (style && style->GetSpanType() == SpanType::CustomSpan) {
2143             return true;
2144         }
2145     }
2146     return false;
2147 }
2148 
SetCaretPositionWithAffinity(PositionWithAffinity positionWithAffinity)2149 void RichEditorPattern::SetCaretPositionWithAffinity(PositionWithAffinity positionWithAffinity)
2150 {
2151     auto currentPosition = static_cast<int32_t>(positionWithAffinity.position_);
2152     SetCaretPosition(currentPosition);
2153     caretAffinityPolicy_ = (positionWithAffinity.affinity_ == TextAffinity::UPSTREAM)
2154                                 ? CaretAffinityPolicy::UPSTREAM_FIRST
2155                                 : CaretAffinityPolicy::DOWNSTREAM_FIRST;
2156 }
2157 
SetCaretPosition(int32_t pos,bool needNotifyImf)2158 bool RichEditorPattern::SetCaretPosition(int32_t pos, bool needNotifyImf)
2159 {
2160     auto correctPos = std::clamp(pos, 0, GetTextContentLength());
2161     IF_TRUE(!isModifyingContent_, AdjustSelector(correctPos, HandleType::SECOND));
2162     ResetLastClickOffset();
2163     caretAffinityPolicy_ = CaretAffinityPolicy::DEFAULT;
2164     CHECK_NULL_RETURN((pos == correctPos), false);
2165     if (caretPosition_ != correctPos) {
2166         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "caret:%{public}d->%{public}d", caretPosition_, correctPos);
2167         lastCaretPosition_ = caretPosition_;
2168         caretPosition_ = correctPos;
2169         FireOnSelectionChange(caretPosition_);
2170         if (caretChangeListener_) {
2171             caretChangeListener_(caretPosition_);
2172         }
2173     }
2174     if (needNotifyImf) {
2175         UpdateCaretInfoToController();
2176     }
2177     return true;
2178 }
2179 
FireOnSelectionChange(const int32_t caretPosition)2180 void RichEditorPattern::FireOnSelectionChange(const int32_t caretPosition)
2181 {
2182     if (!textSelector_.SelectNothing() || !caretTwinkling_) {
2183         return;
2184     }
2185     FireOnSelectionChange(caretPosition, caretPosition);
2186 }
2187 
FireOnSelectionChange(const TextSelector & selector)2188 void RichEditorPattern::FireOnSelectionChange(const TextSelector& selector)
2189 {
2190     if (selector.SelectNothing()) {
2191         return;
2192     }
2193     FireOnSelectionChange(selector.GetStart(), selector.GetEnd());
2194 }
2195 
FireOnSelectionChange(int32_t start,int32_t end,bool isForced)2196 void RichEditorPattern::FireOnSelectionChange(int32_t start, int32_t end, bool isForced)
2197 {
2198     auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
2199     CHECK_NULL_VOID(eventHub);
2200     CHECK_NULL_VOID(isForced || HasFocus() || dataDetectorAdapter_->hasClickedMenuOption_);
2201     bool isSingleHandle = selectOverlay_->IsSingleHandle();
2202     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "onSelectionChange, range=[%{public}d,%{public}d], isTwinkling=%{public}d, "
2203         "isSingleHandle=%{public}d", start, end, caretTwinkling_, isSingleHandle);
2204     if (start < 0 || end < 0) {
2205         return;
2206     }
2207     if (start == end && !caretTwinkling_ && !isSingleHandle) {
2208         return;
2209     }
2210     if (start > end) {
2211         std::swap(start, end);
2212     }
2213     auto range = SelectionRangeInfo(start, end);
2214     if (range == lastSelectionRange_) {
2215         return;
2216     }
2217     lastSelectionRange_ = std::move(range);
2218     eventHub->FireOnSelectionChange(&range);
2219 }
2220 
GetCaretVisible() const2221 bool RichEditorPattern::GetCaretVisible() const
2222 {
2223     return caretVisible_;
2224 }
2225 
OnWindowHide()2226 void RichEditorPattern::OnWindowHide()
2227 {
2228     ScrollablePattern::OnWindowHide();
2229 }
2230 
SetUpdateSpanStyle(struct UpdateSpanStyle updateSpanStyle)2231 void RichEditorPattern::SetUpdateSpanStyle(struct UpdateSpanStyle updateSpanStyle)
2232 {
2233     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "SetUpdateSpanStyle");
2234     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "updateSpanStyle=%{public}s", updateSpanStyle.ToString().c_str());
2235     updateSpanStyle_ = updateSpanStyle;
2236 }
2237 
GetUpdateSpanStyle()2238 UpdateSpanStyle RichEditorPattern::GetUpdateSpanStyle()
2239 {
2240     return updateSpanStyle_;
2241 }
2242 
MarkAISpanStyleChanged()2243 void RichEditorPattern::MarkAISpanStyleChanged()
2244 {
2245     std::for_each(spans_.begin(), spans_.end(), [](const auto& span) {
2246         if (span->aiSpanResultCount != 0) {
2247             span->needReLayout = true;
2248         }
2249     });
2250     TextPattern::MarkAISpanStyleChanged();
2251 }
2252 
HandleOnAskCelia()2253 void RichEditorPattern::HandleOnAskCelia()
2254 {
2255     TextPattern::HandleOnAskCelia();
2256     if (IsUsingMouse()) {
2257         CloseSelectOverlay();
2258     } else {
2259         selectOverlay_->HideMenu();
2260     }
2261 }
2262 
UpdateCaretStyleByTypingStyle(bool isReset)2263 void RichEditorPattern::UpdateCaretStyleByTypingStyle(bool isReset)
2264 {
2265     bool empty = spans_.empty();
2266     bool hasPreviewContent = !previewTextRecord_.previewContent.empty();
2267     bool lastNewLine = !empty && styleManager_->HasTypingParagraphStyle() && spans_.back()->content.back() == u'\n';
2268     CHECK_NULL_VOID(empty || hasPreviewContent || lastNewLine || isReset);
2269     auto host = GetHost();
2270     CHECK_NULL_VOID(host);
2271     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2272 }
2273 
SetTypingStyle(std::optional<struct UpdateSpanStyle> typingStyle,std::optional<TextStyle> textStyle)2274 void RichEditorPattern::SetTypingStyle(std::optional<struct UpdateSpanStyle> typingStyle,
2275     std::optional<TextStyle> textStyle)
2276 {
2277     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "SetTypingStyle, %{public}d", typingStyle.has_value());
2278     bool isReset = typingStyle_.has_value() && !typingStyle.has_value();
2279     typingStyle_ = typingStyle;
2280     typingTextStyle_ = textStyle;
2281     styleManager_->SetTypingStyle(typingStyle, textStyle);
2282     if (typingStyle_.has_value() && typingTextStyle_.has_value()) {
2283         IF_TRUE(typingStyle_->updateTextBackgroundStyle,
2284             typingStyle_->updateTextBackgroundStyle->needCompareGroupId = false);
2285         auto textBackgroundStyle = typingTextStyle_->GetTextBackgroundStyle();
2286         if (textBackgroundStyle) {
2287             textBackgroundStyle->needCompareGroupId = false;
2288             typingTextStyle_->SetTextBackgroundStyle(textBackgroundStyle);
2289         }
2290     }
2291     UpdateCaretStyleByTypingStyle(isReset);
2292 }
2293 
SetTypingParagraphStyle(std::optional<struct UpdateParagraphStyle> typingParagraphStyle)2294 void RichEditorPattern::SetTypingParagraphStyle(std::optional<struct UpdateParagraphStyle> typingParagraphStyle)
2295 {
2296     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "SetTypingParagraphStyle, %{public}d", typingParagraphStyle.has_value());
2297     bool isReset = styleManager_->HasTypingParagraphStyle() && !typingParagraphStyle.has_value();
2298     styleManager_->SetTypingParagraphStyle(typingParagraphStyle);
2299     UpdateCaretStyleByTypingStyle(isReset);
2300 }
2301 
GetTypingStyle()2302 std::optional<struct UpdateSpanStyle> RichEditorPattern::GetTypingStyle()
2303 {
2304     return typingStyle_;
2305 }
2306 
UpdateFontFeatureTextStyle(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle & updateSpanStyle,TextStyle & textStyle)2307 void RichEditorPattern::UpdateFontFeatureTextStyle(
2308     RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle& updateSpanStyle, TextStyle& textStyle)
2309 {
2310     if (updateSpanStyle.updateFontFeature.has_value()) {
2311         spanNode->UpdateFontFeature(textStyle.GetFontFeatures());
2312     }
2313 }
2314 
UpdateTextStyle(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle updateSpanStyle,TextStyle textStyle)2315 void RichEditorPattern::UpdateTextStyle(
2316     RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle updateSpanStyle, TextStyle textStyle)
2317 {
2318     CHECK_NULL_VOID(spanNode->GetTag() == V2::SPAN_ETS_TAG);
2319     auto host = GetHost();
2320     CHECK_NULL_VOID(host);
2321     UpdateFontFeatureTextStyle(spanNode, updateSpanStyle, textStyle);
2322     if (updateSpanStyle.updateTextColor.has_value()) {
2323         spanNode->UpdateTextColorWithoutCheck(textStyle.GetTextColor());
2324         spanNode->GetSpanItem()->useThemeFontColor = false;
2325     }
2326     if (updateSpanStyle.updateLineHeight.has_value()) {
2327         spanNode->UpdateLineHeight(textStyle.GetLineHeight());
2328     }
2329     if (updateSpanStyle.updateHalfLeading.has_value()) {
2330         spanNode->UpdateHalfLeading(textStyle.GetHalfLeading());
2331     }
2332     if (updateSpanStyle.updateLetterSpacing.has_value()) {
2333         spanNode->UpdateLetterSpacing(textStyle.GetLetterSpacing());
2334     }
2335     if (updateSpanStyle.updateFontSize.has_value()) {
2336         spanNode->UpdateFontSize(textStyle.GetFontSize());
2337     }
2338     if (updateSpanStyle.updateItalicFontStyle.has_value()) {
2339         spanNode->UpdateItalicFontStyle(textStyle.GetFontStyle());
2340     }
2341     if (updateSpanStyle.updateFontWeight.has_value()) {
2342         spanNode->UpdateFontWeight(textStyle.GetFontWeight());
2343     }
2344     if (updateSpanStyle.updateFontFamily.has_value()) {
2345         spanNode->UpdateFontFamily(textStyle.GetFontFamilies());
2346     }
2347     UpdateDecoration(spanNode, updateSpanStyle, textStyle);
2348     if (updateSpanStyle.updateTextShadows.has_value()) {
2349         spanNode->UpdateTextShadow(textStyle.GetTextShadows());
2350     }
2351     if (updateSpanStyle.updateTextBackgroundStyle.has_value()) {
2352         UpdateTextBackgroundStyle(spanNode, textStyle.GetTextBackgroundStyle());
2353     }
2354     UpdateUrlStyle(spanNode, updateSpanStyle.updateUrlAddress);
2355 
2356     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2357     host->MarkModifyDone();
2358 }
2359 
UpdateDecoration(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle & updateSpanStyle,TextStyle & textStyle)2360 void RichEditorPattern::UpdateDecoration(
2361     RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle& updateSpanStyle, TextStyle& textStyle)
2362 {
2363     if (updateSpanStyle.updateTextDecoration.has_value()) {
2364         spanNode->UpdateTextDecoration(textStyle.GetTextDecoration());
2365     }
2366     if (updateSpanStyle.updateTextDecorationColor.has_value()) {
2367         spanNode->UpdateTextDecorationColorWithoutCheck(textStyle.GetTextDecorationColor());
2368         spanNode->GetSpanItem()->useThemeDecorationColor = false;
2369     }
2370     if (updateSpanStyle.updateTextDecorationStyle.has_value()) {
2371         spanNode->UpdateTextDecorationStyle(textStyle.GetTextDecorationStyle());
2372     }
2373     if (updateSpanStyle.updateLineThicknessScale.has_value()) {
2374         spanNode->UpdateLineThicknessScale(textStyle.GetLineThicknessScale());
2375     }
2376 }
2377 
UpdateSymbolStyle(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle updateSpanStyle,TextStyle textStyle)2378 void RichEditorPattern::UpdateSymbolStyle(
2379     RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle updateSpanStyle, TextStyle textStyle)
2380 {
2381     CHECK_NULL_VOID(spanNode->GetTag() == V2::SYMBOL_SPAN_ETS_TAG);
2382     auto host = GetHost();
2383     CHECK_NULL_VOID(host);
2384     UpdateFontFeatureTextStyle(spanNode, updateSpanStyle, textStyle);
2385     if (updateSpanStyle.updateSymbolFontSize.has_value()) {
2386         spanNode->UpdateFontSize(updateSpanStyle.updateSymbolFontSize.value());
2387     }
2388     if (updateSpanStyle.updateSymbolFontWeight.has_value()) {
2389         spanNode->UpdateFontWeight(updateSpanStyle.updateSymbolFontWeight.value());
2390     }
2391     if (updateSpanStyle.updateSymbolColor.has_value()) {
2392         spanNode->UpdateSymbolColorList(updateSpanStyle.updateSymbolColor.value());
2393     }
2394     if (updateSpanStyle.updateSymbolRenderingStrategy.has_value()) {
2395         spanNode->UpdateSymbolRenderingStrategy(updateSpanStyle.updateSymbolRenderingStrategy.value());
2396     }
2397     if (updateSpanStyle.updateSymbolEffectStrategy.has_value()) {
2398         spanNode->UpdateSymbolEffectStrategy(updateSpanStyle.updateSymbolEffectStrategy.value());
2399     }
2400     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2401     host->MarkModifyDone();
2402 }
2403 
HasSameTypingStyle(const RefPtr<SpanNode> & spanNode)2404 bool RichEditorPattern::HasSameTypingStyle(const RefPtr<SpanNode>& spanNode)
2405 {
2406     auto spanItem = spanNode->GetSpanItem();
2407     CHECK_NULL_RETURN(spanItem, false);
2408     auto spanTextStyle = spanItem->GetTextStyle();
2409     if (spanTextStyle.has_value() && typingTextStyle_.has_value()) {
2410         return spanTextStyle.value() == typingTextStyle_.value();
2411     } else {
2412         return !(spanTextStyle.has_value() || typingTextStyle_.has_value());
2413     }
2414 }
2415 
UpdateImageStyle(RefPtr<FrameNode> & imageNode,const ImageSpanAttribute & imageStyle)2416 void RichEditorPattern::UpdateImageStyle(RefPtr<FrameNode>& imageNode, const ImageSpanAttribute& imageStyle)
2417 {
2418     CHECK_NULL_VOID(imageNode);
2419     CHECK_NULL_VOID(imageNode->GetTag() == V2::IMAGE_ETS_TAG);
2420     auto host = GetHost();
2421     CHECK_NULL_VOID(host);
2422     auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
2423     CHECK_NULL_VOID(imageLayoutProperty);
2424     if (updateSpanStyle_.updateImageWidth.has_value() || updateSpanStyle_.updateImageHeight.has_value()) {
2425         imageLayoutProperty->UpdateUserDefinedIdealSize(imageStyle.size->GetSize());
2426     }
2427     if (updateSpanStyle_.updateImageFit.has_value()) {
2428         imageLayoutProperty->UpdateImageFit(imageStyle.objectFit.value());
2429     }
2430     if (updateSpanStyle_.updateImageVerticalAlign.has_value()) {
2431         imageLayoutProperty->UpdateVerticalAlign(imageStyle.verticalAlign.value());
2432     }
2433     if (updateSpanStyle_.borderRadius.has_value()) {
2434         auto imageRenderCtx = imageNode->GetRenderContext();
2435         imageRenderCtx->UpdateBorderRadius(imageStyle.borderRadius.value());
2436         imageRenderCtx->SetClipToBounds(true);
2437     }
2438     if (updateSpanStyle_.marginProp.has_value()) {
2439         imageLayoutProperty->UpdateMargin(imageStyle.marginProp.value());
2440     }
2441     UpdateImageAttribute(imageNode, imageStyle);
2442     imageNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2443     imageNode->MarkModifyDone();
2444     IF_PRESENT(oneStepDragController_, MarkDirtyNode(WeakClaim((ImageSpanNode*) RawPtr(imageNode))));
2445     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2446     host->MarkModifyDone();
2447 }
2448 
UpdateImageAttribute(RefPtr<FrameNode> & imageNode,const ImageSpanAttribute & imageStyle)2449 void RichEditorPattern::UpdateImageAttribute(RefPtr<FrameNode>& imageNode, const ImageSpanAttribute& imageStyle)
2450 {
2451     CHECK_NULL_VOID(imageNode);
2452     auto node = DynamicCast<ImageSpanNode>(imageNode);
2453     CHECK_NULL_VOID(node);
2454     auto imageSpanItem = DynamicCast<ImageSpanItem>(node->GetSpanItem());
2455     CHECK_NULL_VOID(imageSpanItem);
2456     auto& imageAttribute = imageSpanItem->options.imageAttribute;
2457     IF_TRUE(!imageAttribute.has_value(), imageAttribute = imageStyle);
2458     if (updateSpanStyle_.updateImageWidth.has_value() || updateSpanStyle_.updateImageHeight.has_value()) {
2459         imageAttribute->size = imageStyle.size;
2460     }
2461     if (updateSpanStyle_.updateImageFit.has_value()) {
2462         imageAttribute->objectFit = imageStyle.objectFit;
2463     }
2464     if (updateSpanStyle_.updateImageVerticalAlign.has_value()) {
2465         imageAttribute->verticalAlign = imageStyle.verticalAlign;
2466     }
2467     if (updateSpanStyle_.borderRadius.has_value()) {
2468         imageAttribute->borderRadius = imageStyle.borderRadius;
2469     }
2470     if (updateSpanStyle_.marginProp.has_value()) {
2471         imageAttribute->marginProp = imageStyle.marginProp;
2472     }
2473     imageSpanItem->MarkDirty();
2474 }
2475 
SymbolSpanUpdateStyle(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle updateSpanStyle,TextStyle textStyle)2476 bool RichEditorPattern::SymbolSpanUpdateStyle(
2477     RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle updateSpanStyle, TextStyle textStyle)
2478 {
2479     if (spanNode->GetTag() == V2::SYMBOL_SPAN_ETS_TAG) {
2480         UpdateSymbolStyle(spanNode, updateSpanStyle_, textStyle);
2481         return true;
2482     }
2483     return false;
2484 }
2485 
UpdateSpanStyle(int32_t start,int32_t end,const TextStyle & textStyle,const ImageSpanAttribute & imageStyle,bool isExternal)2486 void RichEditorPattern::UpdateSpanStyle(
2487     int32_t start, int32_t end, const TextStyle& textStyle, const ImageSpanAttribute& imageStyle, bool isExternal)
2488 {
2489     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "updateSpanStyle, [%{public}d,%{public}d], %{public}s",
2490         start, end, ToBriefString(textStyle, imageStyle, updateSpanStyle_).c_str());
2491     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "textStyle=%{public}s, imageStyle=%{public}s",
2492         textStyle.ToString().c_str(), imageStyle.ToString().c_str());
2493     auto host = GetContentHost();
2494     CHECK_NULL_VOID(host);
2495     AdjustSelector(start, end);
2496     UndoRedoRecord styledRecord;
2497     IF_TRUE(isExternal, undoManager_->UpdateRecordBeforeChange(start, end - start, styledRecord, true));
2498     int32_t spanStart = 0;
2499     int32_t spanEnd = 0;
2500     for (auto it = host->GetChildren().begin(); it != host->GetChildren().end(); ++it) {
2501         auto spanNode = DynamicCast<SpanNode>(*it);
2502         auto imageNode = DynamicCast<FrameNode>(*it);
2503         if (!spanNode) {
2504             if (spanEnd != 0) {
2505                 spanStart = spanEnd;
2506             }
2507             spanEnd = spanStart + 1;
2508         } else {
2509             spanNode->GetSpanItem()->GetIndex(spanStart, spanEnd);
2510         }
2511         if (spanEnd < start) {
2512             continue;
2513         }
2514 
2515         if (spanStart >= start && spanEnd <= end) {
2516             if (spanNode) {
2517                 UpdateSymbolStyle(spanNode, updateSpanStyle_, textStyle);
2518                 UpdateTextStyle(spanNode, updateSpanStyle_, textStyle);
2519             } else {
2520                 UpdateImageStyle(imageNode, imageStyle);
2521             }
2522             if (spanEnd == end) {
2523                 break;
2524             }
2525         } else if ((spanStart < start && start < spanEnd) || (spanStart < end && end < spanEnd)) {
2526             if (SymbolSpanUpdateStyle(spanNode, updateSpanStyle_, textStyle)) {
2527                 continue;
2528             }
2529             auto index = spanStart < start && start < spanEnd ? start : end;
2530             TextSpanSplit(index, true);
2531             --it;
2532         } else if (spanStart >= end) {
2533             break;
2534         }
2535     }
2536     IF_TRUE(isExternal, undoManager_->RecordOperationAfterChange(start, end - start, styledRecord));
2537 }
2538 
GetChildByIndex(int32_t index) const2539 RefPtr<UINode> RichEditorPattern::GetChildByIndex(int32_t index) const
2540 {
2541     auto host = GetContentHost();
2542     CHECK_NULL_RETURN(host, nullptr);
2543     return host->GetChildAtIndex(index);
2544 }
2545 
SetResultObjectText(ResultObject & resultObject,const RefPtr<SpanItem> & spanItem)2546 void RichEditorPattern::SetResultObjectText(ResultObject& resultObject, const RefPtr<SpanItem>& spanItem)
2547 {
2548     CHECK_NULL_VOID(spanItem);
2549     resultObject.valueString = spanItem->content;
2550     if (spanItem->rangeStart <= previewTextRecord_.startOffset && spanItem->position >= previewTextRecord_.endOffset) {
2551         resultObject.previewText = previewTextRecord_.previewContent;
2552     }
2553     resultObject.urlAddress = spanItem->GetUrlAddress();
2554 }
2555 
GetContentBySpans(std::u16string & u16Str)2556 void RichEditorPattern::GetContentBySpans(std::u16string& u16Str)
2557 {
2558     uint32_t length = 1;
2559     for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
2560         length += (*iter)->content.length();
2561     }
2562     u16Str.reserve(length);
2563 
2564     for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
2565         u16Str.append((*iter)->content);
2566     }
2567 }
2568 
SetSelectSpanStyle(int32_t start,int32_t end,KeyCode code,bool isStart)2569 void RichEditorPattern::SetSelectSpanStyle(int32_t start, int32_t end, KeyCode code, bool isStart)
2570 {
2571     TextStyle spanStyle;
2572     struct UpdateSpanStyle updateSpanStyle;
2573     ImageSpanAttribute imageStyle;
2574     auto it = std::find_if(spans_.begin(), spans_.end(), [start](const RefPtr<SpanItem>& spanItem) {
2575         return (spanItem->rangeStart <= start) && (start < spanItem->position);
2576     });
2577     if (it == spans_.end()) {
2578         return;
2579     }
2580     std::optional<TextStyle> spanTextStyle = (*it)->GetTextStyle();
2581     if (spanTextStyle.has_value()) {
2582         spanStyle = spanTextStyle.value();
2583     }
2584     HandleSelectFontStyleWrapper(code, spanStyle);
2585     IF_TRUE(!(*it)->useThemeFontColor, updateSpanStyle.updateTextColor = spanStyle.GetTextColor());
2586     updateSpanStyle.updateFontSize = spanStyle.GetFontSize();
2587     updateSpanStyle.updateItalicFontStyle = spanStyle.GetFontStyle();
2588     updateSpanStyle.updateFontWeight = spanStyle.GetFontWeight();
2589     updateSpanStyle.updateFontFamily = spanStyle.GetFontFamilies();
2590     updateSpanStyle.updateTextDecoration = spanStyle.GetTextDecorationFirst();
2591     if (!isStart) {
2592         auto updateSpanStyle_ = GetUpdateSpanStyle();
2593         switch (code) {
2594             case KeyCode::KEY_B:
2595                 updateSpanStyle.updateFontWeight = updateSpanStyle_.updateFontWeight;
2596                 spanStyle.SetFontWeight(updateSpanStyle_.updateFontWeight.value());
2597                 break;
2598             case KeyCode::KEY_I:
2599                 updateSpanStyle.updateItalicFontStyle = updateSpanStyle_.updateItalicFontStyle;
2600                 spanStyle.SetFontStyle(updateSpanStyle_.updateItalicFontStyle.value());
2601                 break;
2602             case KeyCode::KEY_U:
2603                 updateSpanStyle.updateTextDecoration = updateSpanStyle_.updateTextDecoration;
2604                 spanStyle.SetTextDecoration(updateSpanStyle_.updateTextDecoration.value());
2605                 break;
2606             default:
2607                 LOGW("Unsupported select operation for HandleSelectFontStyleWrapper");
2608                 return;
2609         }
2610     }
2611     SetUpdateSpanStyle(updateSpanStyle);
2612     UpdateSpanStyle(start, end, spanStyle, imageStyle, false);
2613 }
2614 
GetSelectSpansPositionInfo(int32_t & start,int32_t & end,SpanPositionInfo & startPositionSpanInfo,SpanPositionInfo & endPositionSpanInfo)2615 void RichEditorPattern::GetSelectSpansPositionInfo(
2616     int32_t& start, int32_t& end, SpanPositionInfo& startPositionSpanInfo, SpanPositionInfo& endPositionSpanInfo)
2617 {
2618     bool isText = false;
2619     auto host = GetContentHost();
2620     CHECK_NULL_VOID(host);
2621     std::find_if(spans_.begin(), spans_.end(), [&start, &end, &isText](const RefPtr<SpanItem>& spanItem) {
2622         if ((spanItem->rangeStart <= start) && (start < spanItem->position) && start < end) {
2623             if (spanItem->spanItemType == SpanItemType::NORMAL && spanItem->unicode == 0) {
2624                 isText = true;
2625                 return true;
2626             }
2627             start += spanItem->content.length();
2628         }
2629         return false;
2630     });
2631     CHECK_EQUAL_VOID(isText, false);
2632     std::find_if(spans_.rbegin(), spans_.rend(), [&end](const RefPtr<SpanItem>& spanItem) {
2633         if ((spanItem->rangeStart < end) && (end <= spanItem->position)) {
2634             if (spanItem->spanItemType == SpanItemType::NORMAL && spanItem->unicode == 0) {
2635                 return true;
2636             }
2637             end = spanItem->rangeStart;
2638         }
2639         return false;
2640     });
2641     startPositionSpanInfo = GetSpanPositionInfo(start);
2642     startPositionSpanInfo.spanIndex_ =
2643         std::clamp(startPositionSpanInfo.spanIndex_, 0, static_cast<int32_t>(host->GetChildren().size()) - 1);
2644     if (end == GetTextContentLength()) {
2645         endPositionSpanInfo.spanIndex_ = spans_.size() - 1;
2646         auto spanIter = spans_.begin();
2647         endPositionSpanInfo.spanIndex_ =
2648             std::clamp(endPositionSpanInfo.spanIndex_, 0, static_cast<int32_t>(host->GetChildren().size()) - 1);
2649         std::advance(spanIter, endPositionSpanInfo.spanIndex_);
2650         auto contentLen = (*spanIter)->content.length();
2651         endPositionSpanInfo.spanStart_ = (*spanIter)->position - contentLen;
2652         endPositionSpanInfo.spanEnd_ = (*spanIter)->position;
2653         endPositionSpanInfo.spanOffset_ = contentLen;
2654     } else {
2655         endPositionSpanInfo = GetSpanPositionInfo(end);
2656     }
2657     if (endPositionSpanInfo.spanIndex_ == -1) {
2658         endPositionSpanInfo = startPositionSpanInfo;
2659     }
2660 }
2661 
GetSpanNodeIter(int32_t index)2662 std::list<RefPtr<UINode>>::const_iterator RichEditorPattern::GetSpanNodeIter(int32_t index)
2663 {
2664     auto host = GetContentHost();
2665     CHECK_NULL_RETURN(host, {});
2666     auto spanNodeIter = host->GetChildren().begin();
2667     std::advance(spanNodeIter, index);
2668     return spanNodeIter;
2669 }
2670 
GetSpanItemByIndex(int32_t index)2671 RefPtr<SpanItem> RichEditorPattern::GetSpanItemByIndex(int32_t index)
2672 {
2673     auto spanIter = spans_.begin();
2674     std::advance(spanIter, index);
2675     CHECK_NULL_RETURN(spanIter != spans_.end(), nullptr);
2676     return *spanIter;
2677 }
2678 
GetSelectSpanSplit(SpanPositionInfo & startPositionSpanInfo,SpanPositionInfo & endPositionSpanInfo)2679 std::list<SpanPosition> RichEditorPattern::GetSelectSpanSplit(
2680     SpanPositionInfo& startPositionSpanInfo, SpanPositionInfo& endPositionSpanInfo)
2681 {
2682     std::list<SpanPosition> resultObjects;
2683     int32_t spanIndex = 0;
2684     auto itStart = GetSpanNodeIter(startPositionSpanInfo.spanIndex_);
2685     auto itEnd = GetSpanNodeIter(endPositionSpanInfo.spanIndex_);
2686     auto itEndNext = GetSpanNodeIter(endPositionSpanInfo.spanIndex_ + 1);
2687     for (auto itSelect = itStart; itSelect != itEndNext; itSelect++) {
2688         SpanPosition resultObject;
2689         auto spanNode = DynamicCast<SpanNode>(*itSelect);
2690         if (!spanNode || spanNode->GetTag() != V2::SPAN_ETS_TAG) {
2691             continue;
2692         }
2693         auto spanItem = spanNode->GetSpanItem();
2694         if (itSelect == itStart) {
2695             if (startPositionSpanInfo.spanOffset_ == 0) {
2696                 resultObject.spanRange[RichEditorSpanRange::RANGESTART] = startPositionSpanInfo.spanStart_;
2697             } else {
2698                 resultObject.spanRange[RichEditorSpanRange::RANGESTART] =
2699                     startPositionSpanInfo.spanStart_ + startPositionSpanInfo.spanOffset_;
2700             }
2701             resultObject.spanRange[RichEditorSpanRange::RANGEEND] = startPositionSpanInfo.spanEnd_;
2702             resultObject.spanIndex = spanIndex;
2703             spanIndex++;
2704             resultObjects.emplace_back(resultObject);
2705             continue;
2706         }
2707         if (itSelect == itEnd) {
2708             resultObject.spanRange[RichEditorSpanRange::RANGESTART] = endPositionSpanInfo.spanStart_;
2709             if (endPositionSpanInfo.spanOffset_ == static_cast<int32_t>(spanItem->content.size())) {
2710                 resultObject.spanRange[RichEditorSpanRange::RANGEEND] = endPositionSpanInfo.spanEnd_;
2711             } else {
2712                 resultObject.spanRange[RichEditorSpanRange::RANGEEND] =
2713                     endPositionSpanInfo.spanStart_ + endPositionSpanInfo.spanOffset_;
2714             }
2715             resultObject.spanIndex = spanIndex;
2716             spanIndex++;
2717             resultObjects.emplace_back(resultObject);
2718             continue;
2719         }
2720         resultObject.spanRange[RichEditorSpanRange::RANGESTART] =
2721             spanItem->position - spanItem->content.length();
2722         resultObject.spanRange[RichEditorSpanRange::RANGEEND] = spanItem->position;
2723         resultObject.spanIndex = spanIndex;
2724         spanIndex++;
2725         resultObjects.emplace_back(resultObject);
2726     }
2727     return resultObjects;
2728 }
2729 
GetSelectSpanInfo(int32_t start,int32_t end)2730 std::list<SpanPosition> RichEditorPattern::GetSelectSpanInfo(int32_t start, int32_t end)
2731 {
2732     SpanPositionInfo startPositionSpanInfo(-1, -1, -1, -1);
2733     SpanPositionInfo endPositionSpanInfo(-1, -1, -1, -1);
2734     std::list<SpanPosition> resultObjects;
2735     int32_t spanIndex = 0;
2736     GetSelectSpansPositionInfo(start, end, startPositionSpanInfo, endPositionSpanInfo);
2737     CHECK_EQUAL_RETURN(startPositionSpanInfo.spanStart_, -1, resultObjects);
2738     if (startPositionSpanInfo.spanIndex_ == endPositionSpanInfo.spanIndex_) {
2739         SpanPosition resultObject;
2740         resultObject.spanRange[RichEditorSpanRange::RANGESTART] = start;
2741         resultObject.spanRange[RichEditorSpanRange::RANGEEND] = end;
2742         resultObject.spanIndex = spanIndex;
2743         resultObjects.emplace_back(resultObject);
2744     } else {
2745         resultObjects = GetSelectSpanSplit(startPositionSpanInfo, endPositionSpanInfo);
2746     }
2747     return resultObjects;
2748 }
2749 
GetSpansInfoByRange(int32_t start,int32_t end)2750 SelectionInfo RichEditorPattern::GetSpansInfoByRange(int32_t start, int32_t end)
2751 {
2752     auto selectionInfo = GetSpansInfo(start, end , GetSpansMethod::GETSPANS);
2753     auto& resultObjects = selectionInfo.GetSelectionRef().resultObjects;
2754     for (auto& resObj : resultObjects) {
2755         CHECK_NULL_CONTINUE(resObj.type == SelectSpanType::TYPESPAN);
2756         auto uiNode = GetChildByIndex(resObj.spanPosition.spanIndex);
2757         auto spanNode = DynamicCast<SpanNode>(uiNode);
2758         CHECK_NULL_CONTINUE(spanNode);
2759         auto fontFamily = spanNode->GetFontFamily();
2760         IF_TRUE(!fontFamily, resObj.textStyle.fontFamily.clear());
2761     }
2762     return selectionInfo;
2763 }
2764 
UpdateSelectSpanStyle(int32_t start,int32_t end,KeyCode code)2765 void RichEditorPattern::UpdateSelectSpanStyle(int32_t start, int32_t end, KeyCode code)
2766 {
2767     std::list<SpanPosition> resultObjects;
2768     resultObjects = GetSelectSpanInfo(start, end);
2769     bool isFirstText = true;
2770     UndoRedoRecord styledRecord;
2771     undoManager_->UpdateRecordBeforeChange(start, end - start, styledRecord, true);
2772     for (auto& spanStyleIter : resultObjects) {
2773         SetSelectSpanStyle(spanStyleIter.spanRange[RichEditorSpanRange::RANGESTART],
2774             spanStyleIter.spanRange[RichEditorSpanRange::RANGEEND], code, isFirstText);
2775         isFirstText = false;
2776     }
2777     undoManager_->RecordOperationAfterChange(start, end - start, styledRecord);
2778 }
2779 
CheckStyledStringRangeValid(int32_t start,int32_t length)2780 bool RichEditorPattern::CheckStyledStringRangeValid(int32_t start, int32_t length)
2781 {
2782     if (!styledString_ || !styledString_->CheckRange(start, length)) {
2783         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "range:[%{public}d-%{public}d] is invalid or styledString is null",
2784             start, start + length);
2785         return false;
2786     }
2787     return true;
2788 }
2789 
UpdateSelectStyledStringStyle(int32_t start,int32_t end,KeyCode code)2790 void RichEditorPattern::UpdateSelectStyledStringStyle(int32_t start, int32_t end, KeyCode code)
2791 {
2792     auto length = end - start;
2793     CHECK_NULL_VOID(CheckStyledStringRangeValid(start, length));
2794     UndoRedoRecord record;
2795     undoManager_->UpdateRecordBeforeChange(start, length, record, true);
2796     Font updateFont;
2797     bool isFirstSpanStylePresent;
2798     switch (code) {
2799     case KeyCode::KEY_B: {
2800         auto firstFontSpan = DynamicCast<FontSpan>(styledString_->GetSpan(start, 1, SpanType::Font));
2801         isFirstSpanStylePresent = firstFontSpan && firstFontSpan->GetFont().fontWeight == FontWeight::BOLD;
2802         updateFont.fontWeight = isFirstSpanStylePresent ? FontWeight::NORMAL : FontWeight::BOLD;
2803         UpdateStyledStringFontStyle(start, end, updateFont);
2804         record.AddUpdateSpanType(SpanType::Font);
2805         break;
2806     }
2807     case KeyCode::KEY_I: {
2808         auto firstFontSpan = DynamicCast<FontSpan>(styledString_->GetSpan(start, 1, SpanType::Font));
2809         isFirstSpanStylePresent = firstFontSpan && firstFontSpan->GetFont().fontStyle == OHOS::Ace::FontStyle::ITALIC;
2810         updateFont.fontStyle = isFirstSpanStylePresent ? OHOS::Ace::FontStyle::NORMAL : OHOS::Ace::FontStyle::ITALIC;
2811         UpdateStyledStringFontStyle(start, end, updateFont);
2812         record.AddUpdateSpanType(SpanType::Font);
2813         break;
2814     }
2815     case KeyCode::KEY_U: {
2816         auto firstDecorationSpan = DynamicCast<DecorationSpan>(styledString_->GetSpan(start, 1, SpanType::Decoration));
2817         isFirstSpanStylePresent =
2818             firstDecorationSpan &&
2819             V2::HasTextDecoration(firstDecorationSpan->GetTextDecorationTypes(), TextDecoration::UNDERLINE);
2820         auto updateDecorationType = isFirstSpanStylePresent ? TextDecoration::NONE : TextDecoration::UNDERLINE;
2821         UpdateStyledStringDecorationType(start, end, updateDecorationType);
2822         record.AddUpdateSpanType(SpanType::Decoration);
2823         break;
2824     }
2825     default:
2826         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "Unsupported key code for UpdateSelectStyledStringStyle");
2827         return;
2828     }
2829     undoManager_->RecordOperationAfterChange(start, length, record);
2830 }
2831 
2832 template<typename T>
UpdateSpansStyleInRange(int32_t start,int32_t end,const RefPtr<SpanBase> & baseSpan,std::function<RefPtr<T> (const RefPtr<T> &)> && updateSpanFunc)2833 void RichEditorPattern::UpdateSpansStyleInRange(int32_t start, int32_t end, const RefPtr<SpanBase>& baseSpan,
2834     std::function<RefPtr<T>(const RefPtr<T>&)>&& updateSpanFunc)
2835 {
2836     auto length = end - start;
2837     CHECK_NULL_VOID(CheckStyledStringRangeValid(start, length));
2838     CHECK_NULL_VOID(baseSpan);
2839     auto spanType = baseSpan->GetSpanType();
2840     std::vector<RefPtr<SpanBase>> updateSpans;
2841     updateSpans.push_back(baseSpan);
2842     auto originalSpans = styledString_->GetSpans(start, length, spanType);
2843     for (auto& originalSpan : originalSpans) {
2844         auto originalTypedSpan = DynamicCast<T>(originalSpan);
2845         CHECK_NULL_CONTINUE(originalTypedSpan)
2846         updateSpans.push_back(updateSpanFunc(originalTypedSpan));
2847     }
2848     paragraphCache_.Clear();
2849     styledString_->BindWithSpans(updateSpans);
2850     styledString_->NotifySpanWatcher();
2851 }
2852 
UpdateStyledStringFontStyle(int32_t start,int32_t end,const Font & font)2853 void RichEditorPattern::UpdateStyledStringFontStyle(int32_t start, int32_t end, const Font& font)
2854 {
2855     auto fontSpan = AceType::MakeRefPtr<FontSpan>(font, start, end);
2856     auto updateFontSpanFunc = [&font](const RefPtr<FontSpan>& oriFontSpan) -> RefPtr<FontSpan> {
2857         CHECK_NULL_RETURN(oriFontSpan, nullptr);
2858         auto fontStyle = oriFontSpan->GetFont();
2859         if (font.fontStyle.has_value()) {
2860             fontStyle.fontStyle = font.fontStyle.value();
2861         }
2862         if (font.fontWeight.has_value()) {
2863             fontStyle.fontWeight = font.fontWeight.value();
2864         }
2865         return AceType::MakeRefPtr<FontSpan>(fontStyle, oriFontSpan->GetStartIndex(), oriFontSpan->GetEndIndex());
2866     };
2867     UpdateSpansStyleInRange<FontSpan>(start, end, fontSpan, updateFontSpanFunc);
2868 }
2869 
UpdateStyledStringDecorationType(int32_t start,int32_t end,const TextDecoration & type)2870 void RichEditorPattern::UpdateStyledStringDecorationType(int32_t start, int32_t end, const TextDecoration& type)
2871 {
2872     std::optional<Color> colorOption;
2873     std::optional<TextDecorationStyle> styleOption;
2874     std::optional<TextDecorationOptions> options;
2875     std::optional<float> lineThicknessScale;
2876     auto decorationSpan = AceType::MakeRefPtr<DecorationSpan>(
2877         std::vector<TextDecoration>({ type }), colorOption, styleOption, lineThicknessScale, options, start, end);
2878     auto updateDecorationSpanFunc = [&type](const RefPtr<DecorationSpan>& oriDecorationSpan) -> RefPtr<DecorationSpan> {
2879         CHECK_NULL_RETURN(oriDecorationSpan, nullptr);
2880         if (type == TextDecoration::NONE) {
2881             oriDecorationSpan->RemoveTextDecorationType(TextDecoration::UNDERLINE);
2882         } else {
2883             auto lastTypes = oriDecorationSpan->GetTextDecorationTypes();
2884             oriDecorationSpan->SetTextDecorationTypes(std::vector<TextDecoration> { type });
2885             for (auto lastType : lastTypes) {
2886                 oriDecorationSpan->AddTextDecorationType(lastType);
2887             }
2888         }
2889         return oriDecorationSpan;
2890     };
2891     UpdateSpansStyleInRange<DecorationSpan>(start, end, decorationSpan, updateDecorationSpanFunc);
2892 }
2893 
CloseSystemMenu()2894 void RichEditorPattern::CloseSystemMenu()
2895 {
2896     if (!SelectOverlayIsOn()) {
2897         return;
2898     }
2899     auto selectOverlayInfo = selectOverlay_->GetSelectOverlayInfo();
2900     if (selectOverlayInfo && !selectOverlayInfo->menuInfo.menuBuilder) {
2901         CloseSelectOverlay();
2902     }
2903 }
2904 
SetAccessibilityAction()2905 void RichEditorPattern::SetAccessibilityAction()
2906 {
2907     auto host = GetHost();
2908     CHECK_NULL_VOID(host);
2909     auto property = host->GetAccessibilityProperty<AccessibilityProperty>();
2910     CHECK_NULL_VOID(property);
2911     property->SetActionSetSelection([weakPtr = WeakClaim(this)](int32_t start, int32_t end, bool isForward) {
2912         TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
2913             "Accessibility SetSelection, range=[%{public}d,%{public}d], isForward=%{public}d", start, end, isForward);
2914         const auto& pattern = weakPtr.Upgrade();
2915         CHECK_NULL_VOID(pattern);
2916         pattern->SetSelection(start, end, std::nullopt, isForward);
2917     });
2918 
2919     property->SetActionSetIndex([weakPtr = WeakClaim(this)](int32_t index) {
2920         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Accessibility SetCaretOffset, index=%{public}d", index);
2921         const auto& pattern = weakPtr.Upgrade();
2922         CHECK_NULL_VOID(pattern);
2923         pattern->SetCaretOffset(index);
2924     });
2925 
2926     property->SetActionGetIndex([weakPtr = WeakClaim(this)]() -> int32_t {
2927         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Accessibility GetCaretPosition");
2928         const auto& pattern = weakPtr.Upgrade();
2929         CHECK_NULL_RETURN(pattern, -1);
2930         return pattern->GetCaretPosition();
2931     });
2932     SetAccessibilityEditAction();
2933 }
2934 
SetAccessibilityEditAction()2935 void RichEditorPattern::SetAccessibilityEditAction()
2936 {
2937     auto host = GetHost();
2938     CHECK_NULL_VOID(host);
2939     auto property = host->GetAccessibilityProperty<AccessibilityProperty>();
2940     CHECK_NULL_VOID(property);
2941     property->SetActionSetText([weakPtr = WeakClaim(this)](const std::string& value) {
2942         std::u16string u16Value = UtfUtils::Str8ToStr16(value);
2943         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "EditAction setText, length: %{public}d",
2944             static_cast<int32_t>(u16Value.length()));
2945         const auto& pattern = weakPtr.Upgrade();
2946         CHECK_NULL_VOID(pattern);
2947         pattern->InsertValueByOperationType(u16Value, OperationType::ACCESSIBILITY);
2948     });
2949 
2950     property->SetActionCopy([weakPtr = WeakClaim(this)]() {
2951         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "EditAction copy");
2952         const auto& pattern = weakPtr.Upgrade();
2953         CHECK_NULL_VOID(pattern);
2954         pattern->HandleOnCopy();
2955         pattern->CloseSelectionMenu();
2956     });
2957 
2958     property->SetActionCut([weakPtr = WeakClaim(this)]() {
2959         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "EditAction cut");
2960         const auto& pattern = weakPtr.Upgrade();
2961         CHECK_NULL_VOID(pattern);
2962         pattern->suppressAccessibilityEvent_ = false;
2963         pattern->HandleOnCut();
2964     });
2965 
2966     property->SetActionPaste([weakPtr = WeakClaim(this)]() {
2967         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "EditAction paste");
2968         const auto& pattern = weakPtr.Upgrade();
2969         CHECK_NULL_VOID(pattern);
2970         pattern->suppressAccessibilityEvent_ = false;
2971         pattern->HandleOnPaste();
2972         pattern->CloseSelectionMenu();
2973     });
2974 
2975     property->SetActionClearSelection([weakPtr = WeakClaim(this)]() {
2976         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "EditAction clearSelection");
2977         const auto& pattern = weakPtr.Upgrade();
2978         CHECK_NULL_VOID(pattern);
2979         pattern->CloseSelectionMenu();
2980         pattern->ResetSelection();
2981         pattern->StartTwinkling();
2982     });
2983 }
2984 
IsAccessibilityClick()2985 bool RichEditorPattern::IsAccessibilityClick()
2986 {
2987     auto host = GetHost();
2988     CHECK_NULL_RETURN(host, false);
2989     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
2990     CHECK_NULL_RETURN(accessibilityProperty, false);
2991     return accessibilityProperty->GetAccessibilityFocusState();
2992 }
2993 
GetParagraphInfo(int32_t start,int32_t end)2994 std::vector<ParagraphInfo> RichEditorPattern::GetParagraphInfo(int32_t start, int32_t end)
2995 {
2996     std::vector<ParagraphInfo> res;
2997     auto spanNodes = GetParagraphNodes(start, end);
2998     CHECK_NULL_RETURN(!spanNodes.empty(), {});
2999 
3000     auto&& firstSpan = spanNodes.front()->GetSpanItem();
3001     auto paraStart = firstSpan->position - static_cast<int32_t>(firstSpan->content.length());
3002 
3003     for (auto it = spanNodes.begin(); it != spanNodes.end(); ++it) {
3004         if (it == std::prev(spanNodes.end()) || (*it)->GetSpanItem()->content.back() == u'\n') {
3005             ParagraphInfo info;
3006             auto lm = (*it)->GetLeadingMarginValue({});
3007             std::optional<double> spacingOpt;
3008             if (auto spacing = (*it)->GetParagraphSpacing(); spacing.has_value()) {
3009                 spacingOpt = spacing.value().ConvertToFp();
3010             }
3011             std::optional<int32_t> textVerticalAlignOpt;
3012             if (auto textVerticalAlign = (*it)->GetTextVerticalAlign(); textVerticalAlign.has_value()) {
3013                 textVerticalAlignOpt = static_cast<int32_t>(textVerticalAlign.value());
3014             }
3015             res.emplace_back(ParagraphInfo {
3016                 .leadingMarginPixmap = lm.pixmap,
3017                 .leadingMarginSize = { lm.size.Width().ToString(),
3018                     lm.size.Height().ToString() },
3019                 .textAlign = static_cast<int32_t>((*it)->GetTextAlignValue(TextAlign::START)),
3020                 .wordBreak = static_cast<int32_t>((*it)->GetWordBreakValue(WordBreak::BREAK_WORD)),
3021                 .lineBreakStrategy = static_cast<int32_t>((*it)->GetLineBreakStrategyValue(LineBreakStrategy::GREEDY)),
3022                 .paragraphSpacing = spacingOpt,
3023                 .textVerticalAlign = textVerticalAlignOpt,
3024                 .range = { paraStart, (*it)->GetSpanItem()->position },
3025             });
3026             paraStart = (*it)->GetSpanItem()->position;
3027         }
3028     }
3029 
3030     return res;
3031 }
3032 
GetParagraphLength(const std::list<RefPtr<UINode>> & spans) const3033 int32_t RichEditorPattern::GetParagraphLength(const std::list<RefPtr<UINode>>& spans) const
3034 {
3035     if (spans.empty()) {
3036         return 0;
3037     }
3038     int32_t imageSpanCnt = 0;
3039     for (auto it = spans.rbegin(); it != spans.rend(); ++it) {
3040         auto spanNode = DynamicCast<SpanNode>(*it);
3041         if (spanNode) {
3042             return spanNode->GetSpanItem()->position + imageSpanCnt;
3043         }
3044         ++imageSpanCnt;
3045     }
3046     return imageSpanCnt;
3047 }
3048 
GetParagraphNodes(int32_t start,int32_t end) const3049 std::vector<RefPtr<SpanNode>> RichEditorPattern::GetParagraphNodes(int32_t start, int32_t end) const
3050 {
3051     CHECK_NULL_RETURN(start != end, {});
3052     auto host = GetContentHost();
3053     CHECK_NULL_RETURN(host, {});
3054     CHECK_NULL_RETURN(!host->GetChildren().empty(), {});
3055 
3056     const auto& spans = host->GetChildren();
3057     int32_t length = GetParagraphLength(spans);
3058     std::vector<RefPtr<SpanNode>> res;
3059 
3060     if (start >= length) {
3061         return res;
3062     }
3063 
3064     auto headIt = spans.begin();
3065     auto flagNode = headIt;
3066     bool isEnd = false;
3067     int32_t spanEnd = -1;
3068     while (flagNode != spans.end()) {
3069         auto spanNode = DynamicCast<SpanNode>(*flagNode);
3070         if (spanNode) {
3071             auto&& info = spanNode->GetSpanItem();
3072             spanEnd = info->position;
3073             isEnd = info->content.back() == u'\n';
3074         } else {
3075             ++spanEnd;
3076             isEnd = false;
3077         }
3078         flagNode++;
3079         if (spanEnd > start) {
3080             break;
3081         }
3082         if (isEnd) {
3083             headIt = flagNode;
3084         }
3085     }
3086     while (headIt != flagNode) {
3087         auto spanNode = DynamicCast<SpanNode>(*headIt);
3088         if (spanNode) {
3089             res.emplace_back(spanNode);
3090         }
3091         headIt++;
3092     }
3093     while (flagNode != spans.end() && (spanEnd < end || !isEnd)) {
3094         auto spanNode = DynamicCast<SpanNode>(*flagNode);
3095         if (spanNode) {
3096             res.emplace_back(spanNode);
3097             auto&& info = spanNode->GetSpanItem();
3098             spanEnd = info->position;
3099             isEnd = info->content.back() == u'\n';
3100         } else {
3101             ++spanEnd;
3102             isEnd = false;
3103         }
3104         flagNode++;
3105     }
3106 
3107     return res;
3108 }
3109 
3110 // Calculates the range of span nodes(returns (-1, -1) if empty or invalid)
CalcSpansRange(const std::vector<RefPtr<SpanNode>> & spanNodes) const3111 std::pair<int32_t, int32_t> RichEditorPattern::CalcSpansRange(const std::vector<RefPtr<SpanNode>>& spanNodes) const
3112 {
3113     CHECK_NULL_RETURN(!spanNodes.empty(), std::make_pair(-1, -1));
3114     auto& firstSpanNode = spanNodes.front();
3115     auto& lastSpanNode = spanNodes.back();
3116     CHECK_NULL_RETURN(firstSpanNode && lastSpanNode, std::make_pair(-1, -1));
3117     auto& firstSpan = firstSpanNode->GetSpanItem();
3118     auto& lastSpan = lastSpanNode->GetSpanItem();
3119     CHECK_NULL_RETURN(firstSpan && lastSpan, std::make_pair(-1, -1));
3120     return std::make_pair(firstSpan->rangeStart, lastSpan->position);
3121 }
3122 
UpdateParagraphStyle(int32_t start,int32_t end,const struct UpdateParagraphStyle & style)3123 void RichEditorPattern::UpdateParagraphStyle(int32_t start, int32_t end, const struct UpdateParagraphStyle& style)
3124 {
3125     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "updateParagraphStyle, range=[%{public}d,%{public}d]", start, end);
3126     UndoRedoRecord styledRecord;
3127     auto spanNodes = GetParagraphNodes(start, end);
3128     auto spanRange = CalcSpansRange(spanNodes);
3129     auto [changeStart, changeEnd] = (spanRange == std::make_pair(-1,-1)) ? std::make_pair(start, end) : spanRange;
3130     undoManager_->UpdateRecordBeforeChange(changeStart, changeEnd - changeStart, styledRecord, true);
3131     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "spanNode cnt=%{public}d, style=%{public}s",
3132         static_cast<int32_t>(spanNodes.size()), style.ToString().c_str());
3133     for (const auto& spanNode : spanNodes) {
3134         UpdateParagraphStyle(spanNode, style);
3135     }
3136     undoManager_->RecordOperationAfterChange(changeStart, changeEnd - changeStart, styledRecord);
3137 }
3138 
UpdateParagraphStyle(RefPtr<SpanNode> spanNode,const struct UpdateParagraphStyle & style)3139 void RichEditorPattern::UpdateParagraphStyle(RefPtr<SpanNode> spanNode, const struct UpdateParagraphStyle& style)
3140 {
3141     CHECK_NULL_VOID(spanNode);
3142     spanNode->UpdateTextAlign(style.textAlign.value_or(TextAlign::START));
3143     spanNode->UpdateWordBreak(style.wordBreak.value_or(WordBreak::BREAK_WORD));
3144     spanNode->UpdateLineBreakStrategy(style.lineBreakStrategy.value_or(LineBreakStrategy::GREEDY));
3145     spanNode->UpdateTextVerticalAlign(style.textVerticalAlign.value_or(TextVerticalAlign::BASELINE));
3146     auto paragraphSpacing = spanNode->GetParagraphSpacing();
3147     if (style.paragraphSpacing.has_value()) {
3148         spanNode->UpdateParagraphSpacing(style.paragraphSpacing.value());
3149     } else if (!NearEqual(paragraphSpacing->Value(), 0.0f) || paragraphSpacing->Unit() != DimensionUnit::PX) {
3150         spanNode->ResetParagraphSpacing();
3151     }
3152     auto leadingMarginValue = spanNode->GetLeadingMarginValue({});
3153     if (style.leadingMargin.has_value() && !leadingMarginValue.CheckLeadingMargin(style.leadingMargin.value())) {
3154         spanNode->GetSpanItem()->leadingMargin = *style.leadingMargin;
3155         spanNode->UpdateLeadingMargin(*style.leadingMargin);
3156     }
3157     IF_PRESENT(GetHost(), MarkDirtyNode(PROPERTY_UPDATE_MEASURE));
3158 }
3159 
ScheduleCaretTwinkling()3160 void RichEditorPattern::ScheduleCaretTwinkling()
3161 {
3162     ContainerScope scope(richEditorInstanceId_);
3163     auto host = GetHost();
3164     CHECK_NULL_VOID(host);
3165     auto context = host->GetContext();
3166     CHECK_NULL_VOID(context);
3167 
3168     if (!context->GetTaskExecutor()) {
3169         return;
3170     }
3171 
3172     if (isCursorAlwaysDisplayed_) {
3173         return;
3174     }
3175 
3176     auto weak = WeakClaim(this);
3177     caretTwinklingTask_.Reset([weak, instanceId = richEditorInstanceId_] {
3178         ContainerScope scope(instanceId);
3179         auto client = weak.Upgrade();
3180         CHECK_NULL_VOID(client);
3181         client->OnCaretTwinkling();
3182     });
3183     auto taskExecutor = context->GetTaskExecutor();
3184     CHECK_NULL_VOID(taskExecutor);
3185     taskExecutor->PostDelayedTask(caretTwinklingTask_, TaskExecutor::TaskType::UI, twinklingInterval_,
3186         "ArkUIRichEditorScheduleCaretTwinkling");
3187 }
3188 
StartTwinkling()3189 void RichEditorPattern::StartTwinkling()
3190 {
3191     caretTwinklingTask_.Cancel();
3192     caretVisible_ = true;
3193     auto tmpHost = GetHost();
3194     CHECK_NULL_VOID(tmpHost);
3195     tmpHost->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3196     ScheduleCaretTwinkling();
3197     // Fire on selecion change when caret invisible -> visible
3198     if (!caretTwinkling_) {
3199         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "StartTwinkling");
3200         caretTwinkling_ = true;
3201         FireOnSelectionChange(caretPosition_, caretPosition_);
3202     }
3203 }
3204 
ShowCaretWithoutTwinkling()3205 void RichEditorPattern::ShowCaretWithoutTwinkling()
3206 {
3207     isCursorAlwaysDisplayed_ = true;
3208     StartTwinkling();
3209 }
3210 
OnCaretTwinkling()3211 void RichEditorPattern::OnCaretTwinkling()
3212 {
3213     caretTwinklingTask_.Cancel();
3214     caretVisible_ = !caretVisible_;
3215     GetHost()->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3216     ScheduleCaretTwinkling();
3217 }
3218 
StopTwinkling()3219 void RichEditorPattern::StopTwinkling()
3220 {
3221     if (caretTwinkling_) {
3222         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "StopTwinkling");
3223     }
3224     caretTwinkling_ = false;
3225     isCursorAlwaysDisplayed_ = false;
3226     caretTwinklingTask_.Cancel();
3227     if (caretVisible_) {
3228         caretVisible_ = false;
3229         GetHost()->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3230     }
3231 }
3232 
HandleClickEvent(GestureEvent & info)3233 void RichEditorPattern::HandleClickEvent(GestureEvent& info)
3234 {
3235     CreateMultipleClickRecognizer();
3236     ResetAISelected(AIResetSelectionReason::CLICK);
3237     if (selectOverlay_->GetIsHandleMoving() || isMouseSelect_) {
3238         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "click rejected, isHandleMoving=%{public}d, isMouseSelect=%{public}d",
3239             selectOverlay_->GetIsHandleMoving(), isMouseSelect_);
3240         return;
3241     }
3242     auto focusHub = GetFocusHub();
3243     CHECK_NULL_VOID(focusHub);
3244     if (!focusHub->IsFocusable()) {
3245         return;
3246     }
3247 
3248     if (!HasFocus() && !focusHub->IsFocusOnTouch().value_or(true)) {
3249         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleClickEvent fail when IsFocusOnTouch false");
3250         CloseSelectOverlay();
3251         StopTwinkling();
3252         return;
3253     }
3254 
3255     selectionMenuOffsetClick_ = OffsetF(
3256         static_cast<float>(info.GetGlobalLocation().GetX()), static_cast<float>(info.GetGlobalLocation().GetY()));
3257     if (dataDetectorAdapter_->hasClickedAISpan_) {
3258         dataDetectorAdapter_->hasClickedAISpan_ = false;
3259     }
3260     multipleClickRecognizer_->Start(info);
3261     if (multipleClickRecognizer_->IsTripleClick()) {
3262         HandleTripleClickEvent(info);
3263     } else if (multipleClickRecognizer_->IsDoubleClick()) {
3264         HandleDoubleClickEvent(info);
3265     } else {
3266         HandleSingleClickEvent(info);
3267         NotifyCaretChange();
3268     }
3269 }
3270 
HandleClickSelection(const OHOS::Ace::GestureEvent & info)3271 bool RichEditorPattern::HandleClickSelection(const OHOS::Ace::GestureEvent& info)
3272 {
3273     CHECK_NULL_RETURN(!selectOverlay_->GetIsHandleMoving(), true);
3274     if (SelectOverlayIsOn()) {
3275         selectOverlay_->SwitchToOverlayMode();
3276         selectOverlay_->ToggleMenu();
3277     } else {
3278         CalculateHandleOffsetAndShowOverlay();
3279         selectOverlay_->ProcessOverlay({.animation = true, .requestCode = REQUEST_RECREATE});
3280     }
3281     return true;
3282 }
3283 
IsClickEventOnlyForMenuToggle(const OHOS::Ace::GestureEvent & info)3284 bool RichEditorPattern::IsClickEventOnlyForMenuToggle(const OHOS::Ace::GestureEvent& info)
3285 {
3286     CHECK_NULL_RETURN(info.GetSourceDevice() != SourceType::MOUSE, false);
3287     // In preview state or single handle showing, clicking handle has toggled the menu display
3288     bool hasHandledMenuToggleByClick =
3289         selectOverlay_->IsClickAtHandle(info) && (!isEditing_ || selectOverlay_->IsSingleHandleShow());
3290     CHECK_NULL_RETURN(!hasHandledMenuToggleByClick, true);
3291     if (showSelect_ && BetweenSelection(info.GetGlobalLocation())) {
3292         return HandleClickSelection(info);
3293     }
3294     return false;
3295 }
3296 
HandleSingleClickEvent(OHOS::Ace::GestureEvent & info)3297 void RichEditorPattern::HandleSingleClickEvent(OHOS::Ace::GestureEvent& info)
3298 {
3299     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "handleSingleClick");
3300     hasClicked_ = true;
3301     lastClickTimeStamp_ = info.GetTimeStamp();
3302     CHECK_NULL_VOID(!IsClickEventOnlyForMenuToggle(info));
3303     CHECK_NULL_VOID(!HandleUrlSpanClickEvent(info));
3304 
3305     bool isMouseClick = info.GetSourceDevice() == SourceType::MOUSE;
3306     auto localOffset = info.GetLocalLocation();
3307     IF_TRUE(isMouseClick, AdjustMouseLocalOffset(localOffset));
3308 
3309     Offset textOffset = ConvertTouchOffsetToTextOffset(localOffset);
3310     IF_TRUE(!isMousePressed_, HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY())));
3311 
3312     if (dataDetectorAdapter_->hasClickedAISpan_ || dataDetectorAdapter_->pressedByLeftMouse_) {
3313         IF_TRUE(SelectOverlayIsOn(), selectOverlay_->HideMenu());
3314         return;
3315     }
3316 
3317     HandleUserClickEvent(info);
3318     CHECK_NULL_VOID(!info.IsPreventDefault());
3319     bool isMouseClickWithShift = shiftFlag_ && isMouseClick && !IsPreviewTextInputting();
3320     if (textSelector_.IsValid() && !isMouseSelect_ && !isMouseClickWithShift) {
3321         CloseSelectOverlay();
3322         ResetSelection();
3323     }
3324     moveCaretState_.Reset();
3325     caretUpdateType_ = CaretUpdateType::PRESSED;
3326     CHECK_NULL_VOID(overlayMod_);
3327     RectF lastCaretRect = GetCaretRect();
3328     int32_t lastCaretPosition = caretPosition_;
3329     bool isCaretTwinkling = caretTwinkling_;
3330     auto position = paragraphs_.GetIndex(textOffset);
3331     AdjustCursorPosition(position);
3332     if (auto focusHub = GetFocusHub(); focusHub) {
3333         IF_TRUE(!isMouseClick || (blockPress_ && !isMouseClickWithShift), SetCaretPosition(position));
3334         IF_TRUE(isMouseClickWithShift, HandleShiftSelect(position));
3335         if (focusHub->IsCurrentFocus()) {
3336             HandleOnEditChanged(true);
3337         }
3338         RICH_EDITOR_SCOPE(requestFocusBySingleClick_);
3339         if (focusHub->RequestFocusImmediately()) {
3340             IF_TRUE(!shiftFlag_ || textSelector_.SelectNothing(), StartTwinkling());
3341             RequestKeyboard(false, true, true, info.GetSourceDevice());
3342         }
3343     }
3344     UseHostToUpdateTextFieldManager();
3345     CalcCaretInfoByClick(localOffset);
3346     CHECK_NULL_VOID(!isMouseClick);
3347     if (IsShowSingleHandleByClick(info, lastCaretPosition, lastCaretRect, isCaretTwinkling)) {
3348         CreateAndShowSingleHandle();
3349     }
3350 }
3351 
GetTextOffset(const Offset & localLocation,const RectF & contentRect)3352 PointF RichEditorPattern::GetTextOffset(const Offset &localLocation, const RectF &contentRect)
3353 {
3354     PointF textOffset = {static_cast<float>(localLocation.GetX()) - GetTextRect().GetX(),
3355                          static_cast<float>(localLocation.GetY()) - GetTextRect().GetY()};
3356     return textOffset;
3357 }
3358 
GetSelectedRects(int32_t start,int32_t end)3359 std::vector<RectF> RichEditorPattern::GetSelectedRects(int32_t start, int32_t end)
3360 {
3361     return paragraphs_.GetRects(start, end);
3362 }
3363 
ConvertTouchOffsetToTextOffset(const Offset & touchOffset)3364 Offset RichEditorPattern::ConvertTouchOffsetToTextOffset(const Offset& touchOffset)
3365 {
3366     richTextRect_.SetTop(richTextRect_.GetY() - std::min(baselineOffset_, 0.0f));
3367     richTextRect_.SetHeight(richTextRect_.Height() - std::max(baselineOffset_, 0.0f));
3368     return touchOffset - Offset(richTextRect_.GetX(), richTextRect_.GetY());
3369 }
3370 
IsShowSingleHandleByClick(const OHOS::Ace::GestureEvent & info,int32_t lastCaretPosition,const RectF & lastCaretRect,bool isCaretTwinkling)3371 bool RichEditorPattern::IsShowSingleHandleByClick(
3372     const OHOS::Ace::GestureEvent& info, int32_t lastCaretPosition, const RectF& lastCaretRect, bool isCaretTwinkling)
3373 {
3374     if (firstClickAfterWindowFocus_) {
3375         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "do not show single handle by first click after window focus");
3376         firstClickAfterWindowFocus_ = false;
3377         return false;
3378     }
3379     auto isAccessibilityClick = IsAccessibilityClick();
3380     if (!isCaretTwinkling || isAccessibilityClick) {
3381         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "isCaretTwinkling=%{public}d,isAccessibilityClick=%{public}d"
3382             ,isCaretTwinkling, isAccessibilityClick);
3383         return false;
3384     }
3385     auto offset = info.GetLocalLocation();
3386     Offset textOffset = ConvertTouchOffsetToTextOffset(offset);
3387     auto position = (GetTextContentLength() == 0) ? 0 : paragraphs_.GetIndex(textOffset);
3388     if (position != lastCaretPosition) {
3389         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "clickCaretPosition=%{public}d but lastCaretPosition=%{public}d",
3390             position, lastCaretPosition);
3391         return false;
3392     }
3393     auto paragraphEndPos = GetParagraphEndPosition(lastCaretPosition);
3394     if (lastCaretPosition == paragraphEndPos || IsTouchAtLineEnd(lastCaretPosition, textOffset)) {
3395         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "repeat click lineEnd or paragraphEndPos=%{public}d", paragraphEndPos);
3396         return true;
3397     }
3398     return RepeatClickCaret(offset, lastCaretRect);
3399 }
3400 
RepeatClickCaret(const Offset & offset,const RectF & lastCaretRect)3401 bool RichEditorPattern::RepeatClickCaret(const Offset& offset, const RectF& lastCaretRect)
3402 {
3403     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "caretTwinkling=%{public}d offset=%{public}s lastCaretRect=%{public}s",
3404         caretTwinkling_, offset.ToString().c_str(), lastCaretRect.ToString().c_str());
3405     CHECK_NULL_RETURN(caretTwinkling_, false);
3406     auto lastCaretHeight = lastCaretRect.Height();
3407     auto handleHotZone = selectOverlay_->GetHandleHotZoneRadius();
3408     auto caretHotZoneRect =
3409         RectF(lastCaretRect.GetX() - handleHotZone, lastCaretRect.GetY(), handleHotZone * 2, lastCaretHeight);
3410     return caretHotZoneRect.IsInRegion(PointF(offset.GetX(), offset.GetY()));
3411 }
3412 
CreateAndShowSingleHandle()3413 void RichEditorPattern::CreateAndShowSingleHandle()
3414 {
3415     if (IsPreviewTextInputting()) {
3416         return;
3417     }
3418     textResponseType_ = TextResponseType::LONG_PRESS;
3419     selectOverlay_->SetIsSingleHandle(true);
3420     textSelector_.Update(caretPosition_);
3421     CalculateHandleOffsetAndShowOverlay();
3422     UpdateSelectionType(GetSpansInfo(caretPosition_, caretPosition_, GetSpansMethod::ONSELECT));
3423     selectOverlay_->ProcessOverlay({ .animation = true });
3424 }
3425 
MoveCaretAndStartFocus(const Offset & textOffset)3426 void RichEditorPattern::MoveCaretAndStartFocus(const Offset& textOffset)
3427 {
3428     auto position = paragraphs_.GetIndex(textOffset);
3429     AdjustCursorPosition(position);
3430 
3431     auto focusHub = GetFocusHub();
3432     if (focusHub) {
3433         SetCaretPosition(position);
3434         if (focusHub->RequestFocusImmediately()) {
3435             IF_TRUE(!shiftFlag_ || textSelector_.SelectNothing(), StartTwinkling());
3436             if (overlayMod_) {
3437                 RequestKeyboard(false, true, true);
3438             }
3439             HandleOnEditChanged(true);
3440         }
3441     }
3442     UseHostToUpdateTextFieldManager();
3443 }
3444 
HandleDoubleClickEvent(OHOS::Ace::GestureEvent & info)3445 void RichEditorPattern::HandleDoubleClickEvent(OHOS::Ace::GestureEvent& info)
3446 {
3447     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleDoubleClickEvent");
3448     caretUpdateType_ = CaretUpdateType::DOUBLE_CLICK;
3449     HandleDoubleClickOrLongPress(info);
3450     caretUpdateType_ = CaretUpdateType::NONE;
3451 }
3452 
HandleUserGestureEvent(GestureEvent & info,std::function<bool (RefPtr<SpanItem> item,GestureEvent & info)> && gestureFunc)3453 bool RichEditorPattern::HandleUserGestureEvent(
3454     GestureEvent& info, std::function<bool(RefPtr<SpanItem> item, GestureEvent& info)>&& gestureFunc)
3455 {
3456     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleUserGestureEvent");
3457     RectF textContentRect = contentRect_;
3458     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
3459     textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
3460     if (!textContentRect.IsInRegion(PointF(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY())) ||
3461         spans_.empty()) {
3462         return false;
3463     }
3464     PointF textOffset = { info.GetLocalLocation().GetX() - GetTextRect().GetX(),
3465         info.GetLocalLocation().GetY() - GetTextRect().GetY() };
3466     int32_t start = 0;
3467     bool isParagraphHead = true;
3468     Offset paragraphOffset(0, 0);
3469     for (const auto& item : spans_) {
3470         if (!item) {
3471             continue;
3472         }
3473         std::vector<RectF> selectedRects = paragraphs_.GetRects(start, item->position);
3474         start = item->position;
3475         if (isParagraphHead && !selectedRects.empty()) {
3476             if (item->leadingMargin.has_value()) {
3477                 auto addWidth = item->leadingMargin.value().size.Width();
3478                 selectedRects[0].SetLeft(selectedRects[0].GetX() - addWidth.ConvertToPx());
3479                 selectedRects[0].SetWidth(selectedRects[0].GetSize().Width() + addWidth.ConvertToPx());
3480             }
3481             paragraphOffset.SetX(selectedRects[0].GetOffset().GetX());
3482             paragraphOffset.SetY(selectedRects[0].GetOffset().GetY());
3483             isParagraphHead = false;
3484         }
3485         if (!isParagraphHead && item->content.back() == '\n') {
3486             isParagraphHead = true;
3487         }
3488         for (auto&& rect : selectedRects) {
3489             if (!rect.IsInRegion(textOffset)) {
3490                 continue;
3491             }
3492             info = info.SetScreenLocation(
3493                 Offset(textOffset.GetX() - paragraphOffset.GetX(), textOffset.GetY() - paragraphOffset.GetY()));
3494             info = info.SetGlobalDisplayLocation(
3495                 Offset(textOffset.GetX() - paragraphOffset.GetX(), textOffset.GetY() - paragraphOffset.GetY()));
3496             return gestureFunc(item, info);
3497         }
3498     }
3499     return false;
3500 }
3501 
ClickAISpan(const PointF & textOffset,const AISpan & aiSpan)3502 bool RichEditorPattern::ClickAISpan(const PointF& textOffset, const AISpan& aiSpan)
3503 {
3504     auto calculateHandleFunc = [weak = WeakClaim(this)]() {
3505         auto pattern = weak.Upgrade();
3506         CHECK_NULL_VOID(pattern);
3507         pattern->CalculateHandleOffsetAndShowOverlay();
3508     };
3509     auto showSelectOverlayFunc = GetAISelectTextFunc();
3510 
3511     std::vector<RectF> aiRects = paragraphs_.GetRects(aiSpan.start, aiSpan.end);
3512     for (auto&& rect : aiRects) {
3513         if (rect.IsInRegion(textOffset)) {
3514             dataDetectorAdapter_->clickedAISpan_ = aiSpan;
3515             if (leftMousePress_) {
3516                 dataDetectorAdapter_->pressedByLeftMouse_ = true;
3517                 return true;
3518             }
3519             dataDetectorAdapter_->hasClickedAISpan_ = true;
3520             ShowAIEntityMenu(aiSpan, calculateHandleFunc, showSelectOverlayFunc);
3521             return true;
3522         }
3523     }
3524     return false;
3525 }
3526 
CreateAIEntityMenu()3527 RefPtr<FrameNode> RichEditorPattern::CreateAIEntityMenu()
3528 {
3529     CHECK_NULL_RETURN(dataDetectorAdapter_ && IsAiSelected(), nullptr);
3530     auto aiSpan = dataDetectorAdapter_->aiSpanMap_.find(textSelector_.aiStart.value());
3531     if (aiSpan == dataDetectorAdapter_->aiSpanMap_.end()) {
3532         return nullptr;
3533     }
3534     auto host = GetHost();
3535     CHECK_NULL_RETURN(host, nullptr);
3536 
3537     auto calculateHandleFunc = [weak = WeakClaim(this)]() {
3538         auto pattern = weak.Upgrade();
3539         CHECK_NULL_VOID(pattern);
3540         pattern->showSelect_ = true;
3541         pattern->CalculateHandleOffsetAndShowOverlay();
3542     };
3543     auto showSelectOverlayFunc = GetAISelectTextFunc();
3544 
3545     SetOnClickMenu(aiSpan->second, calculateHandleFunc, showSelectOverlayFunc);
3546     auto [isShowCopy, isShowSelectText] = GetCopyAndSelectable();
3547     return dataDetectorAdapter_->CreateAIEntityMenu(aiSpan->second, host, { isShowCopy, isShowSelectText });
3548 }
3549 
GetAISelectTextFunc()3550 std::function<void(const RectF& firstHandle, const RectF& secondHandle)> RichEditorPattern::GetAISelectTextFunc() {
3551     return [weak = WeakClaim(this)](const RectF& firstHandle, const RectF& secondHandle) {
3552         auto pattern = weak.Upgrade();
3553         CHECK_NULL_VOID(pattern);
3554         pattern->SetCaretPosition(pattern->textSelector_.destinationOffset);
3555         auto focusHub = pattern->GetFocusHub();
3556         CHECK_NULL_VOID(focusHub);
3557         focusHub->RequestFocusImmediately();
3558         IF_TRUE(!pattern->isEditing_, pattern->CloseKeyboard(true));
3559         pattern->ShowSelectOverlay(firstHandle, secondHandle);
3560     };
3561 }
3562 
AdjustAIEntityRect(RectF & aiRect)3563 void RichEditorPattern::AdjustAIEntityRect(RectF& aiRect)
3564 {
3565     auto offset = GetPaintRectGlobalOffset(); // component offset relative to window
3566     aiRect -= offset; // aiRect offset relative to component
3567     aiRect = aiRect.IntersectRectT(contentRect_) + offset;
3568 }
3569 
GetStartAndEnd(int32_t start,const RefPtr<SpanItem> & item)3570 std::pair<int32_t, int32_t> RichEditorPattern::GetStartAndEnd(int32_t start, const RefPtr<SpanItem>& item)
3571 {
3572     return isSpanStringMode_
3573         ? TextPattern::GetStartAndEnd(start, item)
3574         : std::make_pair(item->rangeStart, item->position);
3575 }
3576 
HandleUrlSpanClickEvent(const GestureEvent & info)3577 bool RichEditorPattern::HandleUrlSpanClickEvent(const GestureEvent& info)
3578 {
3579     RectF textContentRect = contentRect_;
3580     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
3581     textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
3582 
3583     CheckClickedOnSpanOrText(textContentRect, info.GetLocalLocation());
3584     auto clickedSpanPosition = GetClickedSpanPosition();
3585     if (LessNotEqual(clickedSpanPosition, 0)) {
3586         return false;
3587     }
3588     auto iter = spans_.begin();
3589     std::advance(iter, clickedSpanPosition);
3590     RefPtr<SpanItem> span;
3591     if (iter == spans_.end()) {
3592         span = spans_.back();
3593     } else {
3594         span = *iter;
3595     }
3596     if (span && span->urlOnRelease) {
3597         span->urlOnRelease();
3598         return true;
3599     }
3600     return false;
3601 }
3602 
HandleUserClickEvent(GestureEvent & info)3603 bool RichEditorPattern::HandleUserClickEvent(GestureEvent& info)
3604 {
3605     auto clickFunc = [](RefPtr<SpanItem> item, GestureEvent& info) -> bool {
3606         if (item && item->onClick) {
3607             item->onClick(info);
3608             return true;
3609         }
3610         return false;
3611     };
3612     return HandleUserGestureEvent(info, std::move(clickFunc));
3613 }
3614 
CalcCaretInfoByClick(const Offset & touchOffset)3615 void RichEditorPattern::CalcCaretInfoByClick(const Offset& touchOffset)
3616 {
3617     auto textRect = GetTextRect();
3618     textRect.SetTop(textRect.GetY() - std::min(baselineOffset_, 0.0f));
3619     textRect.SetHeight(textRect.Height() - std::max(baselineOffset_, 0.0f));
3620     Offset textOffset = { touchOffset.GetX() - textRect.GetX(), touchOffset.GetY() - textRect.GetY() };
3621     auto [lastClickOffset, caretHeight] = CalcAndRecordLastClickCaretInfo(textOffset);
3622     CHECK_NULL_VOID(overlayMod_);
3623     DynamicCast<RichEditorOverlayModifier>(overlayMod_)->SetCaretOffsetAndHeight(lastClickOffset, caretHeight);
3624     MoveCaretToContentRect();
3625 }
3626 
CalcAndRecordLastClickCaretInfo(const Offset & textOffset)3627 std::pair<OffsetF, float> RichEditorPattern::CalcAndRecordLastClickCaretInfo(const Offset& textOffset)
3628 {
3629     // get the caret position
3630     auto positionWithAffinity = paragraphs_.GetGlyphPositionAtCoordinate(textOffset);
3631     auto position = static_cast<int32_t>(positionWithAffinity.position_);
3632     // get the caret offset when click
3633     float caretHeight = 0.0f;
3634     auto lastClickOffset = paragraphs_.ComputeCursorInfoByClick(position, caretHeight,
3635         OffsetF(static_cast<float>(textOffset.GetX()), static_cast<float>(textOffset.GetY())));
3636 
3637     lastClickOffset += richTextRect_.GetOffset();
3638     if (isShowPlaceholder_) {
3639         auto caretOffset = CalculateEmptyValueCaretOffset();
3640         lastClickOffset = caretOffset;
3641     }
3642     SetLastClickOffset(lastClickOffset);
3643     caretAffinityPolicy_ = (positionWithAffinity.affinity_ == TextAffinity::UPSTREAM)
3644                                 ? CaretAffinityPolicy::UPSTREAM_FIRST
3645                                 : CaretAffinityPolicy::DOWNSTREAM_FIRST;
3646     return std::make_pair(lastClickOffset, caretHeight);
3647 }
3648 
InitClickEvent(const RefPtr<GestureEventHub> & gestureHub)3649 void RichEditorPattern::InitClickEvent(const RefPtr<GestureEventHub>& gestureHub)
3650 {
3651     CHECK_NULL_VOID(!clickEventInitialized_);
3652     CreateMultipleClickRecognizer();
3653     auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
3654         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "click callback, sourceType=%{public}d", info.GetSourceDevice());
3655         auto pattern = weak.Upgrade();
3656         CHECK_NULL_VOID(pattern);
3657         pattern->sourceType_ = info.GetSourceDevice();
3658         pattern->HandleClickEvent(info);
3659     };
3660     auto clickListener = MakeRefPtr<ClickEvent>(std::move(clickCallback));
3661     gestureHub->AddClickAfterEvent(clickListener);
3662     clickEventInitialized_ = true;
3663 }
3664 
InitFocusEvent(const RefPtr<FocusHub> & focusHub)3665 void RichEditorPattern::InitFocusEvent(const RefPtr<FocusHub>& focusHub)
3666 {
3667     CHECK_NULL_VOID(!focusEventInitialized_);
3668     auto focusTask = [weak = WeakClaim(this)](FocusReason reason) {
3669         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "rich editor in focus");
3670         auto pattern = weak.Upgrade();
3671         CHECK_NULL_VOID(pattern);
3672         pattern->HandleFocusEvent(reason);
3673     };
3674     focusHub->SetOnFocusInternal(focusTask);
3675     auto blurTask = [weak = WeakClaim(this)]() {
3676         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "rich editor in blur");
3677         auto pattern = weak.Upgrade();
3678         CHECK_NULL_VOID(pattern);
3679         pattern->HandleBlurEvent();
3680     };
3681     focusHub->SetOnBlurInternal(blurTask);
3682     focusEventInitialized_ = true;
3683     auto keyTask = [weak = WeakClaim(this)](const KeyEvent& keyEvent) -> bool {
3684         auto pattern = weak.Upgrade();
3685         CHECK_NULL_RETURN(pattern, false);
3686         return pattern->OnKeyEvent(keyEvent);
3687     };
3688     focusHub->SetOnKeyEventInternal(std::move(keyTask));
3689 }
3690 
GetBlurReason()3691 BlurReason RichEditorPattern::GetBlurReason()
3692 {
3693     auto host = GetHost();
3694     CHECK_NULL_RETURN(host, BlurReason::FOCUS_SWITCH);
3695     auto curFocusHub = host->GetFocusHub();
3696     CHECK_NULL_RETURN(curFocusHub, BlurReason::FOCUS_SWITCH);
3697     return curFocusHub->GetBlurReason();
3698 }
3699 
HandleBlurEvent()3700 void RichEditorPattern::HandleBlurEvent()
3701 {
3702     auto reason = GetBlurReason();
3703     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleBlurEvent/%{public}d, blur reason=%{public}d", frameId_, reason);
3704     auto host = GetHost();
3705     ClearOnFocusTextField(RawPtr(host));
3706     host.Reset();
3707     CreateMultipleClickRecognizer();
3708     IF_PRESENT(multipleClickRecognizer_, Stop());
3709     CHECK_NULL_VOID(showSelect_ || !IsSelected());
3710     isLongPress_ = false;
3711     ResetTouchSelectState();
3712     shiftFlag_ = false;
3713     moveCaretState_.Reset();
3714     floatingCaretState_.Reset();
3715     firstClickResetTask_.Cancel();
3716     firstClickAfterWindowFocus_ = false;
3717     StopTwinkling();
3718     // The pattern handles blurevent, Need to close the softkeyboard first.
3719     if ((customKeyboardBuilder_ && isCustomKeyboardAttached_) || reason == BlurReason::FRAME_DESTROY) {
3720         TAG_LOGI(AceLogTag::ACE_KEYBOARD, "RichEditor Blur, Close Keyboard.");
3721         CloseSelectOverlay();
3722         ResetSelection();
3723         CloseKeyboard(false);
3724     }
3725 #ifdef ANDROID_PLATFORM
3726     if (HasConnection()) {
3727         connection_->Close(GetInstanceId());
3728         connection_ = nullptr;
3729     }
3730 #endif
3731     if (magnifierController_) {
3732         magnifierController_->RemoveMagnifierFrameNode();
3733     }
3734     if (IsSelected() && reason == BlurReason::FOCUS_SWITCH) {
3735         CloseSelectOverlay();
3736         ResetSelection();
3737     } else if (IsSelected()) {
3738 #ifdef ANDROID_PLATFORM
3739         CloseSelectOverlay();
3740         ResetSelection();
3741 #else
3742         selectOverlay_->HideMenu(true);
3743 #endif
3744     } else {
3745         CloseSelectOverlay();
3746     }
3747     if (reason != BlurReason::WINDOW_BLUR) {
3748         lastSelectionRange_.reset();
3749     }
3750     HandleOnEditChanged(false);
3751     ReportComponentChangeEvent();
3752 }
3753 
HandleFocusEvent(FocusReason focusReason)3754 void RichEditorPattern::HandleFocusEvent(FocusReason focusReason)
3755 {
3756     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleFocusEvent frameId:%{public}d reason:%{public}d", frameId_, focusReason);
3757     IF_TRUE(focusReason == FocusReason::WINDOW_FOCUS, ScheduleFirstClickResetAfterWindowFocus());
3758     blockKbInFloatingWindow_= false;
3759     UseHostToUpdateTextFieldManager();
3760     if (previewLongPress_ || isOnlyRequestFocus_) {
3761         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleFocusEvent, previewLongPress=%{public}d,"
3762             "OnlyRequestFocus=%{public}d", previewLongPress_, isOnlyRequestFocus_);
3763         isOnlyRequestFocus_ = false;
3764         return;
3765     }
3766     SetIsEnableSubWindowMenu();
3767     if (textSelector_.SelectNothing()) {
3768         StartTwinkling();
3769     }
3770     auto host = GetHost();
3771     if (host) {
3772         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
3773     }
3774 
3775     bool clickAIMenu = dataDetectorAdapter_->hasClickedMenuOption_;
3776     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "longPress=%{public}d, clickAIMenu=%{public}d", isLongPress_, clickAIMenu);
3777     bool bindKeyboard = !isLongPress_ && !clickAIMenu;
3778     CHECK_NULL_VOID(bindKeyboard);
3779 
3780     auto windowMode = GetWindowMode();
3781     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "requestKeyboard=%{public}d, windowMode=%{public}d, rightButton=%{public}d",
3782         needToRequestKeyboardOnFocus_, windowMode, usingMouseRightButton_);
3783 
3784     bool needShowSoftKeyboard = needToRequestKeyboardOnFocus_;
3785     needShowSoftKeyboard &= !usingMouseRightButton_; // do not show kb when mouseRightClick
3786 
3787     if (windowMode == WindowMode::WINDOW_MODE_FLOATING && focusReason == FocusReason::WINDOW_FOCUS) {
3788         blockKbInFloatingWindow_ = needShowSoftKeyboard;
3789         needShowSoftKeyboard = false;
3790     }
3791 
3792     IF_TRUE(!requestFocusBySingleClick_, RequestKeyboard(false, true, needShowSoftKeyboard));
3793     HandleOnEditChanged(true);
3794 }
3795 
OnFocusNodeChange(FocusReason focusReason)3796 void RichEditorPattern::OnFocusNodeChange(FocusReason focusReason)
3797 {
3798     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "OnFocusNodeChange/%{public}d, reason=%{public}d, blockKbInFloating=%{public}d",
3799         frameId_, focusReason, blockKbInFloatingWindow_);
3800     CHECK_NULL_VOID(blockKbInFloatingWindow_);
3801     blockKbInFloatingWindow_= false;
3802     CHECK_NULL_VOID(GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING);
3803     CHECK_NULL_VOID(focusReason != FocusReason::WINDOW_FOCUS);
3804     CHECK_NULL_VOID(HasFocus() && isEditing_);
3805     bool clickAIMenu = dataDetectorAdapter_->hasClickedMenuOption_;
3806     bool bindKeyboard = !isLongPress_ && !clickAIMenu;
3807     CHECK_NULL_VOID(bindKeyboard);
3808     CHECK_NULL_VOID(needToRequestKeyboardOnFocus_ && !usingMouseRightButton_);
3809 
3810     RequestKeyboard(false, true, true);
3811 }
3812 
GetWindowMode()3813 WindowMode RichEditorPattern::GetWindowMode()
3814 {
3815     auto pipelineContext = GetContext();
3816     CHECK_NULL_RETURN(pipelineContext, WindowMode::WINDOW_MODE_UNDEFINED);
3817     auto windowManager = pipelineContext->GetWindowManager();
3818     CHECK_NULL_RETURN(windowManager, WindowMode::WINDOW_MODE_UNDEFINED);
3819     return windowManager->GetWindowMode();
3820 }
3821 
GetIsMidScene()3822 bool RichEditorPattern::GetIsMidScene()
3823 {
3824     auto context = GetContext();
3825     CHECK_NULL_RETURN(context, false);
3826     auto windowManager = context->GetWindowManager();
3827     CHECK_NULL_RETURN(windowManager, false);
3828     bool isMidScene = false;
3829     int32_t ret = windowManager->GetIsMidScene(isMidScene);
3830     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "GetIsMidScene ret=%{public}d", ret);
3831     return isMidScene;
3832 }
3833 
UseHostToUpdateTextFieldManager()3834 void RichEditorPattern::UseHostToUpdateTextFieldManager()
3835 {
3836     auto host = GetHost();
3837     CHECK_NULL_VOID(host);
3838     auto context = host->GetContext();
3839     CHECK_NULL_VOID(context);
3840     auto globalOffset = host->GetPaintRectOffsetNG(false, true) - context->GetRootRect().GetOffset();
3841     UpdateTextFieldManager(Offset(globalOffset.GetX(), globalOffset.GetY()), frameRect_.Height());
3842 }
3843 
CloseKeyboard(bool forceClose)3844 bool RichEditorPattern::CloseKeyboard(bool forceClose)
3845 {
3846     if (customKeyboardBuilder_ && isCustomKeyboardAttached_) {
3847         return CloseCustomKeyboard();
3848     }
3849     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "Request close soft keyboard.");
3850 #if defined(ENABLE_STANDARD_INPUT)
3851 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
3852     if (!imeAttached_ && !forceClose) {
3853         return false;
3854     }
3855 #endif
3856     auto inputMethod = MiscServices::InputMethodController::GetInstance();
3857     CHECK_NULL_RETURN(inputMethod, false);
3858     inputMethod->HideTextInput();
3859     inputMethod->Close();
3860 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
3861     imeAttached_ = false;
3862 #endif
3863 #else
3864     if (HasConnection()) {
3865         connection_->Close(GetInstanceId());
3866         connection_ = nullptr;
3867     }
3868 #endif
3869     return true;
3870 }
3871 
HandleDraggableFlag(bool isTouchSelectArea)3872 void RichEditorPattern::HandleDraggableFlag(bool isTouchSelectArea)
3873 {
3874     if (copyOption_ != CopyOptions::None && (isTouchSelectArea || IsAiSelected())) {
3875         bool isContentDraggalbe = JudgeContentDraggable();
3876         if (isContentDraggalbe) {
3877             dragBoxes_ = GetTextBoxes();
3878         }
3879         SetIsTextDraggable(isContentDraggalbe || IsAiSelected());
3880     } else {
3881         SetIsTextDraggable(false);
3882     }
3883 }
3884 
SetIsTextDraggable(bool isTextDraggable)3885 void RichEditorPattern::SetIsTextDraggable(bool isTextDraggable)
3886 {
3887     auto gestureHub = GetGestureEventHub();
3888     IF_PRESENT(gestureHub, SetIsTextDraggable(isTextDraggable));
3889 }
3890 
JudgeContentDraggable()3891 bool RichEditorPattern::JudgeContentDraggable()
3892 {
3893     if (!IsSelected() || copyOption_ == CopyOptions::None) {
3894         return false ;
3895     }
3896     auto selectInfo = GetSpansInfo(textSelector_.GetTextStart(), textSelector_.GetTextEnd(), GetSpansMethod::ONSELECT);
3897     auto selResult = selectInfo.GetSelection().resultObjects;
3898     auto iter = std::find_if(selResult.begin(), selResult.end(), [](ResultObject& obj) { return obj.isDraggable; });
3899     return iter != selResult.end();
3900 }
3901 
CalculateCaretOffsetAndHeight()3902 std::pair<OffsetF, float> RichEditorPattern::CalculateCaretOffsetAndHeight()
3903 {
3904     OffsetF caretOffset;
3905     float caretHeight = 0.0f;
3906     auto caretPosition = caretPosition_;
3907     float caretHeightUp = 0.0f;
3908     auto caretBoundaryRect = GetCaretBoundaryRect();
3909     OffsetF caretOffsetUp = CalcCursorOffsetByPosition(caretPosition, caretHeightUp, false, false);
3910     if (isShowPlaceholder_) {
3911         auto textAlign = GetTextAlignByDirection();
3912         IF_TRUE(textAlign == TextAlign::END, caretOffsetUp.SetX(caretBoundaryRect.Right()));
3913         return { caretOffsetUp, caretHeightUp };
3914     }
3915     float caretHeightDown = 0.0f;
3916     OffsetF caretOffsetDown = CalcCursorOffsetByPosition(caretPosition, caretHeightDown, true, false);
3917     bool isCaretPosInLineEnd = !NearEqual(caretOffsetDown.GetX(), caretOffsetUp.GetX(), 0.5f);
3918     bool isShowCaretDown = isCaretPosInLineEnd;
3919     if ((caretAffinityPolicy_ != CaretAffinityPolicy::DEFAULT) && isCaretPosInLineEnd) {
3920         // show caret by click
3921         isShowCaretDown = (caretAffinityPolicy_ == CaretAffinityPolicy::DOWNSTREAM_FIRST);
3922     }
3923     caretOffset = isShowCaretDown ? caretOffsetDown : caretOffsetUp;
3924     caretHeight = isShowCaretDown ? caretHeightDown : caretHeightUp;
3925     // Handle caret offset at the right boundary of the content rect
3926     if (GreatOrEqual(caretOffset.GetX(), caretBoundaryRect.Right())) {
3927         auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
3928         CHECK_NULL_RETURN(overlayModifier, std::make_pair(caretOffset, caretHeight));
3929         auto caretWidth = overlayModifier->GetCaretWidth();
3930         caretOffset.SetX(caretOffset.GetX() - caretWidth);
3931     }
3932     return std::make_pair(caretOffset, caretHeight);
3933 }
3934 
CalculateEmptyValueCaretOffset()3935 OffsetF RichEditorPattern::CalculateEmptyValueCaretOffset()
3936 {
3937     OffsetF offset;
3938     offset.SetX(contentRect_.GetX());
3939     offset.SetY(contentRect_.GetY());
3940     auto textAlign = GetTextAlignByDirection();
3941     switch (textAlign) {
3942         case TextAlign::START:
3943             offset.SetX(contentRect_.GetX());
3944             break;
3945         case TextAlign::CENTER:
3946             offset.SetX(contentRect_.GetX() + contentRect_.Width() / 2.0f);
3947             break;
3948         case TextAlign::END: {
3949             auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
3950             auto caretWidth = overlayModifier ? overlayModifier->GetCaretWidth() : 0.0f;
3951             offset.SetX(contentRect_.Right() - caretWidth);
3952             break;
3953         }
3954         default:
3955             break;
3956     }
3957     return offset;
3958 }
3959 
UpdateModifierCaretOffsetAndHeight()3960 void RichEditorPattern::UpdateModifierCaretOffsetAndHeight()
3961 {
3962     CHECK_NULL_VOID(overlayMod_);
3963     auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
3964     CHECK_NULL_VOID(overlayModifier);
3965     auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
3966     overlayModifier->SetCaretOffsetAndHeight(caretOffset, caretHeight);
3967 }
3968 
NotifyCaretChange()3969 void RichEditorPattern::NotifyCaretChange()
3970 {
3971     CHECK_NULL_VOID(!IsSelected());
3972     TriggerAvoidOnCaretChange();
3973 }
3974 
GetTextAlignByDirection()3975 TextAlign RichEditorPattern::GetTextAlignByDirection()
3976 {
3977     auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
3978     CHECK_NULL_RETURN(layoutProperty, TextAlign::START);
3979     auto textAlign = layoutProperty->GetTextAlignValue(TextAlign::START);
3980     auto direction = layoutProperty->GetNonAutoLayoutDirection();
3981     if (direction == TextDirection::RTL) {
3982         if (textAlign == TextAlign::START) {
3983             textAlign = TextAlign::END;
3984         } else {
3985             textAlign = TextAlign::START;
3986         }
3987     }
3988     return textAlign;
3989 }
3990 
HandleLongPress(GestureEvent & info)3991 void RichEditorPattern::HandleLongPress(GestureEvent& info)
3992 {
3993     if (touchedFingerCount_ == 0) {
3994         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "no finger touched, skip long press event");
3995         return;
3996     }
3997     CHECK_NULL_VOID(!selectOverlay_->GetIsHandleMoving());
3998     auto focusHub = GetFocusHub();
3999     CHECK_NULL_VOID(focusHub);
4000     if (!focusHub->IsFocusable()) {
4001         return;
4002     }
4003     if (info.GetFingerList().size() > 1) {
4004         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "More than one finger detected, ignoring this long press event");
4005         return;
4006     }
4007     if (sourceType_ == SourceType::MOUSE && hasUrlSpan_) {
4008         HandleUrlSpanShowShadow(info.GetLocalLocation(), info.GetGlobalLocation(), GetUrlPressColor());
4009     }
4010 
4011     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleLongPress");
4012     moveCaretState_.Reset();
4013     caretUpdateType_ = CaretUpdateType::LONG_PRESSED;
4014     selectionMenuOffsetClick_ = OffsetF(
4015         static_cast<float>(info.GetGlobalLocation().GetX()), static_cast<float>(info.GetGlobalLocation().GetY()));
4016     HandleDoubleClickOrLongPress(info);
4017     caretUpdateType_ = CaretUpdateType::NONE;
4018 }
4019 
HandleUrlSpanShowShadow(const Offset & localLocation,const Offset & globalOffset,const Color & color)4020 bool RichEditorPattern::HandleUrlSpanShowShadow(const Offset& localLocation, const Offset& globalOffset, const Color& color)
4021 {
4022     RectF textContentRect = contentRect_;
4023     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
4024     textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
4025 
4026     auto localLocationOffset = localLocation;
4027     if (selectOverlay_->HasRenderTransform()) {
4028         localLocationOffset = ConvertGlobalToLocalOffset(globalOffset);
4029     }
4030 
4031     PointF textOffset = {static_cast<float>(localLocationOffset.GetX()) - GetTextRect().GetX(),
4032                          static_cast<float>(localLocationOffset.GetY()) - GetTextRect().GetY()};
4033     return ShowShadow(textOffset, color);
4034 }
4035 
HandleDoubleClickOrLongPress(GestureEvent & info)4036 void RichEditorPattern::HandleDoubleClickOrLongPress(GestureEvent& info)
4037 {
4038     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "caretUpdateType=%{public}d", caretUpdateType_);
4039     if (IsPreviewTextInputting()) {
4040         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "do not handle DoubleClickOrLongPress in previewTextInputting");
4041         return;
4042     }
4043     if (IsDragging()) {
4044         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "do not handle DoubleClickOrLongPress during drag");
4045         return;
4046     }
4047     auto host = GetHost();
4048     CHECK_NULL_VOID(host);
4049     textResponseType_ = TextResponseType::LONG_PRESS;
4050     if (caretUpdateType_ == CaretUpdateType::LONG_PRESSED) {
4051         HandleUserLongPressEvent(info);
4052     } else if (caretUpdateType_ == CaretUpdateType::DOUBLE_CLICK) {
4053         HandleUserDoubleClickEvent(info);
4054     }
4055     bool isDoubleClick = caretUpdateType_== CaretUpdateType::DOUBLE_CLICK;
4056     if (isDoubleClick && info.GetSourceTool() == SourceTool::FINGER && IsSelected()) {
4057         showSelect_ = true;
4058         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
4059         ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle);
4060     }
4061     bool isLongPressSelectArea = BetweenSelection(info.GetGlobalLocation()) && !isDoubleClick;
4062     HandleDraggableFlag(isLongPressSelectArea);
4063     bool isLongPressByMouse = isMousePressed_ && caretUpdateType_== CaretUpdateType::LONG_PRESSED;
4064     if (isLongPressSelectArea && !isLongPressByMouse) {
4065         StartVibratorByLongPress();
4066     }
4067     bool isMouseClickWithShift = shiftFlag_ && info.GetSourceDevice() == SourceType::MOUSE;
4068     bool isInterceptEvent = (isLongPressSelectArea && !IsAiSelected()) || isLongPressByMouse || isMouseClickWithShift;
4069     if (isInterceptEvent) {
4070         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "intercept longPressReason:[%{public}d, %{public}d] shiftSelect:%{public}d",
4071             isLongPressSelectArea, isLongPressByMouse, isMouseClickWithShift);
4072         return;
4073     }
4074     HandleDoubleClickOrLongPress(info, host);
4075     if (IsSelected()) {
4076         TriggerAvoidOnCaretChangeNextFrame();
4077     } else {
4078         ForceTriggerAvoidOnCaretChange(true);
4079     }
4080 }
4081 
ConvertGlobalToLocalOffset(const Offset & globalOffset)4082 Offset RichEditorPattern::ConvertGlobalToLocalOffset(const Offset& globalOffset)
4083 {
4084     auto localPoint = OffsetF(globalOffset.GetX(), globalOffset.GetY());
4085     selectOverlay_->RevertLocalPointWithTransform(localPoint);
4086     return Offset(localPoint.GetX(), localPoint.GetY());
4087 }
4088 
HandleSelect(GestureEvent & info,int32_t selectStart,int32_t selectEnd)4089 void RichEditorPattern::HandleSelect(GestureEvent& info, int32_t selectStart, int32_t selectEnd)
4090 {
4091     initSelector_ = { selectStart, selectEnd };
4092     if (IsSelected()) {
4093         showSelect_ = true;
4094     }
4095     FireOnSelect(selectStart, selectEnd);
4096     SetCaretPositionWithAffinity({ selectEnd, TextAffinity::UPSTREAM });
4097     MoveCaretToContentRect();
4098     CalculateHandleOffsetAndShowOverlay();
4099     if (IsShowSelectMenuUsingMouse()) {
4100         CloseSelectOverlay();
4101     }
4102     selectionMenuOffset_ = info.GetGlobalLocation();
4103 }
4104 
HandleLongPressOnAiSelection()4105 bool RichEditorPattern::HandleLongPressOnAiSelection()
4106 {
4107     IF_TRUE(!CheckAIPreviewMenuEnable(), ResetAISelected(AIResetSelectionReason::LONG_PRESS));
4108     if (!IsAiSelected()) {
4109         ResetAISelected(AIResetSelectionReason::LONG_PRESS);
4110         return false;
4111     }
4112     ResetSelection();
4113     CloseSelectOverlay();
4114     ShowAIEntityPreviewMenuTimer();
4115     return true;
4116 }
4117 
HandleDoubleClickOrLongPress(GestureEvent & info,RefPtr<FrameNode> host)4118 void RichEditorPattern::HandleDoubleClickOrLongPress(GestureEvent& info, RefPtr<FrameNode> host)
4119 {
4120     auto focusHub = host->GetOrCreateFocusHub();
4121     CHECK_NULL_VOID(focusHub);
4122     isLongPress_ = true;
4123     auto localOffset = info.GetLocalLocation();
4124     if (selectOverlay_->HasRenderTransform()) {
4125         localOffset = ConvertGlobalToLocalOffset(info.GetGlobalLocation());
4126     }
4127     auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
4128     Offset textOffset = { localOffset.GetX() - textPaintOffset.GetX(), localOffset.GetY() - textPaintOffset.GetY() };
4129     if (caretUpdateType_ == CaretUpdateType::LONG_PRESSED) {
4130         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "LONG_PRESSED and isEditing=%{public}d", isEditing_);
4131         if (textSelector_.IsValid()) {
4132             CloseSelectOverlay();
4133             ResetSelection();
4134         }
4135         IF_TRUE(!IsAiSelected(), StartVibratorByLongPress());
4136         editingLongPress_ = isEditing_;
4137         previewLongPress_ = !isEditing_;
4138     }
4139     CHECK_NULL_VOID(!HandleLongPressOnAiSelection());
4140     focusHub->RequestFocusImmediately();
4141     InitSelection(textOffset);
4142     auto selectEnd = textSelector_.GetTextEnd();
4143     auto selectStart = textSelector_.GetTextStart();
4144     HandleSelect(info, selectStart, selectEnd);
4145     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
4146     if (overlayMod_ && caretUpdateType_ == CaretUpdateType::DOUBLE_CLICK) {
4147         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "double click. shall enter edit state.set 1");
4148         HandleOnEditChanged(true);
4149         RequestKeyboard(false, true, true);
4150     }
4151     bool isDoubleClickByMouse =
4152         info.GetSourceDevice() == SourceType::MOUSE && caretUpdateType_ == CaretUpdateType::DOUBLE_CLICK;
4153     bool isShowSelectOverlay = !isDoubleClickByMouse && caretUpdateType_ != CaretUpdateType::LONG_PRESSED;
4154     if (isShowSelectOverlay) {
4155         selectOverlay_->SwitchToOverlayMode();
4156         selectOverlay_->ProcessOverlay({ .menuIsShow = !selectOverlay_->GetIsHandleMoving(), .animation = true });
4157         StopTwinkling();
4158     } else if (selectStart == selectEnd && isDoubleClickByMouse) {
4159         StartTwinkling();
4160     } else {
4161         StopTwinkling();
4162     }
4163 }
4164 
StartVibratorByLongPress()4165 void RichEditorPattern::StartVibratorByLongPress()
4166 {
4167     CHECK_NULL_VOID(isEnableHapticFeedback_);
4168     VibratorUtils::StartVibraFeedback("longPress.light");
4169 }
4170 
HandleUserLongPressEvent(GestureEvent & info)4171 bool RichEditorPattern::HandleUserLongPressEvent(GestureEvent& info)
4172 {
4173     auto longPressFunc = [](RefPtr<SpanItem> item, GestureEvent& info) -> bool {
4174         if (item && item->onLongPress) {
4175             item->onLongPress(info);
4176             return true;
4177         }
4178         return false;
4179     };
4180     return HandleUserGestureEvent(info, std::move(longPressFunc));
4181 }
4182 
HandleUserDoubleClickEvent(GestureEvent & info)4183 bool RichEditorPattern::HandleUserDoubleClickEvent(GestureEvent& info)
4184 {
4185     auto doubleClickFunc = [](RefPtr<SpanItem> item, GestureEvent& info) -> bool {
4186         if (item && item->onDoubleClick) {
4187             item->onDoubleClick(info);
4188             return true;
4189         }
4190         return false;
4191     };
4192     return HandleUserGestureEvent(info, std::move(doubleClickFunc));
4193 }
4194 
HandleMenuCallbackOnSelectAll(bool isShowMenu)4195 void RichEditorPattern::HandleMenuCallbackOnSelectAll(bool isShowMenu)
4196 {
4197     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleMenuCallbackOnSelectAll");
4198     if (selectOverlay_->IsUsingMouse()) {
4199         CloseSelectOverlay();
4200     }
4201     auto textSize = GetTextContentLength();
4202     textSelector_.Update(0, textSize);
4203     CalculateHandleOffsetAndShowOverlay();
4204     IF_TRUE(IsSelected(), StopTwinkling());
4205     auto selectOverlayInfo = selectOverlay_->GetSelectOverlayInfo();
4206     if (selectOverlayInfo && selectOverlay_->IsUsingMouse()) {
4207         textResponseType_ = static_cast<TextResponseType>(selectOverlayInfo->menuInfo.responseType.value_or(0));
4208     } else {
4209         textResponseType_ = TextResponseType::LONG_PRESS;
4210     }
4211     selectMenuInfo_.showCopyAll = false;
4212     auto host = GetHost();
4213     CHECK_NULL_VOID(host);
4214     FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
4215     showSelect_ = true;
4216     if (!selectOverlay_->IsUsingMouse()) {
4217         selectOverlay_->ProcessOverlay({ .menuIsShow = isShowMenu, .animation = true });
4218     }
4219     SetCaretPosition(textSize);
4220     MoveCaretToContentRect();
4221     TriggerAvoidOnCaretChangeNextFrame();
4222     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
4223 }
4224 
InitLongPressEvent(const RefPtr<GestureEventHub> & gestureHub)4225 void RichEditorPattern::InitLongPressEvent(const RefPtr<GestureEventHub>& gestureHub)
4226 {
4227     CHECK_NULL_VOID(!longPressEvent_);
4228     auto longPressCallback = [weak = WeakClaim(this)](GestureEvent& info) {
4229         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "long press callback, sourceType=%{public}d", info.GetSourceDevice());
4230         auto pattern = weak.Upgrade();
4231         CHECK_NULL_VOID(pattern);
4232         pattern->sourceType_ = info.GetSourceDevice();
4233         pattern->HandleLongPress(info);
4234     };
4235     longPressEvent_ = MakeRefPtr<LongPressEvent>(std::move(longPressCallback));
4236     gestureHub->SetLongPressEvent(longPressEvent_);
4237 
4238     auto onTextSelectorChange = [weak = WeakClaim(this), &selector = textSelector_]() {
4239         auto pattern = weak.Upgrade();
4240         CHECK_NULL_VOID(pattern);
4241         if (!selector.SelectNothing()) {
4242             pattern->StopTwinkling();
4243         }
4244         IF_PRESENT(pattern->oneStepDragController_,
4245             SetEnableEventResponse(selector, pattern->imageNodes, pattern->builderNodes));
4246         pattern->FireOnSelectionChange(selector);
4247         auto frameNode = pattern->GetHost();
4248         CHECK_NULL_VOID(frameNode);
4249         frameNode->OnAccessibilityEvent(AccessibilityEventType::TEXT_SELECTION_UPDATE);
4250     };
4251     textSelector_.SetOnAccessibility(std::move(onTextSelectorChange));
4252 }
4253 
UpdateSelector(int32_t start,int32_t end)4254 void RichEditorPattern::UpdateSelector(int32_t start, int32_t end)
4255 {
4256     AdjustSelector(start, end);
4257     textSelector_.Update(start, end);
4258 }
4259 
AdjustSelector(int32_t & start,int32_t & end,SelectorAdjustPolicy policy)4260 void RichEditorPattern::AdjustSelector(int32_t& start, int32_t& end, SelectorAdjustPolicy policy)
4261 {
4262     AdjustSelector(start, HandleType::FIRST, policy);
4263     AdjustSelector(end, HandleType::SECOND, policy);
4264 }
4265 
AdjustSelector(int32_t & index,HandleType handleType,SelectorAdjustPolicy policy)4266 void RichEditorPattern::AdjustSelector(int32_t& index, HandleType handleType,  SelectorAdjustPolicy policy)
4267 {
4268     bool isAdjust = AdjustSelectorForSymbol(index, handleType, policy);
4269     CHECK_NULL_VOID(!isAdjust);
4270     AdjustSelectorForEmoji(index, handleType, policy);
4271 }
4272 
AdjustSelectorForSymbol(int32_t & index,HandleType handleType,SelectorAdjustPolicy policy)4273 bool RichEditorPattern::AdjustSelectorForSymbol(int32_t& index, HandleType handleType, SelectorAdjustPolicy policy)
4274 {
4275     auto it = GetSpanIter(index);
4276     CHECK_NULL_RETURN((it != spans_.end()), false);
4277     auto spanItem = *it;
4278     CHECK_NULL_RETURN(spanItem, false);
4279 
4280     auto spanStart = spanItem->rangeStart;
4281     auto spanEnd = spanItem->position;
4282     if (spanItem->unicode != 0 && spanItem->Contains(index)) {
4283         auto it = SELECTOR_ADJUST_DIR_MAP.find({ handleType, policy });
4284         index = (it->second == MoveDirection::BACKWARD) ? spanStart : spanEnd;
4285         return true;
4286     }
4287     return false;
4288 }
4289 
GetEmojiRelation(int index)4290 EmojiRelation RichEditorPattern::GetEmojiRelation(int index)
4291 {
4292     auto it = GetSpanIter(index);
4293     CHECK_NULL_RETURN((it != spans_.end()), EmojiRelation::NO_EMOJI);
4294     auto spanItem = *it;
4295     CHECK_NULL_RETURN(spanItem, EmojiRelation::NO_EMOJI);
4296     int32_t emojiStartIndex;
4297     int32_t emojiEndIndex;
4298     return TextEmojiProcessor::GetIndexRelationToEmoji(index - spanItem->rangeStart, spanItem->content,
4299         emojiStartIndex, emojiEndIndex);
4300 }
4301 
AdjustSelectorForEmoji(int & index,HandleType handleType,SelectorAdjustPolicy policy)4302 bool RichEditorPattern::AdjustSelectorForEmoji(int& index, HandleType handleType, SelectorAdjustPolicy policy)
4303 {
4304     auto it = GetSpanIter(index);
4305     CHECK_NULL_RETURN((it != spans_.end()), false);
4306     auto spanItem = *it;
4307     CHECK_NULL_RETURN(spanItem, false);
4308 
4309     int32_t emojiStartIndex;
4310     int32_t emojiEndIndex;
4311     int32_t spanStart = spanItem->rangeStart;
4312     EmojiRelation relation = TextEmojiProcessor::GetIndexRelationToEmoji(index - spanStart, spanItem->content,
4313         emojiStartIndex, emojiEndIndex);
4314     if (relation != EmojiRelation::IN_EMOJI && relation != EmojiRelation::MIDDLE_EMOJI) {
4315         // no need adjusting when index is not warpped in emojis
4316         return false;
4317     }
4318     int32_t start = 0;
4319     int32_t end = 0;
4320     bool isBoundaryGet = paragraphs_.GetWordBoundary(index, start, end); // boundary from engine
4321     if (isBoundaryGet) {
4322         if (handleType == HandleType::FIRST) {
4323             index = start;
4324         } else {
4325             if (index > start) {
4326                 // index to emoji, move index to end of emoji, double check "in emoji state"
4327                 index = end;
4328             }
4329         }
4330     } else {
4331         if (relation == EmojiRelation::IN_EMOJI) {
4332             int32_t indexInSpan = (handleType == HandleType::FIRST) ? emojiStartIndex : emojiEndIndex;
4333             index = spanItem->rangeStart + indexInSpan;
4334         }
4335     }
4336     TAG_LOGD(AceLogTag::ACE_RICH_TEXT,
4337         "index=%{public}d, handleType=%{public}d, emojiRange=[%{public}d,%{public}d] isBoundaryGet=%{public}d "\
4338         "boundary=[%{public}d, %{public}d]",
4339         index, handleType, emojiStartIndex, emojiEndIndex, isBoundaryGet, start, end);
4340     return true;
4341 }
4342 
GetSpanIter(int32_t index)4343 std::list<RefPtr<SpanItem>>::iterator RichEditorPattern::GetSpanIter(int32_t index)
4344 {
4345     return std::find_if(spans_.begin(), spans_.end(), [index](const RefPtr<SpanItem>& spanItem) {
4346         return spanItem->rangeStart <= index && index < spanItem->position;
4347     });
4348 }
4349 
GetSpanType(int32_t index)4350 SpanItemType RichEditorPattern::GetSpanType(int32_t index)
4351 {
4352     auto it = GetSpanIter(index);
4353     CHECK_NULL_RETURN((it != spans_.end()), SpanItemType::NORMAL);
4354     auto& spanItem = *it;
4355     CHECK_NULL_RETURN(spanItem, SpanItemType::NORMAL);
4356     return spanItem->spanItemType;
4357 }
4358 
GetGlyphPositionAtCoordinate(int32_t x,int32_t y)4359 PositionWithAffinity RichEditorPattern::GetGlyphPositionAtCoordinate(int32_t x, int32_t y)
4360 {
4361     Offset offset(x, y);
4362     return paragraphs_.GetGlyphPositionAtCoordinate(ConvertTouchOffsetToTextOffset(offset));
4363 }
4364 
InitDragDropEvent()4365 void RichEditorPattern::InitDragDropEvent()
4366 {
4367     auto host = GetHost();
4368     CHECK_NULL_VOID(host);
4369     auto gestureHub = host->GetOrCreateGestureEventHub();
4370     CHECK_NULL_VOID(gestureHub);
4371     gestureHub->InitDragDropEvent();
4372     gestureHub->SetThumbnailCallback(GetThumbnailCallback());
4373     auto eventHub = host->GetOrCreateEventHub<EventHub>();
4374     CHECK_NULL_VOID(eventHub);
4375     auto onDragMove = [weakPtr = WeakClaim(this)](
4376                           const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& extraParams) {
4377         auto pattern = weakPtr.Upgrade();
4378         CHECK_NULL_VOID(pattern);
4379         pattern->showSelect_ = false;
4380         pattern->OnDragMove(event);
4381     };
4382     eventHub->SetOnDragMove(std::move(onDragMove));
4383     OnDragStartAndEnd();
4384     onDragDropAndLeave();
4385 }
4386 
OnDragStartAndEnd()4387 void RichEditorPattern::OnDragStartAndEnd()
4388 {
4389     auto host = GetHost();
4390     CHECK_NULL_VOID(host);
4391     auto eventHub = host->GetOrCreateEventHub<EventHub>();
4392     CHECK_NULL_VOID(eventHub);
4393     auto onDragStart = [weakPtr = WeakClaim(this)](const RefPtr<OHOS::Ace::DragEvent>& event,
4394                            const std::string& extraParams) -> NG::DragDropInfo {
4395         NG::DragDropInfo itemInfo;
4396         auto pattern = weakPtr.Upgrade();
4397         CHECK_NULL_RETURN(pattern, itemInfo);
4398         return pattern->HandleDragStart(event, extraParams);
4399     };
4400     eventHub->SetDefaultOnDragStart(std::move(onDragStart));
4401     auto onDragEnd = [weakPtr = WeakClaim(this), scopeId = Container::CurrentId()](
4402                          const RefPtr<OHOS::Ace::DragEvent>& event) {
4403         ContainerScope scope(scopeId);
4404         auto pattern = weakPtr.Upgrade();
4405         CHECK_NULL_VOID(pattern);
4406         pattern->isDragSponsor_ = false;
4407         pattern->dragRange_ = { 0, 0 };
4408         pattern->showSelect_ = true;
4409         pattern->isMousePressed_ = false;
4410         pattern->StopAutoScroll();
4411         pattern->ClearRedoOperationRecords();
4412         pattern->OnDragEnd(event);
4413     };
4414     eventHub->SetOnDragEnd(std::move(onDragEnd));
4415 }
4416 
HandleDragStart(const RefPtr<Ace::DragEvent> & event,const std::string & extraParams)4417 NG::DragDropInfo RichEditorPattern::HandleDragStart(const RefPtr<Ace::DragEvent>& event, const std::string& extraParams)
4418 {
4419     if (!isDragSponsor_) {
4420         isDragSponsor_ = true;
4421         dragRange_ = IsAiSelected()
4422             ? std::make_pair(textSelector_.aiStart.value(), textSelector_.aiEnd.value())
4423             : std::make_pair(textSelector_.GetTextStart(),textSelector_.GetTextEnd());
4424     }
4425     sourceTool_ = event ? event->GetSourceTool() : SourceTool::UNKNOWN;
4426     timestamp_ = std::chrono::system_clock::now().time_since_epoch().count();
4427     auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
4428     CHECK_NULL_RETURN(eventHub, {});
4429     eventHub->SetTimestamp(timestamp_);
4430     showSelect_ = false;
4431     HandleUrlSpanForegroundClear();
4432     auto dropInfo = OnDragStart(event, extraParams);
4433     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleDragStart dragStatus=%{public}d", status_);
4434     return dropInfo;
4435 }
4436 
onDragDropAndLeave()4437 void RichEditorPattern::onDragDropAndLeave()
4438 {
4439     auto host = GetHost();
4440     CHECK_NULL_VOID(host);
4441     auto eventHub = host->GetOrCreateEventHub<EventHub>();
4442     CHECK_NULL_VOID(eventHub);
4443     auto onDragDrop = [weakPtr = WeakClaim(this), scopeId = Container::CurrentId()](
4444                           const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& value) {
4445         ContainerScope scope(scopeId);
4446         auto pattern = weakPtr.Upgrade();
4447         CHECK_NULL_VOID(pattern);
4448         auto host = pattern->GetHost();
4449         CHECK_NULL_VOID(host);
4450         auto eventHub = host->GetOrCreateEventHub<EventHub>();
4451         pattern->sourceTool_ = event ? event->GetSourceTool() : SourceTool::UNKNOWN;
4452         CHECK_NULL_VOID(eventHub && eventHub->IsEnabled());
4453         bool isCopy = false;
4454         if (pattern->status_ == Status::DRAGGING) {
4455             CHECK_NULL_VOID(event);
4456             auto gesturePressedCodes = event->GetPressedKeyCodes();
4457             if ((gesturePressedCodes.size() == 1) && ((gesturePressedCodes[0] == KeyCode::KEY_CTRL_LEFT) ||
4458                 (gesturePressedCodes[0] == KeyCode::KEY_CTRL_RIGHT))) {
4459                 isCopy = true;
4460             }
4461         }
4462         pattern->status_ = Status::ON_DROP;
4463         pattern->HandleOnDragDrop(event, isCopy);
4464         pattern->status_ = Status::NONE;
4465     };
4466     eventHub->SetOnDrop(std::move(onDragDrop));
4467     auto onDragDragLeave = [weakPtr = WeakClaim(this), scopeId = Container::CurrentId()](
4468                                const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& value) {
4469         ContainerScope scope(scopeId);
4470         auto pattern = weakPtr.Upgrade();
4471         IF_PRESENT(pattern, StopAutoScroll());
4472     };
4473     eventHub->SetOnDragLeave(onDragDragLeave);
4474 }
4475 
ClearDragDropEvent()4476 void RichEditorPattern::ClearDragDropEvent()
4477 {
4478     auto host = GetHost();
4479     CHECK_NULL_VOID(host);
4480     SetIsTextDraggable(false);
4481     auto eventHub = host->GetOrCreateEventHub<EventHub>();
4482     CHECK_NULL_VOID(eventHub);
4483     eventHub->SetOnDragStart(nullptr);
4484     eventHub->SetOnDragEnter(nullptr);
4485     eventHub->SetOnDragMove(nullptr);
4486     eventHub->SetOnDragLeave(nullptr);
4487     eventHub->SetOnDragEnd(nullptr);
4488     eventHub->SetOnDrop(nullptr);
4489 }
4490 
OnDragMove(const RefPtr<OHOS::Ace::DragEvent> & event)4491 void RichEditorPattern::OnDragMove(const RefPtr<OHOS::Ace::DragEvent>& event)
4492 {
4493     auto theme = GetTheme<RichEditorTheme>();
4494     CHECK_NULL_VOID(theme);
4495     auto touchX = event->GetX();
4496     auto touchY = event->GetY();
4497     auto textRect = GetTextRect();
4498     textRect.SetTop(textRect.GetY() - std::min(baselineOffset_, 0.0f));
4499     Offset textOffset = { touchX - textRect.GetX() - GetParentGlobalOffset().GetX(),
4500         touchY - textRect.GetY() - GetParentGlobalOffset().GetY() - theme->GetInsertCursorOffset().ConvertToPx() };
4501     auto position = isShowPlaceholder_? 0 : paragraphs_.GetIndex(textOffset);
4502     ResetSelection();
4503     CloseSelectOverlay();
4504     SetCaretPosition(position);
4505     CalcAndRecordLastClickCaretInfo(textOffset);
4506     auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
4507     CHECK_NULL_VOID(overlayMod_);
4508     auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
4509     overlayModifier->SetCaretOffsetAndHeight(caretOffset, caretHeight);
4510 
4511     auto host = GetHost();
4512     CHECK_NULL_VOID(host);
4513     if (host->GetDragPreviewOption().enableEdgeAutoScroll) {
4514         AutoScrollParam param = { .autoScrollEvent = AutoScrollEvent::DRAG, .showScrollbar = true };
4515         auto localOffset = OffsetF(touchX, touchY) - parentGlobalOffset_;
4516         AutoScrollByEdgeDetection(param, localOffset, EdgeDetectionStrategy::IN_BOUNDARY);
4517     } else if (isAutoScrollRunning_) {
4518         StopAutoScroll();
4519     }
4520 }
4521 
OnDragEnd(const RefPtr<Ace::DragEvent> & event)4522 void RichEditorPattern::OnDragEnd(const RefPtr<Ace::DragEvent>& event)
4523 {
4524     ResetDragRecordSize(-1);
4525     if (status_ == Status::DRAGGING) {
4526         status_ = Status::NONE;
4527     }
4528     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "OnDragEnd dragStatus=%{public}d", status_);
4529     ResetDragSpanItems();
4530     CHECK_NULL_VOID(!recoverDragResultObjects_.empty());
4531     UpdateSpanItemDragStatus(recoverDragResultObjects_, false);
4532     recoverDragResultObjects_.clear();
4533     auto focusHub = GetFocusHub();
4534     if (event && focusHub && event->GetResult() != DragRet::DRAG_SUCCESS && focusHub->IsFocusable()) {
4535         afterDragSelect_ = true;
4536         HandleSelectionChange(recoverStart_, recoverEnd_);
4537         showSelect_ = true;
4538         CalculateHandleOffsetAndShowOverlay();
4539         ResetSelection();
4540     }
4541     auto host = GetHost();
4542     IF_PRESENT(host, MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF));
4543 }
4544 
ToStyledString(int32_t start,int32_t end)4545 RefPtr<SpanString> RichEditorPattern::ToStyledString(int32_t start, int32_t end)
4546 {
4547     auto length = GetTextContentLength();
4548     int32_t realStart = (start == -1) ? 0 : std::clamp(start, 0, length);
4549     int32_t realEnd = (end == -1) ? length : std::clamp(end, 0, length);
4550     if (realStart > realEnd) {
4551         std::swap(realStart, realEnd);
4552     }
4553     RefPtr<SpanString> spanString = MakeRefPtr<SpanString>(u"");
4554     if (aiWriteAdapter_->GetAIWrite()) {
4555         SetSubSpansWithAIWrite(spanString, realStart, realEnd);
4556     } else {
4557         SetSubSpans(spanString, realStart, realEnd, spans_);
4558     }
4559     SetSubMap(spanString);
4560     return spanString;
4561 }
4562 
FromStyledString(const RefPtr<SpanString> & spanString)4563 SelectionInfo RichEditorPattern::FromStyledString(const RefPtr<SpanString>& spanString)
4564 {
4565     std::list<ResultObject> resultObjects;
4566     int32_t start = 0;
4567     int32_t end = 0;
4568     if (spanString && !spanString->GetSpanItems().empty()) {
4569         auto spans = spanString->GetSpanItems();
4570         int32_t index = 0;
4571         std::for_each(spans.begin(), spans.end(),
4572             [&index, &resultObjects, weak = WeakClaim(this)](RefPtr<SpanItem>& item) {
4573                 CHECK_NULL_VOID(item);
4574                 auto pattern = weak.Upgrade();
4575                 CHECK_NULL_VOID(pattern);
4576                 auto obj = item->GetSpanResultObject(item->interval.first, item->interval.second);
4577                 if (AceType::InstanceOf<ImageSpanItem>(item)) {
4578                     obj.imageStyle = pattern->GetImageStyleBySpanItem(item);
4579                 } else if (!AceType::InstanceOf<CustomSpanItem>(item)) {
4580                     obj.textStyle = pattern->GetTextStyleBySpanItem(item);
4581                 }
4582                 obj.spanPosition.spanIndex = index;
4583                 ++index;
4584                 if (obj.isInit) {
4585                     resultObjects.emplace_back(obj);
4586                 }
4587         });
4588         if (spans.back()) {
4589             end = spans.back()->interval.second;
4590         }
4591         if (spans.front()) {
4592             start = spans.front()->interval.first;
4593         }
4594     }
4595     SelectionInfo selection;
4596     selection.SetSelectionEnd(end);
4597     selection.SetSelectionStart(start);
4598     selection.SetResultObjectList(resultObjects);
4599     return selection;
4600 }
4601 
GetTextStyleBySpanItem(const RefPtr<SpanItem> & spanItem)4602 TextStyleResult RichEditorPattern::GetTextStyleBySpanItem(const RefPtr<SpanItem>& spanItem)
4603 {
4604     TextStyleResult textStyle;
4605     CHECK_NULL_RETURN(spanItem, textStyle);
4606     auto theme = GetTheme<RichEditorTheme>();
4607     TextStyle style = theme ? theme->GetTextStyle() : TextStyle();
4608     if (spanItem->fontStyle) {
4609         textStyle.fontColor = spanItem->fontStyle->GetTextColor().value_or(style.GetTextColor()).ColorToString();
4610         textStyle.fontSize =
4611             spanItem->fontStyle->GetFontSize().value_or(Dimension(DEFAULT_TEXT_SIZE, DimensionUnit::FP)).ConvertToFp();
4612         textStyle.fontStyle =
4613             static_cast<int32_t>(spanItem->fontStyle->GetItalicFontStyle().value_or(OHOS::Ace::FontStyle::NORMAL));
4614         textStyle.fontWeight = static_cast<int32_t>(spanItem->fontStyle->GetFontWeight().value_or(FontWeight::NORMAL));
4615         std::string fontFamilyValue;
4616         const std::vector<std::string> defaultFontFamily = { "HarmonyOS Sans" };
4617         auto fontFamily = spanItem->fontStyle->GetFontFamily().value_or(defaultFontFamily);
4618         for (const auto& str : fontFamily) {
4619             fontFamilyValue += str;
4620             fontFamilyValue += ",";
4621         }
4622         fontFamilyValue = fontFamilyValue.substr(0, fontFamilyValue.size() ? fontFamilyValue.size() - 1 : 0);
4623         textStyle.fontFamily = !fontFamilyValue.empty() ? fontFamilyValue : defaultFontFamily.front();
4624         textStyle.decorationType =
4625             static_cast<int32_t>(spanItem->fontStyle->GetTextDecorationFirst());
4626         textStyle.decorationColor =
4627             spanItem->fontStyle->GetTextDecorationColor().value_or(style.GetTextDecorationColor()).ColorToString();
4628         textStyle.decorationStyle =
4629             static_cast<int32_t>(spanItem->fontStyle->GetTextDecorationStyle().value_or(TextDecorationStyle::SOLID));
4630         textStyle.lineThicknessScale = static_cast<float>(spanItem->fontStyle->GetLineThicknessScale().value_or(1.0f));
4631         textStyle.fontFeature = spanItem->fontStyle->GetFontFeature().value_or(ParseFontFeatureSettings("\"pnum\" 1"));
4632         textStyle.letterSpacing = spanItem->fontStyle->GetLetterSpacing().value_or(Dimension()).ConvertToFp();
4633     }
4634     CopyTextLineStyleToTextStyleResult(spanItem, textStyle);
4635     textStyle.textBackgroundStyle = spanItem->backgroundStyle;
4636     return textStyle;
4637 }
4638 
CopyTextLineStyleToTextStyleResult(const RefPtr<SpanItem> & spanItem,TextStyleResult & textStyle)4639 void RichEditorPattern::CopyTextLineStyleToTextStyleResult(const RefPtr<SpanItem>& spanItem,
4640     TextStyleResult& textStyle)
4641 {
4642     CHECK_NULL_VOID(spanItem->textLineStyle);
4643     textStyle.lineHeight = spanItem->textLineStyle->GetLineHeight().value_or(Dimension()).ConvertToFp();
4644     textStyle.halfLeading = spanItem->textLineStyle->GetHalfLeading().value_or(false);
4645     textStyle.lineSpacing = spanItem->textLineStyle->GetLineSpacing().value_or(Dimension()).ConvertToFp();
4646     textStyle.textAlign = static_cast<int32_t>(spanItem->textLineStyle->GetTextAlign().value_or(TextAlign::START));
4647     auto lm = spanItem->textLineStyle->GetLeadingMargin();
4648     if (lm.has_value()) {
4649         textStyle.leadingMarginSize[RichEditorLeadingRange::LEADING_START] = lm.value().size.Width().ToString();
4650         textStyle.leadingMarginSize[RichEditorLeadingRange::LEADING_END] = lm.value().size.Height().ToString();
4651     }
4652     textStyle.wordBreak =
4653         static_cast<int32_t>(spanItem->textLineStyle->GetWordBreak().value_or(WordBreak::BREAK_WORD));
4654     textStyle.lineBreakStrategy =
4655         static_cast<int32_t>(spanItem->textLineStyle->GetLineBreakStrategy().value_or(LineBreakStrategy::GREEDY));
4656     textStyle.paragraphSpacing = spanItem->textLineStyle->GetParagraphSpacing();
4657     auto verticalAlign = spanItem->textLineStyle->GetTextVerticalAlign();
4658     IF_TRUE(verticalAlign.has_value(), textStyle.textVerticalAlign = static_cast<int32_t>(verticalAlign.value()));
4659 }
4660 
GetImageStyleBySpanItem(const RefPtr<SpanItem> & spanItem)4661 ImageStyleResult RichEditorPattern::GetImageStyleBySpanItem(const RefPtr<SpanItem>& spanItem)
4662 {
4663     ImageStyleResult imageStyle;
4664     auto imageSpanItem = DynamicCast<ImageSpanItem>(spanItem);
4665     CHECK_NULL_RETURN(imageSpanItem, imageStyle);
4666     auto imageAttributeOp = imageSpanItem->options.imageAttribute;
4667     CHECK_NULL_RETURN(imageAttributeOp.has_value(), imageStyle);
4668     auto imageSizeOp = imageAttributeOp->size;
4669     if (imageSizeOp.has_value() && imageSizeOp->width.has_value() && imageSizeOp->height.has_value()) {
4670         imageStyle.size[RichEditorImageSize::SIZEWIDTH] = imageSizeOp->width->ConvertToPx();
4671         imageStyle.size[RichEditorImageSize::SIZEHEIGHT] = imageSizeOp->height->ConvertToPx();
4672     }
4673     if (imageAttributeOp->verticalAlign.has_value()) {
4674         imageStyle.verticalAlign = static_cast<int32_t>(imageAttributeOp->verticalAlign.value());
4675     }
4676     if (imageAttributeOp->objectFit.has_value()) {
4677         imageStyle.objectFit = static_cast<int32_t>(imageAttributeOp->objectFit.value());
4678     }
4679     if (imageAttributeOp->marginProp.has_value()) {
4680         imageStyle.margin = imageAttributeOp->marginProp->ToString();
4681     }
4682     if (imageAttributeOp->borderRadius.has_value()) {
4683         imageStyle.borderRadius = imageAttributeOp->borderRadius->ToString();
4684     }
4685     return imageStyle;
4686 }
4687 
SetSubSpansWithAIWrite(RefPtr<SpanString> & spanString,int32_t start,int32_t end)4688 void RichEditorPattern::SetSubSpansWithAIWrite(RefPtr<SpanString>& spanString, int32_t start, int32_t end)
4689 {
4690     placeholderSpansMap_.clear();
4691     CHECK_NULL_VOID(spanString);
4692     std::list<RefPtr<SpanItem>> subSpans;
4693     std::u16string text;
4694     size_t index = 0;
4695     size_t placeholderGains = 0;
4696     for (const auto& spanItem : spans_) {
4697         if (!spanItem) {
4698             continue;
4699         }
4700         auto oldEnd = spanItem->position;
4701         auto oldStart = spanItem->rangeStart;
4702         if (oldEnd <= start || oldStart >= end) {
4703             continue;
4704         }
4705         RefPtr<SpanItem> newSpanItem = MakeRefPtr<SpanItem>();
4706         auto spanStart = oldStart <= start ? 0 : oldStart - start;
4707         auto spanEnd = oldEnd < end ? oldEnd - start : end - start;
4708         spanStart += static_cast<int32_t>(placeholderGains);
4709         if (spanItem->spanItemType == SpanItemType::NORMAL) {
4710             newSpanItem = spanItem->GetSameStyleSpanItem();
4711             newSpanItem->urlAddress = spanItem->urlAddress;
4712             newSpanItem->content = spanItem->content
4713                     .substr(std::max(start - oldStart, 0), std::min(end, oldEnd) - std::max(start, oldStart));
4714         } else {
4715             InitPlaceholderSpansMap(newSpanItem, spanItem, index, placeholderGains);
4716             spanEnd += static_cast<int32_t>(placeholderGains);
4717         }
4718         newSpanItem->interval = {spanStart, spanEnd};
4719         newSpanItem->position = spanEnd;
4720         newSpanItem->rangeStart = spanStart;
4721         newSpanItem->textLineStyle->ResetLeadingMargin();
4722         text.append(newSpanItem->content);
4723         subSpans.emplace_back(newSpanItem);
4724     }
4725     spanString->SetString(text);
4726     spanString->SetSpanItems(std::move(subSpans));
4727 }
4728 
InitPlaceholderSpansMap(RefPtr<SpanItem> & newSpanItem,const RefPtr<SpanItem> & spanItem,size_t & index,size_t & placeholderGains)4729 void RichEditorPattern::InitPlaceholderSpansMap(
4730     RefPtr<SpanItem>& newSpanItem, const RefPtr<SpanItem>& spanItem, size_t& index, size_t& placeholderGains)
4731 {
4732     newSpanItem->content = UtfUtils::Str8ToStr16("![id" + std::to_string(index++) + "]");
4733     switch (spanItem->spanItemType) {
4734         case SpanItemType::SYMBOL: {
4735             placeholderSpansMap_[newSpanItem->content] = spanItem;
4736             placeholderGains += PLACEHOLDER_LENGTH - SYMBOL_CONTENT_LENGTH;
4737             break;
4738         }
4739         case SpanItemType::CustomSpan: {
4740             if (!isSpanStringMode_) {
4741                 placeholderSpansMap_[newSpanItem->content] = spanItem;
4742             } else {
4743                 auto customSpanItem = DynamicCast<CustomSpanItem>(spanItem);
4744                 placeholderSpansMap_[newSpanItem->content] = customSpanItem;
4745             }
4746             placeholderGains += PLACEHOLDER_LENGTH - CUSTOM_CONTENT_LENGTH;
4747             break;
4748         }
4749         case SpanItemType::IMAGE: {
4750             placeholderSpansMap_[newSpanItem->content] = spanItem;
4751             placeholderGains += PLACEHOLDER_LENGTH - CUSTOM_CONTENT_LENGTH;
4752             break;
4753         }
4754         default:
4755             break;
4756     }
4757 }
4758 
SetSubSpans(RefPtr<SpanString> & spanString,int32_t start,int32_t end,const std::list<RefPtr<SpanItem>> & spans)4759 void RichEditorPattern::SetSubSpans(RefPtr<SpanString>& spanString, int32_t start, int32_t end,
4760     const std::list<RefPtr<SpanItem>>& spans)
4761 {
4762     CHECK_NULL_VOID(spanString);
4763     std::list<RefPtr<SpanItem>> subSpans;
4764     std::u16string text;
4765     for (const auto& spanItem : spans) {
4766         if (!spanItem || spanItem->spanItemType == SpanItemType::CustomSpan ||
4767             spanItem->spanItemType == SpanItemType::SYMBOL) {
4768             continue;
4769         }
4770         auto spanEndPos = spanItem->position;
4771         auto spanStartPos = spanItem->rangeStart;
4772         if (spanEndPos > start && spanStartPos < end) {
4773             int32_t oldStart = spanStartPos;
4774             int32_t oldEnd = spanEndPos;
4775             auto spanStart = oldStart <= start ? 0 : oldStart - start;
4776             auto spanEnd = oldEnd < end ? oldEnd - start : end - start;
4777             auto newSpanItem = GetSameSpanItem(spanItem);
4778             CHECK_NULL_CONTINUE(newSpanItem);
4779             newSpanItem->spanItemType = spanItem->spanItemType;
4780             newSpanItem->interval = {spanStart, spanEnd};
4781             newSpanItem->position = spanEnd;
4782             newSpanItem->rangeStart = spanStart;
4783             newSpanItem->content = spanItem->content
4784                     .substr(std::max(start - oldStart, 0), std::min(end, oldEnd) - std::max(start, oldStart));
4785             text.append(newSpanItem->content);
4786             subSpans.emplace_back(newSpanItem);
4787         }
4788     }
4789     spanString->SetString(text);
4790     spanString->SetSpanItems(std::move(subSpans));
4791 }
4792 
GetSameSpanItem(const RefPtr<SpanItem> & spanItem)4793 RefPtr<SpanItem> RichEditorPattern::GetSameSpanItem(const RefPtr<SpanItem>& spanItem)
4794 {
4795     CHECK_NULL_RETURN(spanItem, nullptr);
4796     if (spanItem->spanItemType == SpanItemType::IMAGE) {
4797         auto imageSpanItem = DynamicCast<ImageSpanItem>(spanItem);
4798         CHECK_NULL_RETURN(imageSpanItem, nullptr);
4799         auto newSpanItem = MakeRefPtr<ImageSpanItem>();
4800         auto options = imageSpanItem->options;
4801         if (!options.imagePixelMap) {
4802             auto imageNode = GetImageSpanNodeBySpanItem(imageSpanItem);
4803             CHECK_NULL_RETURN(imageNode, nullptr);
4804             auto pattern = imageNode->GetPattern<ImagePattern>();
4805             CHECK_NULL_RETURN(pattern, nullptr);
4806             auto image = pattern->GetCanvasImage();
4807             CHECK_NULL_RETURN(image, nullptr);
4808             auto pixelMap = image->GetPixelMap();
4809             if (!pixelMap) {
4810                 pixelMap = imageNode->GetDragPixelMap();
4811             }
4812             options.imagePixelMap = pixelMap;
4813         }
4814         newSpanItem->SetImageSpanOptions(options);
4815         return newSpanItem;
4816     } else if (spanItem->spanItemType == SpanItemType::NORMAL) {
4817         auto newSpanItem = spanItem->GetSameStyleSpanItem();
4818         newSpanItem->urlAddress = spanItem->urlAddress;
4819         return newSpanItem;
4820     }
4821     return nullptr;
4822 }
4823 
GetImageSpanNodeBySpanItem(const RefPtr<ImageSpanItem> & spanItem)4824 RefPtr<ImageSpanNode> RichEditorPattern::GetImageSpanNodeBySpanItem(const RefPtr<ImageSpanItem>& spanItem)
4825 {
4826     auto host = GetContentHost();
4827     CHECK_NULL_RETURN(host, nullptr);
4828     auto uiNodes = host->GetChildren();
4829     auto it = std::find_if(uiNodes.begin(), uiNodes.end(), [spanItem](const RefPtr<UINode>& uiNode) {
4830         auto imageSpanNode = DynamicCast<ImageSpanNode>(uiNode);
4831         CHECK_NULL_RETURN(imageSpanNode, false);
4832         return imageSpanNode->GetSpanItem() == spanItem;
4833     });
4834     CHECK_NULL_RETURN(it != uiNodes.end(), nullptr);
4835     return DynamicCast<ImageSpanNode>(*it);
4836 }
4837 
SetSubMap(RefPtr<SpanString> & spanString)4838 void RichEditorPattern::SetSubMap(RefPtr<SpanString>& spanString)
4839 {
4840     CHECK_NULL_VOID(spanString);
4841     auto subSpans = spanString->GetSpanItems();
4842     std::unordered_map<SpanType, std::list<RefPtr<SpanBase>>> subMap;
4843     for (auto& spanItem : subSpans) {
4844         if (!spanItem) {
4845             continue;
4846         }
4847         auto start = spanItem->rangeStart;
4848         auto end = spanItem->position;
4849         std::list<RefPtr<SpanBase>> spanBases;
4850         if (spanItem->spanItemType == SpanItemType::IMAGE) {
4851             spanBases = { spanString->ToImageSpan(spanItem, start, end) };
4852         } else if (spanItem->spanItemType == SpanItemType::NORMAL) {
4853             spanBases = { spanString->ToFontSpan(spanItem, start, end),
4854                 spanString->ToDecorationSpan(spanItem, start, end),
4855                 spanString->ToBaselineOffsetSpan(spanItem, start, end),
4856                 spanString->ToLetterSpacingSpan(spanItem, start, end),
4857                 spanString->ToGestureSpan(spanItem, start, end),
4858                 spanString->ToParagraphStyleSpan(spanItem, start, end),
4859                 spanString->ToLineHeightSpan(spanItem, start, end),
4860                 spanString->ToBackgroundColorSpan(spanItem, start, end),
4861                 spanString->ToUrlSpan(spanItem, start, end) };
4862         }
4863         for (auto& spanBase : spanBases) {
4864             if (!spanBase) {
4865                 continue;
4866             }
4867             auto it = subMap.find(spanBase->GetSpanType());
4868             if (it == subMap.end()) {
4869                 subMap.insert({ spanBase->GetSpanType(), { spanBase } });
4870             } else {
4871                 it->second.emplace_back(std::move(spanBase));
4872             }
4873         }
4874     }
4875     spanString->SetSpanMap(std::move(subMap));
4876 }
4877 
AddSpanByPasteData(const RefPtr<SpanString> & spanString,TextChangeReason reason)4878 void RichEditorPattern::AddSpanByPasteData(const RefPtr<SpanString>& spanString, TextChangeReason reason)
4879 {
4880     CHECK_NULL_VOID(spanString);
4881     if (spanString->GetSpansMap().empty()) {
4882         CompleteStyledString(const_cast<RefPtr<SpanString>&>(spanString));
4883     }
4884     if (isSpanStringMode_) {
4885         InsertStyledStringByPaste(spanString);
4886     } else {
4887         AddSpansByPaste(spanString->GetSpanItems(), reason);
4888     }
4889 
4890     if (aiWriteAdapter_->GetAIWrite()) {
4891         return;
4892     }
4893     StartTwinkling();
4894     auto host = GetHost();
4895     CHECK_NULL_VOID(host);
4896     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
4897     host->MarkModifyDone();
4898 }
4899 
CompleteStyledString(RefPtr<SpanString> & spanString)4900 void RichEditorPattern::CompleteStyledString(RefPtr<SpanString>& spanString)
4901 {
4902     CHECK_NULL_VOID(spanString);
4903     std::u16string text;
4904     auto spans = spanString->GetSpanItems();
4905     std::for_each(spans.begin(), spans.end(), [&text](RefPtr<SpanItem>& item) {
4906         CHECK_NULL_VOID(item);
4907         text.append(item->content);
4908         item->position = item->interval.second;
4909         item->rangeStart = item->interval.first;
4910     });
4911     spanString->SetString(std::move(text));
4912     SetSubMap(spanString);
4913 }
4914 
InsertStyledStringByPaste(const RefPtr<SpanString> & spanString)4915 void RichEditorPattern::InsertStyledStringByPaste(const RefPtr<SpanString>& spanString)
4916 {
4917     InsertStyledString(spanString, caretPosition_, true);
4918 }
4919 
InsertStyledString(const RefPtr<SpanString> & spanString,int32_t insertIndex,bool updateCaret)4920 void RichEditorPattern::InsertStyledString(const RefPtr<SpanString>& spanString, int32_t insertIndex, bool updateCaret)
4921 {
4922     CHECK_NULL_VOID(spanString && styledString_);
4923     int32_t changeStart = insertIndex;
4924     int32_t changeLength = 0;
4925     if (textSelector_.IsValid()) {
4926         changeStart = textSelector_.GetTextStart();
4927         changeLength = textSelector_.GetTextEnd() - textSelector_.GetTextStart();
4928     }
4929     UndoRedoRecord record;
4930     undoManager_->ApplyOperationToRecord(changeStart, changeLength, spanString, record);
4931     CHECK_NULL_VOID(BeforeStyledStringChange(record));
4932     undoManager_->RecordOperation(record);
4933     auto subSpanString = spanString;
4934     int32_t startLength = maxLength_.value_or(INT_MAX) + changeLength - GetTextContentLength();
4935     if (spanString->GetLength() >= startLength) {
4936         auto range = TextEmojiProcessor::CalSubU16stringRange(
4937             startLength, spanString->GetLength() - startLength, spanString->GetU16string(), true, true);
4938         auto subLength = range.endIndex - range.startIndex;
4939         subSpanString = subSpanString->GetSubSpanString(0, spanString->GetLength() - subLength);
4940     }
4941 
4942     if (changeLength > 0 && subSpanString->GetLength() > 0) {
4943         DeleteForwardInStyledString(changeLength, false);
4944     }
4945     ResetSelection();
4946     styledString_->InsertSpanString(changeStart, subSpanString);
4947     IF_TRUE(updateCaret, SetCaretPosition(insertIndex + subSpanString->GetLength()));
4948     AfterStyledStringChange(changeStart, changeLength, subSpanString->GetU16string());
4949 }
4950 
HandleOnDragInsertStyledString(const RefPtr<SpanString> & spanString,bool isCopy)4951 void RichEditorPattern::HandleOnDragInsertStyledString(const RefPtr<SpanString>& spanString, bool isCopy)
4952 {
4953     CHECK_NULL_VOID(spanString);
4954     int currentCaretPosition = caretPosition_;
4955     auto strLength = spanString->GetLength();
4956     insertValueLength_ = strLength;
4957     UndoRedoRecord record;
4958     undoManager_->ApplyOperationToRecord(currentCaretPosition, 0, spanString, record);
4959     if (isDragSponsor_ && !isCopy) {
4960         bool isInsertForward = currentCaretPosition < dragRange_.first;
4961         bool isInsertBackward = currentCaretPosition > dragRange_.second;
4962         CHECK_NULL_VOID(isInsertForward || isInsertBackward);
4963         CHECK_NULL_VOID(BeforeStyledStringChange(record));
4964         undoManager_->RecordOperation(record);
4965         styledString_->InsertSpanString(currentCaretPosition, spanString);
4966         AfterStyledStringChange(record);
4967         if (isInsertForward) {
4968             // Record current state before updating caret position by insertion
4969             undoManager_->RecordSelectionBefore();
4970             SetCaretPosition(currentCaretPosition + strLength);
4971             dragRange_.first += strLength;
4972             dragRange_.second += strLength;
4973         }
4974         DeleteValueInStyledString(dragRange_.first, strLength, true, false);
4975         undoManager_->ClearSelectionBefore();
4976     } else {
4977         CHECK_NULL_VOID(BeforeStyledStringChange(record));
4978         undoManager_->RecordOperation(record);
4979         styledString_->InsertSpanString(currentCaretPosition, spanString);
4980         SetCaretPosition(currentCaretPosition + strLength);
4981         AfterStyledStringChange(record);
4982     }
4983     StartTwinkling();
4984     auto host = GetContentHost();
4985     CHECK_NULL_VOID(host);
4986     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
4987 }
4988 
AddSpansByPaste(const std::list<RefPtr<NG::SpanItem>> & spans,TextChangeReason reason)4989 void RichEditorPattern::AddSpansByPaste(const std::list<RefPtr<NG::SpanItem>>& spans, TextChangeReason reason)
4990 {
4991     if (GetTextContentLength() >= maxLength_.value_or(INT_MAX)) {
4992         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "AddSpansByPaste: Reach the maxLength. maxLength=%{public}d", maxLength_.value_or(INT_MAX));
4993         return;
4994     }
4995     if (textSelector_.IsValid()) {
4996         auto deleteStart = textSelector_.GetTextStart();
4997         auto deleteLength = textSelector_.GetTextEnd() - deleteStart;
4998         UndoRedoRecord styledRecord;
4999         undoManager_->UpdateRecordBeforeChange(deleteStart, deleteLength, styledRecord);
5000         SetCaretPosition(deleteStart);
5001         DeleteForward(deleteStart, deleteLength);
5002         ResetSelection();
5003         undoManager_->RecordOperationAfterChange(deleteStart, 0, styledRecord);
5004     }
5005     for (const auto& spanItem : spans) {
5006         if (!spanItem) {
5007             continue;
5008         }
5009         auto imageSpanItem = DynamicCast<ImageSpanItem>(spanItem);
5010         if (imageSpanItem) {
5011             auto options = imageSpanItem->options;
5012             options.offset = caretPosition_;
5013             AddImageSpan(options, reason, true, caretPosition_, true);
5014         } else {
5015             auto options = GetTextSpanOptions(spanItem);
5016             AddTextSpan(options, reason, true, caretPosition_);
5017         }
5018     }
5019 }
5020 
CalculateTruncationLength(const std::u16string & insertValue,int32_t start)5021 int32_t RichEditorPattern::CalculateTruncationLength(const std::u16string& insertValue, int32_t start)
5022 {
5023     if (!textSelector_.SelectNothing()) {
5024         start += textSelector_.GetTextEnd() - textSelector_.GetTextStart();
5025     }
5026     auto truncationLength = static_cast<int32_t>(insertValue.length()) - start;
5027     auto range = TextEmojiProcessor::CalSubU16stringRange(start, truncationLength, insertValue, true, true);
5028     auto allowInsertLength = static_cast<int32_t>(insertValue.length()) - range.endIndex + range.startIndex;
5029     return allowInsertLength;
5030 }
5031 
GetParagraphStyle(const RefPtr<SpanItem> & spanItem)5032 struct UpdateParagraphStyle RichEditorPattern::GetParagraphStyle(const RefPtr<SpanItem>& spanItem)
5033 {
5034     struct UpdateParagraphStyle paraStyle;
5035     paraStyle.textAlign = spanItem->textLineStyle->GetTextAlign();
5036     paraStyle.leadingMargin = spanItem->textLineStyle->GetLeadingMargin();
5037     paraStyle.wordBreak = spanItem->textLineStyle->GetWordBreak();
5038     paraStyle.lineBreakStrategy = spanItem->textLineStyle->GetLineBreakStrategy();
5039     paraStyle.paragraphSpacing = spanItem->textLineStyle->GetParagraphSpacing();
5040     paraStyle.textVerticalAlign = spanItem->textLineStyle->GetTextVerticalAlign();
5041     return paraStyle;
5042 }
5043 
GetTextSpanOptions(const RefPtr<SpanItem> & spanItem)5044 TextSpanOptions RichEditorPattern::GetTextSpanOptions(const RefPtr<SpanItem>& spanItem)
5045 {
5046     CHECK_NULL_RETURN(spanItem, {});
5047     TextStyle textStyle = GetDefaultTextStyle();
5048     UseSelfStyle(spanItem->fontStyle, spanItem->textLineStyle, textStyle);
5049     textStyle.SetTextBackgroundStyle(spanItem->backgroundStyle);
5050     TextSpanOptions options;
5051     options.value = spanItem->content;
5052     options.offset = caretPosition_;
5053     UserGestureOptions gestureOption;
5054     gestureOption.onClick = spanItem->onClick;
5055     gestureOption.onLongPress = spanItem->onLongPress;
5056     options.urlAddress = spanItem->urlAddress;
5057     options.userGestureOption = gestureOption;
5058     options.style = textStyle;
5059     options.paraStyle = GetParagraphStyle(spanItem);
5060     return options;
5061 }
5062 
ResetDragSpanItems()5063 void RichEditorPattern::ResetDragSpanItems()
5064 {
5065     auto host = GetContentHost();
5066     CHECK_NULL_VOID(host);
5067     std::unordered_set<int32_t> nodeIds;
5068     std::for_each(dragSpanItems_.begin(), dragSpanItems_.end(), [&nodeIds](RefPtr<SpanItem>& item) {
5069         CHECK_NULL_VOID(item);
5070         item->EndDrag();
5071         auto imageSpanItem = DynamicCast<ImageSpanItem>(item);
5072         if (imageSpanItem) {
5073             nodeIds.emplace(imageSpanItem->nodeId_);
5074             return;
5075         }
5076         auto placeholderSpanItem = DynamicCast<PlaceholderSpanItem>(item);
5077         if (placeholderSpanItem) {
5078             nodeIds.emplace(placeholderSpanItem->placeholderSpanNodeId);
5079         }
5080     });
5081     const auto& childrens = host->GetChildren();
5082     for (const auto& child : childrens) {
5083         auto findResult = nodeIds.find(child->GetId());
5084         CHECK_NULL_CONTINUE(findResult != nodeIds.end());
5085         auto node = DynamicCast<FrameNode>(child);
5086         CHECK_NULL_CONTINUE(node);
5087         auto renderContext = node->GetRenderContext();
5088         CHECK_NULL_CONTINUE(renderContext);
5089         renderContext->UpdateOpacity(1);
5090         node->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
5091     }
5092     dragSpanItems_.clear();
5093 }
5094 
SelectOverlayIsOn()5095 bool RichEditorPattern::SelectOverlayIsOn()
5096 {
5097     return selectOverlay_->SelectOverlayIsOn();
5098 }
5099 
UpdateEditingValue(const std::shared_ptr<TextEditingValue> & value,bool needFireChangeEvent)5100 void RichEditorPattern::UpdateEditingValue(const std::shared_ptr<TextEditingValue>& value, bool needFireChangeEvent)
5101 {
5102 #ifdef ENABLE_STANDARD_INPUT
5103     InsertValue(UtfUtils::Str8ToStr16(value->text), true);
5104 #else
5105     if (value->isDelete) {
5106         HandleOnDelete(true);
5107     } else {
5108 #ifdef CROSS_PLATFORM
5109 #ifdef IOS_PLATFORM
5110         compose_ = value->compose;
5111         unmarkText_ = value->unmarkText;
5112 #endif
5113 #ifdef ANDROID_PLATFORM
5114         if (value->appendText.empty()) {
5115             return;
5116         }
5117 #endif
5118         InsertValue(UtfUtils::Str8ToStr16(value->appendText), true);
5119 #else
5120         InsertValue(UtfUtils::Str8ToStr16(value->appendText));
5121 #endif
5122     }
5123 #endif
5124 }
5125 
HandleAISpanHoverEvent(const MouseInfo & info)5126 void RichEditorPattern::HandleAISpanHoverEvent(const MouseInfo& info)
5127 {
5128     if (info.GetAction() != MouseAction::MOVE || !NeedShowAIDetect()) {
5129         return;
5130     }
5131     auto scrollBar = GetScrollBar();
5132     if (scrollBar && (scrollBar->IsHover() || scrollBar->IsPressed())) {
5133         return;
5134     }
5135     if (dataDetectorAdapter_->aiSpanRects_.empty()) {
5136         for (const auto& kv : dataDetectorAdapter_->aiSpanMap_) {
5137             auto& aiSpan = kv.second;
5138             const auto& aiRects = paragraphs_.GetRects(aiSpan.start, aiSpan.end);
5139             dataDetectorAdapter_->aiSpanRects_.insert(
5140                 dataDetectorAdapter_->aiSpanRects_.end(), aiRects.begin(), aiRects.end());
5141         }
5142     }
5143 
5144     auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
5145     PointF textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
5146         info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
5147     auto host = GetHost();
5148     CHECK_NULL_VOID(host);
5149     auto pipeline = GetContext();
5150     CHECK_NULL_VOID(pipeline);
5151     auto nodeId = host->GetId();
5152     for (auto&& rect : dataDetectorAdapter_->aiSpanRects_) {
5153         if (!rect.IsInRegion(textOffset)) {
5154             continue;
5155         }
5156         if (currentMouseStyle_ != MouseFormat::HAND_POINTING) {
5157             pipeline->ChangeMouseStyle(nodeId, MouseFormat::HAND_POINTING);
5158             currentMouseStyle_ = MouseFormat::HAND_POINTING;
5159         }
5160         return;
5161     }
5162     if (currentMouseStyle_ != MouseFormat::TEXT_CURSOR) {
5163         pipeline->ChangeMouseStyle(nodeId, MouseFormat::TEXT_CURSOR);
5164         currentMouseStyle_ = MouseFormat::TEXT_CURSOR;
5165     }
5166 }
5167 
InitMouseEvent()5168 void RichEditorPattern::InitMouseEvent()
5169 {
5170     CHECK_NULL_VOID(!mouseEventInitialized_);
5171     auto host = GetHost();
5172     CHECK_NULL_VOID(host);
5173     auto eventHub = host->GetOrCreateEventHub<EventHub>();
5174     CHECK_NULL_VOID(eventHub);
5175     auto inputHub = eventHub->GetOrCreateInputEventHub();
5176     CHECK_NULL_VOID(inputHub);
5177 
5178     auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
5179         auto pattern = weak.Upgrade();
5180         CHECK_NULL_VOID(pattern);
5181         pattern->HandleMouseEvent(info);
5182     };
5183     auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
5184     inputHub->AddOnMouseEvent(mouseEvent);
5185     auto hoverTask = [weak = WeakClaim(this)](bool isHover, HoverInfo& hoverInfo) {
5186         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "on hover event isHover=%{public}d", isHover);
5187         auto pattern = weak.Upgrade();
5188         if (pattern) {
5189             pattern->OnHover(isHover, hoverInfo);
5190         }
5191     };
5192     auto hoverEvent = MakeRefPtr<InputEvent>(std::move(hoverTask));
5193     inputHub->AddOnHoverEvent(hoverEvent);
5194     mouseEventInitialized_ = true;
5195 }
5196 
OnHover(bool isHover,HoverInfo & hoverInfo)5197 void RichEditorPattern::OnHover(bool isHover, HoverInfo& hoverInfo)
5198 {
5199     auto sourceTool = hoverInfo.GetSourceTool();
5200     auto mouseAction = hoverInfo.GetMouseAction();
5201     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "isHover=%{public}d, sourceTool=%{public}d, mouseAction=%{public}d",
5202         isHover, sourceTool, mouseAction);
5203     if (!isHover && lastHoverSpanItem_) {
5204         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "spanItem hover false");
5205         lastHoverSpanItem_->onHover_(false, lastHoverInfo_);
5206         lastHoverSpanItem_.Reset();
5207     }
5208     auto scrollBar = GetScrollBar();
5209     if (isHover && (!scrollBar || !scrollBar->IsPressed())) {
5210         ChangeMouseStyle(MouseFormat::TEXT_CURSOR);
5211     } else {
5212         auto host = GetHost();
5213         auto pipeline = GetContext();
5214         bool hideMouseByTouch = sourceTool == SourceTool::FINGER && mouseAction == MouseAction::WINDOW_LEAVE;
5215         bool keepStyle = host && pipeline && hideMouseByTouch;
5216         if (keepStyle) {
5217             pipeline->FreeMouseStyleHoldNode(host->GetId());
5218         } else {
5219             ChangeMouseStyle(MouseFormat::DEFAULT, true);
5220         }
5221         HandleUrlSpanForegroundClear();
5222     }
5223 }
5224 
ChangeMouseStyle(MouseFormat format,bool freeMouseHoldNode)5225 void RichEditorPattern::ChangeMouseStyle(MouseFormat format, bool freeMouseHoldNode)
5226 {
5227     auto host = GetHost();
5228     CHECK_NULL_VOID(host);
5229     auto pipeline = GetContext();
5230     CHECK_NULL_VOID(pipeline);
5231     auto nodeId = host->GetId();
5232     // Do not change mouse style to text-cursor if the right-button custom menu is showing
5233     bool shouldPreventChange = (format == MouseFormat::TEXT_CURSOR && selectOverlay_->IsRightButtonCustomMenuShow());
5234     CHECK_NULL_VOID(!shouldPreventChange);
5235     pipeline->SetMouseStyleHoldNode(nodeId);
5236     pipeline->ChangeMouseStyle(nodeId, format);
5237     currentMouseStyle_ = format;
5238     IF_TRUE(freeMouseHoldNode, pipeline->FreeMouseStyleHoldNode(nodeId));
5239 }
5240 
RequestKeyboard(bool isFocusViewChanged,bool needStartTwinkling,bool needShowSoftKeyboard,SourceType sourceType)5241 bool RichEditorPattern::RequestKeyboard(bool isFocusViewChanged, bool needStartTwinkling, bool needShowSoftKeyboard,
5242     SourceType sourceType)
5243 {
5244     auto host = GetHost();
5245     CHECK_NULL_RETURN(host, false);
5246     auto context = host->GetContext();
5247     CHECK_NULL_RETURN(context, false);
5248     CHECK_NULL_RETURN(needShowSoftKeyboard, false);
5249     if (needShowSoftKeyboard && customKeyboardBuilder_) {
5250         return RequestCustomKeyboard();
5251     }
5252 #if defined(ENABLE_STANDARD_INPUT)
5253     if (!EnableStandardInput(needShowSoftKeyboard, sourceType)) {
5254         return false;
5255     }
5256 #else
5257     if (!UnableStandardInput(isFocusViewChanged)) {
5258         return false;
5259     }
5260 #endif
5261     return true;
5262 }
5263 
5264 #if defined(ENABLE_STANDARD_INPUT)
5265 #ifdef WINDOW_SCENE_SUPPORTED
GetSCBSystemWindowId()5266 uint32_t RichEditorPattern::GetSCBSystemWindowId()
5267 {
5268     RefPtr<FrameNode> frameNode = GetHost();
5269     CHECK_NULL_RETURN(frameNode, {});
5270     auto focusSystemWindowId = WindowSceneHelper::GetFocusSystemWindowId(frameNode);
5271     return focusSystemWindowId;
5272 }
5273 #endif
5274 
EnableStandardInput(bool needShowSoftKeyboard,SourceType sourceType)5275 bool RichEditorPattern::EnableStandardInput(bool needShowSoftKeyboard, SourceType sourceType)
5276 {
5277     auto host = GetHost();
5278     CHECK_NULL_RETURN(host, false);
5279     auto context = host->GetContext();
5280     CHECK_NULL_RETURN(context, false);
5281     if (richEditTextChangeListener_ == nullptr) {
5282         richEditTextChangeListener_ = new OnTextChangedListenerImpl(WeakClaim(this));
5283     }
5284     auto inputMethod = MiscServices::InputMethodController::GetInstance();
5285     CHECK_NULL_RETURN(inputMethod, false);
5286     auto miscTextConfig = GetMiscTextConfig();
5287     CHECK_NULL_RETURN(miscTextConfig.has_value(), false);
5288     MiscServices::TextConfig textconfig = miscTextConfig.value();
5289     TAG_LOGD(
5290         AceLogTag::ACE_RICH_TEXT, "RequestKeyboard set calling window id is : %{public}u"
5291         " RequestKeyboard set placeholder length is : %{public}zu", miscTextConfig->windowId,
5292         CountUtf16Chars(textconfig.inputAttribute.placeholder));
5293 #ifdef WINDOW_SCENE_SUPPORTED
5294     auto systemWindowId = GetSCBSystemWindowId();
5295     if (systemWindowId) {
5296         miscTextConfig->windowId = systemWindowId;
5297     }
5298 #endif
5299     auto textFieldManager = DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
5300     if (host && textFieldManager) {
5301         textFieldManager->SetImeAttached(true);
5302         textFieldManager->SetLastRequestKeyboardId(host->GetId());
5303     }
5304     OHOS::MiscServices::AttachOptions attachOptions;
5305     attachOptions.isShowKeyboard = needShowSoftKeyboard;
5306     attachOptions.requestKeyboardReason =
5307         static_cast<OHOS::MiscServices::RequestKeyboardReason>(static_cast<int32_t>(sourceType));
5308     auto ret = inputMethod->Attach(richEditTextChangeListener_, attachOptions, textconfig);
5309     if (ret == MiscServices::ErrorCode::NO_ERROR) {
5310         textFieldManager->SetIsImeAttached(true);
5311         textFieldManager->SetAttachInputId(host->GetId());
5312     }
5313     UpdateCaretInfoToController();
5314     if (context) {
5315         inputMethod->SetCallingWindow(context->GetWindowId());
5316     }
5317 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
5318     imeAttached_ = true;
5319 #endif
5320     return true;
5321 }
5322 
GetMiscTextConfig()5323 std::optional<MiscServices::TextConfig> RichEditorPattern::GetMiscTextConfig()
5324 {
5325     auto tmpHost = GetHost();
5326     CHECK_NULL_RETURN(tmpHost, {});
5327     auto pipeline = tmpHost->GetContextRefPtr();
5328     auto renderContext = tmpHost->GetRenderContext();
5329     CHECK_NULL_RETURN(pipeline && renderContext, {});
5330 
5331     float caretHeight = 0.0f;
5332     OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight);
5333     caretHeight = CalcCursorHeight(caretHeight);
5334 
5335     // richeditor relative to root node offset(without transform)
5336     auto parentGlobalOffset = renderContext->GetPaintRectWithoutTransform().GetOffset() -
5337         pipeline->GetRootRect().GetOffset();
5338     // caret top (without transform)
5339     auto caretTop = caretOffset.GetY() + parentGlobalOffset.GetY();
5340     double positionY = parentGlobalOffset.GetY();
5341     double height = caretTop + caretHeight + KEYBOARD_AVOID_OFFSET.ConvertToPx() - positionY;
5342     std::u16string placeholder = TruncateText(UtfUtils::Str8ToStr16(GetPlaceHolder()), MAX_PLACEHOLDER_SIZE);
5343     std::u16string abilityName = TruncateText(UtfUtils::Str8ToStr16(AceApplicationInfo::GetInstance()
5344         .GetAbilityName()), MAX_ABILITY_NAME_SIZE);
5345 
5346     if (auto manager = pipeline->GetSafeAreaManager(); manager) {
5347         auto keyboardOffset = manager->GetKeyboardOffset();
5348         positionY -= keyboardOffset;
5349     }
5350     OffsetF caretLeftTopPoint(caretOffset.GetX() + parentGlobalOffset.GetX(), caretTop);
5351     OffsetF caretRightBottomPoint(caretLeftTopPoint.GetX() + GetCaretWidth(), caretLeftTopPoint.GetY() + caretHeight);
5352     HandlePointWithTransform(caretLeftTopPoint);
5353     HandlePointWithTransform(caretRightBottomPoint);
5354     // window rect relative to screen
5355     auto windowRect = pipeline->GetCurrentWindowRect();
5356 
5357     ContainerScope scope(GetInstanceId());
5358     auto container = AceType::DynamicCast<Platform::AceContainer>(Container::Current());
5359 
5360     MiscServices::CursorInfo cursorInfo { .left = caretLeftTopPoint.GetX() + windowRect.Left(),
5361         .top = caretLeftTopPoint.GetY() + windowRect.Top(),
5362         .width = std::abs(caretLeftTopPoint.GetX() - caretRightBottomPoint.GetX()),
5363         .height = std::abs(caretLeftTopPoint.GetY() - caretRightBottomPoint.GetY()) };
5364     MiscServices::InputAttribute inputAttribute = { .inputPattern = (int32_t)TextInputType::UNSPECIFIED,
5365         .enterKeyType = (int32_t)GetTextInputActionValue(GetDefaultTextInputAction()),
5366         .isTextPreviewSupported = isTextPreviewSupported_ && (!isSpanStringMode_ || isAPI18Plus),
5367         .immersiveMode = static_cast<int32_t>(keyboardAppearance_),
5368         .placeholder = placeholder,
5369         .abilityName = abilityName };
5370     auto start = textSelector_.IsValid() ? textSelector_.GetStart() : caretPosition_;
5371     auto end = textSelector_.IsValid() ? textSelector_.GetEnd() : caretPosition_;
5372     MiscServices::TextConfig textConfig = { .inputAttribute = inputAttribute,
5373         .cursorInfo = cursorInfo,
5374         .range = { .start = start, .end = end },
5375         .windowId = pipeline->GetFocusWindowId(),
5376         .positionY = positionY + windowRect.Top(),
5377         .height = height,
5378         .abilityToken = container ? container->GetToken() : nullptr
5379     };
5380     return textConfig;
5381 }
5382 
CalcCursorHeight(float & caretHeight)5383 float RichEditorPattern::CalcCursorHeight(float& caretHeight)
5384 {
5385     if (NearZero(caretHeight)) {
5386         auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
5387         caretHeight = overlayModifier ? overlayModifier->GetCaretHeight() : DEFAULT_CARET_HEIGHT;
5388     }
5389     if (NearZero(caretHeight)) {
5390         auto [caretAdjustOffset, caretAdjustHeight] = CalculateCaretOffsetAndHeight();
5391         caretHeight = caretAdjustHeight;
5392     }
5393     return caretHeight;
5394 }
5395 
5396 #else
UnableStandardInput(bool isFocusViewChanged)5397 bool RichEditorPattern::UnableStandardInput(bool isFocusViewChanged)
5398 {
5399     auto host = GetHost();
5400     CHECK_NULL_RETURN(host, false);
5401     auto context = host->GetContext();
5402     CHECK_NULL_RETURN(context, false);
5403 #ifndef CROSS_PLATFORM
5404     if (HasConnection()) {
5405         connection_->Show(isFocusViewChanged, GetInstanceId());
5406         return true;
5407     }
5408     TextInputConfiguration config;
5409     config.type = TextInputType::UNSPECIFIED;
5410     config.action = TextInputAction::DONE;
5411     config.obscureText = false;
5412 #else
5413     TextInputConfiguration config;
5414     if (UnableStandardInputCrossPlatform(config, isFocusViewChanged)) {
5415         return true;
5416     }
5417 #endif
5418     connection_ =
5419         TextInputProxy::GetInstance().Attach(WeakClaim(this), config, context->GetTaskExecutor(), GetInstanceId());
5420     if (!HasConnection()) {
5421         return false;
5422     }
5423     TextEditingValue value;
5424     if (spans_.empty()) {
5425         value.text = UtfUtils::Str16ToStr8(textForDisplay_);
5426     } else {
5427         for (auto it = spans_.begin(); it != spans_.end(); it++) {
5428             if ((*it)->placeholderIndex < 0) {
5429                 value.text.append(UtfUtils::Str16ToStr8((*it)->content));
5430             } else {
5431                 value.text.append(" ");
5432             }
5433         }
5434     }
5435     value.selection.Update(caretPosition_, caretPosition_);
5436     connection_->SetEditingState(value, GetInstanceId());
5437     connection_->Show(isFocusViewChanged, GetInstanceId());
5438     return true;
5439 }
5440 #endif
5441 #ifdef CROSS_PLATFORM
UnableStandardInputCrossPlatform(TextInputConfiguration & config,bool isFocusViewChanged)5442 bool RichEditorPattern::UnableStandardInputCrossPlatform(TextInputConfiguration& config, bool isFocusViewChanged)
5443 {
5444 #ifdef ANDROID_PLATFORM
5445     if (HasConnection()) {
5446         auto isCurrentClient = Platform::TextInputClientHandler::GetInstance().ConnectionIsCurrent(GetInstanceId(),
5447             AceType::RawPtr(connection_));
5448         if (!isCurrentClient) {
5449             connection_ = nullptr;
5450         }
5451     }
5452 #endif
5453     if (HasConnection()) {
5454 #ifdef IOS_PLATFORM
5455         Platform::TextInputClientHandler::GetInstance().SetCurrentConnection(connection_);
5456 #endif
5457         connection_->Show(isFocusViewChanged, GetInstanceId());
5458         return true;
5459     }
5460     config.type = TextInputType::UNSPECIFIED;
5461     config.action = GetTextInputActionValue(GetDefaultTextInputAction());
5462     config.maxLength = maxLength_.value_or(INT_MAX);
5463     config.obscureText = false;
5464     return false;
5465 }
5466 #endif
5467 
OnColorConfigurationUpdate()5468 void RichEditorPattern::OnColorConfigurationUpdate()
5469 {
5470     auto colorMode = GetColorMode();
5471     floatingCaretState_.UpdateOriginCaretColor(GetDisplayColorMode());
5472     if (colorMode == ColorMode::COLOR_MODE_UNDEFINED) {
5473         OnCommonColorChange();
5474     }
5475 }
5476 
OnThemeScopeUpdate(int32_t themeScopeId)5477 bool RichEditorPattern::OnThemeScopeUpdate(int32_t themeScopeId)
5478 {
5479     IF_PRESENT(magnifierController_, SetColorModeChange(true));
5480     floatingCaretState_.UpdateOriginCaretColor(GetDisplayColorMode());
5481     OnCommonColorChange();
5482     return false;
5483 }
5484 
OnCommonColorChange()5485 void RichEditorPattern::OnCommonColorChange()
5486 {
5487     auto host = GetHost();
5488     auto theme = GetTheme<RichEditorTheme>();
5489     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
5490     CHECK_NULL_VOID(host && theme && textLayoutProperty);
5491 
5492     auto displayColorMode = GetDisplayColorMode();
5493     COLOR_MODE_LOCK(displayColorMode);
5494 
5495     const auto& themeTextStyle = theme->GetTextStyle();
5496     auto themeTextColor = themeTextStyle.GetTextColor();
5497     auto themeTextDecColor = themeTextStyle.GetTextDecorationColor();
5498     textLayoutProperty->UpdateTextColor(themeTextColor);
5499     textLayoutProperty->UpdateTextDecorationColor(themeTextDecColor);
5500     auto themeUrlSpanColor = GetUrlSpanColor();
5501     textLayoutProperty->UpdateUrlDefualtColor(themeUrlSpanColor);
5502     textLayoutProperty->UpdateUrlHoverColor(GetUrlHoverColor());
5503     textLayoutProperty->UpdateUrlPressedColor(GetUrlPressColor());
5504 
5505     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "theme, ColorMode=%{public}d, TextColor=%{public}s, DecorationColor=%{public}s",
5506         displayColorMode, themeTextColor.ToString().c_str(), themeTextDecColor.ToString().c_str());
5507 
5508     const auto& spans = host->GetChildren();
5509     for (const auto& uiNode : spans) {
5510         auto placeholderSpan = DynamicCast<PlaceholderSpanNode>(uiNode);
5511         if (placeholderSpan) {
5512             auto spanItem = placeholderSpan->GetSpanItem();
5513             CHECK_NULL_CONTINUE(spanItem);
5514             IF_PRESENT(spanItem, UpdateColorByResourceId());
5515         }
5516         auto spanNode = DynamicCast<SpanNode>(uiNode);
5517         CHECK_NULL_CONTINUE(spanNode);
5518         auto spanItem = spanNode->GetSpanItem();
5519         CHECK_NULL_CONTINUE(spanItem);
5520         auto& textColor = spanItem->urlOnRelease ? themeUrlSpanColor : themeTextColor;
5521         IF_TRUE(spanItem->useThemeFontColor, spanNode->UpdateTextColorWithoutCheck(textColor));
5522         IF_TRUE(spanItem->useThemeDecorationColor, spanNode->UpdateTextDecorationColorWithoutCheck(themeTextDecColor));
5523         spanNode->UpdateColorByResourceId();
5524     }
5525     paragraphCache_.Clear();
5526     IF_PRESENT(typingTextStyle_, UpdateColorByResourceId());
5527     IF_PRESENT(typingStyle_, UpdateColorByResourceId());
5528     IF_PRESENT(selectedBackgroundColor_, UpdateColorByResourceId());
5529 
5530     IF_PRESENT(magnifierController_, SetColorModeChange(true));
5531     auto scrollBar = GetScrollBar();
5532     auto scrollbarTheme = GetTheme<ScrollBarTheme>();
5533     CHECK_NULL_VOID(scrollBar && scrollbarTheme);
5534     scrollBar->SetForegroundColor(scrollbarTheme->GetForegroundColor());
5535     scrollBar->SetBackgroundColor(scrollbarTheme->GetBackgroundColor());
5536 }
5537 
UpdateCaretInfoToController()5538 void RichEditorPattern::UpdateCaretInfoToController()
5539 {
5540     CHECK_NULL_VOID(HasFocus());
5541     std::u16string text = u"";
5542     for (auto iter = spans_.begin(); iter != spans_.end(); iter++) {
5543         text += (*iter)->content;
5544     }
5545     auto start = textSelector_.IsValid() ? textSelector_.GetTextStart() : caretPosition_;
5546     auto end = textSelector_.IsValid() ? textSelector_.GetTextEnd() : caretPosition_;
5547 #if defined(ENABLE_STANDARD_INPUT)
5548     auto miscTextConfig = GetMiscTextConfig();
5549     CHECK_NULL_VOID(miscTextConfig.has_value());
5550     MiscServices::CursorInfo cursorInfo = miscTextConfig.value().cursorInfo;
5551     MiscServices::InputMethodController::GetInstance()->OnCursorUpdate(cursorInfo);
5552     MiscServices::InputMethodController::GetInstance()->OnSelectionChange(
5553         text, start, end);
5554     TAG_LOGD(AceLogTag::ACE_RICH_TEXT,
5555         "CursorInfo: pos=[%{public}.2f,%{public}.2f], size=[%{public}.2f,%{public}.2f], caret=%{public}d;"
5556         "OnSelectionChange: textLen=%{public}zu, range=[%{public}d,%{public}d]",
5557         cursorInfo.left, cursorInfo.top, cursorInfo.width, cursorInfo.height, caretPosition_,
5558         text.length(), start, end);
5559 #else
5560     if (HasConnection()) {
5561         TextEditingValue editingValue;
5562         editingValue.text = UtfUtils::Str16ToStr8(text);
5563         editingValue.hint = "";
5564         editingValue.selection.Update(start, end);
5565         connection_->SetEditingState(editingValue, GetInstanceId());
5566     }
5567 #endif
5568 }
5569 
HasConnection() const5570 bool RichEditorPattern::HasConnection() const
5571 {
5572 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
5573     return imeAttached_;
5574 #else
5575     return connection_ != nullptr;
5576 #endif
5577 }
5578 
SetCustomKeyboardOption(bool supportAvoidance)5579 void RichEditorPattern::SetCustomKeyboardOption(bool supportAvoidance)
5580 {
5581     keyboardAvoidance_ = supportAvoidance;
5582 }
5583 
RequestCustomKeyboard()5584 bool RichEditorPattern::RequestCustomKeyboard()
5585 {
5586 #if defined(ENABLE_STANDARD_INPUT)
5587     auto inputMethod = MiscServices::InputMethodController::GetInstance();
5588     if (inputMethod) {
5589         TAG_LOGI(AceLogTag::ACE_KEYBOARD, "RequestCKeyboard,close softkeyboard.");
5590         inputMethod->RequestHideInput();
5591         inputMethod->Close();
5592     }
5593 #else
5594     if (HasConnection()) {
5595         connection_->Close(GetInstanceId());
5596         connection_ = nullptr;
5597     }
5598 #endif
5599 
5600     if (isCustomKeyboardAttached_) {
5601         return true;
5602     }
5603     CHECK_NULL_RETURN(customKeyboardBuilder_, false);
5604     auto frameNode = GetHost();
5605     CHECK_NULL_RETURN(frameNode, false);
5606     auto pipeline = frameNode->GetContext();
5607     CHECK_NULL_RETURN(pipeline, false);
5608     auto overlayManager = pipeline->GetOverlayManager();
5609     CHECK_NULL_RETURN(overlayManager, false);
5610     overlayManager->SetCustomKeyboardOption(keyboardAvoidance_);
5611     auto textFieldManager = DynamicCast<TextFieldManagerNG>(pipeline->GetTextFieldManager());
5612     CHECK_NULL_RETURN(textFieldManager, false);
5613     if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_FOURTEEN)) {
5614         textFieldManager->SetUsingCustomKeyboardAvoid(keyboardAvoidance_);
5615     }
5616     overlayManager->BindKeyboard(customKeyboardBuilder_, frameNode->GetId());
5617     isCustomKeyboardAttached_ = true;
5618     contentChange_ = false;
5619     keyboardOverlay_ = overlayManager;
5620     auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
5621     keyboardOverlay_->AvoidCustomKeyboard(frameNode->GetId(), caretHeight);
5622     return true;
5623 }
5624 
CloseCustomKeyboard()5625 bool RichEditorPattern::CloseCustomKeyboard()
5626 {
5627     auto frameNode = GetHost();
5628     CHECK_NULL_RETURN(frameNode, false);
5629     CHECK_NULL_RETURN(keyboardOverlay_, false);
5630     keyboardOverlay_->CloseKeyboard(frameNode->GetId());
5631     isCustomKeyboardAttached_ = false;
5632     contentChange_ = false;
5633     auto pipeline = frameNode->GetContext();
5634     CHECK_NULL_RETURN(pipeline, false);
5635     auto textFieldManager = DynamicCast<TextFieldManagerNG>(pipeline->GetTextFieldManager());
5636     CHECK_NULL_RETURN(textFieldManager, false);
5637     if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_FOURTEEN)) {
5638         textFieldManager->SetUsingCustomKeyboardAvoid(keyboardAvoidance_);
5639     }
5640     return true;
5641 }
5642 
CheckPreviewTextValidate(const std::u16string & previewTextValue,const PreviewRange range)5643 int32_t RichEditorPattern::CheckPreviewTextValidate(const std::u16string& previewTextValue, const PreviewRange range)
5644 {
5645     if (IsDragging()) {
5646         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "SetPreviewText is not allowed while dragging");
5647         return ERROR_BAD_PARAMETERS;
5648     }
5649     return NO_ERRORS;
5650 }
5651 
CheckPreviewTextValidate(const std::string & previewTextValue,const PreviewRange range)5652 int32_t RichEditorPattern::CheckPreviewTextValidate(const std::string& previewTextValue, const PreviewRange range)
5653 {
5654     return CheckPreviewTextValidate(UtfUtils::Str8DebugToStr16(previewTextValue), range);
5655 }
5656 
SetPreviewText(const std::u16string & previewTextValue,const PreviewRange range)5657 int32_t RichEditorPattern::SetPreviewText(const std::u16string& previewTextValue, const PreviewRange range)
5658 {
5659     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "SetPreviewText, range=[%{public}d,%{public}d], isSSMode=%{public}d",
5660         range.start, range.end, isSpanStringMode_);
5661     CHECK_NULL_RETURN(!isSpanStringMode_ || isAPI18Plus, ERROR_BAD_PARAMETERS);
5662     SEC_TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "previewText=%{public}s", UtfUtils::Str16ToStr8(previewTextValue).c_str());
5663     auto host = GetHost();
5664     CHECK_NULL_RETURN(host, ERROR_BAD_PARAMETERS);
5665     CHECK_NULL_RETURN(!IsDragging(), ERROR_BAD_PARAMETERS);
5666 
5667     if (!IsPreviewTextInputting()) {
5668         if (!InitPreviewText(previewTextValue, range)) {
5669             return ERROR_BAD_PARAMETERS;
5670         }
5671     } else {
5672         if (!UpdatePreviewText(previewTextValue, range)) {
5673             return ERROR_BAD_PARAMETERS;
5674         }
5675     }
5676     previewTextRecord_.replacedRange.Set(previewTextRecord_.startOffset, previewTextRecord_.endOffset);
5677     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
5678     return NO_ERRORS;
5679 }
5680 
SetPreviewTextForDelete(int32_t oriLength,bool isBackward,bool isByIME)5681 bool RichEditorPattern::SetPreviewTextForDelete(int32_t oriLength, bool isBackward, bool isByIME)
5682 {
5683     if (!IsPreviewTextInputting() || !isByIME) {
5684         return true;
5685     }
5686     auto previewContent = previewTextRecord_.previewContent;
5687     auto startOffset = previewTextRecord_.startOffset;
5688     auto endOffset = previewTextRecord_.endOffset;
5689     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "SetPreviewTextForDelete offset=[%{public}d, %{public}d]",
5690         startOffset, endOffset);
5691     if (caretPosition_ < startOffset || caretPosition_ > endOffset) {
5692         TAG_LOGW(AceLogTag::ACE_RICH_TEXT,
5693             "preview abnormal, offset=[%{public}d, %{public}d], caretPosition_=%{public}d",
5694             startOffset, endOffset, caretPosition_);
5695         return true;
5696     }
5697 
5698     std::u16string previewTextValue = u"";
5699     int32_t deleteLength = 0;
5700     PreviewRange range;
5701     if (isBackward) {
5702         deleteLength = std::clamp(oriLength, 0, caretPosition_ - startOffset);
5703         range.Set(startOffset, caretPosition_);
5704         previewTextValue = previewContent.substr(0, std::max(0, caretPosition_ - deleteLength - startOffset));
5705     } else {
5706         deleteLength = std::clamp(oriLength, 0, endOffset - caretPosition_);
5707         range.Set(startOffset, std::min(caretPosition_ + deleteLength, endOffset));
5708         previewTextValue = previewContent.substr(0, std::max(0, caretPosition_ - startOffset));
5709     }
5710     if (deleteLength == 0) {
5711         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "SetPreviewTextForDelete not deleted");
5712         return false;
5713     }
5714     SetPreviewText(previewTextValue, range);
5715     if (previewTextRecord_.previewContent.empty()) {
5716         FinishTextPreview();
5717     }
5718     return false;
5719 }
5720 
InitPreviewText(const std::u16string & previewTextValue,const PreviewRange & range)5721 bool RichEditorPattern::InitPreviewText(const std::u16string& previewTextValue, const PreviewRange& range)
5722 {
5723     if (range.start != -1 || range.end != -1) {
5724         return ReplaceText(previewTextValue, range);
5725     }
5726     // interrupt touch selecting when initialize preview text
5727     ResetTouchSelectState();
5728     IF_PRESENT(magnifierController_, RemoveMagnifierFrameNode());
5729     auto& record = previewTextRecord_;
5730     record.needReplacePreviewText = true;
5731     record.previewTextHasStarted = true;
5732     record.replacedRange = range;
5733     record.startOffset = textSelector_.SelectNothing() ? caretPosition_ : textSelector_.GetTextStart();
5734     record.newPreviewContent = previewTextValue;
5735     auto length = static_cast<int32_t>(previewTextValue.length());
5736     record.endOffset = record.startOffset + length;
5737     auto spanCountBefore = spans_.size();
5738     ProcessInsertValue(previewTextValue, OperationType::IME, false);
5739     if (!previewTextRecord_.previewTextHasStarted) {
5740         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "previewText ends abnormally");
5741         return false;
5742     }
5743     record.isSpanSplit = spans_.size() - spanCountBefore > 1;
5744     record.previewContent = record.newPreviewContent;
5745     record.newPreviewContent.clear();
5746     record.needReplacePreviewText = false;
5747     return true;
5748 }
5749 
ReplaceText(const std::u16string & previewTextValue,const PreviewRange & range)5750 bool RichEditorPattern::ReplaceText(const std::u16string& previewTextValue, const PreviewRange& range)
5751 {
5752     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "ReplaceText");
5753     if (range.start < 0 || range.end < range.start || range.end > GetTextContentLength()) {
5754         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "bad PreviewRange");
5755         return false;
5756     }
5757     previewTextRecord_.replacedRange = range;
5758     previewTextRecord_.needReplaceText = true;
5759     previewTextRecord_.startOffset = range.start;
5760     previewTextRecord_.endOffset = range.end;
5761     ProcessInsertValue(previewTextValue, OperationType::IME, false);
5762     previewTextRecord_.needReplaceText = false;
5763     return true;
5764 }
5765 
5766 // Used for text replacement, without notifying developer caret change
DeleteByRange(OperationRecord * const record,int32_t start,int32_t end)5767 void RichEditorPattern::DeleteByRange(OperationRecord* const record, int32_t start, int32_t end)
5768 {
5769     auto length = end - start;
5770     CHECK_NULL_VOID(length > 0);
5771     lastCaretPosition_ = caretPosition_;
5772     caretPosition_ = std::clamp(start, 0, GetTextContentLength());
5773     if (isSpanStringMode_) {
5774         DeleteValueInStyledString(start, length, true, false);
5775         return;
5776     }
5777     std::u16string deleteText = DeleteForwardOperation(length, false);
5778     if (record && deleteText.length() != 0) {
5779         record->deleteText = deleteText;
5780     }
5781 }
5782 
NotUpdateCaretInPreview(int32_t caret,const PreviewTextRecord & record)5783 bool RichEditorPattern::NotUpdateCaretInPreview(int32_t caret, const PreviewTextRecord& record)
5784 {
5785     CHECK_NULL_RETURN(record.IsValid(), false);
5786     bool caretInSecondPos = caret == record.startOffset + 1;
5787     bool caretNotInEndPos = caret != record.endOffset;
5788 
5789     auto& curContent = record.previewContent;
5790     auto& newContent = record.newPreviewContent;
5791     auto enFilter = [](char c) { return isalpha(c) || c == '\''; };
5792     bool curAllEn = std::all_of(curContent.begin(), curContent.end(), enFilter);
5793     bool newAllEn = std::all_of(newContent.begin(), newContent.end(), enFilter);
5794 
5795     return caretInSecondPos && caretNotInEndPos && curAllEn && newAllEn;
5796 }
5797 
UpdatePreviewText(const std::u16string & previewTextValue,const PreviewRange & range)5798 bool RichEditorPattern::UpdatePreviewText(const std::u16string& previewTextValue, const PreviewRange& range)
5799 {
5800     auto& record = previewTextRecord_;
5801     if (range.start == -1 && range.end == -1 && !record.previewContent.empty()) {
5802         record.replacedRange.Set(record.startOffset, record.endOffset);
5803         record.newPreviewContent = previewTextValue;
5804         record.needReplacePreviewText = true;
5805         record.needUpdateCaret = !NotUpdateCaretInPreview(caretPosition_, record);
5806         ProcessInsertValue(previewTextValue, OperationType::IME, false);
5807         record.previewContent = record.newPreviewContent;
5808         record.newPreviewContent.clear();
5809         record.endOffset = record.startOffset + static_cast<int32_t>(previewTextValue.length());
5810         record.needReplacePreviewText = false;
5811         record.needUpdateCaret = true;
5812     } else {
5813         if (range.start < record.startOffset || range.end > record.endOffset || range.end < range.start) {
5814             TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "bad PreviewRange");
5815             return false;
5816         }
5817         if (previewTextValue.empty() && range.start == range.end) {
5818             SetCaretPosition(range.end);
5819             return false;
5820         }
5821         auto replaceIndex = range.start - record.startOffset;
5822         auto replaceLength = range.end - range.start;
5823         auto oldContent = record.previewContent;
5824         auto oldPreviewLength = static_cast<int32_t>(oldContent.length());
5825         if (replaceIndex < 0 || replaceIndex + replaceLength > oldPreviewLength) {
5826             TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "bad replaced range ");
5827             return false;
5828         }
5829         auto newContent = oldContent.replace(replaceIndex, replaceLength, previewTextValue);
5830         record.replacedRange = range;
5831         record.newPreviewContent = newContent;
5832         record.needReplacePreviewText = true;
5833         ProcessInsertValue(previewTextValue, OperationType::IME, false);
5834         record.previewContent = record.newPreviewContent;
5835         record.newPreviewContent.clear();
5836         record.endOffset = record.startOffset + static_cast<int32_t>(newContent.length());
5837         record.needReplacePreviewText = false;
5838     }
5839     return true;
5840 }
5841 
GetPreviewTextInfo() const5842 const PreviewTextInfo RichEditorPattern::GetPreviewTextInfo() const
5843 {
5844     PreviewTextInfo info;
5845     if (!previewTextRecord_.previewContent.empty()) {
5846         info.value = previewTextRecord_.previewContent;
5847         info.offset = previewTextRecord_.startOffset;
5848     }
5849     return info;
5850 }
5851 
MergeAdjacentSpans(int32_t caretPosition)5852 void RichEditorPattern::MergeAdjacentSpans(int32_t caretPosition)
5853 {
5854     auto host = GetContentHost();
5855     CHECK_NULL_VOID(host);
5856     auto uiNodes = host->GetChildren();
5857     auto it = std::find_if(uiNodes.begin(), uiNodes.end(), [caretPosition](const RefPtr<UINode>& uiNode) {
5858         auto spanNode = DynamicCast<SpanNode>(uiNode);
5859         CHECK_NULL_RETURN(spanNode, false);
5860         return spanNode->GetSpanItem()->position == caretPosition;
5861     });
5862     CHECK_NULL_VOID(it != uiNodes.end());
5863     auto beforeSpanNode = DynamicCast<SpanNode>(*it);
5864     ++it;
5865     CHECK_NULL_VOID(it != uiNodes.end());
5866     auto afterSpanNode = DynamicCast<SpanNode>(*it);
5867     CHECK_NULL_VOID(beforeSpanNode && afterSpanNode);
5868     CHECK_NULL_VOID(beforeSpanNode->GetTag() == V2::SPAN_ETS_TAG && afterSpanNode->GetTag() == V2::SPAN_ETS_TAG);
5869     auto beforeSpanItem = beforeSpanNode->GetSpanItem();
5870     auto afterSpanItem = afterSpanNode->GetSpanItem();
5871     CHECK_NULL_VOID(beforeSpanItem && afterSpanItem);
5872     beforeSpanNode->UpdateContent(beforeSpanItem->content + afterSpanItem->content);
5873     afterSpanItem->content.clear();
5874     RemoveEmptySpans();
5875 }
5876 
FinishTextPreview()5877 void RichEditorPattern::FinishTextPreview()
5878 {
5879     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "FinishTextPreview byImf");
5880     if (previewTextRecord_.previewContent.empty()) {
5881         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "previewContent is empty");
5882         RemoveEmptySpans();
5883         IF_TRUE(previewTextRecord_.isSpanSplit, MergeAdjacentSpans(caretPosition_));
5884         previewTextRecord_.Reset();
5885         return;
5886     }
5887     auto previewContent = previewTextRecord_.previewContent;
5888     FinishTextPreviewInner();
5889     ProcessInsertValue(previewContent, OperationType::FINISH_PREVIEW, true);
5890 }
5891 
FinishTextPreviewInner(bool deletePreviewText)5892 void RichEditorPattern::FinishTextPreviewInner(bool deletePreviewText)
5893 {
5894     CHECK_NULL_VOID(previewTextRecord_.previewTextHasStarted);
5895     TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
5896         "FinishTextPreviewInner, deleteText=%{public}d, previewContent is empty=%{public}d", deletePreviewText,
5897         previewTextRecord_.previewContent.empty());
5898     previewTextRecord_.previewTextExiting = true;
5899     IF_TRUE(deletePreviewText && !previewTextRecord_.previewContent.empty(),
5900         DeleteByRange(nullptr, previewTextRecord_.startOffset, previewTextRecord_.endOffset));
5901     previewTextRecord_.Reset();
5902 }
5903 
NotifyExitTextPreview(bool deletePreviewText)5904 void RichEditorPattern::NotifyExitTextPreview(bool deletePreviewText)
5905 {
5906     CHECK_NULL_VOID(previewTextRecord_.previewTextHasStarted);
5907     CHECK_NULL_VOID(HasFocus());
5908     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "NotifyExitTextPreview");
5909     auto startOffset = previewTextRecord_.startOffset;
5910     auto length = previewTextRecord_.endOffset - previewTextRecord_.startOffset;
5911     undoManager_->RecordSelectionBefore(TextRange{ startOffset, startOffset });
5912     FinishTextPreviewInner(deletePreviewText);
5913     if (!deletePreviewText) {
5914         UndoRedoRecord styledRecord;
5915         undoManager_->UpdateRecordBeforeChange(startOffset, 0, styledRecord);
5916         undoManager_->UpdateRecordAfterChange(startOffset, length, styledRecord);
5917         undoManager_->RecordInsertOperation(styledRecord);
5918     }
5919     NotifyImfFinishTextPreview();
5920 }
5921 
NotifyImfFinishTextPreview()5922 void RichEditorPattern::NotifyImfFinishTextPreview()
5923 {
5924 #if defined(ENABLE_STANDARD_INPUT)
5925     MiscServices::InputMethodController::GetInstance()->OnSelectionChange(u"", caretPosition_, caretPosition_);
5926     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "notify imf that richEditor exit textPreview");
5927 #endif
5928 }
5929 
GetPreviewTextRects()5930 std::vector<RectF> RichEditorPattern::GetPreviewTextRects()
5931 {
5932     auto rects = paragraphs_.GetRects(previewTextRecord_.startOffset, previewTextRecord_.endOffset,
5933         RectHeightPolicy::COVER_TEXT);
5934     auto offset = GetTextRect().GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
5935     for (RectF& rect : rects) {
5936         rect += offset;
5937     }
5938     return rects;
5939 }
5940 
GetPreviewTextStyle() const5941 PreviewTextStyle RichEditorPattern::GetPreviewTextStyle() const
5942 {
5943     auto previewTextStyle = PreviewTextStyle::NORMAL;
5944     auto property = GetLayoutProperty<RichEditorLayoutProperty>();
5945     if (property && property->HasPreviewTextStyle()) {
5946         auto style = property->GetPreviewTextStyle();
5947         CHECK_NULL_RETURN(style.has_value(), previewTextStyle);
5948         if (style.value() == PREVIEW_STYLE_NORMAL) {
5949             previewTextStyle = PreviewTextStyle::NORMAL;
5950         } else if (style.value() == PREVIEW_STYLE_UNDERLINE) {
5951             previewTextStyle = PreviewTextStyle::UNDERLINE;
5952         } else {
5953             TAG_LOGW(
5954                 AceLogTag::ACE_RICH_TEXT, "invalid previewTextStyle of RichEditorLayoutProperty");
5955         }
5956     }
5957     return previewTextStyle;
5958 }
5959 
GetPreviewTextDecorationColor() const5960 const Color& RichEditorPattern::GetPreviewTextDecorationColor() const
5961 {
5962     auto pipeline = PipelineBase::GetCurrentContextSafelyWithCheck();
5963     CHECK_NULL_RETURN(pipeline, Color::TRANSPARENT);
5964     auto theme = pipeline->GetTheme<RichEditorTheme>();
5965     CHECK_NULL_RETURN(theme, Color::TRANSPARENT);
5966     if (GetPreviewTextStyle() == PreviewTextStyle::UNDERLINE) {
5967         return theme->GetPreviewUnderLineColor();
5968     }
5969     return Color::TRANSPARENT;
5970 }
5971 
GetPreviewTextUnderlineWidth() const5972 float RichEditorPattern::GetPreviewTextUnderlineWidth() const
5973 {
5974     auto pipeline = PipelineBase::GetCurrentContextSafelyWithCheck();
5975     CHECK_NULL_RETURN(pipeline, 0.0f);
5976     auto theme = pipeline->GetTheme<RichEditorTheme>();
5977     CHECK_NULL_RETURN(theme, 0.0f);
5978     return theme->GetPreviewUnderlineWidth().ConvertToPx();
5979 }
5980 
IsIMEOperation(OperationType operationType)5981 bool RichEditorPattern::IsIMEOperation(OperationType operationType)
5982 {
5983     return operationType == OperationType::IME
5984         || operationType == OperationType::FINISH_PREVIEW
5985         || operationType == OperationType::STYLUS;
5986 }
5987 
InsertValue(const std::string & insertValue,bool isIME)5988 void RichEditorPattern::InsertValue(const std::string& insertValue, bool isIME)
5989 {
5990     InsertValue(UtfUtils::Str8ToStr16(insertValue), isIME);
5991 }
5992 
InsertValue(const std::u16string & insertValue,bool isIME)5993 void RichEditorPattern::InsertValue(const std::u16string& insertValue, bool isIME)
5994 {
5995     InsertValueByOperationType(insertValue, isIME ? OperationType::IME : OperationType::DEFAULT);
5996 }
5997 
InsertValueByOperationType(const std::u16string & insertValue,OperationType operationType)5998 void RichEditorPattern::InsertValueByOperationType(const std::u16string& insertValue, OperationType operationType)
5999 {
6000     ProcessInsertValue(insertValue, operationType, true);
6001 }
6002 
ProcessTextTruncationOperation(std::u16string & text,bool shouldCommitInput)6003 bool RichEditorPattern::ProcessTextTruncationOperation(std::u16string& text, bool shouldCommitInput)
6004 {
6005 #if defined(IOS_PLATFORM)
6006     if (compose_.IsValid()) {
6007         return true;
6008     }
6009     if (GetTextContentLength() - text.length() < maxLength_.value_or(INT_MAX) && text.length() == 1 && !unmarkText_) {
6010         return true;
6011     }
6012 #endif
6013     bool needTruncationInsertValue = shouldCommitInput || !previewTextRecord_.needReplacePreviewText;
6014     int32_t selectLength =
6015         textSelector_.SelectNothing() ? 0 : textSelector_.GetTextEnd() - textSelector_.GetTextStart();
6016     int32_t previewContentLength = previewTextRecord_.previewContent.empty()
6017                                        ? previewTextRecord_.replacedRange.end - previewTextRecord_.replacedRange.start
6018                                        : static_cast<int32_t>(previewTextRecord_.previewContent.length());
6019 
6020     if (!needTruncationInsertValue || GetTextContentLength() - previewContentLength < maxLength_.value_or(INT_MAX)) {
6021         if (needTruncationInsertValue && text.length() != 1) {
6022             auto maxLength = maxLength_.value_or(INT_MAX) - GetTextContentLength() + previewContentLength;
6023             auto allowInsertLength =  CalculateTruncationLength(text, maxLength);
6024             if (allowInsertLength == 0) {
6025                 TAG_LOGD(AceLogTag::ACE_RICH_TEXT,
6026                     "ProcessTextTruncation: No space to insert text. maxLength=%{public}d",
6027                     maxLength_.value_or(INT_MAX));
6028                 return false;
6029             }
6030             text = text.substr(0, allowInsertLength);
6031             return true;
6032         }
6033         return true;
6034     }
6035 
6036     if (previewTextRecord_.needReplaceText) {
6037         text = text.substr(0, maxLength_.value_or(INT_MAX) + selectLength);
6038         return true;
6039     }
6040     if (!textSelector_.SelectNothing()) {
6041         auto allowInsertLength = CalculateTruncationLength(text, 0);
6042         if (allowInsertLength == 0) {
6043             TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "ProcessTextTruncation: No space to insert text. maxLength=%{public}d",
6044                 maxLength_.value_or(INT_MAX));
6045             return false;
6046         }
6047         text = text.substr(0, allowInsertLength);
6048         return true;
6049     }
6050     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "ProcessTextTruncation: maxLength=%{public}d", maxLength_.value_or(INT_MAX));
6051     IF_TRUE(IsPreviewTextInputting(), FinishTextPreviewInner());
6052     return false;
6053 }
6054 
ProcessInsertValueMore(const std::u16string & text,OperationRecord record,OperationType operationType,RichEditorChangeValue changeValue,PreviewTextRecord preRecord,bool shouldCommitInput)6055 void RichEditorPattern::ProcessInsertValueMore(const std::u16string& text, OperationRecord record,
6056     OperationType operationType, RichEditorChangeValue changeValue, PreviewTextRecord preRecord, bool shouldCommitInput)
6057 {
6058     if (preRecord.needReplacePreviewText && !previewTextRecord_.needReplacePreviewText) {
6059         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "previewText finished when ProcessInsertValue");
6060         NotifyImfFinishTextPreview();
6061         return;
6062     }
6063     ClearRedoOperationRecords();
6064 #if defined(IOS_PLATFORM)
6065     if (compose_.IsValid() && (record.addText.value_or(u"").length() > 0 || unmarkText_)) {
6066         DeleteByRange(&record, compose_.GetStart(), compose_.GetEnd());
6067     }
6068 #endif
6069     InsertValueOperation(text, &record, operationType, shouldCommitInput);
6070     record.afterCaretPosition = caretPosition_;
6071     if (isDragSponsor_) {
6072         record.deleteCaretPosition = dragRange_.first;
6073     }
6074     IF_TRUE(shouldCommitInput, AddInsertOperationRecord(record));
6075     AfterContentChange(changeValue);
6076 }
6077 
6078 // operationType: when type is IME, it controls whether to perform ime callbacks
6079 // shouldCommitInput: true means real input; false means preview input
ProcessInsertValue(const std::u16string & insertValue,OperationType operationType,bool shouldCommitInput)6080 void RichEditorPattern::ProcessInsertValue(const std::u16string& insertValue, OperationType operationType,
6081     bool shouldCommitInput)
6082 {
6083     CONTENT_MODIFY_LOCK(this);
6084     auto text = insertValue;
6085     if (!ProcessTextTruncationOperation(text, shouldCommitInput)) {
6086         return;
6087     }
6088     bool isIME = IsIMEOperation(operationType);
6089     TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
6090         "insertLen=%{public}zu, isIME=%{public}d, shouldCommitInput=%{public}d, isSpanString=%{public}d",
6091         insertValue.length(), isIME, shouldCommitInput, isSpanStringMode_);
6092     SEC_TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "insertValue=%{public}s",
6093         StringUtils::RestoreEscape(UtfUtils::Str16ToStr8(insertValue)).c_str());
6094 
6095     if (isIME && shouldCommitInput && (!isEditing_ || IsDragging()) && operationType != OperationType::FINISH_PREVIEW) {
6096         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "NOT allow input, isEditing=%{public}d, isDragging=%{public}d",
6097             isEditing_, IsDragging());
6098         return;
6099     }
6100     if (isSpanStringMode_) {
6101         InsertValueInStyledString(text, shouldCommitInput);
6102         return;
6103     }
6104     OperationRecord record;
6105     record.beforeCaretPosition = textSelector_.IsValid() ? textSelector_.GetTextStart() : caretPosition_ + moveLength_;
6106     record.addText = text;
6107 
6108     RichEditorChangeValue changeValue(OPERATION_REASON_MAP.find(operationType)->second);
6109     PreviewTextRecord preRecord = previewTextRecord_;
6110     bool allowContentChange = BeforeChangeText(changeValue, record, RecordType::INSERT);
6111     if (shouldCommitInput && previewTextRecord_.IsValid()) {
6112         FinishTextPreviewInner();
6113         record.beforeCaretPosition = caretPosition_;
6114     }
6115     bool allowImeInput = isIME ? BeforeIMEInsertValue(text) : true;
6116     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "allowContentChange=%{public}d, allowImeInput=%{public}d, needReplacePreviewText=%{public}d",
6117         allowContentChange, allowImeInput, previewTextRecord_.needReplacePreviewText);
6118     bool allowPreviewText = previewTextRecord_.needReplacePreviewText;
6119     bool isAllowInsert = (allowContentChange && allowImeInput) || allowPreviewText;
6120     if (!isAllowInsert) {
6121         previewInputRecord_.Reset();
6122         undoManager_->ClearPreviewInputRecord();
6123 #if defined(IOS_PLATFORM)
6124         if (compose_.IsValid() && (record.addText.value_or(u"").length() > 0 || unmarkText_)) {
6125             DeleteByRange(&record, compose_.GetStart(), compose_.GetEnd());
6126         }
6127 #endif
6128         return;
6129     }
6130     ProcessInsertValueMore(text, record, operationType, changeValue, preRecord, shouldCommitInput);
6131 }
6132 
DeleteSelectionOrPreviewText(OperationRecord * const record,UndoRedoRecord & styledRecord,bool shouldCommitInput)6133 void RichEditorPattern::DeleteSelectionOrPreviewText(
6134     OperationRecord* const record, UndoRedoRecord& styledRecord, bool shouldCommitInput)
6135 {
6136     bool isSelector = textSelector_.IsValid();
6137     auto rangeStart = isSelector ? TextRange{ textSelector_.GetTextStart(), textSelector_.GetTextEnd() }
6138         : TextRange{ caretPosition_, caretPosition_};
6139     if (shouldCommitInput) {
6140         undoManager_->UpdateRecordBeforeChange(rangeStart.start, rangeStart.GetLength(), styledRecord);
6141     } else if (isSelector) {
6142         undoManager_->RecordPreviewInputtingStart(rangeStart.start, rangeStart.GetLength());
6143     }
6144     if (isSelector) {
6145         DeleteByRange(record, textSelector_.GetTextStart(), textSelector_.GetTextEnd());
6146         if (!shouldCommitInput && record) {
6147             previewInputRecord_.deleteText = record->deleteText;
6148             previewInputRecord_.beforeCaretPosition = rangeStart.start;
6149         }
6150         ResetSelection();
6151     } else if (previewTextRecord_.needReplacePreviewText || previewTextRecord_.needReplaceText) {
6152         DeleteByRange(record, previewTextRecord_.replacedRange.start, previewTextRecord_.replacedRange.end);
6153     }
6154 }
6155 
InsertValueOperation(const std::u16string & insertValue,OperationRecord * const record,OperationType operationType,bool shouldCommitInput)6156 void RichEditorPattern::InsertValueOperation(const std::u16string& insertValue, OperationRecord* const record,
6157     OperationType operationType, bool shouldCommitInput)
6158 {
6159     UndoRedoRecord styledRecord;
6160     DeleteSelectionOrPreviewText(record, styledRecord, shouldCommitInput);
6161     bool isSingleHandleMoving = selectOverlay_->IsSingleHandleMoving();
6162     CloseSelectOverlay();
6163     TextInsertValueInfo info;
6164     CalcInsertValueObj(info);
6165     IF_TRUE((!caretVisible_ || isSingleHandleMoving) && HasFocus(), StartTwinkling());
6166     auto host = GetContentHost();
6167     CHECK_NULL_VOID(host);
6168     bool isIME = IsIMEOperation(operationType);
6169     RefPtr<SpanNode> spanNode = DynamicCast<SpanNode>(host->GetChildAtIndex(info.GetSpanIndex()));
6170     RefPtr<SpanNode> spanNodeBefore = DynamicCast<SpanNode>(host->GetChildAtIndex(info.GetSpanIndex() - 1));
6171     RefPtr<SpanNode> targetSpanNode = spanNode;
6172     bool needCreateNewSpan = host->GetChildren().empty();
6173     if (info.GetOffsetInSpan() == 0) {
6174         bool spanNodeBeforeCanInsert = spanNodeBefore && spanNodeBefore->GetTag() == V2::SPAN_ETS_TAG;
6175         bool spanNodeCanInsert = spanNode && spanNode->GetTag() == V2::SPAN_ETS_TAG;
6176         bool insertToBeforeNode = spanNodeBeforeCanInsert && !spanNodeCanInsert;
6177         insertToBeforeNode |= spanNodeBeforeCanInsert && spanNodeCanInsert && !IsLineSeparatorInLast(spanNodeBefore);
6178         if (insertToBeforeNode) {
6179             auto spanItem = spanNodeBefore->GetSpanItem();
6180             info.SetSpanIndex(info.GetSpanIndex() - 1);
6181             info.SetOffsetInSpan(spanItem->position - spanItem->rangeStart);
6182             targetSpanNode = spanNodeBefore;
6183         }
6184         needCreateNewSpan |= !spanNodeBeforeCanInsert && !spanNodeCanInsert;
6185     }
6186     auto insertLength = static_cast<int32_t>(insertValue.length());
6187     bool isCreate = true;
6188     if (needCreateNewSpan) {
6189         CreateTextSpanNode(targetSpanNode, info, insertValue);
6190     } else if (typingStyle_.has_value() && !HasSameTypingStyle(targetSpanNode)) {
6191         InsertDiffStyleValueInSpan(targetSpanNode, info, insertValue);
6192     } else {
6193         InsertValueToSpanNode(targetSpanNode, insertValue, info);
6194         isCreate = false;
6195     }
6196     if (shouldCommitInput) {
6197         undoManager_->UpdateRecordAfterChange(styledRecord.rangeBefore.start, insertLength, styledRecord);
6198         undoManager_->RecordInsertOperation(styledRecord);
6199     }
6200     AfterInsertValue(targetSpanNode, insertLength, isCreate, isIME);
6201 }
6202 
CreateTextStyleByTypingStyle()6203 TextStyle RichEditorPattern::CreateTextStyleByTypingStyle()
6204 {
6205     auto theme = GetTheme<RichEditorTheme>();
6206     auto ret = theme ? theme->GetTextStyle() : TextStyle();
6207     CHECK_NULL_RETURN(typingStyle_.has_value() && typingTextStyle_.has_value(), ret);
6208     const auto& updateSpanStyle = typingStyle_.value();
6209     const auto& textStyle = typingTextStyle_.value();
6210     IF_TRUE(updateSpanStyle.updateFontFeature, ret.SetFontFeatures(textStyle.GetFontFeatures()));
6211     IF_TRUE(updateSpanStyle.updateTextColor, ret.SetTextColor(textStyle.GetTextColor()));
6212     IF_TRUE(updateSpanStyle.updateLineHeight, ret.SetLineHeight(textStyle.GetLineHeight()));
6213     IF_TRUE(updateSpanStyle.updateLetterSpacing, ret.SetLetterSpacing(textStyle.GetLetterSpacing()));
6214     IF_TRUE(updateSpanStyle.updateFontSize, ret.SetFontSize(textStyle.GetFontSize()));
6215     IF_TRUE(updateSpanStyle.updateItalicFontStyle, ret.SetFontStyle(textStyle.GetFontStyle()));
6216     IF_TRUE(updateSpanStyle.updateFontWeight, ret.SetFontWeight(textStyle.GetFontWeight()));
6217     IF_TRUE(updateSpanStyle.updateFontFamily, ret.SetFontFamilies(textStyle.GetFontFamilies()));
6218     IF_TRUE(updateSpanStyle.updateTextShadows, ret.SetTextShadows(textStyle.GetTextShadows()));
6219     IF_TRUE(updateSpanStyle.updateHalfLeading, ret.SetHalfLeading(textStyle.GetHalfLeading()));
6220     IF_TRUE(updateSpanStyle.updateTextDecoration, ret.SetTextDecoration(textStyle.GetTextDecoration()));
6221     IF_TRUE(updateSpanStyle.updateTextDecorationColor, ret.SetTextDecorationColor(textStyle.GetTextDecorationColor()));
6222     IF_TRUE(updateSpanStyle.updateTextDecorationStyle, ret.SetTextDecorationStyle(textStyle.GetTextDecorationStyle()));
6223     IF_TRUE(updateSpanStyle.updateLineThicknessScale, ret.SetLineThicknessScale(textStyle.GetLineThicknessScale()));
6224     IF_TRUE(updateSpanStyle.updateTextBackgroundStyle, ret.SetTextBackgroundStyle(textStyle.GetTextBackgroundStyle()));
6225     return ret;
6226 }
6227 
InsertDiffStyleValueInSpan(RefPtr<SpanNode> & spanNode,const TextInsertValueInfo & info,const std::u16string & insertValue)6228 void RichEditorPattern::InsertDiffStyleValueInSpan(
6229     RefPtr<SpanNode>& spanNode, const TextInsertValueInfo& info, const std::u16string& insertValue)
6230 {
6231     auto host = GetContentHost();
6232     CHECK_NULL_VOID(host);
6233     TextSpanOptions options;
6234     options.value = insertValue;
6235     options.offset = caretPosition_;
6236     options.style = CreateTextStyleByTypingStyle();
6237     options.useThemeFontColor = typingStyle_->useThemeFontColor;
6238     options.useThemeDecorationColor = typingStyle_->useThemeDecorationColor;
6239     options.optionSource = OptionSource::IME_INSERT;
6240     bool useTypingParaStyle = styleManager_->UseTypingParaStyle(spans_, caretPosition_);
6241     IF_TRUE(useTypingParaStyle, options.paraStyle = styleManager_->GetTypingParagraphStyle());
6242     auto newSpanIndex = AddTextSpanOperation(options, false, -1,  true, false);
6243     auto newSpanNode = DynamicCast<SpanNode>(host->GetChildAtIndex(newSpanIndex));
6244     if (!useTypingParaStyle || !options.paraStyle.has_value()) {
6245         CopyTextSpanLineStyle(spanNode, newSpanNode, true);
6246     }
6247     spanNode = newSpanNode;
6248 }
6249 
IsLineSeparatorInLast(RefPtr<SpanNode> & spanNode)6250 bool RichEditorPattern::IsLineSeparatorInLast(RefPtr<SpanNode>& spanNode)
6251 {
6252     std::u16string content = spanNode->GetSpanItem()->content;
6253     return !content.empty() && content.back() == u'\n';
6254 }
6255 
InsertValueToSpanNode(RefPtr<SpanNode> & spanNode,const std::u16string & insertValue,const TextInsertValueInfo & info)6256 void RichEditorPattern::InsertValueToSpanNode(
6257     RefPtr<SpanNode>& spanNode, const std::u16string& insertValue, const TextInsertValueInfo& info)
6258 {
6259     auto spanItem = spanNode->GetSpanItem();
6260     CHECK_NULL_VOID(spanItem);
6261     auto textTemp = spanItem->content;
6262     auto textTempSize = static_cast<int32_t>(textTemp.size());
6263     if (textTempSize < info.GetOffsetInSpan() || info.GetOffsetInSpan() < 0) {
6264         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "InsertValueToSpanNode error, spanSize=%{public}d, offsetInSpan=%{public}d",
6265             textTempSize, info.GetOffsetInSpan());
6266         RichEditorInfo errorInfo { RichEditorErrorType::INSERT_VALUE, textTempSize,
6267             static_cast<int32_t>(insertValue.length()), info.GetOffsetInSpan() };
6268         RichEditorErrorReport(errorInfo);
6269         return;
6270     }
6271     bool needTypingParaStyle = styleManager_->NeedTypingParagraphStyle(spans_, caretPosition_);
6272     textTemp.insert(info.GetOffsetInSpan(), insertValue);
6273     spanNode->UpdateContent(textTemp);
6274     UpdateSpanPosition();
6275     SpanNodeFission(spanNode);
6276     if (needTypingParaStyle) {
6277         auto spanIter = spans_.begin();
6278         auto spanIndex = info.GetSpanIndex();
6279         std::advance(spanIter, spanIndex + 1);
6280         while (spanIter != spans_.end()) {
6281             styleManager_->UpdateStyleByTypingParagraphStyle(*(spanIter));
6282             ++spanIter;
6283         }
6284     }
6285 }
6286 
CreateTextSpanNode(RefPtr<SpanNode> & spanNode,const TextInsertValueInfo & info,const std::u16string & insertValue)6287 void RichEditorPattern::CreateTextSpanNode(
6288     RefPtr<SpanNode>& spanNode, const TextInsertValueInfo& info, const std::u16string& insertValue)
6289 {
6290     auto host = GetContentHost();
6291     CHECK_NULL_VOID(host);
6292     spanNode = SpanNode::GetOrCreateSpanNode(ElementRegister::GetInstance()->MakeUniqueId());
6293     spanNode->MountToParent(host, info.GetSpanIndex());
6294     auto spanItem = spanNode->GetSpanItem();
6295     if (typingStyle_.has_value() && typingTextStyle_.has_value()) {
6296         spanItem->useThemeFontColor = typingStyle_->useThemeFontColor;
6297         spanItem->useThemeDecorationColor = typingStyle_->useThemeDecorationColor;
6298         UpdateTextStyle(spanNode, typingStyle_.value(), typingTextStyle_.value());
6299         auto spanItem = spanNode->GetSpanItem();
6300         spanItem->SetTextStyle(typingTextStyle_);
6301     } else {
6302         spanNode->UpdateFontSize(Dimension(DEFAULT_TEXT_SIZE, DimensionUnit::FP));
6303         SetDefaultColor(spanNode);
6304     }
6305     bool needTypingParaStyle = styleManager_->UseTypingParaStyle(spans_, caretPosition_);
6306     bool useTypingParaStyle = needTypingParaStyle && styleManager_->UpdateStyleByTypingParagraphStyle(spanNode);
6307     AddSpanItem(spanItem, info.GetSpanIndex());
6308     spanNode->UpdateContent(insertValue);
6309     UpdateSpanPosition();
6310     SpanNodeFission(spanNode, useTypingParaStyle);
6311 }
6312 
SetDefaultColor(RefPtr<SpanNode> & spanNode)6313 void RichEditorPattern::SetDefaultColor(RefPtr<SpanNode>& spanNode)
6314 {
6315     auto richEditorTheme = GetTheme<RichEditorTheme>();
6316     CHECK_NULL_VOID(richEditorTheme);
6317     Color textColor = richEditorTheme->GetTextStyle().GetTextColor();
6318     spanNode->UpdateTextColorWithoutCheck(textColor);
6319     spanNode->UpdateTextDecorationColorWithoutCheck(textColor);
6320     if (auto& spanItem = spanNode->GetSpanItem(); spanItem && spanItem->urlOnRelease) {
6321         spanNode->UpdateTextColor(GetUrlSpanColor());
6322     }
6323 }
6324 
BeforeIMEInsertValue(const std::u16string & insertValue)6325 bool RichEditorPattern::BeforeIMEInsertValue(const std::u16string& insertValue)
6326 {
6327     auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
6328     CHECK_NULL_RETURN(eventHub, true);
6329     RichEditorInsertValue insertValueInfo;
6330     insertValueInfo.SetInsertOffset(caretPosition_);
6331     if (!previewTextRecord_.newPreviewContent.empty()) {
6332         insertValueInfo.SetPreviewText(previewTextRecord_.newPreviewContent);
6333     } else {
6334         insertValueInfo.SetInsertValue(insertValue);
6335     }
6336     return eventHub->FireAboutToIMEInput(insertValueInfo);
6337 }
6338 
AfterInsertValue(const RefPtr<SpanNode> & spanNode,int32_t insertValueLength,bool isCreate,bool isIME)6339 void RichEditorPattern::AfterInsertValue(
6340     const RefPtr<SpanNode>& spanNode, int32_t insertValueLength, bool isCreate, bool isIME)
6341 {
6342     isTextChange_ = true;
6343     moveDirection_ = MoveDirection::FORWARD;
6344     moveLength_ += insertValueLength;
6345     IF_TRUE(!previewTextRecord_.needUpdateCaret, moveLength_ = 0);
6346     UpdateSpanPosition();
6347     if (isIME || aiWriteAdapter_->GetAIWrite()) {
6348         AfterIMEInsertValue(spanNode, insertValueLength, isCreate);
6349         return;
6350     }
6351     MoveCaretAfterTextChange();
6352 }
6353 
AfterIMEInsertValue(const RefPtr<SpanNode> & spanNode,int32_t insertValueLength,bool isCreate)6354 bool RichEditorPattern::AfterIMEInsertValue(const RefPtr<SpanNode>& spanNode, int32_t insertValueLength, bool isCreate)
6355 {
6356     ACE_SCOPED_TRACE("RichEditorAfterIMEInsertValue");
6357     auto host = GetContentHost();
6358     CHECK_NULL_RETURN(host, false);
6359     auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
6360     CHECK_NULL_RETURN(eventHub, false);
6361 
6362     RichEditorAbstractSpanResult retInfo;
6363     retInfo.SetSpanIndex(host->GetChildIndex(spanNode));
6364     retInfo.SetEraseLength(insertValueLength);
6365     auto spanItem = spanNode->GetSpanItem();
6366     if (!previewTextRecord_.newPreviewContent.empty()) {
6367         retInfo.SetPreviewText(previewTextRecord_.newPreviewContent);
6368     } else {
6369         retInfo.SetValue(spanItem->content);
6370     }
6371     auto contentLength = static_cast<int32_t>(spanItem->content.length());
6372     retInfo.SetSpanRangeStart(spanItem->position - contentLength);
6373     retInfo.SetSpanRangeEnd(spanItem->position);
6374     retInfo.SetOffsetInSpan(caretPosition_ - retInfo.GetSpanRangeStart());
6375     retInfo.SetFontColor(spanNode->GetTextColorValue(Color::BLACK).ColorToString());
6376     retInfo.SetFontSize(spanNode->GetFontSizeValue(Dimension(DEFAULT_TEXT_SIZE, DimensionUnit::FP)).ConvertToVp());
6377     retInfo.SetFontStyle(spanNode->GetItalicFontStyleValue(OHOS::Ace::FontStyle::NORMAL));
6378     retInfo.SetFontWeight(static_cast<int32_t>(spanNode->GetFontWeightValue(FontWeight::NORMAL)));
6379     retInfo.SetTextStyle(GetTextStyleObject(spanNode));
6380     retInfo.SetUrlAddress(spanItem->GetUrlAddress());
6381     std::string fontFamilyValue;
6382     auto fontFamily = spanNode->GetFontFamilyValue({ "HarmonyOS Sans" });
6383     for (const auto& str : fontFamily) {
6384         fontFamilyValue += str;
6385     }
6386     retInfo.SetFontFamily(fontFamilyValue);
6387     retInfo.SetTextDecoration(spanNode->GetTextDecorationFirst());
6388     retInfo.SetTextDecorationStyle(spanNode->GetTextDecorationStyleValue(TextDecorationStyle::SOLID));
6389     retInfo.SetLineThicknessScale(spanNode->GetLineThicknessScaleValue(1.0f));
6390     retInfo.SetFontFeature(spanNode->GetFontFeatureValue(ParseFontFeatureSettings("\"pnum\" 1")));
6391     retInfo.SetColor(spanNode->GetTextDecorationColorValue(Color::BLACK).ColorToString());
6392     TextRange onDidIMEInputRange{ caretPosition_, caretPosition_ + insertValueLength };
6393     MoveCaretAfterTextChange();
6394     eventHub->FireOnIMEInputComplete(retInfo);
6395     OnReportRichEditorEvent("onIMEInputComplete");
6396     eventHub->FireOnDidIMEInput(onDidIMEInputRange);
6397     return true;
6398 }
6399 
DoDeleteActions(int32_t currentPosition,int32_t length,RichEditorDeleteValue & info,bool isIME)6400 bool RichEditorPattern::DoDeleteActions(int32_t currentPosition, int32_t length, RichEditorDeleteValue& info,
6401     bool isIME)
6402 {
6403     auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
6404     CHECK_NULL_RETURN(eventHub, false);
6405     auto allowDelete = eventHub->FireAboutToDelete(info);
6406     info.ResetRichEditorDeleteSpans();
6407     CalcDeleteValueObj(currentPosition, length, info);
6408     bool doDelete = allowDelete || IsPreviewTextInputting();
6409     bool needRecord = isIME && !IsPreviewTextInputting();
6410     if (doDelete) {
6411         bool isSingleHandleMoving = selectOverlay_->IsSingleHandleMoving();
6412         UndoRedoRecord styledRecord;
6413         IF_TRUE(needRecord, undoManager_->UpdateRecordBeforeChange(currentPosition, length, styledRecord));
6414         CloseSelectOverlay();
6415         ResetSelection();
6416         DeleteByDeleteValueInfo(info);
6417         IF_TRUE((!caretVisible_ || isSingleHandleMoving) && HasFocus(), StartTwinkling());
6418         undoManager_->RecordOperationAfterChange(currentPosition, 0, styledRecord);
6419         eventHub->FireOnDeleteComplete();
6420         OnReportRichEditorEvent("OnDeleteComplete");
6421     }
6422     return doDelete;
6423 }
6424 
IsEmojiOnCaretPosition(int32_t & emojiLength,bool isBackward,int32_t length)6425 std::pair<bool, bool> RichEditorPattern::IsEmojiOnCaretPosition(int32_t& emojiLength, bool isBackward, int32_t length)
6426 {
6427     bool isEmojiOnCaretBackward = false;
6428     bool isEmojiOnCaretForward = false;
6429     std::u16string u16;
6430     GetContentBySpans(u16);
6431     auto caretPos = std::clamp(caretPosition_, 0, static_cast<int32_t>(u16.length()));
6432     emojiLength = TextEmojiProcessor::Delete(caretPos, length, u16, isBackward);
6433     if (emojiLength > 0) {
6434         if (isBackward) {
6435             isEmojiOnCaretBackward = true;
6436         } else {
6437             isEmojiOnCaretForward = true;
6438         }
6439     }
6440     return std::make_pair(isEmojiOnCaretBackward, isEmojiOnCaretForward);
6441 }
6442 
HandleOnDelete(bool backward)6443 void RichEditorPattern::HandleOnDelete(bool backward)
6444 {
6445     if (backward) {
6446 #if defined(PREVIEW)
6447         DeleteForward(1, TextChangeReason::INPUT);
6448 #else
6449         DeleteBackward(1, TextChangeReason::INPUT);
6450 #endif
6451     } else {
6452 #if defined(PREVIEW)
6453         DeleteBackward(1, TextChangeReason::INPUT);
6454 #else
6455         DeleteForward(1, TextChangeReason::INPUT);
6456 #endif
6457     }
6458 }
6459 
CalculateDeleteLength(int32_t length,bool isBackward)6460 int32_t RichEditorPattern::CalculateDeleteLength(int32_t length, bool isBackward)
6461 {
6462     // handle selector
6463     if (!textSelector_.SelectNothing()) {
6464         lastCaretPosition_ = caretPosition_;
6465         caretPosition_ = isBackward ? textSelector_.GetTextEnd() : textSelector_.GetTextStart();
6466         return textSelector_.GetTextEnd() - textSelector_.GetTextStart();
6467     }
6468 
6469     // handle symbol, assume caret is not within symbol
6470     auto iter = std::find_if(spans_.begin(), spans_.end(), [index = caretPosition_, isBackward]
6471     (const RefPtr<SpanItem>& spanItem) {
6472         return isBackward
6473         ? (spanItem->rangeStart < index && index <= spanItem->position)
6474         : (spanItem->rangeStart <= index && index < spanItem->position);
6475     });
6476     CHECK_NULL_RETURN(iter == spans_.end() || !(*iter) || (*iter)->unicode == 0, SYMBOL_SPAN_LENGTH);
6477 
6478     // handle emoji
6479     int32_t emojiLength = 0;
6480     auto [isEmojiOnCaretBackward, isEmojiOnCaretForward] = IsEmojiOnCaretPosition(emojiLength, isBackward, length);
6481     if ((isBackward && isEmojiOnCaretBackward) || (!isBackward && isEmojiOnCaretForward)) {
6482         return emojiLength;
6483     }
6484 
6485     return length;
6486 }
6487 
6488 // only called by IME
DeleteBackward(int32_t length)6489 void RichEditorPattern::DeleteBackward(int32_t length)
6490 {
6491     DeleteBackward(length, TextChangeReason::INPUT, true);
6492 }
6493 
DeleteBackward(int32_t oriLength,TextChangeReason reason,bool isByIME)6494 void RichEditorPattern::DeleteBackward(int32_t oriLength, TextChangeReason reason, bool isByIME)
6495 {
6496     int32_t length = isAPI14Plus ? std::clamp(oriLength, 0, caretPosition_) : oriLength;
6497     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "oriLength=%{public}d, length=%{public}d, isDragging=%{public}d",
6498         oriLength, length, IsDragging());
6499     CHECK_NULL_VOID(!IsDragging());
6500     CHECK_NULL_VOID(SetPreviewTextForDelete(oriLength, true, isByIME));
6501     if (isSpanStringMode_) {
6502         DeleteBackwardInStyledString(length);
6503         return;
6504     }
6505     OperationRecord record;
6506     record.beforeCaretPosition = caretPosition_;
6507     RichEditorChangeValue changeValue(reason);
6508     CHECK_NULL_VOID(BeforeChangeText(changeValue, record, RecordType::DEL_BACKWARD, length));
6509     std::u16string deleteText = DeleteBackwardOperation(length);
6510     if (deleteText.length() != 0) {
6511         ClearRedoOperationRecords();
6512         record.deleteText = deleteText;
6513         record.afterCaretPosition = caretPosition_;
6514         AddOperationRecord(record);
6515         AfterContentChange(changeValue);
6516     }
6517 }
6518 
DeleteBackwardOperation(int32_t length)6519 std::u16string RichEditorPattern::DeleteBackwardOperation(int32_t length)
6520 {
6521     length = CalculateDeleteLength(length, true);
6522     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "delete length=%{public}d", length);
6523     std::u16string textContent;
6524     GetContentBySpans(textContent);
6525 
6526     if (static_cast<int32_t>(textContent.length()) != GetTextContentLength()) {
6527         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "textContent length mismatch, %{public}d vs. %{public}d",
6528             static_cast<int32_t>(textContent.length()), GetTextContentLength());
6529     }
6530     auto start = std::clamp(caretPosition_ - length, 0, static_cast<int32_t>(textContent.length()));
6531     std::u16string deleteText =
6532         textContent.substr(static_cast<uint32_t>(start), static_cast<uint32_t>(caretPosition_ - start));
6533     RichEditorDeleteValue info;
6534     info.SetRichEditorDeleteDirection(RichEditorDeleteDirection::BACKWARD);
6535     if (caretPosition_ == 0) {
6536         info.SetLength(0);
6537         DoDeleteActions(0, 0, info);
6538         return deleteText;
6539     }
6540     info.SetOffset(caretPosition_ - length);
6541     info.SetLength(length);
6542     int32_t currentPosition = std::clamp((caretPosition_ - length), 0, static_cast<int32_t>(GetTextContentLength()));
6543     if (!spans_.empty()) {
6544         CalcDeleteValueObj(currentPosition, length, info);
6545         bool doDelete = DoDeleteActions(currentPosition, length, info);
6546         if (!doDelete) {
6547             return u"";
6548         }
6549     }
6550     auto host = GetContentHost();
6551     if (host && host->GetChildren().empty()) {
6552         textForDisplay_.clear();
6553     }
6554     RequestKeyboardToEdit();
6555     return deleteText;
6556 }
6557 
6558 // only called by IME
DeleteForward(int32_t length)6559 void RichEditorPattern::DeleteForward(int32_t length)
6560 {
6561     DeleteForward(length, TextChangeReason::INPUT, true);
6562 }
6563 
DeleteForward(int32_t oriLength,TextChangeReason reason,bool isByIME)6564 void RichEditorPattern::DeleteForward(int32_t oriLength, TextChangeReason reason, bool isByIME)
6565 {
6566     int32_t length = isAPI14Plus ? std::clamp(oriLength, 0, GetTextContentLength() - caretPosition_) : oriLength;
6567     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "oriLength=%{public}d, length=%{public}d, isDragging=%{public}d",
6568         oriLength, length, IsDragging());
6569     CHECK_NULL_VOID(!IsDragging());
6570     CHECK_NULL_VOID(SetPreviewTextForDelete(oriLength, false, isByIME));
6571     if (isSpanStringMode_) {
6572         DeleteForwardInStyledString(length);
6573         return;
6574     }
6575     if (IsPreviewTextInputting()) {
6576         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "do not handle DeleteForward in previewTextInputting");
6577         return;
6578     }
6579     OperationRecord record;
6580     record.beforeCaretPosition = caretPosition_;
6581     RichEditorChangeValue changeValue(reason);
6582     CHECK_NULL_VOID(BeforeChangeText(changeValue, record, RecordType::DEL_FORWARD, length));
6583     std::u16string deleteText = DeleteForwardOperation(length);
6584     if (deleteText.length() != 0) {
6585         ClearRedoOperationRecords();
6586         record.deleteText = deleteText;
6587         record.afterCaretPosition = caretPosition_;
6588         AddOperationRecord(record);
6589         AfterContentChange(changeValue);
6590     }
6591 }
6592 
DeleteForwardOperation(int32_t length,bool isIME)6593 std::u16string RichEditorPattern::DeleteForwardOperation(int32_t length, bool isIME)
6594 {
6595     length = CalculateDeleteLength(length, false);
6596     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "delete length=%{public}d", length);
6597     std::u16string textContent;
6598     GetContentBySpans(textContent);
6599     if (static_cast<int32_t>(textContent.length()) != GetTextContentLength()) {
6600         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "textContent length mismatch, %{public}d vs. %{public}d",
6601             static_cast<int32_t>(textContent.length()), GetTextContentLength());
6602     }
6603     auto end = std::clamp(caretPosition_ + length, 0, static_cast<int32_t>(textContent.length()));
6604     std::u16string deleteText = textContent.substr(
6605         static_cast<uint32_t>(std::clamp(caretPosition_, 0, static_cast<int32_t>(textContent.length()))),
6606         static_cast<uint32_t>(end - caretPosition_));
6607     RichEditorDeleteValue info;
6608     info.SetOffset(caretPosition_);
6609     info.SetRichEditorDeleteDirection(RichEditorDeleteDirection::FORWARD);
6610     int32_t currentPosition = caretPosition_;
6611     if (currentPosition == GetTextContentLength()) {
6612         info.SetLength(0);
6613         DoDeleteActions(currentPosition, 0, info, isIME);
6614         return deleteText;
6615     }
6616     info.SetLength(length);
6617     if (!spans_.empty()) {
6618         CalcDeleteValueObj(currentPosition, length, info);
6619         bool doDelete = DoDeleteActions(currentPosition, length, info, isIME);
6620         if (!doDelete) {
6621             return u"";
6622         }
6623     }
6624     return deleteText;
6625 }
6626 
DeleteContent(int32_t length)6627 void RichEditorPattern::DeleteContent(int32_t length)
6628 {
6629     length = CalculateDeleteLength(length, true);
6630     std::u16string textContent;
6631     GetContentBySpans(textContent);
6632 
6633     auto start = std::clamp(GetTextContentLength() - length, 0, static_cast<int32_t>(textContent.length()));
6634     std::u16string deleteText =
6635         textContent.substr(static_cast<uint32_t>(start), static_cast<uint32_t>(GetTextContentLength() - start));
6636     RichEditorDeleteValue info;
6637     info.SetRichEditorDeleteDirection(RichEditorDeleteDirection::BACKWARD);
6638     if (GetTextContentLength() == 0) {
6639         info.SetLength(0);
6640         DoDeleteActions(0, 0, info);
6641         return;
6642     }
6643     info.SetOffset(GetTextContentLength() - length);
6644     info.SetLength(length);
6645     int32_t currentPosition = std::clamp((GetTextContentLength() - length), 0, static_cast<int32_t>(GetTextContentLength()));
6646     if (!spans_.empty()) {
6647         CalcDeleteValueObj(currentPosition, length, info);
6648         bool doDelete = DoDeleteActions(currentPosition, length, info);
6649         if (!doDelete) {
6650             return;
6651         }
6652     }
6653     auto host = GetContentHost();
6654     if (host && host->GetChildren().empty()) {
6655         textForDisplay_.clear();
6656     }
6657     RequestKeyboardToEdit();
6658 }
6659 
DeleteToMaxLength(std::optional<int32_t> length)6660 void RichEditorPattern::DeleteToMaxLength(std::optional<int32_t> length)
6661 {
6662     int maxLength = length.value_or(INT_MAX);
6663     if (maxLength >= GetTextContentLength() || maxLength < 0) {
6664         return;
6665     }
6666     int32_t textContentLength = GetTextContentLength();
6667     if (isSpanStringMode_) {
6668         DeleteValueInStyledString(maxLength, GetTextContentLength() - maxLength);
6669     } else {
6670         while (textContentLength > maxLength) {
6671             textContentLength -= CalculateDeleteLength(CUSTOM_CONTENT_LENGTH, true);
6672             DeleteContent(CUSTOM_CONTENT_LENGTH);
6673         }
6674     }
6675 }
6676 
OnBackPressed()6677 bool RichEditorPattern::OnBackPressed()
6678 {
6679     auto tmpHost = GetHost();
6680     CHECK_NULL_RETURN(tmpHost, false);
6681     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "RichEditor %{public}d receives back press event, isStopBackPress=%{public}d",
6682         tmpHost->GetId(), isStopBackPress_);
6683     if (SelectOverlayIsOn()) {
6684         CloseSelectOverlay();
6685         textSelector_.Update(textSelector_.destinationOffset);
6686         StartTwinkling();
6687         return isStopBackPress_;
6688     }
6689 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
6690     if (!imeShown_ && !isCustomKeyboardAttached_) {
6691 #else
6692     if (!isCustomKeyboardAttached_) {
6693 #endif
6694         return false;
6695     }
6696     tmpHost->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6697     ResetSelection();
6698     CloseKeyboard(false);
6699     FocusHub::LostFocusToViewRoot();
6700 #if defined(ANDROID_PLATFORM)
6701     return false;
6702 #else
6703     return isStopBackPress_;
6704 #endif
6705 }
6706 
6707 void RichEditorPattern::SetInputMethodStatus(bool keyboardShown)
6708 {
6709 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
6710     imeShown_ = keyboardShown;
6711 #endif
6712 }
6713 
6714 bool RichEditorPattern::BeforeStatusCursorMove(bool isLeft)
6715 {
6716     CHECK_NULL_RETURN(textSelector_.IsValid(), true);
6717     CHECK_NULL_RETURN(!selectOverlay_->IsSingleHandleShow(), true);
6718     SetCaretPosition(isLeft ? textSelector_.GetTextStart() : textSelector_.GetTextEnd());
6719     MoveCaretToContentRect();
6720     StartTwinkling();
6721     CloseSelectOverlay();
6722     ResetSelection();
6723     return false;
6724 }
6725 
6726 bool RichEditorPattern::CursorMoveLeft()
6727 {
6728     CHECK_NULL_RETURN(BeforeStatusCursorMove(true), false);
6729     CloseSelectOverlay();
6730     ResetSelection();
6731     int32_t emojiLength = 0;
6732     int32_t caretPosition = caretPosition_;
6733     constexpr int32_t DELETE_COUNT = 1;
6734     auto [isEmojiOnCaretBackward, isEmojiOnCaretForward] = IsEmojiOnCaretPosition(emojiLength, true, DELETE_COUNT);
6735     if (isEmojiOnCaretBackward) {
6736         caretPosition = std::clamp((caretPosition_ - emojiLength), 0, static_cast<int32_t>(GetTextContentLength()));
6737     } else {
6738         caretPosition = std::clamp((caretPosition_ - 1), 0, static_cast<int32_t>(GetTextContentLength()));
6739     }
6740     AdjustSelectorForSymbol(caretPosition, HandleType::SECOND, SelectorAdjustPolicy::EXCLUDE);
6741     if (caretPosition_ == caretPosition) {
6742         return false;
6743     }
6744     SetCaretPosition(caretPosition);
6745     MoveCaretToContentRect();
6746     StartTwinkling();
6747     auto host = GetHost();
6748     CHECK_NULL_RETURN(host, false);
6749     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6750     return true;
6751 }
6752 
6753 bool RichEditorPattern::CursorMoveRight()
6754 {
6755     CHECK_NULL_RETURN(BeforeStatusCursorMove(false), false);
6756     CloseSelectOverlay();
6757     ResetSelection();
6758     int32_t emojiLength = 0;
6759     int32_t caretPosition = caretPosition_;
6760     constexpr int32_t DELETE_COUNT = 1;
6761     auto [isEmojiOnCaretBackward, isEmojiOnCaretForward] = IsEmojiOnCaretPosition(emojiLength, false, DELETE_COUNT);
6762     if (isEmojiOnCaretForward) {
6763         caretPosition = std::clamp((caretPosition_ + emojiLength), 0, static_cast<int32_t>(GetTextContentLength()));
6764     } else {
6765         caretPosition = std::clamp((caretPosition_ + 1), 0, static_cast<int32_t>(GetTextContentLength()));
6766     }
6767     AdjustSelectorForSymbol(caretPosition, HandleType::SECOND, SelectorAdjustPolicy::INCLUDE);
6768     if (caretPosition_ == caretPosition) {
6769         return false;
6770     }
6771     SetCaretPosition(caretPosition);
6772     MoveCaretToContentRect();
6773     StartTwinkling();
6774     auto host = GetHost();
6775     CHECK_NULL_RETURN(host, false);
6776     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6777     return true;
6778 }
6779 
6780 bool RichEditorPattern::CursorMoveUp()
6781 {
6782     CloseSelectOverlay();
6783     ResetSelection();
6784     float caretHeight = 0.0f;
6785     float leadingMarginOffset = 0.0f;
6786     CaretOffsetInfo caretInfo;
6787     if (static_cast<int32_t>(GetTextContentLength()) > 1) {
6788         caretInfo = GetCaretOffsetInfoByPosition();
6789         int32_t caretPosition = CalcMoveUpPos(leadingMarginOffset);
6790         CHECK_NULL_RETURN(overlayMod_, false);
6791         auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
6792         auto currentCaretOffsetOverlay = overlayMod->GetCaretOffset();
6793         auto caretOffsetWidth = overlayMod->GetCaretWidth();
6794         auto rectLineInfo = CalcLineInfoByPosition();
6795         caretPosition = std::clamp(caretPosition, 0, static_cast<int32_t>(GetTextContentLength()));
6796         if (caretPosition_ == caretPosition) {
6797             caretPosition = 0;
6798         }
6799         // at line middle or line end
6800         bool cursorNotAtLineStart =
6801             NearEqual(currentCaretOffsetOverlay.GetX(), caretInfo.caretOffsetUp.GetX(), caretOffsetWidth);
6802         bool isEnter = NearZero(currentCaretOffsetOverlay.GetX() - richTextRect_.GetX(), rectLineInfo.GetX());
6803         SetCaretPosition(caretPosition);
6804         MoveCaretToContentRect();
6805         if (cursorNotAtLineStart && !isEnter) {
6806             OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight, false, false);
6807             SetLastClickOffset(caretOffset);
6808             caretAffinityPolicy_ = CaretAffinityPolicy::UPSTREAM_FIRST;
6809         }
6810     }
6811     StartTwinkling();
6812     auto host = GetHost();
6813     CHECK_NULL_RETURN(host, false);
6814     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6815     return true;
6816 }
6817 
6818 bool RichEditorPattern::CursorMoveDown()
6819 {
6820     CloseSelectOverlay();
6821     ResetSelection();
6822     if (static_cast<int32_t>(GetTextContentLength()) > 1) {
6823         float caretHeight = 0.0f;
6824         float leadingMarginOffset = 0.0f;
6825         float caretHeightEnd = 0.0f;
6826         CaretOffsetInfo caretInfo;
6827         int32_t caretPositionEnd;
6828         caretInfo = GetCaretOffsetInfoByPosition();
6829         caretPositionEnd = CalcMoveDownPos(leadingMarginOffset);
6830         CHECK_NULL_RETURN(overlayMod_, false);
6831         auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
6832         auto caretOffsetOverlay = overlayMod->GetCaretOffset();
6833         auto caretOffsetWidth = overlayMod->GetCaretWidth();
6834         bool cursorNotAtLineStart =
6835             NearEqual(caretOffsetOverlay.GetX(), caretInfo.caretOffsetUp.GetX(), caretOffsetWidth);
6836         bool isEnter = NearZero(caretInfo.caretOffsetUp.GetX() - richTextRect_.GetX(), leadingMarginOffset);
6837         caretPositionEnd = std::clamp(caretPositionEnd, 0, static_cast<int32_t>(GetTextContentLength()));
6838         auto currentLineInfo = CalcLineInfoByPosition();
6839         if (caretPositionEnd <= caretPosition_) {
6840             OffsetF caretOffsetEnd = CalcCursorOffsetByPosition(GetTextContentLength(), caretHeightEnd);
6841             if (NearEqual(caretOffsetEnd.GetY() - GetTextRect().GetY(), currentLineInfo.GetY(), 0.5f)) {
6842                 caretPositionEnd = GetTextContentLength();
6843             } else {
6844                 caretPositionEnd += 1;
6845             }
6846         }
6847         SetCaretPosition(caretPositionEnd);
6848         if (cursorNotAtLineStart && caretPosition_ != 0 && !isEnter) {
6849             OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight, false, false);
6850             SetLastClickOffset(caretOffset);
6851             caretAffinityPolicy_ = CaretAffinityPolicy::UPSTREAM_FIRST;
6852         }
6853         MoveCaretToContentRect();
6854     }
6855     StartTwinkling();
6856     auto host = GetHost();
6857     CHECK_NULL_RETURN(host, false);
6858     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6859     return true;
6860 }
6861 
6862 void RichEditorPattern::CursorMoveToNextWord(CaretMoveIntent direction)
6863 {
6864     CHECK_NULL_VOID(direction == CaretMoveIntent::LeftWord || direction == CaretMoveIntent::RightWord);
6865     bool isDirectionLeft = direction == CaretMoveIntent::LeftWord;
6866     auto index = caretPosition_;
6867     if (!textSelector_.SelectNothing()) {
6868         index = isDirectionLeft ? textSelector_.GetTextStart() + 1 : textSelector_.GetTextEnd() - 1;
6869     }
6870     auto newPos = isDirectionLeft ? GetLeftWordIndex(index) : GetRightWordIndex(index);
6871     CloseSelectOverlay();
6872     ResetSelection();
6873     SetCaretPosition(newPos);
6874     MoveCaretToContentRect();
6875     IF_TRUE(isEditing_, StartTwinkling());
6876     auto host = GetHost();
6877     CHECK_NULL_VOID(host);
6878     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6879 }
6880 
6881 int32_t RichEditorPattern::GetLeftWordIndex(int32_t index)
6882 {
6883     AdjustIndexSkipSpace(index, MoveDirection::BACKWARD);
6884     int32_t newPos = std::max(0, index - 1);
6885     AdjustSelectorForSymbol(newPos, HandleType::FIRST, SelectorAdjustPolicy::INCLUDE);
6886     AdjustWordSelection(newPos, index);
6887     AdjustSelector(newPos, HandleType::FIRST);
6888     return newPos;
6889 }
6890 
6891 int32_t RichEditorPattern::GetRightWordIndex(int32_t index)
6892 {
6893     int32_t newPos = std::min(index + 1, GetTextContentLength());
6894     AdjustWordSelection(index, newPos);
6895     AdjustSelector(newPos, HandleType::SECOND);
6896     AdjustIndexSkipSpace(newPos, MoveDirection::FORWARD);
6897     return newPos;
6898 }
6899 
6900 bool RichEditorPattern::CursorMoveToParagraphBegin()
6901 {
6902     CloseSelectOverlay();
6903     ResetSelection();
6904     auto newPos = GetParagraphBeginPosition(caretPosition_);
6905     if (newPos == caretPosition_ && caretPosition_ > 0) {
6906         newPos = GetParagraphBeginPosition(caretPosition_ - 1);
6907     }
6908     if (newPos == caretPosition_) {
6909         return false;
6910     }
6911     SetCaretPosition(newPos);
6912     MoveCaretToContentRect();
6913     StartTwinkling();
6914     auto host = GetHost();
6915     CHECK_NULL_RETURN(host, false);
6916     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6917     return true;
6918 }
6919 
6920 bool RichEditorPattern::CursorMoveToParagraphEnd()
6921 {
6922     CloseSelectOverlay();
6923     ResetSelection();
6924     auto newPos = GetParagraphEndPosition(caretPosition_);
6925     if (newPos == caretPosition_ && caretPosition_ < static_cast<int32_t>(GetTextContentLength())) {
6926         newPos = GetParagraphEndPosition(caretPosition_ + 1);
6927     }
6928     if (newPos == caretPosition_) {
6929         return false;
6930     }
6931     SetCaretPosition(newPos);
6932     MoveCaretToContentRect();
6933     StartTwinkling();
6934     auto host = GetHost();
6935     CHECK_NULL_RETURN(host, false);
6936     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6937     return true;
6938 }
6939 
6940 bool RichEditorPattern::CursorMoveHome()
6941 {
6942     CloseSelectOverlay();
6943     ResetSelection();
6944     if (0 == caretPosition_) {
6945         return false;
6946     }
6947     SetCaretPosition(0);
6948     MoveCaretToContentRect();
6949     StartTwinkling();
6950     auto host = GetHost();
6951     CHECK_NULL_RETURN(host, false);
6952     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6953     return true;
6954 }
6955 
6956 bool RichEditorPattern::CursorMoveEnd()
6957 {
6958     int32_t currentPositionIndex = 0;
6959     if (textSelector_.SelectNothing()) {
6960         currentPositionIndex = caretPosition_;
6961     } else {
6962         currentPositionIndex = textSelector_.GetTextEnd();
6963     }
6964     CloseSelectOverlay();
6965     ResetSelection();
6966     auto newPos = GetTextContentLength();
6967     if (newPos == currentPositionIndex) {
6968         StartTwinkling();
6969         return false;
6970     }
6971     SetCaretPosition(newPos);
6972     MoveCaretToContentRect();
6973     StartTwinkling();
6974     auto host = GetHost();
6975     CHECK_NULL_RETURN(host, false);
6976     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6977     return true;
6978 }
6979 
6980 int32_t RichEditorPattern::GetLeftWordPosition(int32_t caretPosition)
6981 {
6982     int32_t offset = 0;
6983     bool jumpSpace = true;
6984     for (auto iter = spans_.rbegin(); iter != spans_.rend(); iter++) {
6985         auto span = *iter;
6986         auto content = span->content;
6987         if (caretPosition <= span->position - static_cast<int32_t>(content.length())) {
6988             continue;
6989         }
6990         int32_t position = span->position;
6991         for (auto iterContent = content.rbegin(); iterContent != content.rend(); iterContent++) {
6992             if (position-- > caretPosition) {
6993                 continue;
6994             }
6995             if (*iterContent != u' ' || span->placeholderIndex >= 0) {
6996                 jumpSpace = false;
6997             }
6998             if (position + 1 == caretPosition) {
6999                 if (!(StringUtils::IsLetterOrNumberForWchar(*iterContent) ||
7000                         (*iterContent == u' ' && span->placeholderIndex < 0))) {
7001                     return std::clamp(caretPosition - 1, 0, static_cast<int32_t>(GetTextContentLength()));
7002                 }
7003             }
7004             if (!jumpSpace) {
7005                 if (!StringUtils::IsLetterOrNumberForWchar(*iterContent)) {
7006                     return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
7007                 }
7008             } else {
7009                 if (*iterContent == u' ' && span->placeholderIndex >= 0) {
7010                     return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
7011                 }
7012             }
7013             offset++;
7014         }
7015     }
7016     return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
7017 }
7018 
7019 int32_t RichEditorPattern::GetRightWordPosition(int32_t caretPosition)
7020 {
7021     int32_t offset = 0;
7022     bool jumpSpace = false;
7023     for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
7024         auto span = *iter;
7025         auto content = span->content;
7026         if (caretPosition > span->position) {
7027             continue;
7028         }
7029         int32_t position = span->position - static_cast<int32_t>(content.length());
7030         for (auto iterContent = content.cbegin(); iterContent != content.cend(); iterContent++) {
7031             if (position++ < caretPosition) {
7032                 continue;
7033             }
7034             if (*iterContent == u' ' && span->placeholderIndex < 0) {
7035                 jumpSpace = true;
7036                 offset++;
7037                 continue;
7038             }
7039             if (position - 1 == caretPosition) {
7040                 if (!StringUtils::IsLetterOrNumberForWchar(*iterContent)) {
7041                     return std::clamp(caretPosition + 1, 0, static_cast<int32_t>(GetTextContentLength()));
7042                 }
7043             }
7044             if (jumpSpace) {
7045                 if (*iterContent != u' ' || span->placeholderIndex >= 0) {
7046                     return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
7047                 }
7048             } else {
7049                 if (!(StringUtils::IsLetterOrNumberForWchar(*iterContent) ||
7050                         (*iterContent == u' ' && span->placeholderIndex < 0))) {
7051                     return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
7052                 }
7053             }
7054             offset++;
7055         }
7056     }
7057     return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
7058 }
7059 
7060 int32_t RichEditorPattern::GetParagraphBeginPosition(int32_t caretPosition)
7061 {
7062     int32_t offset = 0;
7063     for (auto iter = spans_.rbegin(); iter != spans_.rend(); iter++) {
7064         auto span = *iter;
7065         auto content = span->content;
7066         if (caretPosition <= span->position - static_cast<int32_t>(content.length())) {
7067             continue;
7068         }
7069         int32_t position = span->position;
7070         for (auto iterContent = content.rbegin(); iterContent != content.rend(); iterContent++) {
7071             if (position-- > caretPosition) {
7072                 continue;
7073             }
7074             if (*iterContent == u'\n') {
7075                 return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
7076             }
7077             offset++;
7078         }
7079     }
7080     return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
7081 }
7082 
7083 int32_t RichEditorPattern::GetParagraphEndPosition(int32_t caretPosition)
7084 {
7085     int32_t offset = 0;
7086     for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
7087         auto span = *iter;
7088         auto content = span->content;
7089         if (caretPosition > span->position) {
7090             continue;
7091         }
7092         int32_t position = span->position - static_cast<int32_t>(content.length());
7093         for (auto iterContent = content.cbegin(); iterContent != content.cend(); iterContent++) {
7094             if (position++ < caretPosition) {
7095                 continue;
7096             }
7097             if (*iterContent == u'\n') {
7098                 return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
7099             }
7100             offset++;
7101         }
7102     }
7103     return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
7104 }
7105 
7106 void RichEditorPattern::HandleOnSelectAll()
7107 {
7108     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnSelectAll IsPreviewTextInputting:%{public}d", IsPreviewTextInputting());
7109     CHECK_NULL_VOID(!IsPreviewTextInputting());
7110     selectOverlay_->CloseOverlay(true, CloseReason::CLOSE_REASON_SELECT_ALL);
7111     auto host = GetHost();
7112     CHECK_NULL_VOID(host);
7113     textResponseType_.reset();
7114     int32_t newPos = static_cast<int32_t>(GetTextContentLength());
7115     textSelector_.Update(0, newPos);
7116     FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
7117     SetCaretPosition(newPos);
7118     MoveCaretToContentRect();
7119     IF_TRUE(IsSelected(), StopTwinkling());
7120     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
7121 }
7122 
7123 int32_t RichEditorPattern::CaretPositionSelectEmoji(CaretMoveIntent direction)
7124 {
7125     int32_t newPos = caretPosition_;
7126     int32_t emojiLength = 0;
7127     constexpr int32_t DELETE_COUNT = 1;
7128     if (direction == CaretMoveIntent::Left) {
7129         auto [isEmojiOnCaretBackward, isEmojiOnCaretForward] = IsEmojiOnCaretPosition(emojiLength, true, DELETE_COUNT);
7130         if (isEmojiOnCaretBackward) {
7131             newPos = caretPosition_ - emojiLength;
7132         } else {
7133             newPos = caretPosition_ - 1;
7134         }
7135         AdjustSelectorForSymbol(newPos, HandleType::FIRST, SelectorAdjustPolicy::INCLUDE);
7136         return newPos;
7137     }
7138     auto [isEmojiOnCaretBackward, isEmojiOnCaretForward] = IsEmojiOnCaretPosition(emojiLength, false, DELETE_COUNT);
7139     if (direction == CaretMoveIntent::Right) {
7140         if (isEmojiOnCaretForward) {
7141             newPos = caretPosition_ + emojiLength;
7142         } else {
7143             newPos = caretPosition_ + 1;
7144         }
7145         AdjustSelectorForSymbol(newPos, HandleType::SECOND, SelectorAdjustPolicy::INCLUDE);
7146     }
7147     return newPos;
7148 }
7149 
7150 void RichEditorPattern::HandleSelect(CaretMoveIntent direction)
7151 {
7152     if (IsPreviewTextInputting()) {
7153         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleSelect blocked during preview text input");
7154         return;
7155     }
7156     ResetTouchAndMoveCaretState(false);
7157     CloseSelectOverlay();
7158     auto host = GetHost();
7159     CHECK_NULL_VOID(host);
7160     int32_t newPos, fixedPos = caretPosition_;
7161     if (IsSelected()) {
7162         fixedPos = (caretPosition_ == textSelector_.GetTextStart() ? textSelector_.GetTextEnd()
7163                                                                    : textSelector_.GetTextStart());
7164     }
7165     newPos = HandleSelectWrapper(direction, fixedPos);
7166     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleSelect [%{public}d-%{public}d] direction=%{public}d",
7167         fixedPos, newPos, direction);
7168     if (newPos == -1) {
7169         return;
7170     }
7171     newPos = std::clamp(newPos, 0, static_cast<int32_t>(GetTextContentLength()));
7172     if (newPos == caretPosition_) {
7173         return;
7174     }
7175     UpdateSelector(fixedPos, newPos);
7176     FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
7177     SetCaretPosition(newPos);
7178     MoveCaretToContentRect();
7179     if (textSelector_.SelectNothing()) {
7180         StartTwinkling();
7181     } else {
7182         StopTwinkling();
7183     }
7184     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
7185 }
7186 
7187 void RichEditorPattern::ClearOperationRecords()
7188 {
7189     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "ClearOperationRecords");
7190     undoManager_->ClearUndoRedoRecords();
7191     previewInputRecord_.Reset();
7192     ClearRedoOperationRecords();
7193     if (operationRecords_.empty()) {
7194         return;
7195     }
7196     operationRecords_.clear();
7197 }
7198 
7199 void RichEditorPattern::ClearRedoOperationRecords()
7200 {
7201     if (redoOperationRecords_.empty()) {
7202         return;
7203     }
7204     redoOperationRecords_.clear();
7205 }
7206 
7207 void RichEditorPattern::AddInsertOperationRecord(OperationRecord& record)
7208 {
7209     if (previewInputRecord_.deleteText) {
7210         record.deleteText = previewInputRecord_.deleteText;
7211     }
7212     if (previewInputRecord_.beforeCaretPosition != -1) {
7213         record.beforeCaretPosition = previewInputRecord_.beforeCaretPosition;
7214     }
7215     previewInputRecord_.Reset();
7216     AddOperationRecord(record);
7217 }
7218 
7219 void RichEditorPattern::AddOperationRecord(const OperationRecord& record)
7220 {
7221     if (operationRecords_.size() >= RECORD_MAX_LENGTH) {
7222         // case of max length is 0
7223         if (operationRecords_.empty()) {
7224             return;
7225         }
7226         operationRecords_.erase(operationRecords_.begin());
7227     }
7228     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AddOperationRecord record:%{public}s", record.ToString().c_str());
7229     operationRecords_.emplace_back(record);
7230 }
7231 
7232 void RichEditorPattern::UpdateShiftFlag(const KeyEvent& keyEvent)
7233 {
7234     bool hasKeyShift = keyEvent.HasKey(KeyCode::KEY_SHIFT_LEFT) || keyEvent.HasKey(KeyCode::KEY_SHIFT_RIGHT);
7235     auto action = keyEvent.action;
7236     bool isShiftPressed = hasKeyShift && (action == KeyAction::DOWN || action == KeyAction::UP);
7237     if (isShiftPressed != shiftFlag_) {
7238         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "UpdateShiftFlag:%{public}d by action:%{public}d", isShiftPressed, action);
7239         shiftFlag_ = isShiftPressed;
7240     }
7241 }
7242 
7243 bool RichEditorPattern::HandleOnEscape()
7244 {
7245     CloseSelectOverlay();
7246     return false;
7247 }
7248 
7249 void RichEditorPattern::HandleOnUndoAction()
7250 {
7251     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnUndoAction, IsSupportStyledUndo:%{public}d", IsSupportStyledUndo());
7252     if (IsSupportStyledUndo()) {
7253         undoManager_->UndoByRecords();
7254         return;
7255     }
7256     if (operationRecords_.empty()) {
7257         return;
7258     }
7259     auto value = operationRecords_.back();
7260     RichEditorChangeValue changeValue(TextChangeReason::UNDO);
7261     CHECK_NULL_VOID(BeforeChangeText(changeValue, value, RecordType::UNDO));
7262     operationRecords_.pop_back();
7263     if (redoOperationRecords_.size() >= RECORD_MAX_LENGTH && !(redoOperationRecords_.empty())) {
7264         redoOperationRecords_.erase(redoOperationRecords_.begin());
7265     }
7266     redoOperationRecords_.push_back(value);
7267     CloseSelectOverlay();
7268     ResetSelection();
7269     if (value.addText.has_value() && value.deleteCaretPosition != -1) {
7270         UndoDrag(value);
7271         AfterContentChange(changeValue);
7272         return;
7273     }
7274     if (value.addText.has_value() && value.deleteText.has_value()) {
7275         SetCaretPosition(value.afterCaretPosition);
7276         DeleteBackwardOperation(TextEmojiProcessor::GetCharacterNum(value.addText.value_or(u"")));
7277         InsertValueOperation(value.deleteText.value_or(u""));
7278         AfterContentChange(changeValue);
7279         return;
7280     }
7281     if (value.addText.has_value()) {
7282         SetCaretPosition(value.afterCaretPosition);
7283         DeleteBackwardOperation(TextEmojiProcessor::GetCharacterNum(value.addText.value_or(u"")));
7284     }
7285     if (value.deleteText.has_value()) {
7286         SetCaretPosition(value.afterCaretPosition);
7287         InsertValueOperation(value.deleteText.value_or(u""));
7288     }
7289     AfterContentChange(changeValue);
7290 }
7291 
7292 void RichEditorPattern::HandleOnRedoAction()
7293 {
7294     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnRedoAction, IsSupportStyledUndo:%{public}d", IsSupportStyledUndo());
7295     if (IsSupportStyledUndo()) {
7296         undoManager_->RedoByRecords();
7297         return;
7298     }
7299     if (redoOperationRecords_.empty()) {
7300         return;
7301     }
7302     auto value = redoOperationRecords_.back();
7303     RichEditorChangeValue changeValue(TextChangeReason::REDO);
7304     CHECK_NULL_VOID(BeforeChangeText(changeValue, value, RecordType::REDO));
7305     redoOperationRecords_.pop_back();
7306     if (value.addText.has_value() && value.deleteCaretPosition != -1) {
7307         RedoDrag(value);
7308         AfterContentChange(changeValue);
7309         return;
7310     }
7311     if (value.addText.has_value() && value.deleteText.has_value()) {
7312         SetCaretPosition(value.beforeCaretPosition);
7313         DeleteForwardOperation(value.deleteText.value_or(u"").length());
7314         InsertValueOperation(value.addText.value_or(u""));
7315         operationRecords_.push_back(value);
7316         AfterContentChange(changeValue);
7317         return;
7318     }
7319     if (value.deleteText.has_value()) {
7320         SetCaretPosition(value.beforeCaretPosition);
7321         if (value.beforeCaretPosition != value.afterCaretPosition) {
7322             DeleteBackwardOperation(value.deleteText.value_or(u"").length());
7323         } else {
7324             DeleteForwardOperation(value.deleteText.value_or(u"").length());
7325         }
7326     }
7327     if (value.addText.has_value()) {
7328         SetCaretPosition(value.beforeCaretPosition);
7329         InsertValueOperation(value.addText.value_or(u""));
7330     }
7331     operationRecords_.push_back(value);
7332     AfterContentChange(changeValue);
7333 }
7334 
7335 void RichEditorPattern::CalcInsertValueObj(TextInsertValueInfo& info)
7336 {
7337     if (spans_.empty()) {
7338         info.SetSpanIndex(0);
7339         info.SetOffsetInSpan(0);
7340         return;
7341     }
7342     auto it = std::find_if(
7343         spans_.begin(), spans_.end(), [caretPosition = caretPosition_ + moveLength_](const RefPtr<SpanItem>& spanItem) {
7344             if (spanItem->content.empty()) {
7345                 return spanItem->position == caretPosition;
7346             }
7347             return spanItem->rangeStart <= caretPosition && caretPosition < spanItem->position;
7348         });
7349     if (it != spans_.end() && (*it)->unicode != 0 && (*it)->position - caretPosition_ + moveLength_ == 1) {
7350         it++;
7351         moveLength_++;
7352     }
7353     info.SetSpanIndex(std::distance(spans_.begin(), it));
7354     if (it == spans_.end()) {
7355         info.SetOffsetInSpan(0);
7356         return;
7357     }
7358     info.SetOffsetInSpan(
7359         caretPosition_ + moveLength_ - ((*it)->position - (*it)->content.length()));
7360 }
7361 
7362 void RichEditorPattern::CalcDeleteValueObj(int32_t currentPosition, int32_t length, RichEditorDeleteValue& info)
7363 {
7364     auto it =
7365         std::find_if(spans_.begin(), spans_.end(), [caretPosition = currentPosition](const RefPtr<SpanItem>& spanItem) {
7366             return (spanItem->position - static_cast<int32_t>(spanItem->content.length()) <= caretPosition) &&
7367                    (caretPosition < spanItem->position);
7368         });
7369     while (it != spans_.end() && length > 0) {
7370         if ((*it)->placeholderIndex >= 0 || (*it)->unicode != 0) {
7371             RichEditorAbstractSpanResult spanResult;
7372             spanResult.SetSpanIndex(std::distance(spans_.begin(), it));
7373             int32_t eraseLength = 0;
7374             if ((*it)->unicode != 0) {
7375                 eraseLength = DeleteValueSetSymbolSpan(*it, spanResult);
7376             } else if (AceType::InstanceOf<ImageSpanItem>(*it)) {
7377                 eraseLength = DeleteValueSetImageSpan(*it, spanResult);
7378             } else {
7379                 eraseLength = DeleteValueSetBuilderSpan(*it, spanResult);
7380             }
7381             currentPosition += eraseLength;
7382             length -= eraseLength;
7383             info.SetRichEditorDeleteSpans(spanResult);
7384         } else {
7385             RichEditorAbstractSpanResult spanResult;
7386             spanResult.SetSpanIndex(std::distance(spans_.begin(), it));
7387             auto eraseLength = DeleteValueSetTextSpan(*it, currentPosition, length, spanResult);
7388             length -= eraseLength;
7389             currentPosition += eraseLength;
7390             info.SetRichEditorDeleteSpans(spanResult);
7391         }
7392         std::advance(it, 1);
7393     }
7394 }
7395 
7396 RefPtr<SpanNode> RichEditorPattern::GetSpanNodeBySpanItem(const RefPtr<SpanItem> spanItem)
7397 {
7398     RefPtr<SpanNode> spanNode;
7399     auto iter = std::find(spans_.begin(), spans_.end(), spanItem);
7400     if (iter == spans_.end()) {
7401         return spanNode;
7402     }
7403     auto spanIndex = std::distance(spans_.begin(), iter);
7404     auto host = GetContentHost();
7405     CHECK_NULL_RETURN(host, spanNode);
7406     auto it = host->GetChildren().begin();
7407     std::advance(it, spanIndex);
7408     spanNode = AceType::DynamicCast<SpanNode>(*it);
7409     return spanNode;
7410 }
7411 
7412 int32_t RichEditorPattern::DeleteValueSetSymbolSpan(
7413     const RefPtr<SpanItem>& spanItem, RichEditorAbstractSpanResult& spanResult)
7414 {
7415     spanResult.SetSpanType(SpanResultType::SYMBOL);
7416     spanResult.SetSpanRangeEnd(spanItem->position);
7417     spanResult.SetSpanRangeStart(spanItem->position - SYMBOL_SPAN_LENGTH);
7418     spanResult.SetEraseLength(SYMBOL_SPAN_LENGTH);
7419     spanResult.SetValueString(std::to_string(spanItem->unicode));
7420     spanResult.SetValueResource(spanItem->GetResourceObject());
7421     auto spanNode = GetSpanNodeBySpanItem(spanItem);
7422     if (spanNode) {
7423         spanResult.SetSymbolSpanStyle(GetSymbolSpanStyleObject(spanNode));
7424     }
7425     return SYMBOL_SPAN_LENGTH;
7426 }
7427 
7428 int32_t RichEditorPattern::DeleteValueSetImageSpan(
7429     const RefPtr<SpanItem>& spanItem, RichEditorAbstractSpanResult& spanResult)
7430 {
7431     spanResult.SetSpanType(SpanResultType::IMAGE);
7432     spanResult.SetSpanRangeEnd(spanItem->position);
7433     spanResult.SetSpanRangeStart(spanItem->position - 1);
7434     spanResult.SetEraseLength(1);
7435     auto host = GetContentHost();
7436     CHECK_NULL_RETURN(host, IMAGE_SPAN_LENGTH);
7437     auto uiNode = host->GetChildAtIndex(spanResult.GetSpanIndex());
7438     CHECK_NULL_RETURN(uiNode, IMAGE_SPAN_LENGTH);
7439     auto imageNode = AceType::DynamicCast<FrameNode>(uiNode);
7440     CHECK_NULL_RETURN(imageNode, IMAGE_SPAN_LENGTH);
7441     auto imageRenderCtx = imageNode->GetRenderContext();
7442     if (imageRenderCtx->GetBorderRadius()) {
7443         BorderRadiusProperty brp;
7444         auto jsonObject = JsonUtil::Create(true);
7445         auto jsonBorder = JsonUtil::Create(true);
7446         InspectorFilter filter;
7447         imageRenderCtx->GetBorderRadiusValue(brp).ToJsonValue(jsonObject, jsonBorder, filter);
7448         spanResult.SetBorderRadius(jsonObject->GetValue("borderRadius")->IsObject()
7449                                        ? jsonObject->GetValue("borderRadius")->ToString()
7450                                        : jsonObject->GetString("borderRadius"));
7451     }
7452     auto geometryNode = imageNode->GetGeometryNode();
7453     CHECK_NULL_RETURN(geometryNode, IMAGE_SPAN_LENGTH);
7454     auto imageLayoutProperty = DynamicCast<ImageLayoutProperty>(imageNode->GetLayoutProperty());
7455     CHECK_NULL_RETURN(imageLayoutProperty, IMAGE_SPAN_LENGTH);
7456     spanResult.SetSizeWidth(geometryNode->GetMarginFrameSize().Width());
7457     spanResult.SetSizeHeight(geometryNode->GetMarginFrameSize().Height());
7458     if (imageLayoutProperty->GetMarginProperty()) {
7459         spanResult.SetMargin(imageLayoutProperty->GetMarginProperty()->ToString());
7460     }
7461     if (!imageLayoutProperty->GetImageSourceInfo()->GetPixmap()) {
7462         spanResult.SetValueResourceStr(imageLayoutProperty->GetImageSourceInfo()->GetSrc());
7463     } else {
7464         spanResult.SetValuePixelMap(imageLayoutProperty->GetImageSourceInfo()->GetPixmap());
7465     }
7466     if (imageLayoutProperty->HasImageFit()) {
7467         spanResult.SetImageFit(imageLayoutProperty->GetImageFitValue());
7468     }
7469     if (imageLayoutProperty->HasVerticalAlign()) {
7470         spanResult.SetVerticalAlign(imageLayoutProperty->GetVerticalAlignValue());
7471     }
7472     return IMAGE_SPAN_LENGTH;
7473 }
7474 
7475 int32_t RichEditorPattern::DeleteValueSetBuilderSpan(
7476     const RefPtr<SpanItem>& spanItem, RichEditorAbstractSpanResult& spanResult)
7477 {
7478     spanResult.SetSpanType(SpanResultType::IMAGE);
7479     spanResult.SetSpanRangeEnd(spanItem->position);
7480     spanResult.SetSpanRangeStart(spanItem->position - 1);
7481     spanResult.SetEraseLength(1);
7482     auto host = GetContentHost();
7483     CHECK_NULL_RETURN(host, 1);
7484     auto uiNode = host->GetChildAtIndex(spanResult.GetSpanIndex());
7485     CHECK_NULL_RETURN(uiNode, 1);
7486     auto builderNode = AceType::DynamicCast<FrameNode>(uiNode);
7487     CHECK_NULL_RETURN(builderNode, 1);
7488     auto geometryNode = builderNode->GetGeometryNode();
7489     CHECK_NULL_RETURN(geometryNode, 1);
7490     spanResult.SetSizeWidth(geometryNode->GetMarginFrameSize().Width());
7491     spanResult.SetSizeHeight(geometryNode->GetMarginFrameSize().Height());
7492     return 1;
7493 }
7494 
7495 int32_t RichEditorPattern::DeleteValueSetTextSpan(
7496     const RefPtr<SpanItem>& spanItem, int32_t currentPosition, int32_t length, RichEditorAbstractSpanResult& spanResult)
7497 {
7498     spanResult.SetSpanType(SpanResultType::TEXT);
7499     auto contentStartPosition
7500         = spanItem->position - static_cast<int32_t>(spanItem->content.length());
7501     spanResult.SetSpanRangeStart(contentStartPosition);
7502     int32_t eraseLength = 0;
7503     if (spanItem->position - currentPosition >= length) {
7504         eraseLength = length;
7505     } else {
7506         eraseLength = spanItem->position - currentPosition;
7507     }
7508     spanResult.SetSpanRangeEnd(spanItem->position);
7509     if (!previewTextRecord_.previewContent.empty()) {
7510         spanResult.SetPreviewText(previewTextRecord_.previewContent);
7511     } else {
7512         spanResult.SetValue(spanItem->content);
7513     }
7514     spanResult.SetOffsetInSpan(currentPosition - contentStartPosition);
7515     spanResult.SetEraseLength(eraseLength);
7516     spanResult.SetUrlAddress(spanItem->GetUrlAddress());
7517     if (!spanItem->GetTextStyle().has_value()) {
7518         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "SpanItem text style is empty.");
7519         return eraseLength;
7520     }
7521     spanResult.SetFontColor(spanItem->GetTextStyle()->GetTextColor().ColorToString());
7522     spanResult.SetFontSize(spanItem->GetTextStyle()->GetFontSize().ConvertToFp());
7523     spanResult.SetFontStyle(spanItem->GetTextStyle()->GetFontStyle());
7524     spanResult.SetFontWeight((int32_t)(spanItem->GetTextStyle()->GetFontWeight()));
7525     if (!spanItem->GetTextStyle()->GetFontFamilies().empty()) {
7526         spanResult.SetFontFamily(spanItem->GetTextStyle()->GetFontFamilies().at(0));
7527     }
7528     spanResult.SetColor(spanItem->GetTextStyle()->GetTextDecorationColor().ColorToString());
7529     spanResult.SetTextDecoration(spanItem->GetTextStyle()->GetTextDecorationFirst());
7530     spanResult.SetTextDecorationStyle(spanItem->GetTextStyle()->GetTextDecorationStyle());
7531     spanResult.SetLineThicknessScale(spanItem->GetTextStyle()->GetLineThicknessScale());
7532     spanResult.SetFontFeature(spanItem->GetTextStyle()->GetFontFeatures());
7533     auto host = GetContentHost();
7534     CHECK_NULL_RETURN(host, eraseLength);
7535     auto uiNode = host->GetChildAtIndex(spanResult.GetSpanIndex());
7536     CHECK_NULL_RETURN(uiNode, eraseLength);
7537     auto spanNode = DynamicCast<SpanNode>(uiNode);
7538     CHECK_NULL_RETURN(spanNode, eraseLength);
7539     spanResult.SetTextStyle(GetTextStyleObject(spanNode));
7540     return eraseLength;
7541 }
7542 
7543 void RichEditorPattern::DeleteByDeleteValueInfo(const RichEditorDeleteValue& info)
7544 {
7545     auto deleteSpans = info.GetRichEditorDeleteSpans();
7546     if (deleteSpans.empty()) {
7547         return;
7548     }
7549     auto host = GetHost();
7550     CHECK_NULL_VOID(host);
7551     ProcessDeleteNodes(deleteSpans);
7552     UpdateSpanPosition();
7553     SetCaretPosition(info.GetOffset(), false);
7554     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
7555     OnModifyDone();
7556 }
7557 
7558 int32_t RichEditorPattern::ProcessDeleteNodes(std::list<RichEditorAbstractSpanResult>& deleteSpans)
7559 {
7560     auto eraseLength = 0;
7561     auto host = GetContentHost();
7562     CHECK_NULL_RETURN(host, eraseLength);
7563     std::set<int32_t, std::greater<int32_t>> deleteNodes;
7564     for (const auto& it : deleteSpans) {
7565         eraseLength += it.GetEraseLength();
7566         switch (it.GetType()) {
7567             case SpanResultType::TEXT: {
7568                 auto ui_node = host->GetChildAtIndex(it.GetSpanIndex());
7569                 CHECK_NULL_RETURN(ui_node, eraseLength);
7570                 auto spanNode = DynamicCast<SpanNode>(ui_node);
7571                 CHECK_NULL_RETURN(spanNode, eraseLength);
7572                 auto spanItem = spanNode->GetSpanItem();
7573                 CHECK_NULL_RETURN(spanItem, eraseLength);
7574                 auto textTemp = spanItem->content;
7575                 auto textTempSize = static_cast<int32_t>(textTemp.size());
7576                 if (textTempSize < it.OffsetInSpan()) {
7577                     TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "ProcessDeleteNodes failed, "
7578                         "contentLen=%{public}zu, spanItemSize=%{public}d, offsetInSpan=%{public}d",
7579                         textTemp.length(), textTempSize, it.OffsetInSpan());
7580                     RichEditorInfo errorInfo { RichEditorErrorType::DELETE_NODE, textTempSize,
7581                         it.GetEraseLength(), it.OffsetInSpan() };
7582                     RichEditorErrorReport(errorInfo);
7583                     continue;
7584                 }
7585                 textTemp.erase(it.OffsetInSpan(), it.GetEraseLength());
7586                 if (textTemp.size() == 0) {
7587                     deleteNodes.emplace(it.GetSpanIndex());
7588                 }
7589                 spanNode->UpdateContent(textTemp);
7590                 spanItem->position -= it.GetEraseLength();
7591                 break;
7592             }
7593             case SpanResultType::IMAGE:
7594                 deleteNodes.emplace(it.GetSpanIndex());
7595                 placeholderCount_--;
7596                 break;
7597             case SpanResultType::SYMBOL:
7598                 deleteNodes.emplace(it.GetSpanIndex());
7599                 break;
7600             default:
7601                 break;
7602         }
7603     }
7604     RemoveEmptySpan(deleteNodes);
7605     return eraseLength;
7606 }
7607 
7608 void RichEditorPattern::RemoveEmptySpan(std::set<int32_t, std::greater<int32_t>>& deleteSpanIndexs)
7609 {
7610     auto host = GetContentHost();
7611     CHECK_NULL_VOID(host);
7612     for (auto index : deleteSpanIndexs) {
7613         host->RemoveChildAtIndex(index);
7614         auto it = spans_.begin();
7615         std::advance(it, index);
7616         if (it != spans_.end()) {
7617             spans_.erase(it);
7618         }
7619     }
7620 }
7621 
7622 RefPtr<GestureEventHub> RichEditorPattern::GetGestureEventHub() {
7623     auto host = GetHost();
7624     CHECK_NULL_RETURN(host, nullptr);
7625     return host->GetOrCreateGestureEventHub();
7626 }
7627 
7628 bool RichEditorPattern::OnKeyEvent(const KeyEvent& keyEvent)
7629 {
7630     return TextInputClient::HandleKeyEvent(keyEvent);
7631 }
7632 
7633 void RichEditorPattern::HandleSetSelection(int32_t start, int32_t end, bool showHandle)
7634 {
7635     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleSetSelection, range=[%{public}d,%{public}d], showHandle=%{public}d",
7636         start, end, showHandle);
7637     SelectionOptions options;
7638     options.handlePolicy = showHandle ? HandlePolicy::SHOW : HandlePolicy::HIDE;
7639     SetSelection(start, end, options);
7640 }
7641 
7642 void RichEditorPattern::HandleExtendAction(int32_t action)
7643 {
7644     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleExtendAction %{public}d", action);
7645     switch (action) {
7646         case ACTION_SELECT_ALL:
7647             HandleMenuCallbackOnSelectAll(false);
7648             break;
7649         case ACTION_CUT:
7650             HandleOnCut();
7651             break;
7652         case ACTION_COPY:
7653             HandleOnCopy();
7654             break;
7655         case ACTION_PASTE:
7656             HandleOnPaste();
7657             break;
7658         default:
7659             break;
7660     }
7661 }
7662 
7663 void RichEditorPattern::CursorMove(CaretMoveIntent direction)
7664 {
7665     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "direction=%{public}d", direction);
7666     switch (direction) {
7667         case CaretMoveIntent::Left:
7668             CursorMoveLeft();
7669             break;
7670         case CaretMoveIntent::Right:
7671             CursorMoveRight();
7672             break;
7673         case CaretMoveIntent::Up:
7674             CursorMoveUp();
7675             break;
7676         case CaretMoveIntent::Down:
7677             CursorMoveDown();
7678             break;
7679         case CaretMoveIntent::LeftWord:
7680             CursorMoveToNextWord(direction);
7681             break;
7682         case CaretMoveIntent::RightWord:
7683             CursorMoveToNextWord(direction);
7684             break;
7685         case CaretMoveIntent::ParagraghBegin:
7686             CursorMoveToParagraphBegin();
7687             break;
7688         case CaretMoveIntent::ParagraghEnd:
7689             CursorMoveToParagraphEnd();
7690             break;
7691         case CaretMoveIntent::Home:
7692             CursorMoveHome();
7693             break;
7694         case CaretMoveIntent::End:
7695             CursorMoveEnd();
7696             break;
7697         case CaretMoveIntent::LineBegin:
7698             CursorMoveLineBegin();
7699             break;
7700         case CaretMoveIntent::LineEnd:
7701             CursorMoveLineEnd();
7702             break;
7703         default:
7704             LOGW("Unsupported cursor move operation for rich editor");
7705     }
7706 }
7707 
7708 void RichEditorPattern::MoveCaretAfterTextChange()
7709 {
7710     CHECK_NULL_VOID(isTextChange_ && moveLength_ != 0);
7711     isTextChange_ = false;
7712     switch (moveDirection_) {
7713         case MoveDirection::BACKWARD:
7714             SetCaretPosition(std::clamp((caretPosition_ - moveLength_), 0, GetTextContentLength()), false);
7715             break;
7716         case MoveDirection::FORWARD:
7717             SetCaretPosition(std::clamp((caretPosition_ + moveLength_), 0, GetTextContentLength()), false);
7718             break;
7719         default:
7720             break;
7721     }
7722     moveLength_ = 0;
7723 }
7724 
7725 void RichEditorPattern::InitTouchEvent()
7726 {
7727     CHECK_NULL_VOID(!touchListener_);
7728     auto gesture = GetGestureEventHub();
7729     CHECK_NULL_VOID(gesture);
7730     auto touchTask = [weak = WeakClaim(this)](TouchEventInfo& info) {
7731         auto pattern = weak.Upgrade();
7732         CHECK_NULL_VOID(pattern);
7733         pattern->sourceType_ = info.GetSourceDevice();
7734         pattern->HandleTouchEvent(info);
7735     };
7736     touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
7737     gesture->AddTouchEvent(touchListener_);
7738 }
7739 
7740 void RichEditorPattern::InitPanEvent()
7741 {
7742     CHECK_NULL_VOID(!panEvent_);
7743     auto gestureHub = GetGestureEventHub();
7744     CHECK_NULL_VOID(gestureHub);
7745     auto actionStartTask = [](const GestureEvent& info) {};
7746     auto actionUpdateTask = [](const GestureEvent& info) {};
7747     auto actionEndTask = [](const GestureEvent& info) {};
7748     GestureEventNoParameter actionCancelTask;
7749     panEvent_ = MakeRefPtr<PanEvent>(std::move(actionStartTask), std::move(actionUpdateTask),
7750         std::move(actionEndTask), std::move(actionCancelTask));
7751     PanDirection panDirection = { .type = PanDirection::ALL };
7752     PanDistanceMap distanceMap = { { SourceTool::UNKNOWN, DEFAULT_PAN_DISTANCE.ConvertToPx() },
7753         { SourceTool::PEN, DEFAULT_PEN_PAN_DISTANCE.ConvertToPx() } };
7754     gestureHub->AddPanEvent(panEvent_, panDirection, 1, distanceMap);
7755     gestureHub->SetPanEventType(GestureTypeName::PAN_GESTURE);
7756     gestureHub->SetOnGestureJudgeNativeBegin([weak = WeakClaim(this)](const RefPtr<NG::GestureInfo>& gestureInfo,
7757                                                  const std::shared_ptr<BaseGestureEvent>& info) -> GestureJudgeResult {
7758         auto pattern = weak.Upgrade();
7759         CHECK_NULL_RETURN(pattern, GestureJudgeResult::CONTINUE);
7760         auto gestureType = gestureInfo->GetType();
7761         auto inputEventType = gestureInfo->GetInputEventType();
7762         bool isDraggingCaret = (gestureType == GestureTypeName::PAN_GESTURE)
7763             && (inputEventType == InputEventType::TOUCH_SCREEN) && pattern->moveCaretState_.isMoveCaret;
7764         bool isMouseSelecting = (gestureType == GestureTypeName::PAN_GESTURE)
7765             && (inputEventType == InputEventType::MOUSE_BUTTON) && !pattern->blockPress_;
7766         if (isDraggingCaret || isMouseSelecting) {
7767             TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "prevent pan gesture draggingCaret=%{public}d mouseSelecting=%{public}d",
7768                 isDraggingCaret, isMouseSelecting);
7769             return GestureJudgeResult::CONTINUE;
7770         }
7771         CHECK_NULL_RETURN(gestureInfo->GetType() != GestureTypeName::PAN_GESTURE, GestureJudgeResult::REJECT);
7772         return GestureJudgeResult::CONTINUE;
7773     });
7774 }
7775 
7776 void RichEditorPattern::HandleTouchEvent(TouchEventInfo& info)
7777 {
7778     CHECK_NULL_VOID(!selectOverlay_->IsTouchAtHandle(info));
7779     CHECK_NULL_VOID(!info.GetTouches().empty());
7780     HandleUserTouchEvent(info);
7781     auto acceptedTouchInfo = GetAcceptedTouchLocationInfo(info);
7782     CHECK_NULL_VOID(acceptedTouchInfo.has_value());
7783     auto touchInfo = acceptedTouchInfo.value();
7784     auto touchType = touchInfo.GetTouchType();
7785     if (touchType == TouchType::DOWN) {
7786         HandleTouchDown(touchInfo);
7787         if (hasUrlSpan_) {
7788             HandleUrlSpanShowShadow(touchInfo.GetLocalLocation(), touchInfo.GetGlobalLocation(), GetUrlPressColor());
7789         }
7790     } else if (touchType == TouchType::UP) {
7791         IF_TRUE(status_ == Status::FLOATING, status_ = Status::NONE);
7792         HandleTouchUp();
7793         if (hasUrlSpan_) {
7794             HandleUrlSpanForegroundClear();
7795         }
7796     } else if (touchType == TouchType::MOVE) {
7797         HandleTouchMove(touchInfo);
7798     } else if (touchType == TouchType::CANCEL) {
7799         IF_PRESENT(magnifierController_, RemoveMagnifierFrameNode());
7800         HandleTouchCancelAfterLongPress();
7801         ResetTouchAndMoveCaretState();
7802     }
7803 }
7804 
7805 std::optional<TouchLocationInfo> RichEditorPattern::GetAcceptedTouchLocationInfo(const TouchEventInfo& info)
7806 {
7807     const auto& touchInfos = info.GetChangedTouches();
7808     CHECK_NULL_RETURN(!touchInfos.empty(), std::nullopt);
7809     CHECK_NULL_RETURN(isTouchSelecting_ || moveCaretState_.touchFingerId.has_value(), touchInfos.front());
7810     const int32_t touchFingerId = isTouchSelecting_ ? selectingFingerId_ : moveCaretState_.touchFingerId.value();
7811     for (const auto& touchInfo : touchInfos) {
7812         if (touchInfo.GetFingerId() == touchFingerId) {
7813             return touchInfo;
7814         }
7815     }
7816     return std::nullopt;
7817 }
7818 
7819 void RichEditorPattern::HandleUrlSpanForegroundClear()
7820 {
7821     CHECK_NULL_VOID(overlayMod_);
7822     overlayMod_->ClearSelectedForegroundColorAndRects();
7823     MarkDirtySelf();
7824 }
7825 
7826 void RichEditorPattern::HandleTouchDown(const TouchLocationInfo& info)
7827 {
7828     auto sourceTool = info.GetSourceTool();
7829     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Touch down longPressState=[%{public}d, %{public}d], source=%{public}d,"
7830         "fingers=%{public}d", previewLongPress_, editingLongPress_, sourceTool, ++touchedFingerCount_);
7831     globalOffsetOnMoveStart_ = GetPaintRectGlobalOffset();
7832     moveCaretState_.Reset();
7833     ResetTouchSelectState();
7834     CHECK_NULL_VOID(HasFocus() && sourceTool == SourceTool::FINGER);
7835     auto touchDownOffset = info.GetLocalLocation();
7836     moveCaretState_.touchDownOffset = touchDownOffset;
7837     RectF lastCaretRect = GetCaretRect();
7838     if (RepeatClickCaret(touchDownOffset, lastCaretRect)) {
7839         moveCaretState_.isTouchCaret = true;
7840     }
7841 }
7842 
7843 void RichEditorPattern::HandleTouchUp()
7844 {
7845     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleTouchUp, fingers=%{public}d", --touchedFingerCount_);
7846     HandleTouchUpAfterLongPress();
7847     ResetTouchAndMoveCaretState();
7848     ResetTouchSelectState();
7849     if (magnifierController_) {
7850         magnifierController_->RemoveMagnifierFrameNode();
7851     }
7852 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
7853     if (isLongPress_) {
7854         isLongPress_ = false;
7855     }
7856 #endif
7857 }
7858 
7859 void RichEditorPattern::StartFloatingCaretLand()
7860 {
7861     AnimationUtils::StopAnimation(magnifierAnimation_);
7862     CHECK_NULL_VOID(floatingCaretState_.isFloatingCaretVisible);
7863     auto caretOffset = CalculateCaretOffsetAndHeight().first;
7864     auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
7865     IF_PRESENT(overlayMod, StartFloatingCaretLand(caretOffset));
7866 }
7867 
7868 void RichEditorPattern::ResetTouchAndMoveCaretState(bool needAnimation)
7869 {
7870     if (moveCaretState_.isMoveCaret) {
7871         isCursorAlwaysDisplayed_ = false;
7872         IF_TRUE(isEditing_, StartTwinkling());
7873     }
7874     StopAutoScroll();
7875     CheckScrollable();
7876     UpdateScrollBarOffset();
7877     moveCaretState_.Reset();
7878     needAnimation ? StartFloatingCaretLand() : ResetFloatingCaretState();
7879 }
7880 
7881 void RichEditorPattern::ResetTouchSelectState()
7882 {
7883     selectingFingerId_ = -1;
7884     isTouchSelecting_ = false;
7885     previewLongPress_ = false;
7886     editingLongPress_ = false;
7887 }
7888 
7889 void RichEditorPattern::ScheduleFirstClickResetAfterWindowFocus()
7890 {
7891     auto pipeline = GetContext();
7892     CHECK_NULL_VOID(pipeline);
7893     auto taskExecutor = pipeline->GetTaskExecutor();
7894     CHECK_NULL_VOID(taskExecutor);
7895     firstClickResetTask_.Cancel();
7896     firstClickResetTask_.Reset([weak = WeakClaim(this)]() {
7897         auto pattern = weak.Upgrade();
7898         CHECK_NULL_VOID(pattern);
7899         pattern->firstClickAfterWindowFocus_ = false;
7900     });
7901     firstClickAfterWindowFocus_ = true;
7902     taskExecutor->PostDelayedTask(firstClickResetTask_, TaskExecutor::TaskType::UI,
7903         RICH_EDITOR_TWINKLING_INTERVAL_MS, "ResetFirstClickAfterWindowFocus");
7904 }
7905 
7906 void RichEditorPattern::HandleTouchUpAfterLongPress()
7907 {
7908     CHECK_NULL_VOID((editingLongPress_ || previewLongPress_) && !IsAiSelected());
7909     auto selectStart = std::min(textSelector_.GetTextStart(), GetTextContentLength());
7910     auto selectEnd = std::min(textSelector_.GetTextEnd(), GetTextContentLength());
7911     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "after long press textSelector=[%{public}d, %{public}d] isEditing=%{public}d",
7912         selectStart, selectEnd, isEditing_);
7913     textSelector_.Update(selectStart, selectEnd);
7914     FireOnSelect(selectStart, selectEnd);
7915     SetCaretPositionWithAffinity({ selectEnd, TextAffinity::UPSTREAM });
7916     CalculateHandleOffsetAndShowOverlay();
7917     selectOverlay_->ProcessOverlay({ .animation = true });
7918     FireOnSelectionChange(selectStart, selectEnd);
7919     IF_TRUE(IsSingleHandle(), ForceTriggerAvoidOnCaretChange());
7920 }
7921 
7922 void RichEditorPattern::HandleTouchCancelAfterLongPress()
7923 {
7924     CHECK_NULL_VOID(editingLongPress_ || previewLongPress_);
7925     auto selectStart = std::min(textSelector_.GetTextStart(), GetTextContentLength());
7926     auto selectEnd = std::min(textSelector_.GetTextEnd(), GetTextContentLength());
7927     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "touch canceled, textSelector=[%{public}d, %{public}d] isEditing=%{public}d",
7928         selectStart, selectEnd, isEditing_);
7929     textSelector_.Update(selectStart, selectEnd);
7930     SetCaretPositionWithAffinity({ selectEnd, TextAffinity::UPSTREAM });
7931     CalculateHandleOffsetAndShowOverlay();
7932     selectOverlay_->ProcessOverlay({ .menuIsShow = selectOverlay_->IsCurrentMenuVisibile(), .animation = true });
7933     FireOnSelectionChange(selectStart, selectEnd);
7934 }
7935 
7936 void RichEditorPattern::HandleTouchMove(const TouchLocationInfo& info)
7937 {
7938     auto originalLocaloffset = info.GetLocalLocation();
7939     auto offset = AdjustLocalOffsetOnMoveEvent(originalLocaloffset);
7940     if ((previewLongPress_ || editingLongPress_) && !IsAiSelected()) {
7941         if (!isTouchSelecting_ && !IsAiSelected()) {
7942             TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Touch selecting start id= %{public}d", info.GetFingerId());
7943             showSelect_ = true;
7944             isTouchSelecting_ = true;
7945             selectingFingerId_ = info.GetFingerId();
7946         }
7947         UpdateSelectionByTouchMove(offset);
7948         return;
7949     }
7950     CHECK_NULL_VOID(moveCaretState_.isTouchCaret && caretTwinkling_);
7951     if (!moveCaretState_.isMoveCaret) {
7952         auto moveDistance = (offset - moveCaretState_.touchDownOffset).GetDistance();
7953         IF_TRUE(GreatNotEqual(moveDistance, moveCaretState_.minDistance.ConvertToPx()),
7954             OnMoveCaretStart(info.GetFingerId()));
7955     }
7956     UpdateCaretByTouchMove(offset);
7957 }
7958 
7959 void RichEditorPattern::OnMoveCaretStart(int32_t fingerId)
7960 {
7961     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Move caret start id=%{public}d", fingerId);
7962     moveCaretState_.isMoveCaret = true;
7963     moveCaretState_.touchFingerId = fingerId;
7964     scrollable_ = false;
7965     SetScrollEnabled(scrollable_);
7966     UpdateScrollBarOffset();
7967     ShowCaretWithoutTwinkling();
7968 }
7969 
7970 void RichEditorPattern::UpdateCaretByTouchMove(const Offset& offset)
7971 {
7972     CHECK_NULL_VOID(moveCaretState_.isMoveCaret);
7973     auto host = GetHost();
7974     CHECK_NULL_VOID(host);
7975     if (SelectOverlayIsOn()) {
7976         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Close select overlay while dragging caret");
7977         selectOverlay_->CloseOverlay(false, CloseReason::CLOSE_REASON_NORMAL);
7978     }
7979     auto touchOffset = Offset(offset.GetX(), std::max(offset.GetY(), static_cast<double>(contentRect_.GetY())));
7980     Offset textOffset = ConvertTouchOffsetToTextOffset(touchOffset);
7981     auto positionWithAffinity = paragraphs_.GetGlyphPositionAtCoordinate(textOffset);
7982     SetCaretPositionWithAffinity(positionWithAffinity);
7983     SetCaretTouchMoveOffset(offset);
7984     CalcAndRecordLastClickCaretInfo(textOffset);
7985     auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
7986     auto floatingCaretCenter =
7987         Offset(floatingCaretState_.touchMoveOffset->GetX(), caretOffset.GetY() + caretHeight / 2);
7988     SetMagnifierOffsetWithAnimation(floatingCaretCenter);
7989     AutoScrollParam param = { .autoScrollEvent = AutoScrollEvent::CARET, .showScrollbar = true };
7990     AutoScrollByEdgeDetection(param, OffsetF(offset.GetX(), offset.GetY()), EdgeDetectionStrategy::OUT_BOUNDARY);
7991     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
7992 }
7993 
7994 void RichEditorPattern::SetCaretTouchMoveOffset(const Offset& localOffset)
7995 {
7996     double moveDistance = 0.0;
7997     auto positionType = GetPositionTypeFromLine();
7998     auto caretBoundaryRect = GetCaretBoundaryRect();
7999     if (positionType == PositionType::DEFAULT) {
8000         floatingCaretState_.UpdateByTouchMove(localOffset, moveDistance, caretBoundaryRect);
8001         return;
8002     }
8003     auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
8004     bool isCaretAtEmptyParagraph =
8005         positionType == PositionType::PARAGRAPH_START && caretPosition_ == GetParagraphEndPosition(caretPosition_);
8006     if (isCaretAtEmptyParagraph) {
8007         moveDistance = std::abs(localOffset.GetX() - caretOffset.GetX());
8008     } else {
8009         moveDistance = (caretAffinityPolicy_ == CaretAffinityPolicy::DOWNSTREAM_FIRST || caretPosition_ == 0)
8010                         ? caretOffset.GetX() - localOffset.GetX() : localOffset.GetX() - caretOffset.GetX();
8011     }
8012     floatingCaretState_.UpdateByTouchMove(localOffset, moveDistance, caretBoundaryRect);
8013 }
8014 
8015 RectF RichEditorPattern::GetCaretBoundaryRect()
8016 {
8017     auto caretBoundaryRect = contentRect_;
8018     auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
8019     CHECK_NULL_RETURN(overlayModifier, caretBoundaryRect);
8020     auto caretWidth = overlayModifier->GetCaretWidth();
8021     caretBoundaryRect.SetWidth(caretBoundaryRect.Width() - caretWidth);
8022     return caretBoundaryRect;
8023 }
8024 
8025 void RichEditorPattern::SetMagnifierLocalOffset(Offset offset)
8026 {
8027     CHECK_NULL_VOID(magnifierController_);
8028     auto localOffset = OffsetF{ offset.GetX(), offset.GetY() };
8029     auto localOffsetWithTrans = localOffset;
8030     selectOverlay_->GetLocalPointWithTransform(localOffsetWithTrans);
8031     magnifierController_->SetLocalOffset(localOffsetWithTrans, localOffset);
8032 }
8033 
8034 void RichEditorPattern::SetMagnifierOffsetWithAnimation(Offset offset)
8035 {
8036     CHECK_NULL_VOID(magnifierController_);
8037     auto currentLocalOffset = magnifierController_->GetLocalOffset();
8038     auto currentOffset = magnifierController_->GetLocalOffsetWithoutTrans().value_or(currentLocalOffset);
8039     if (NearEqual(currentOffset.GetY(), offset.GetY(), 0.5f) || !magnifierController_->GetShowMagnifier()) {
8040         SetMagnifierLocalOffset(offset);
8041         return;
8042     }
8043     AnimationUtils::StopAnimation(magnifierAnimation_);
8044     AnimationOption option{ MAGNIFIER_ANIMATION_CURVE, MAGNIFIER_ANIMATION_DURATION };
8045     magnifierAnimation_ = AnimationUtils::StartAnimation(option, [weak = WeakClaim(this), offset]() {
8046         auto pattern = weak.Upgrade();
8047         CHECK_NULL_VOID(pattern);
8048         pattern->SetMagnifierLocalOffset(offset);
8049     });
8050 }
8051 
8052 Offset RichEditorPattern::AdjustLocalOffsetOnMoveEvent(const Offset& originalOffset)
8053 {
8054     auto deltaOffset = GetPaintRectGlobalOffset() - globalOffsetOnMoveStart_;
8055     return { originalOffset.GetX() - deltaOffset.GetX(), originalOffset.GetY() - deltaOffset.GetY() };
8056 }
8057 
8058 void RichEditorPattern::StartVibratorByIndexChange(int32_t currentIndex, int32_t preIndex)
8059 {
8060     CHECK_NULL_VOID(isEnableHapticFeedback_ && (currentIndex != preIndex));
8061     VibratorUtils::StartVibraFeedback("slide");
8062 }
8063 
8064 bool RichEditorPattern::IsScrollBarPressed(const MouseInfo& info)
8065 {
8066     auto scrollBar = GetScrollBar();
8067     bool isScrollBarShow = scrollBar && scrollBar->NeedPaint();
8068     CHECK_NULL_RETURN(isScrollBarShow, false);
8069     Point point(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY());
8070     return scrollBar->InBarRectRegion(point);
8071 }
8072 
8073 void RichEditorPattern::HandleMouseLeftButtonMove(const MouseInfo& info)
8074 {
8075     ACE_SCOPED_TRACE("RichEditorHandleMouseLeftButtonMove");
8076     CHECK_NULL_VOID(!IsPreviewTextInputting());
8077     if (blockPress_) {
8078         ACE_SCOPED_TRACE("RichEditorUpdateDragBoxes");
8079         dragBoxes_ = GetTextBoxes();
8080         return;
8081     }
8082     CHECK_NULL_VOID(leftMousePress_);
8083 
8084     auto localOffset = info.GetLocalLocation();
8085     const auto& globalOffset = info.GetGlobalLocation();
8086     auto paintOffset = GetPaintRectGlobalOffset();
8087     if (!selectOverlay_->HasRenderTransform()) {
8088         localOffset = Offset(globalOffset.GetX() - paintOffset.GetX(), globalOffset.GetY() - paintOffset.GetY());
8089     }
8090     AdjustMouseLocalOffset(localOffset);
8091     Offset textOffset = ConvertTouchOffsetToTextOffset(localOffset);
8092     if (dataDetectorAdapter_->pressedByLeftMouse_) {
8093         dataDetectorAdapter_->pressedByLeftMouse_ = false;
8094         MoveCaretAndStartFocus(textOffset);
8095     }
8096 
8097     auto focusHub = GetFocusHub();
8098     CHECK_NULL_VOID(focusHub);
8099     CHECK_NULL_VOID(focusHub->IsCurrentFocus());
8100 
8101     mouseStatus_ = MouseStatus::MOVE;
8102     HandleMouseSelect(localOffset);
8103     auto host = GetHost();
8104     CHECK_NULL_VOID(host);
8105     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
8106 }
8107 
8108 void RichEditorPattern::AdjustMouseLocalOffset(Offset& offset)
8109 {
8110     CHECK_NULL_VOID(GreatNotEqual(offset.GetY(), richTextRect_.Bottom()));
8111     offset.SetX(richTextRect_.Right());
8112 }
8113 
8114 void RichEditorPattern::HandleMouseSelect(const Offset& localOffset)
8115 {
8116     if (!textSelector_.IsValid()) {
8117         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "prevent mouse selecting because selection has been reset");
8118         return;
8119     }
8120     Offset textOffset = ConvertTouchOffsetToTextOffset(localOffset);
8121     auto position = GetTextContentLength() == 0 ? 0 : paragraphs_.GetIndex(textOffset);
8122     UpdateSelector(textSelector_.baseOffset, position);
8123     if (!isFirstMouseSelect_) {
8124         AdjustCursorPosition(position);
8125         SetCaretPosition(position);
8126         AutoScrollParam param = {
8127             .autoScrollEvent = AutoScrollEvent::MOUSE, .showScrollbar = true, .eventOffset = localOffset
8128         };
8129         AutoScrollByEdgeDetection(param, OffsetF(localOffset.GetX(), localOffset.GetY()),
8130             EdgeDetectionStrategy::OUT_BOUNDARY);
8131         showSelect_ = true;
8132     } else {
8133         isFirstMouseSelect_ = false;
8134     }
8135     if (textSelector_.SelectNothing()) {
8136         StartTwinkling();
8137     } else {
8138         StopTwinkling();
8139     }
8140     isMouseSelect_ = true;
8141 }
8142 
8143 void RichEditorPattern::HandleMouseLeftButtonPress(const MouseInfo& info)
8144 {
8145     isMousePressed_ = true;
8146     auto frameNodeRange = GetSpanRangeByLocalOffset(info.GetLocalLocation());
8147     bool frameNodeSelected = textSelector_.ContainsRange(frameNodeRange);
8148     bool pressFrameNode = !frameNodeSelected && InRangeRect(info.GetGlobalLocation(), frameNodeRange);
8149     if (IsPreviewTextInputting() || IsScrollBarPressed(info) || BetweenSelectedPosition(info.GetGlobalLocation()) ||
8150         pressFrameNode) {
8151         blockPress_ = true;
8152         return;
8153     }
8154     auto focusHub = GetFocusHub();
8155     CHECK_NULL_VOID(focusHub);
8156     if (!focusHub->IsFocusable()) {
8157         return;
8158     }
8159     auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
8160     auto localOffset = info.GetLocalLocation();
8161     AdjustMouseLocalOffset(localOffset);
8162     Offset textOffset = { localOffset.GetX() - textPaintOffset.GetX(), localOffset.GetY() - textPaintOffset.GetY() };
8163     int32_t position = (GetTextContentLength() == 0) ? 0 : paragraphs_.GetIndex(textOffset);
8164     if (shiftFlag_) {
8165         HandleShiftSelect(position);
8166     } else {
8167         IF_TRUE(!textSelector_.SelectNothing(), ResetSelection());
8168         textSelector_.Update(position);
8169     }
8170     leftMousePress_ = true;
8171     globalOffsetOnMoveStart_ = GetPaintRectGlobalOffset();
8172     mouseStatus_ = MouseStatus::PRESSED;
8173     blockPress_ = false;
8174     caretUpdateType_ = CaretUpdateType::PRESSED;
8175     dataDetectorAdapter_->pressedByLeftMouse_ = false;
8176     HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY()));
8177     if (dataDetectorAdapter_->pressedByLeftMouse_) {
8178         return;
8179     }
8180     UseHostToUpdateTextFieldManager();
8181     MoveCaretAndStartFocus(textOffset);
8182     CalcCaretInfoByClick(localOffset);
8183 }
8184 
8185 void RichEditorPattern::HandleShiftSelect(int32_t position)
8186 {
8187     CHECK_NULL_VOID(shiftFlag_);
8188     int32_t start = textSelector_.SelectNothing() ? caretPosition_ : textSelector_.baseOffset;
8189     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleShiftSelect [%{public}d-%{public}d]", start, position);
8190     UpdateSelector(start, position);
8191     SetCaretPosition(position);
8192     FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
8193     auto host = GetHost();
8194     CHECK_NULL_VOID(host);
8195     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
8196 }
8197 
8198 void RichEditorPattern::HandleMouseLeftButtonRelease(const MouseInfo& info)
8199 {
8200     blockPress_ = false;
8201     leftMousePress_ = false;
8202     CHECK_NULL_VOID(!IsPreviewTextInputting());
8203     auto oldMouseStatus = mouseStatus_;
8204     mouseStatus_ = MouseStatus::RELEASED;
8205     isMouseSelect_ = false;
8206     isFirstMouseSelect_ = true;
8207     if (!showSelect_) {
8208         showSelect_ = true;
8209         ResetSelection();
8210     }
8211     if (dataDetectorAdapter_->pressedByLeftMouse_ && oldMouseStatus != MouseStatus::MOVE && !IsDragging()) {
8212         dataDetectorAdapter_->ResponseBestMatchItem(dataDetectorAdapter_->clickedAISpan_);
8213         dataDetectorAdapter_->pressedByLeftMouse_ = false;
8214         isMousePressed_ = false;
8215         return;
8216     }
8217 
8218     auto selectStart = std::min(textSelector_.baseOffset, textSelector_.destinationOffset);
8219     auto selectEnd = std::max(textSelector_.baseOffset, textSelector_.destinationOffset);
8220     if (selectStart != selectEnd && isMousePressed_ && oldMouseStatus == MouseStatus::MOVE) {
8221         FireOnSelect(selectStart, selectEnd);
8222     }
8223     StopAutoScroll();
8224     if (textSelector_.IsValid() && !textSelector_.StartEqualToDest() && IsSelectedBindSelectionMenu() &&
8225         oldMouseStatus == MouseStatus::MOVE) {
8226         auto offsetX = static_cast<float>(info.GetGlobalLocation().GetX());
8227         auto offsetY = static_cast<float>(info.GetGlobalLocation().GetY());
8228         selectionMenuOffsetByMouse_ = OffsetF(offsetX, offsetY);
8229         selectionMenuOffsetClick_ = OffsetF(offsetX, offsetY);
8230         ShowSelectOverlay(RectF(), RectF(), false, TextResponseType::SELECTED_BY_MOUSE);
8231     }
8232     isMousePressed_ = false;
8233     RequestKeyboardToEdit();
8234 }
8235 
8236 void RichEditorPattern::HandleMouseLeftButton(const MouseInfo& info)
8237 {
8238     if (info.GetAction() == MouseAction::MOVE) {
8239         HandleMouseLeftButtonMove(info);
8240     } else if (info.GetAction() == MouseAction::PRESS) {
8241         HandleMouseLeftButtonPress(info);
8242     } else if (info.GetAction() == MouseAction::RELEASE) {
8243         HandleMouseLeftButtonRelease(info);
8244     }
8245 }
8246 
8247 void RichEditorPattern::HandleMouseRightButton(const MouseInfo& info)
8248 {
8249     CHECK_NULL_VOID(!IsPreviewTextInputting());
8250     IF_TRUE(IsDragging(), isInterceptMouseRightRelease_ = true);
8251     auto focusHub = GetFocusHub();
8252     CHECK_NULL_VOID(focusHub && focusHub->IsFocusable());
8253     if (info.GetAction() == MouseAction::RELEASE && isInterceptMouseRightRelease_) {
8254         usingMouseRightButton_ = false;
8255         isMousePressed_ = false;
8256         isInterceptMouseRightRelease_ = false;
8257         return;
8258     }
8259     if (info.GetAction() == MouseAction::PRESS) {
8260         isMousePressed_ = true;
8261         usingMouseRightButton_ = true;
8262         CloseSelectOverlay();
8263     } else if (info.GetAction() == MouseAction::RELEASE) {
8264         auto offsetX = static_cast<float>(info.GetGlobalLocation().GetX());
8265         auto offsetY = static_cast<float>(info.GetGlobalLocation().GetY());
8266         selectionMenuOffsetByMouse_ = OffsetF(offsetX, offsetY);
8267         selectionMenuOffsetClick_ = OffsetF(offsetX, offsetY);
8268         selectOverlay_->SetIsSingleHandle(false);
8269         if (textSelector_.IsValid() && BetweenSelection(info.GetGlobalLocation())) {
8270             ShowSelectOverlay(RectF(), RectF(), IsSelectAll(), TextResponseType::RIGHT_CLICK);
8271             isMousePressed_ = false;
8272             usingMouseRightButton_ = false;
8273             return;
8274         }
8275         auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
8276         Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
8277             info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
8278         HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY()));
8279         if (dataDetectorAdapter_->hasClickedAISpan_) {
8280             dataDetectorAdapter_->hasClickedAISpan_ = false;
8281             isMousePressed_ = false;
8282             usingMouseRightButton_ = false;
8283             return;
8284         }
8285         if (textSelector_.IsValid()) {
8286             CloseSelectOverlay();
8287             ResetSelection();
8288         }
8289         MouseRightFocus(info);
8290         ShowSelectOverlay(RectF(), RectF(), IsSelectAll(), TextResponseType::RIGHT_CLICK);
8291         isMousePressed_ = false;
8292         usingMouseRightButton_ = false;
8293     }
8294 }
8295 
8296 std::pair<int32_t, int32_t> RichEditorPattern::GetSpanRangeByLocalOffset(Offset localOffset)
8297 {
8298     Offset textOffset = ConvertTouchOffsetToTextOffset(localOffset);
8299     auto [pos, affinity] = paragraphs_.GetGlyphPositionAtCoordinate(textOffset);
8300     auto spanFilter = [](SpanItemType itemType) {
8301         return itemType == SpanItemType::IMAGE
8302             || itemType == SpanItemType::PLACEHOLDER
8303             || itemType == SpanItemType::CustomSpan; };
8304     auto it = std::find_if(spans_.begin(), spans_.end(),
8305         [pos = static_cast<int32_t>(pos), affinity = affinity, spanFilter](const RefPtr<SpanItem>& spanItem) {
8306             CHECK_NULL_RETURN(spanFilter(spanItem->spanItemType), false);
8307             return pos == (affinity == TextAffinity::UPSTREAM ? spanItem->position : spanItem->rangeStart);
8308         });
8309     bool isMouseOnTarget = it != spans_.end();
8310     CHECK_NULL_RETURN(isMouseOnTarget, std::make_pair(-1, -1));
8311     return affinity == TextAffinity::UPSTREAM ? std::make_pair(pos - 1, pos) : std::make_pair(pos, pos + 1);
8312 }
8313 
8314 void RichEditorPattern::MouseRightFocus(const MouseInfo& info)
8315 {
8316     auto host = GetHost();
8317     CHECK_NULL_VOID(host);
8318     auto focusHub = host->GetOrCreateFocusHub();
8319     CHECK_NULL_VOID(focusHub);
8320 
8321     auto selectRange = GetSpanRangeByLocalOffset(info.GetLocalLocation());
8322     if (InRangeRect(info.GetGlobalLocation(), selectRange)) {
8323         selectedType_ = TextSpanType::IMAGE;
8324         textSelector_.Update(selectRange.first, selectRange.second);
8325         focusHub->RequestFocusImmediately();
8326         SetCaretPositionWithAffinity({ selectRange.second, TextAffinity::UPSTREAM });
8327         FireOnSelect(selectRange.first, selectRange.second);
8328         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
8329         return;
8330     }
8331     if (textSelector_.IsValid()) {
8332         ResetSelection();
8333     }
8334     Offset textOffset = ConvertTouchOffsetToTextOffset(info.GetLocalLocation());
8335     auto position = paragraphs_.GetIndex(textOffset);
8336     SetCaretPosition(position);
8337     focusHub->RequestFocusImmediately();
8338     CalcAndRecordLastClickCaretInfo(textOffset);
8339     auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
8340     selectedType_ = TextSpanType::TEXT;
8341     CHECK_NULL_VOID(overlayMod_);
8342     DynamicCast<RichEditorOverlayModifier>(overlayMod_)->SetCaretOffsetAndHeight(caretOffset, caretHeight);
8343     StartTwinkling();
8344     RequestKeyboardToEdit();
8345 }
8346 
8347 void RichEditorPattern::FireOnSelect(int32_t selectStart, int32_t selectEnd)
8348 {
8349     auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
8350     CHECK_NULL_VOID(eventHub);
8351     auto textSelectInfo = GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT);
8352     if (!textSelectInfo.GetSelection().resultObjects.empty()) {
8353         eventHub->FireOnSelect(&textSelectInfo);
8354     }
8355     UpdateSelectionType(textSelectInfo);
8356 }
8357 
8358 void RichEditorPattern::UpdateSelectionType(const SelectionInfo& textSelectInfo)
8359 {
8360     if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
8361         TextPattern::UpdateSelectionType(GetAdjustedSelectionInfo(textSelectInfo));
8362     } else {
8363         TextPattern::UpdateSelectionType(textSelectInfo);
8364     }
8365 }
8366 
8367 SelectionInfo RichEditorPattern::GetAdjustedSelectionInfo(const SelectionInfo& textSelectInfo)
8368 {
8369     auto selection = textSelectInfo.GetSelection();
8370     auto resultObjects = selection.resultObjects;
8371     std::for_each(resultObjects.begin(), resultObjects.end(), [](ResultObject& object) {
8372         if (object.type == SelectSpanType::TYPEIMAGE && object.valueString == u" " && object.valuePixelMap == nullptr) {
8373             object.type = SelectSpanType::TYPEBUILDERSPAN;
8374         }
8375     });
8376     SelectionInfo adjustedInfo;
8377     adjustedInfo.SetSelectionStart(selection.selection[RichEditorSpanRange::RANGESTART]);
8378     adjustedInfo.SetSelectionEnd(selection.selection[RichEditorSpanRange::RANGEEND]);
8379     adjustedInfo.SetResultObjectList(resultObjects);
8380     return adjustedInfo;
8381 }
8382 
8383 void RichEditorPattern::HandleMouseEvent(const MouseInfo& info)
8384 {
8385     HandleImageHoverEvent(info);
8386     if (selectOverlay_->IsHandleShow() && info.GetAction() == MouseAction::PRESS) {
8387         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Close selectOverlay when handle is showing");
8388         CloseSelectOverlay();
8389     }
8390     auto scrollBar = GetScrollBar();
8391     if (scrollBar && (scrollBar->IsHover() || scrollBar->IsPressed())) {
8392         ChangeMouseStyle(MouseFormat::DEFAULT);
8393         HandleUrlSpanForegroundClear();
8394         return;
8395     }
8396 
8397     if (hasUrlSpan_ && !IsDragging()) {
8398         auto show = HandleUrlSpanShowShadow(info.GetLocalLocation(), info.GetGlobalLocation(), GetUrlHoverColor());
8399         if (show) {
8400             ChangeMouseStyle(MouseFormat::HAND_POINTING);
8401         } else {
8402             ChangeMouseStyle(MouseFormat::TEXT_CURSOR);
8403         }
8404     }
8405 
8406     if (currentMouseStyle_ == MouseFormat::DEFAULT && !IsDragging()) {
8407         ChangeMouseStyle(MouseFormat::TEXT_CURSOR);
8408     }
8409 
8410     caretUpdateType_ = CaretUpdateType::NONE;
8411     if (info.GetButton() == MouseButton::LEFT_BUTTON) {
8412         sourceType_ = info.GetSourceDevice();
8413         HandleMouseLeftButton(info);
8414     } else if (info.GetButton() == MouseButton::RIGHT_BUTTON) {
8415         sourceType_ = info.GetSourceDevice();
8416         HandleMouseRightButton(info);
8417     }
8418 }
8419 
8420 Color RichEditorPattern::GetUrlHoverColor()
8421 {
8422     auto theme = GetTheme<RichEditorTheme>();
8423     CHECK_NULL_RETURN(theme, Color());
8424     return theme->GetUrlHoverColor();
8425 }
8426 
8427 Color RichEditorPattern::GetUrlPressColor()
8428 {
8429     auto theme = GetTheme<RichEditorTheme>();
8430     CHECK_NULL_RETURN(theme, Color());
8431     return theme->GetUrlPressColor();
8432 }
8433 
8434 Color RichEditorPattern::GetUrlSpanColor()
8435 {
8436     auto theme = GetTheme<RichEditorTheme>();
8437     CHECK_NULL_RETURN(theme, Color());
8438     return theme->GetUrlDefaultColor();
8439 }
8440 
8441 void RichEditorPattern::TriggerAvoidOnCaretChange()
8442 {
8443     CHECK_NULL_VOID(HasFocus());
8444     auto pipeline = GetContext();
8445     CHECK_NULL_VOID(pipeline);
8446     auto textFieldManager = DynamicCast<TextFieldManagerNG>(pipeline->GetTextFieldManager());
8447     CHECK_NULL_VOID(textFieldManager);
8448     CHECK_NULL_VOID(pipeline->UsingCaretAvoidMode());
8449     auto safeAreaManager = pipeline->GetSafeAreaManager();
8450     if (!safeAreaManager || NearZero(safeAreaManager->GetKeyboardInset().Length(), 0)) {
8451         return;
8452     }
8453     auto lastCaretPos = GetLastCaretPos();
8454     auto caretPos = textFieldManager->GetFocusedNodeCaretRect().Top() + textFieldManager->GetHeight();
8455     if (lastCaretPos.has_value() && caretPos > lastCaretPos.value() && !isTriggerAvoidOnCaretAvoidMode_) {
8456         return;
8457     }
8458     SetLastCaretPos(caretPos);
8459     textFieldManager->SetHeight(GetCaretRect().Height());
8460     auto taskExecutor = pipeline->GetTaskExecutor();
8461     CHECK_NULL_VOID(taskExecutor);
8462     taskExecutor->PostTask([manager = WeakPtr<TextFieldManagerNG>(textFieldManager)] {
8463         auto textFieldManager = manager.Upgrade();
8464         CHECK_NULL_VOID(textFieldManager);
8465         textFieldManager->TriggerAvoidOnCaretChange();
8466     }, TaskExecutor::TaskType::UI, "ArkUIRichEditorTriggerAvoidOnCaretChange", PriorityType::VIP);
8467 }
8468 
8469 void RichEditorPattern::OnWindowSizeChanged(int32_t width, int32_t height, WindowSizeChangeReason type)
8470 {
8471     selectOverlay_->UpdateMenuOnWindowSizeChanged(type);
8472     CHECK_NULL_VOID(type == WindowSizeChangeReason::ROTATION);
8473     auto host = GetHost();
8474     CHECK_NULL_VOID(host);
8475     auto context = host->GetContextRefPtr();
8476     CHECK_NULL_VOID(context);
8477     auto taskExecutor = context->GetTaskExecutor();
8478     CHECK_NULL_VOID(taskExecutor);
8479     auto textFieldManager = DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
8480     CHECK_NULL_VOID(textFieldManager);
8481     textFieldManager->ResetOptionalClickPosition();
8482     taskExecutor->PostTask(
8483         [weak = WeakClaim(this), manager = WeakPtr<TextFieldManagerNG>(textFieldManager)] {
8484             auto pattern = weak.Upgrade();
8485             CHECK_NULL_VOID(pattern);
8486             pattern->UpdateParentOffsetAndOverlay();
8487             pattern->UpdateModifierCaretOffsetAndHeight();
8488             pattern->UpdateTextFieldManager(Offset(pattern->parentGlobalOffset_.GetX(),
8489                 pattern->parentGlobalOffset_.GetY()), pattern->frameRect_.Height());
8490             pattern->UpdateCaretInfoToController();
8491             if (pattern->HasFocus()) {
8492                 auto textFieldManager = manager.Upgrade();
8493                 CHECK_NULL_VOID(textFieldManager);
8494                 auto container = Container::Current();
8495                 CHECK_NULL_VOID(container);
8496                 auto displayInfo = container->GetDisplayInfo();
8497                 if (displayInfo) {
8498                     auto dmRotation = static_cast<int32_t>(displayInfo->GetRotation());
8499                     textFieldManager->SetFocusFieldOrientation(dmRotation);
8500                 }
8501             }
8502         },
8503         TaskExecutor::TaskType::UI, "ArkUIRichEditorOnWindowSizeChangedRotation", PriorityType::VIP);
8504 }
8505 
8506 void RichEditorPattern::CopySelectionMenuParams(SelectOverlayInfo& selectInfo, TextResponseType responseType)
8507 {
8508     auto selectType = selectedType_.value_or(TextSpanType::NONE);
8509     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "textSpanType=%{public}d, responseType=%{public}d", selectType, responseType);
8510     std::shared_ptr<SelectionMenuParams> menuParams = GetMenuParams(selectType, responseType);
8511     CHECK_NULL_VOID(menuParams);
8512 
8513     // long pressing on the image needs to set the position of the pop-up menu following the long pressing position
8514     if (selectType == TextSpanType::IMAGE && !selectInfo.isUsingMouse) {
8515         selectInfo.menuInfo.menuOffset = OffsetF(selectionMenuOffset_.GetX(), selectionMenuOffset_.GetY());
8516     }
8517 
8518     CopyBindSelectionMenuParams(selectInfo, menuParams);
8519 }
8520 
8521 void RichEditorPattern::ProcessOverlay(const OverlayRequest& request)
8522 {
8523     // this selectOverlay_ and selectOverlay_ in TextPattern are two distinct objects.
8524     selectOverlay_->ProcessOverlay(request);
8525 }
8526 
8527 void RichEditorPattern::ShowSelectOverlay(const RectF& firstHandle, const RectF& secondHandle, bool isCopyAll,
8528     TextResponseType responseType, bool handleReverse)
8529 {
8530     CHECK_NULL_VOID(!IsPreviewTextInputting());
8531     textResponseType_ = responseType;
8532     ProcessOverlay({ .animation = true });
8533 }
8534 
8535 void RichEditorPattern::SetIsEnableSubWindowMenu()
8536 {
8537     selectOverlay_->SetIsHostNodeEnableSubWindowMenu(true);
8538 }
8539 
8540 void RichEditorPattern::CheckEditorTypeChange()
8541 {
8542     CHECK_NULL_VOID(selectOverlayProxy_);
8543     CHECK_NULL_VOID(!selectOverlayProxy_->IsClosed());
8544     if (selectOverlayProxy_->GetSelectOverlayMangerInfo().menuInfo.editorType.value_or(static_cast<int32_t>(
8545             TextSpanType::NONE)) != static_cast<int32_t>(selectedType_.value_or(TextSpanType::NONE))) {
8546         CloseSelectOverlay();
8547     }
8548 }
8549 
8550 void RichEditorPattern::AsyncHandleOnCopyStyledStringHtml(RefPtr<SpanString>& subSpanString)
8551 {
8552     auto pipeline = GetContext();
8553     CHECK_NULL_VOID(pipeline);
8554     auto taskExecutor = pipeline->GetTaskExecutor();
8555     CHECK_NULL_VOID(taskExecutor);
8556     auto multiTypeRecordImpl = AceType::MakeRefPtr<MultiTypeRecordImpl>();
8557     subSpanString->EncodeTlv(multiTypeRecordImpl->GetSpanStringBuffer());
8558     multiTypeRecordImpl->SetPlainText(subSpanString->GetString());
8559     std::list<RefPtr<SpanItem>> copySpans = CopySpansForClipboard(subSpanString->GetSpanItems());
8560     taskExecutor->PostTask(
8561         [copySpans, multiTypeRecordImpl, weak = WeakClaim(this), task = WeakClaim(RawPtr(taskExecutor))]() {
8562             auto richEditor = weak.Upgrade();
8563             CHECK_NULL_VOID(richEditor);
8564             RefPtr<PasteDataMix> pasteData = richEditor->clipboard_->CreatePasteDataMix();
8565             CHECK_NULL_VOID(multiTypeRecordImpl);
8566             std::string htmlStr = HtmlUtils::ToHtml(copySpans);
8567             multiTypeRecordImpl->SetHtmlText(htmlStr);
8568 
8569             auto uiTaskExecutor = task.Upgrade();
8570             CHECK_NULL_VOID(uiTaskExecutor);
8571             uiTaskExecutor->PostTask([weak, pasteData, multiTypeRecordImpl]() {
8572                 auto richEditor = weak.Upgrade();
8573                 CHECK_NULL_VOID(richEditor);
8574                 richEditor->clipboard_->AddMultiTypeRecord(pasteData, multiTypeRecordImpl);
8575                 richEditor->clipboard_->SetData(pasteData, richEditor->copyOption_);
8576             }, TaskExecutor::TaskType::UI, "AsyncHandleOnCopyStyledStringSetClipboardData");
8577         }, TaskExecutor::TaskType::BACKGROUND, "AsyncHandleOnCopyStyledStringHtml");
8578 }
8579 
8580 void RichEditorPattern::OnCopyOperationExt(RefPtr<PasteDataMix>& pasteData)
8581 {
8582     auto subSpanString =
8583         ToStyledString(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
8584     std::vector<uint8_t> tlvData;
8585     subSpanString->EncodeTlv(tlvData);
8586     auto text = subSpanString->GetString();
8587     clipboard_->AddSpanStringRecord(pasteData, tlvData);
8588 }
8589 
8590 void RichEditorPattern::HandleOnCopyStyledString()
8591 {
8592     auto subSpanString = styledString_->GetSubSpanString(textSelector_.GetTextStart(),
8593         textSelector_.GetTextEnd() - textSelector_.GetTextStart());
8594     subSpanString->isFromStyledStringMode = true;
8595 #ifdef PREVIEW
8596     clipboard_->SetData(subSpanString->GetString(), CopyOptions::Distributed);
8597 #else
8598     AsyncHandleOnCopyStyledStringHtml(subSpanString);
8599 #endif
8600 }
8601 
8602 std::list<RefPtr<SpanItem>> RichEditorPattern::CopySpansForClipboard(const std::list<RefPtr<SpanItem>>& spans)
8603 {
8604     std::list<RefPtr<SpanItem>> copySpans;
8605     for (const auto& spanItem : spans) {
8606         // only make normal/image spanItem, because copy or cut only for normal/image spanItem
8607         auto newSpanItem = GetSameSpanItem(spanItem);
8608         CHECK_NULL_CONTINUE(newSpanItem);
8609         newSpanItem->position = spanItem->position;
8610         newSpanItem->rangeStart = spanItem->rangeStart;
8611         newSpanItem->paragraphIndex = spanItem->paragraphIndex;
8612         newSpanItem->content = spanItem->content;
8613         newSpanItem->description = spanItem->description;
8614         copySpans.emplace_back(newSpanItem);
8615     }
8616     return copySpans;
8617 }
8618 
8619 void RichEditorPattern::OnCopyOperation(bool isUsingExternalKeyboard)
8620 {
8621     if (isSpanStringMode_) {
8622         HandleOnCopyStyledString();
8623         return;
8624     }
8625     caretUpdateType_ = CaretUpdateType::NONE;
8626     auto pipeline = GetContext();
8627     CHECK_NULL_VOID(pipeline);
8628     auto taskExecutor = pipeline->GetTaskExecutor();
8629     CHECK_NULL_VOID(taskExecutor);
8630     auto selectStart = textSelector_.GetTextStart();
8631     auto selectEnd = textSelector_.GetTextEnd();
8632     auto textSelectInfo = GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT);
8633     auto copyResultObjects = textSelectInfo.GetSelection().resultObjects;
8634     CHECK_NULL_VOID(!copyResultObjects.empty());
8635     std::list<RefPtr<SpanItem>> copySpans = CopySpansForClipboard(spans_);
8636     ACE_SCOPED_TRACE("RichEditorOnCopyOperation");
8637     taskExecutor->PostTask(
8638         [weak = WeakClaim(this), task = WeakClaim(RawPtr(taskExecutor)), copyResultObjects, copySpans]() {
8639             auto richEditor = weak.Upgrade();
8640             CHECK_NULL_VOID(richEditor);
8641             ACE_SCOPED_TRACE("RichEditorAsyncHandleOnCopy");
8642             RefPtr<PasteDataMix> pasteData = richEditor->clipboard_->CreatePasteDataMix();
8643             for (auto resultObj = copyResultObjects.rbegin(); resultObj != copyResultObjects.rend(); ++resultObj) {
8644                 richEditor->ProcessResultObject(pasteData, *resultObj, copySpans);
8645             }
8646             auto uiTaskExecutor = task.Upgrade();
8647             CHECK_NULL_VOID(uiTaskExecutor);
8648             uiTaskExecutor->PostTask([weak, pasteData]() {
8649                 auto richEditor = weak.Upgrade();
8650                 CHECK_NULL_VOID(richEditor);
8651                 richEditor->clipboard_->SetData(pasteData, richEditor->copyOption_);
8652             }, TaskExecutor::TaskType::UI, "AsyncHandleOnCopyWithoutSpanStringSetClipboardData");
8653         }, TaskExecutor::TaskType::BACKGROUND, "AsyncHandleOnCopyWithoutSpanStringHtml");
8654 }
8655 
8656 void RichEditorPattern::ProcessResultObject(RefPtr<PasteDataMix> pasteData, const ResultObject& result,
8657     const std::list<RefPtr<SpanItem>>& spans)
8658 {
8659     CHECK_NULL_VOID(pasteData);
8660     auto multiTypeRecordImpl = AceType::MakeRefPtr<MultiTypeRecordImpl>();
8661     if (result.type == SelectSpanType::TYPESPAN) {
8662         auto data = UtfUtils::Str16ToStr8(GetSelectedSpanText(result.valueString,
8663             result.offsetInSpan[RichEditorSpanRange::RANGESTART], result.offsetInSpan[RichEditorSpanRange::RANGEEND]));
8664 #ifdef PREVIEW
8665         clipboard_->SetData(data, CopyOptions::Distributed);
8666 #else
8667         multiTypeRecordImpl->SetPlainText(data);
8668         auto subSpanString = GetSpanStringByResultObject(result, spans);
8669         subSpanString->EncodeTlv(multiTypeRecordImpl->GetSpanStringBuffer());
8670         auto htmlStr = HtmlUtils::ToHtml(RawPtr(subSpanString));
8671         multiTypeRecordImpl->SetHtmlText(htmlStr);
8672         clipboard_->AddMultiTypeRecord(pasteData, multiTypeRecordImpl);
8673 #endif
8674         return;
8675     }
8676     if (result.type == SelectSpanType::TYPEIMAGE) {
8677 #ifdef PREVIEW
8678         if (result.valuePixelMap) {
8679             clipboard_->AddPixelMapRecord(pasteData, result.valuePixelMap);
8680         } else {
8681             clipboard_->AddImageRecord(pasteData, UtfUtils::Str16ToStr8(result.valueString));
8682         }
8683 #else
8684         if (result.valuePixelMap) {
8685             multiTypeRecordImpl->SetPixelMap(result.valuePixelMap);
8686         } else {
8687             multiTypeRecordImpl->SetUri(UtfUtils::Str16ToStr8(result.valueString));
8688         }
8689         auto subSpanString = GetSpanStringByResultObject(result, spans);
8690         subSpanString->EncodeTlv(multiTypeRecordImpl->GetSpanStringBuffer());
8691         auto htmlStr = HtmlUtils::ToHtml(RawPtr(subSpanString));
8692         multiTypeRecordImpl->SetHtmlText(htmlStr);
8693         clipboard_->AddMultiTypeRecord(pasteData, multiTypeRecordImpl);
8694 #endif
8695     }
8696 }
8697 
8698 std::pair<int32_t, int32_t> RichEditorPattern::GetSpanRangeByResultObject(const ResultObject& result)
8699 {
8700     auto selectStart = result.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] +
8701         result.offsetInSpan[RichEditorSpanRange::RANGESTART];
8702     auto selectEnd = result.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] +
8703         result.offsetInSpan[RichEditorSpanRange::RANGEEND];
8704     return { selectStart, selectEnd };
8705 }
8706 
8707 RefPtr<SpanString> RichEditorPattern::GetSpanStringByResultObject(const ResultObject& result,
8708     const std::list<RefPtr<SpanItem>>& spans)
8709 {
8710     auto [selectStart, selectEnd] = GetSpanRangeByResultObject(result);
8711     RefPtr<SpanString> spanString = MakeRefPtr<SpanString>(u"");
8712     SetSubSpans(spanString, selectStart, selectEnd, spans);
8713     SetSubMap(spanString);
8714     return spanString;
8715 }
8716 
8717 void RichEditorPattern::HandleOnCopy(bool isUsingExternalKeyboard)
8718 {
8719     CHECK_NULL_VOID(clipboard_);
8720     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "isUsingExternalKeyboard=%{public}d, copyOption=%{public}d",
8721         isUsingExternalKeyboard, copyOption_);
8722     if (copyOption_ == CopyOptions::None) {
8723         return;
8724     }
8725     auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
8726     CHECK_NULL_VOID(eventHub);
8727     TextCommonEvent event;
8728     eventHub->FireOnCopy(event);
8729     IF_TRUE(!event.IsPreventDefault(), OnCopyOperation(isUsingExternalKeyboard));
8730     if (selectOverlay_->IsUsingMouse() || isUsingExternalKeyboard) {
8731         CloseSelectOverlay();
8732     } else {
8733         selectOverlay_->HideMenu();
8734     }
8735 }
8736 
8737 void RichEditorPattern::ResetAfterPaste()
8738 {
8739     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "ResetAfterPaste");
8740     auto pasteStr = GetPasteStr();
8741     SetCaretSpanIndex(-1);
8742     StartTwinkling();
8743     RequestKeyboardToEdit();
8744     CloseSelectOverlay();
8745     InsertValueByPaste(pasteStr);
8746     ClearPasteStr();
8747 }
8748 
8749 void RichEditorPattern::InsertValueByPaste(const std::u16string& pasteStr)
8750 {
8751     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "InsertValueByPaste");
8752     if (isSpanStringMode_) {
8753         InsertValueInStyledString(pasteStr, true, true);
8754         return;
8755     }
8756     InsertValueByOperationType(pasteStr, OperationType::PASTE);
8757 }
8758 
8759 void RichEditorPattern::HandleOnPaste()
8760 {
8761     if (IsPreviewTextInputting()) {
8762         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Paste blocked during preview text input");
8763         suppressAccessibilityEvent_ = true;
8764         return;
8765     }
8766     auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
8767     CHECK_NULL_VOID(eventHub);
8768     TextCommonEvent event;
8769     eventHub->FireOnPaste(event);
8770     OnReportRichEditorEvent("onPasteComplete");
8771     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleOnPaste, preventDefault=%{public}d", event.IsPreventDefault());
8772     if (event.IsPreventDefault()) {
8773         CloseSelectOverlay();
8774         ResetSelection();
8775         StartTwinkling();
8776         RequestKeyboardToEdit();
8777         suppressAccessibilityEvent_ = true;
8778         return;
8779     }
8780     CHECK_NULL_VOID(clipboard_);
8781 #ifdef PREVIEW
8782     auto pasteCallback = [weak = WeakClaim(this)](const std::string& text) {
8783         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "pasteCallback callback in previewer");
8784         auto richEditor = weak.Upgrade();
8785         CHECK_NULL_VOID(richEditor);
8786         richEditor->PasteStr(text);
8787     };
8788     clipboard_->GetData(pasteCallback);
8789 #else
8790     clipboard_->GetSpanStringData(CreatePasteCallback());
8791 #endif
8792 }
8793 
8794 std::function<void(std::vector<std::vector<uint8_t>>&, const std::string&, bool&)>
8795     RichEditorPattern::CreatePasteCallback()
8796 {
8797     auto isSpanStringMode = isSpanStringMode_;
8798     auto pasteCallback = [weak = WeakClaim(this), isSpanStringMode](std::vector<std::vector<uint8_t>>& arrs,
8799                              const std::string& text, bool& isMulitiTypeRecord) {
8800         auto richEditor = weak.Upgrade();
8801         CHECK_NULL_VOID(richEditor);
8802         std::list<RefPtr<SpanString>> spanStrings;
8803         bool isFromStyledString = false;
8804         for (auto&& arr : arrs) {
8805             auto spanString = SpanString::DecodeTlv(arr);
8806             IF_TRUE(spanString->isFromStyledStringMode, isFromStyledString = true);
8807             spanStrings.push_back(spanString);
8808         }
8809         TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
8810             "pasteCallback callback, isMulitiTypeRecord : [%{public}d], isSpanStringMode : [%{public}d], "
8811             "isFromStyledString : [%{public}d]",
8812             isMulitiTypeRecord, isSpanStringMode, isFromStyledString);
8813         if (spanStrings.empty() || (!isSpanStringMode && !isFromStyledString)) {
8814             richEditor->PasteStr(text);
8815             return;
8816         }
8817         richEditor->CloseSelectOverlay();
8818         for (auto&& spanString : spanStrings) {
8819             richEditor->AddSpanByPasteData(spanString);
8820             richEditor->RequestKeyboardToEdit();
8821         }
8822     };
8823     return pasteCallback;
8824 }
8825 
8826 void RichEditorPattern::PasteStr(const std::string& text)
8827 {
8828     if (text.empty()) {
8829         ResetSelection();
8830         StartTwinkling();
8831         CloseSelectOverlay();
8832         RequestKeyboardToEdit();
8833         return;
8834     }
8835     AddPasteStr(text);
8836     ResetAfterPaste();
8837 }
8838 
8839 void RichEditorPattern::SetCaretSpanIndex(int32_t index)
8840 {
8841     caretSpanIndex_ = index;
8842 }
8843 
8844 void RichEditorPattern::HandleOnCut()
8845 {
8846     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "copyOption=%{public}d, textSelector_.IsValid()=%{public}d",
8847         copyOption_, textSelector_.IsValid());
8848     if (copyOption_ == CopyOptions::None) {
8849         suppressAccessibilityEvent_ = true;
8850         return;
8851     }
8852     if (!textSelector_.IsValid()) {
8853         suppressAccessibilityEvent_ = true;
8854         return;
8855     }
8856     auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
8857     CHECK_NULL_VOID(eventHub);
8858     TextCommonEvent event;
8859     eventHub->FireOnCut(event);
8860     if (event.IsPreventDefault()) {
8861         CloseSelectOverlay();
8862         ResetSelection();
8863         StartTwinkling();
8864         RequestKeyboardToEdit();
8865         suppressAccessibilityEvent_ = true;
8866         return;
8867     }
8868 
8869     caretUpdateType_ = CaretUpdateType::NONE;
8870     OnCopyOperation();
8871     DeleteBackward(1, TextChangeReason::CUT);
8872 }
8873 
8874 void RichEditorPattern::HandleOnShare()
8875 {
8876     auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
8877     CHECK_NULL_VOID(eventHub);
8878     TextCommonEvent event;
8879     eventHub->FireOnShare(event);
8880     selectOverlay_->HideMenu(true);
8881     auto value = selectOverlay_->GetSelectedText();
8882     auto shareWord = std::regex_replace(value, REMOVE_SPACE_CHARS, "");
8883     CHECK_NULL_VOID(!shareWord.empty());
8884     auto pipeline = GetContext();
8885     CHECK_NULL_VOID(pipeline);
8886     auto containerId = pipeline->GetInstanceId();
8887     auto contentRect = selectOverlay_->GetSelectArea();
8888     TextShareAdapter::StartTextShareTask(containerId, contentRect, shareWord);
8889 }
8890 
8891 void RichEditorPattern::HandleAIMenuOption(const std::string& labelInfo)
8892 {
8893     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleAIMenuOption labelInfo=%{public}s", labelInfo.c_str());
8894     TextPattern::HandleAIMenuOption(labelInfo);
8895 }
8896 
8897 void RichEditorPattern::UpdateAIMenuOptions()
8898 {
8899     bool isLocalOrDistributed = copyOption_ == CopyOptions::Local || copyOption_ == CopyOptions::Distributed;
8900     if (!isLocalOrDistributed || !NeedShowAIDetect()) {
8901         isShowAIMenuOption_ = false;
8902     } else {
8903         auto aiItemOptions = GetAIItemOption();
8904         isShowAIMenuOption_ = TextPattern::PrepareAIMenuOptions(aiItemOptions);
8905         aiMenuOptions_ = aiItemOptions;
8906     }
8907     bool isAskCeliaEnabled = isLocalOrDistributed;
8908     if (IsEditing()) {
8909         isAskCeliaEnabled &= IsSelected();
8910     } else {
8911         isAskCeliaEnabled &= !IsShowAIMenuOption();
8912     }
8913     SetIsAskCeliaEnabled(isAskCeliaEnabled);
8914     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "UpdateAIMenuOptions isShowAIMenuOption=%{public}d "
8915         "isAskCeliaEnabled=%{public}d", isShowAIMenuOption_, isAskCeliaEnabled_);
8916     if (!IsSupportAskCelia()) {
8917         SetIsAskCeliaEnabled(false);
8918     }
8919     CHECK_NULL_VOID(dataDetectorAdapter_);
8920     if (isAskCeliaEnabled_ && !NeedShowAIDetect() &&
8921         dataDetectorAdapter_->textDetectResult_.menuOptionAndAction.empty()) {
8922         dataDetectorAdapter_->GetAIEntityMenu();
8923     }
8924 }
8925 
8926 Offset RichEditorPattern::ConvertGlobalToTextOffset(const Offset& globalOffset)
8927 {
8928     auto host = GetHost();
8929     CHECK_NULL_RETURN(host, {});
8930     auto offset = host->GetPaintRectOffset(false, true);
8931     auto localOffset = globalOffset - Offset(offset.GetX(), offset.GetY());
8932     if (selectOverlay_->HasRenderTransform()) {
8933         localOffset = ConvertGlobalToLocalOffset(globalOffset);
8934     }
8935     return ConvertTouchOffsetToTextOffset(localOffset);
8936 }
8937 
8938 bool RichEditorPattern::CheckAIPreviewMenuEnable()
8939 {
8940     return TextPattern::CheckAIPreviewMenuEnable() && copyOption_ != CopyOptions::None;
8941 }
8942 
8943 void RichEditorPattern::InitAiSelection(const Offset& globalOffset, bool isBetweenSelection)
8944 {
8945     ResetAISelected(AIResetSelectionReason::INIT_SELECTION);
8946     CHECK_NULL_VOID(CheckAIPreviewMenuEnable());
8947     if (showSelect_ && isBetweenSelection) {
8948         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "no need for InitAiSelection");
8949         return;
8950     }
8951     int32_t extend = 0;
8952     auto host = GetHost();
8953     CHECK_NULL_VOID(host);
8954     Offset textOffset = ConvertGlobalToTextOffset(globalOffset);
8955     auto [pos, affinity] = paragraphs_.GetGlyphPositionAtCoordinate(textOffset);
8956     int32_t start = 0;
8957     int32_t end = 0;
8958     bool isAiSpan = false;
8959     auto aiSpanIter = dataDetectorAdapter_->aiSpanMap_.upper_bound(pos);
8960     if (aiSpanIter != dataDetectorAdapter_->aiSpanMap_.begin()) {
8961         --aiSpanIter;
8962     }
8963     start = aiSpanIter->second.start;
8964     end = aiSpanIter->second.end;
8965     auto position = static_cast<int32_t>(pos);
8966     if (position >= start && position < end && InRangeRect(globalOffset, { start, end })) {
8967         isAiSpan = true;
8968     }
8969     if (isAiSpan && start >= 0 && start < end) {
8970         textSelector_.aiStart = start;
8971         textSelector_.aiEnd = end;
8972     }
8973     TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
8974         "InitAiSelection[id:%{public}d][extend:%{public}d][start:%{public}d][end:%{public}d]", host->GetId(), extend,
8975         textSelector_.aiStart.value_or(-1), textSelector_.aiEnd.value_or(-1));
8976 }
8977 
8978 std::function<void(Offset)> RichEditorPattern::GetThumbnailCallback()
8979 {
8980     return [wk = WeakClaim(this)](const Offset& point) {
8981         auto pattern = wk.Upgrade();
8982         CHECK_NULL_VOID(pattern);
8983         auto isBetweenSelection = pattern->BetweenSelectedPosition(point);
8984         pattern->InitAiSelection(point, isBetweenSelection);
8985         if (!isBetweenSelection && !pattern->IsAiSelected()) {
8986             return;
8987         }
8988         auto isContentDraggable = pattern->JudgeContentDraggable();
8989         if (!isContentDraggable && !pattern->IsAiSelected()) {
8990             TAG_LOGE(AceLogTag::ACE_RICH_TEXT, "GetThumbnailCallback call, draggable is false");
8991             pattern->SetIsTextDraggable(false);
8992             return;
8993         }
8994         if (pattern->dragBoxes_.empty()) {
8995             pattern->dragBoxes_ = pattern->GetTextBoxes();
8996         }
8997         pattern->CreateDragNode();
8998     };
8999 }
9000 
9001 void RichEditorPattern::CreateDragNode()
9002 {
9003     auto host = GetHost();
9004     auto contentHost = GetContentHost();
9005     CHECK_NULL_VOID(host && contentHost);
9006     auto children = contentHost->GetChildren();
9007     std::list<RefPtr<FrameNode>> imageChildren;
9008     for (const auto& child : children) {
9009         auto node = DynamicCast<FrameNode>(child);
9010         CHECK_NULL_CONTINUE(node);
9011         if (auto& tag = node->GetTag(); tag == V2::IMAGE_ETS_TAG || tag == V2::PLACEHOLDER_SPAN_ETS_TAG) {
9012             imageChildren.emplace_back(node);
9013         }
9014     }
9015     TextDragInfo info;
9016     info.maxSelectedWidth = GetMaxSelectedWidth();
9017     info.handleColor = GetCaretColor();
9018     info.selectedBackgroundColor = GetSelectedBackgroundColor();
9019     auto selectOverlayInfo = selectOverlay_->GetSelectOverlayInfo();
9020     if (selectOverlayInfo.has_value()) {
9021         if (selectOverlayInfo->firstHandle.isShow) {
9022             info.firstHandle = selectOverlayInfo->firstHandle.paintRect;
9023         }
9024         if (selectOverlayInfo->secondHandle.isShow) {
9025             info.secondHandle =  selectOverlayInfo->secondHandle.paintRect;
9026         }
9027     }
9028     if (textSelector_.GetTextEnd() - textSelector_.GetTextStart() == 1) {
9029         auto spanItem = GetSpanItemByPosition(textSelector_.GetTextStart());
9030         auto placeholderSpanItem = DynamicCast<PlaceholderSpanItem>(spanItem);
9031         if (placeholderSpanItem) {
9032             info.dragBackgroundColor = placeholderSpanItem->dragBackgroundColor_;
9033             info.isDragShadowNeeded = placeholderSpanItem->isDragShadowNeeded_;
9034         }
9035     }
9036     dragNode_ = RichEditorDragPattern::CreateDragNode(host, imageChildren, info);
9037     CHECK_NULL_VOID(dragNode_);
9038     InitDragShadow(host, dragNode_, info.isDragShadowNeeded, info.dragBackgroundColor.has_value());
9039     FrameNode::ProcessOffscreenNode(dragNode_);
9040 }
9041 
9042 float RichEditorPattern::GetMaxSelectedWidth()
9043 {
9044     auto boxes = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
9045     CHECK_NULL_RETURN(!boxes.empty(), 0.0f);
9046     float startX = boxes.front().Left();
9047     float endX = boxes.front().Right();
9048     for (const auto& box : boxes) {
9049         startX = std::min(startX, box.Left());
9050         endX = std::max(endX, box.Right());
9051     }
9052     startX = std::min(0.0f, startX);
9053     return std::abs(startX - endX);
9054 }
9055 
9056 void RichEditorPattern::InitDragShadow(const RefPtr<FrameNode>& host, const RefPtr<FrameNode>& dragNode,
9057     bool isDragShadowNeeded, bool hasDragBackgroundColor)
9058 {
9059     CHECK_NULL_VOID(host && dragNode);
9060     auto textDragPattern = dragNode->GetPattern<TextDragPattern>();
9061     CHECK_NULL_VOID(textDragPattern);
9062     auto option = host->GetDragPreviewOption();
9063     if (isDragShadowNeeded) {
9064         option.options.shadowPath = textDragPattern->GetBackgroundPath()->ConvertToSVGString();
9065         option.options.shadow = Shadow(RICH_DEFAULT_ELEVATION, {0.0, 0.0}, Color(RICH_DEFAULT_SHADOW_COLOR),
9066             ShadowStyle::OuterFloatingSM);
9067         option.options.isFilled = !hasDragBackgroundColor;
9068     } else {
9069         option.options.shadowPath.clear();
9070         option.options.shadow.reset();
9071         option.options.isFilled = true;
9072     }
9073     host->SetDragPreviewOptions(option);
9074 }
9075 
9076 void RichEditorPattern::CreateHandles()
9077 {
9078     if (IsDragging()) {
9079         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "do not show handles when dragging");
9080         return;
9081     }
9082     auto host = GetHost();
9083     CHECK_NULL_VOID(host);
9084     CalculateHandleOffsetAndShowOverlay();
9085     bool isCurrentMenuVisibile = selectOverlay_->IsCurrentMenuVisibile();
9086     selectOverlay_->UpdateMenuOffset();
9087     if (!isCurrentMenuVisibile) {
9088         selectOverlay_->HideMenu();
9089     }
9090 }
9091 
9092 void RichEditorPattern::ShowHandles(const bool isNeedShowHandles)
9093 {
9094     if (!IsSelected()) {
9095         showSelect_ = true;
9096         IF_TRUE(isEditing_, StartTwinkling());
9097         return;
9098     }
9099     ShowHandles();
9100 }
9101 
9102 void RichEditorPattern::ShowHandles()
9103 {
9104     auto host = GetHost();
9105     CHECK_NULL_VOID(host);
9106     if (!selectOverlay_->IsBothHandlesShow() && !selectOverlay_->SelectOverlayIsCreating()) {
9107         showSelect_ = true;
9108         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
9109         CHECK_NULL_VOID(textSelector_.IsValid());
9110         CHECK_NULL_VOID(!isMouseSelect_);
9111         CalculateHandleOffsetAndShowOverlay();
9112         selectOverlay_->ProcessOverlay({.menuIsShow = false, .animation = false});
9113     }
9114 }
9115 
9116 void RichEditorPattern::OnAreaChangedInner()
9117 {
9118     auto context = GetContext();
9119     CHECK_NULL_VOID(context);
9120     auto prevParentGlobalOffset = parentGlobalOffset_;
9121     UpdateParentOffsetAndOverlay();
9122     IF_TRUE(parentGlobalOffset_ != prevParentGlobalOffset,
9123         UpdateTextFieldManager(Offset(parentGlobalOffset_.GetX(), parentGlobalOffset_.GetY()), frameRect_.Height()));
9124     IF_TRUE(parentGlobalOffset_ != prevParentGlobalOffset, UpdateCaretInfoToController());
9125 }
9126 
9127 void RichEditorPattern::UpdateParentOffsetAndOverlay()
9128 {
9129     auto parentGlobalOffset = GetPaintRectGlobalOffset(); // offset on screen(with transformation)
9130     CHECK_NULL_VOID(parentGlobalOffset != parentGlobalOffset_);
9131     parentGlobalOffset_ = parentGlobalOffset;
9132     selectOverlay_->UpdateSelectOverlayOnAreaChanged();
9133     CloseAIMenu();
9134 }
9135 
9136 void RichEditorPattern::CloseAIMenu()
9137 {
9138     auto context = GetContext();
9139     CHECK_NULL_VOID(context);
9140     auto overlayManager = context->GetOverlayManager();
9141     IF_PRESENT(overlayManager, CloseAIEntityMenu(frameId_));
9142 }
9143 
9144 void RichEditorPattern::CloseSelectionMenu()
9145 {
9146     // used by sdk
9147     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "CloseSelectionMenu");
9148     CloseSelectOverlay();
9149 }
9150 
9151 void RichEditorPattern::OnDragNodeFloating()
9152 {
9153     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "OnDragNodeFloating");
9154     status_ = Status::FLOATING;
9155 }
9156 
9157 void RichEditorPattern::CloseSelectOverlay()
9158 {
9159     // used by inner
9160     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "CloseSelectOverlay");
9161     selectOverlay_->CloseOverlay(true, CloseReason::CLOSE_REASON_NORMAL);
9162 }
9163 
9164 void RichEditorPattern::CloseHandleAndSelect()
9165 {
9166     selectOverlay_->CloseOverlay(false, CloseReason::CLOSE_REASON_DRAG_FLOATING);
9167     showSelect_ = false;
9168     auto host = GetHost();
9169     IF_PRESENT(host, MarkDirtyNode(PROPERTY_UPDATE_RENDER));
9170 }
9171 
9172 void RichEditorPattern::CalculateHandleOffsetAndShowOverlay(bool isUsingMouse)
9173 {
9174     auto globalOffset = GetGlobalOffset();
9175     if (!selectOverlay_->GetIsHandleMoving()) {
9176         textSelector_.ReverseTextSelector();
9177     }
9178     int32_t baseOffset = std::min(textSelector_.baseOffset, GetTextContentLength());
9179     int32_t destinationOffset = std::min(textSelector_.destinationOffset, GetTextContentLength());
9180     SizeF firstHandlePaintSize;
9181     SizeF secondHandlePaintSize;
9182     OffsetF firstHandleOffset;
9183     OffsetF secondHandleOffset;
9184     auto isSingleHandle = IsSingleHandle();
9185     selectOverlay_->SetIsSingleHandle(isSingleHandle);
9186     if (isSingleHandle) {
9187         auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
9188         // only show the second handle.
9189         secondHandlePaintSize = { SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), caretHeight };
9190         secondHandleOffset = caretOffset + globalOffset;
9191     } else {
9192         float startSelectHeight = 0.0f;
9193         float endSelectHeight = 0.0f;
9194         auto startOffset = CalcCursorOffsetByPosition(baseOffset, startSelectHeight, true, false);
9195         auto endOffset = CalcCursorOffsetByPosition(destinationOffset, endSelectHeight, false, false);
9196         firstHandlePaintSize = { SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), startSelectHeight };
9197         secondHandlePaintSize = { SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), endSelectHeight };
9198         firstHandleOffset = startOffset + globalOffset;
9199         secondHandleOffset = endOffset + globalOffset;
9200         firstHandleOffset.SetX(firstHandleOffset.GetX() - firstHandlePaintSize.Width() / 2.0f);
9201         secondHandleOffset.SetX(secondHandleOffset.GetX() - secondHandlePaintSize.Width() / 2.0f);
9202     }
9203     textSelector_.selectionBaseOffset = firstHandleOffset;
9204     textSelector_.selectionDestinationOffset = secondHandleOffset;
9205     textSelector_.firstHandle = RectF{ firstHandleOffset, firstHandlePaintSize };
9206     textSelector_.secondHandle = RectF{ secondHandleOffset, secondHandlePaintSize };
9207 }
9208 
9209 void RichEditorPattern::CalculateDefaultHandleHeight(float& height)
9210 {
9211 #ifdef ENABLE_ROSEN_BACKEND
9212     MeasureContext content;
9213     content.textContent = "a";
9214     content.fontSize = TEXT_DEFAULT_FONT_SIZE;
9215     auto fontweight = StringUtils::FontWeightToString(FontWeight::NORMAL);
9216     content.fontWeight = fontweight;
9217     height = std::max(static_cast<float>(RosenRenderCustomPaint::MeasureTextSizeInner(content).Height()), 0.0f);
9218 #endif
9219 }
9220 
9221 OffsetF RichEditorPattern::GetGlobalOffset() const
9222 {
9223     auto host = GetHost();
9224     CHECK_NULL_RETURN(host, OffsetF());
9225     auto pipeline = host->GetContext();
9226     CHECK_NULL_RETURN(pipeline, OffsetF());
9227     auto rootOffset = pipeline->GetRootRect().GetOffset();
9228     auto richEditorPaintOffset = host->GetPaintRectOffsetNG(false, true);
9229     if (selectOverlay_->HasRenderTransform()) {
9230         richEditorPaintOffset = selectOverlay_->GetPaintOffsetWithoutTransform();
9231     }
9232     return richEditorPaintOffset - rootOffset;
9233 }
9234 
9235 bool RichEditorPattern::IsSingleHandle()
9236 {
9237     CHECK_NULL_RETURN(!selectOverlay_->GetIsHandleMoving(), selectOverlay_->IsSingleHandle());
9238     return GetTextContentLength() == 0 || !IsSelected();
9239 }
9240 
9241 bool RichEditorPattern::IsHandlesShow()
9242 {
9243     return selectOverlay_->IsBothHandlesShow();
9244 }
9245 
9246 void RichEditorPattern::ResetSelection()
9247 {
9248     bool selectNothing = textSelector_.SelectNothing();
9249     textSelector_.Update(-1, -1);
9250     if (!selectNothing) {
9251         TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "ResetSelection");
9252         auto host = GetHost();
9253         CHECK_NULL_VOID(host);
9254         auto eventHub = host->GetOrCreateEventHub<RichEditorEventHub>();
9255         CHECK_NULL_VOID(eventHub);
9256         auto textSelectInfo = GetSpansInfo(-1, -1, GetSpansMethod::ONSELECT);
9257         eventHub->FireOnSelect(&textSelectInfo);
9258         UpdateSelectionType(textSelectInfo);
9259         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
9260     }
9261 }
9262 
9263 bool RichEditorPattern::BetweenSelection(const Offset& globalOffset)
9264 {
9265     if (IsAiSelected()) {
9266         return InRangeRect(globalOffset, { textSelector_.aiStart.value(), textSelector_.aiEnd.value() });
9267     } else {
9268         return InRangeRect(globalOffset, { textSelector_.GetTextStart(), textSelector_.GetTextEnd() });
9269     }
9270 }
9271 
9272 bool RichEditorPattern::InRangeRect(const Offset& globalOffset, const std::pair<int32_t, int32_t>& range)
9273 {
9274     auto host = GetHost();
9275     CHECK_NULL_RETURN(host, false);
9276     CHECK_NULL_RETURN(0 <= range.first && range.first < range.second, false);
9277     auto offset = host->GetPaintRectOffsetNG(false, true);
9278     auto localOffset = globalOffset - Offset(offset.GetX(), offset.GetY());
9279     if (selectOverlay_->HasRenderTransform()) {
9280         localOffset = ConvertGlobalToLocalOffset(globalOffset);
9281     }
9282     auto eventHub = host->GetOrCreateEventHub<EventHub>();
9283     if (GreatNotEqual(range.second, range.first)) {
9284         // Determine if the pan location is in the selected area
9285         auto rangeRects = paragraphs_.GetRects(range.first, range.second);
9286         auto panOffset = OffsetF(localOffset.GetX(), localOffset.GetY()) - GetTextRect().GetOffset() +
9287                          OffsetF(0.0, std::min(baselineOffset_, 0.0f));
9288         for (const auto& rangeRect : rangeRects) {
9289             if (rangeRect.IsInRegion(PointF(panOffset.GetX(), panOffset.GetY()))) {
9290                 return true;
9291             }
9292         }
9293     }
9294     return false;
9295 }
9296 
9297 bool RichEditorPattern::BetweenSelectedPosition(const Offset& globalOffset)
9298 {
9299     return copyOption_ != CopyOptions::None && BetweenSelection(globalOffset);
9300 }
9301 
9302 void RichEditorPattern::HandleSurfaceChanged(
9303     int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight, WindowSizeChangeReason type)
9304 {
9305     if (newWidth != prevWidth || newHeight != prevHeight) {
9306         TextPattern::HandleSurfaceChanged(newWidth, newHeight, prevWidth, prevHeight, type);
9307         UpdateOriginIsMenuShow(false);
9308     }
9309     UpdateCaretInfoToController();
9310     if (magnifierController_) {
9311         magnifierController_->RemoveMagnifierFrameNode();
9312     }
9313 }
9314 
9315 void RichEditorPattern::HandleSurfacePositionChanged(int32_t posX, int32_t posY)
9316 {
9317     UpdateCaretInfoToController();
9318 }
9319 
9320 void RichEditorPattern::DumpInfo()
9321 {
9322     auto& dumpLog = DumpLog::GetInstance();
9323     if (customKeyboardBuilder_) {
9324         dumpLog.AddDesc(std::string("CustomKeyboard, Attached: ").append(std::to_string(isCustomKeyboardAttached_)));
9325     }
9326     auto host = GetHost();
9327     CHECK_NULL_VOID(host);
9328     auto richEditorTheme = GetTheme<RichEditorTheme>();
9329     CHECK_NULL_VOID(richEditorTheme);
9330     dumpLog.AddDesc(std::string("caret offset: ").append(GetCaretRect().GetOffset().ToString()));
9331     dumpLog.AddDesc(std::string("caret height: ")
9332             .append(std::to_string(NearZero(GetCaretRect().Height())
9333                                        ? richEditorTheme->GetDefaultCaretHeight().ConvertToPx()
9334                                        : GetCaretRect().Height())));
9335     dumpLog.AddDesc(std::string("text rect: ").append(richTextRect_.ToString()));
9336     dumpLog.AddDesc(std::string("content rect: ").append(contentRect_.ToString()));
9337     auto richEditorPaintOffset = host->GetPaintRectOffsetNG(false, true);
9338     bool hasRenderTransform = selectOverlay_->HasRenderTransform();
9339     if (hasRenderTransform) {
9340         richEditorPaintOffset = selectOverlay_->GetPaintOffsetWithoutTransform();
9341     }
9342     dumpLog.AddDesc(std::string("hasRenderTransform: ").append(std::to_string(hasRenderTransform)));
9343     dumpLog.AddDesc(std::string("richEditorPaintOffset: ").append(richEditorPaintOffset.ToString()));
9344     auto selectOverlayInfo = selectOverlay_->GetSelectOverlayInfo();
9345     CHECK_NULL_VOID(selectOverlayInfo);
9346     dumpLog.AddDesc(std::string("selectOverlay info: ").append(selectOverlayInfo->ToString()));
9347     dumpLog.AddDesc(std::string("IsAIWrite: ").append(std::to_string(IsShowAIWrite())));
9348     dumpLog.AddDesc(std::string("keyboardAppearance: ")
9349             .append(std::to_string(static_cast<int32_t>(keyboardAppearance_))));
9350 }
9351 
9352 void RichEditorPattern::RichEditorErrorReport(RichEditorInfo& info)
9353 {
9354     auto pipeline = GetContext();
9355     CHECK_NULL_VOID(pipeline);
9356     auto taskExecutor = pipeline->GetTaskExecutor();
9357     CHECK_NULL_VOID(taskExecutor);
9358     taskExecutor->PostTask(
9359         [info] {
9360             EventReport::ReportRichEditorInfo(info);
9361         },
9362         TaskExecutor::TaskType::BACKGROUND, "ArkUIRichEditorErrorReport");
9363 }
9364 
9365 bool RichEditorPattern::HasFocus() const
9366 {
9367     auto focusHub = GetFocusHub();
9368     CHECK_NULL_RETURN(focusHub, false);
9369     return focusHub->IsCurrentFocus();
9370 }
9371 
9372 void RichEditorPattern::UpdateTextFieldManager(const Offset& offset, float height)
9373 {
9374     CHECK_NULL_VOID(HasFocus());
9375     auto context = GetContext();
9376     CHECK_NULL_VOID(context);
9377     auto richEditorTheme = context->GetTheme<RichEditorTheme>();
9378     CHECK_NULL_VOID(richEditorTheme);
9379     auto textFieldManager = DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
9380     CHECK_NULL_VOID(textFieldManager);
9381     auto safeAreaManager = context->GetSafeAreaManager();
9382     CHECK_NULL_VOID(safeAreaManager);
9383     auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
9384     textFieldManager->SetClickPosition({ offset.GetX() + caretOffset.GetX(), offset.GetY() + caretOffset.GetY() });
9385     textFieldManager->SetHeight(NearZero(caretHeight)
9386                                     ? richEditorTheme->GetDefaultCaretHeight().ConvertToPx()
9387                                     : caretHeight);
9388     textFieldManager->SetClickPositionOffset(safeAreaManager->GetKeyboardOffset());
9389     textFieldManager->SetOnFocusTextField(WeakClaim(this));
9390     if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_FOURTEEN)) {
9391         textFieldManager->SetUsingCustomKeyboardAvoid(keyboardAvoidance_);
9392     }
9393     if (!isTextChange_) {
9394         return;
9395     }
9396     auto taskExecutor = context->GetTaskExecutor();
9397     CHECK_NULL_VOID(taskExecutor);
9398     taskExecutor->PostTask(
9399         [weak = WeakClaim(this)] {
9400             auto pattern = weak.Upgrade();
9401             CHECK_NULL_VOID(pattern);
9402             pattern->ScrollToSafeArea();
9403         },
9404         TaskExecutor::TaskType::UI, "ArkUIRichEditorScrollToSafeArea", PriorityType::VIP);
9405 }
9406 
9407 bool RichEditorPattern::IsDisabled() const
9408 {
9409     auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
9410     CHECK_NULL_RETURN(eventHub, true);
9411     return !eventHub->IsEnabled();
9412 }
9413 
9414 void RichEditorPattern::MouseDoubleClickParagraphEnd(int32_t& index)
9415 {
9416     bool isMouseDoubleClick = caretUpdateType_ == CaretUpdateType::DOUBLE_CLICK && sourceType_ == SourceType::MOUSE;
9417     CHECK_NULL_VOID(isMouseDoubleClick);
9418     auto paragraphEndPos = GetParagraphEndPosition(index);
9419     auto paragraphBeginPos = GetParagraphBeginPosition(index);
9420     bool isBeginEqualEnd = paragraphBeginPos == paragraphEndPos;
9421     CHECK_NULL_VOID(!isBeginEqualEnd);
9422     if (index == paragraphEndPos) {
9423         index -= 1;
9424     }
9425 }
9426 
9427 void RichEditorPattern::AdjustSelectionExcludeSymbol(int32_t& start, int32_t& end)
9428 {
9429     AdjustSelectorForSymbol(start, HandleType::FIRST, SelectorAdjustPolicy::EXCLUDE);
9430     AdjustSelectorForSymbol(end, HandleType::SECOND, SelectorAdjustPolicy::EXCLUDE);
9431 }
9432 
9433 void RichEditorPattern::InitSelection(const Offset& pos)
9434 {
9435     auto [currentPosition, selectType] = JudgeSelectType(pos);
9436     switch (selectType) {
9437         case SelectType::SELECT_NOTHING:
9438             TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "select nothing currentPos=%{public}d", currentPosition);
9439             textSelector_.Update(currentPosition, currentPosition);
9440             return;
9441         case SelectType::SELECT_BACKWARD:
9442             currentPosition = std::max(0, currentPosition - 1);
9443             break;
9444         case SelectType::SELECT_FORWARD:
9445             break;
9446         default:
9447             TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "exception select type");
9448     }
9449     int32_t nextPosition = std::min(currentPosition + 1, GetTextContentLength());
9450     AdjustSelectionExcludeSymbol(currentPosition, nextPosition);
9451     if (!IsCustomSpanInCaretPos(currentPosition, true)) {
9452         AdjustWordSelection(currentPosition, nextPosition);
9453     }
9454     AdjustSelector(currentPosition, nextPosition);
9455     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "init select [%{public}d--%{public}d]", currentPosition, nextPosition);
9456     textSelector_.Update(currentPosition, nextPosition);
9457     if (IsSelectEmpty(currentPosition, nextPosition)) {
9458         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "select rect is empty, select nothing");
9459         textSelector_.Update(currentPosition, currentPosition);
9460     }
9461 }
9462 
9463 std::pair<int32_t, SelectType> RichEditorPattern::JudgeSelectType(const Offset& pos)
9464 {
9465     auto positionWithAffinity = paragraphs_.GetGlyphPositionAtCoordinate(pos);
9466     auto currentPosition = (GetTextContentLength() == 0) ? 0 : static_cast<int32_t>(positionWithAffinity.position_);
9467     auto selectType = SelectType::SELECT_NOTHING;
9468     CHECK_NULL_RETURN(GetTextContentLength() != 0, std::make_pair(currentPosition, selectType));
9469     bool isNeedSkipLineSeparator = !editingLongPress_ && IsSelectEmpty(currentPosition, currentPosition + 1);
9470     if (isNeedSkipLineSeparator && AdjustIndexSkipLineSeparator(currentPosition)) {
9471         return std::make_pair(currentPosition, SelectType::SELECT_BACKWARD);
9472     }
9473     auto height = paragraphs_.GetHeight();
9474     if (GreatNotEqual(pos.GetY(), height)) {
9475         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "touchPosY[%{public}f] > paragraphsHeight[%{public}f]", pos.GetY(), height);
9476         IF_TRUE(!editingLongPress_, selectType = SelectType::SELECT_BACKWARD);
9477         return std::make_pair(GetTextContentLength(), selectType);
9478     }
9479     TextAffinity currentAffinity = positionWithAffinity.affinity_;
9480     bool isTouchLineEnd = currentAffinity == TextAffinity::UPSTREAM && !IsTouchBeforeCaret(currentPosition, pos);
9481     if (editingLongPress_ && isTouchLineEnd) {
9482         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "touchLineEnd select nothing currentAffinity=%{public}d", currentAffinity);
9483         return std::make_pair(currentPosition, selectType);
9484     }
9485     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "currentPosition=%{public}d, currentAffinity=%{public}d",
9486         currentPosition, currentAffinity);
9487     selectType = (currentAffinity == TextAffinity::UPSTREAM) ? SelectType::SELECT_BACKWARD : SelectType::SELECT_FORWARD;
9488     return std::make_pair(currentPosition, selectType);
9489 }
9490 
9491 bool RichEditorPattern::IsSelectEmpty(int32_t start, int32_t end)
9492 {
9493     auto selectedRects = paragraphs_.GetRects(start, end);
9494     return selectedRects.empty() || (selectedRects.size() == 1 && NearZero((selectedRects[0].Width())));
9495 }
9496 
9497 bool RichEditorPattern::AdjustIndexSkipLineSeparator(int32_t& currentPosition)
9498 {
9499     std::u16string contentText;
9500     for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
9501         contentText.append((*iter)->content);
9502         CHECK_NULL_BREAK(currentPosition > (*iter)->position);
9503     }
9504     auto contentLength = static_cast<int32_t>(contentText.length());
9505     if (currentPosition > contentLength) {
9506         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "currentPosition=%{public}d but contentLength=%{public}d",
9507             currentPosition, contentLength);
9508         return false;
9509     }
9510     auto index = currentPosition - 1;
9511     while (index > 0) {
9512         CHECK_NULL_BREAK(contentText[index] == u'\n');
9513         index--;
9514     }
9515     if (index != currentPosition - 1) {
9516         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "skip lineSeparator %{public}d->%{public}d", currentPosition, index + 1);
9517         currentPosition = index + 1;
9518         return true;
9519     }
9520     return false;
9521 }
9522 
9523 bool RichEditorPattern::AdjustIndexSkipSpace(int32_t& currentPosition, const MoveDirection direction)
9524 {
9525     bool isBackward = (direction == MoveDirection::BACKWARD);
9526     std::u16string contentText;
9527     GetContentBySpans(contentText);
9528     auto contentLength = static_cast<int32_t>(contentText.length());
9529     bool isPositionInvalid = (isBackward && currentPosition == 0) || (!isBackward && currentPosition == contentLength);
9530     if (isPositionInvalid) {
9531         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "AdjustIndexSkipSpace position=%{public}d but contentLength=%{public}d",
9532             currentPosition, contentLength);
9533         return false;
9534     }
9535     int32_t index = isBackward ? (currentPosition - 1) : currentPosition;
9536     while (isBackward ? index >= 0 : index < contentLength) {
9537         CHECK_NULL_BREAK(contentText[index] == u' ' && GetSpanType(index) == SpanItemType::NORMAL);
9538         isBackward ? index-- : index++;
9539     }
9540     int32_t adjustedIndex = isBackward ? (index + 1) : index;
9541     if (adjustedIndex != currentPosition) {
9542         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "skip space %{public}d->%{public}d", currentPosition, adjustedIndex);
9543         currentPosition = adjustedIndex;
9544         return true;
9545     }
9546     return false;
9547 }
9548 
9549 bool RichEditorPattern::ResetOnInvalidSelection(int32_t start, int32_t end)
9550 {
9551     if (start < end) {
9552         return false;
9553     }
9554     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "SetSelection failed, the selected area is empty.");
9555     CloseSelectOverlay();
9556     ResetSelection();
9557     StartTwinkling();
9558     return true;
9559 }
9560 
9561 bool RichEditorPattern::IsShowHandle()
9562 {
9563     auto richEditorTheme = GetTheme<RichEditorTheme>();
9564     CHECK_NULL_RETURN(richEditorTheme, false);
9565     return !richEditorTheme->IsRichEditorShowHandle();
9566 }
9567 
9568 void RichEditorPattern::UpdateSelectionInfo(int32_t start, int32_t end)
9569 {
9570     UpdateSelectionType(GetSpansInfo(start, end, GetSpansMethod::ONSELECT));
9571     auto selectOverlayInfo = selectOverlay_->GetSelectOverlayInfo();
9572     textResponseType_ = selectOverlayInfo
9573                         ? static_cast<TextResponseType>(selectOverlayInfo->menuInfo.responseType.value_or(0))
9574                         : TextResponseType::LONG_PRESS;
9575     if (IsShowHandle() && !IsUsingMouse()) {
9576         ResetIsMousePressed();
9577         sourceType_ = SourceType::TOUCH;
9578     }
9579 }
9580 
9581 void RichEditorPattern::SetSelection(int32_t start, int32_t end, const std::optional<SelectionOptions>& options,
9582     bool isForward)
9583 {
9584     bool hasFocus = HasFocus();
9585     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "setSelection, range=[%{public}d,%{public}d], hasFocus=%{public}d, "
9586         "isEditing=%{public}d", start, end, hasFocus, isEditing_);
9587     CHECK_NULL_VOID(hasFocus);
9588     if (IsPreviewTextInputting()) {
9589         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "SetSelection failed for previewText inputting");
9590         return;
9591     }
9592     if (start == -1 && end == -1) {
9593         start = 0;
9594         end = GetTextContentLength();
9595     } else {
9596         start = std::clamp(start, 0, GetTextContentLength());
9597         end = std::clamp(end, 0, GetTextContentLength());
9598     }
9599     CHECK_NULL_VOID(!ResetOnInvalidSelection(start, end));
9600     UpdateSelector(start, end);
9601 
9602     if (textSelector_.IsValid() && !textSelector_.StartEqualToDest()) {
9603         StopTwinkling();
9604         if (start != textSelector_.GetTextStart() || end != textSelector_.GetTextEnd()) {
9605             FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
9606         }
9607     }
9608     SetCaretPosition(isForward ? textSelector_.GetTextStart() : textSelector_.GetTextEnd());
9609     MoveCaretToContentRect();
9610     CalculateHandleOffsetAndShowOverlay();
9611     UpdateSelectionInfo(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
9612     ProcessOverlayOnSetSelection(options);
9613     auto host = GetHost();
9614     CHECK_NULL_VOID(host);
9615     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
9616 }
9617 
9618 void RichEditorPattern::ProcessOverlayOnSetSelection(const std::optional<SelectionOptions>& options)
9619 {
9620     if (options.has_value()) {
9621         auto handlePolicy = options.value().handlePolicy;
9622         IF_TRUE(handlePolicy == HandlePolicy::SHOW, selectOverlay_->ProcessOverlay({ .animation = true }));
9623         IF_TRUE(handlePolicy == HandlePolicy::HIDE, CloseSelectOverlay());
9624         CHECK_NULL_VOID(handlePolicy == HandlePolicy::DEFAULT);
9625     }
9626     if (!IsShowHandle()) {
9627         CloseSelectOverlay();
9628     } else if (!options.has_value() || options.value().menuPolicy == MenuPolicy::DEFAULT) {
9629         selectOverlay_->ProcessOverlay({ .menuIsShow = selectOverlay_->IsCurrentMenuVisibile(),
9630             .animation = true, .requestCode = REQUEST_RECREATE });
9631         IF_PRESENT(magnifierController_, RemoveMagnifierFrameNode());
9632     } else if (options.value().menuPolicy == MenuPolicy::HIDE) {
9633         if (selectOverlay_->IsUsingMouse()) {
9634             CloseSelectOverlay();
9635         } else {
9636             selectOverlay_->ProcessOverlay({ .menuIsShow = false, .animation = true });
9637         }
9638     } else if (options.value().menuPolicy == MenuPolicy::SHOW) {
9639         if (selectOverlay_->IsUsingMouse() || sourceType_ == SourceType::MOUSE) {
9640             selectionMenuOffsetByMouse_ = selectionMenuOffsetClick_;
9641         }
9642         selectOverlay_->ProcessOverlay({ .animation = true, .requestCode = REQUEST_RECREATE });
9643         IF_PRESENT(magnifierController_, RemoveMagnifierFrameNode());
9644     }
9645 }
9646 
9647 void RichEditorPattern::BindSelectionMenu(TextResponseType type, TextSpanType richEditorType,
9648     std::function<void()>& menuBuilder, const SelectMenuParam& menuParam)
9649 {
9650     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "BindSelectionMenu spanType = %{public}d, responseType = %{public}d",
9651         richEditorType, type);
9652     TextPattern::BindSelectionMenu(richEditorType, type, menuBuilder, menuParam);
9653 }
9654 
9655 RefPtr<NodePaintMethod> RichEditorPattern::CreateNodePaintMethod()
9656 {
9657     if (!overlayMod_) {
9658         auto scrollBar = GetScrollBar();
9659         if (scrollBar) {
9660             auto scrollBarModifier = AceType::MakeRefPtr<ScrollBarOverlayModifier>();
9661             scrollBarModifier->SetRect(scrollBar->GetActiveRect());
9662             scrollBarModifier->SetPositionMode(scrollBar->GetPositionMode());
9663             SetScrollBarOverlayModifier(scrollBarModifier);
9664         }
9665         SetEdgeEffect(EdgeEffect::FADE, GetAlwaysEnabled());
9666         SetEdgeEffect();
9667         overlayMod_ = AceType::MakeRefPtr<RichEditorOverlayModifier>(
9668             WeakClaim(this), GetScrollBarOverlayModifier(), GetScrollEdgeEffect());
9669     }
9670     return MakeRefPtr<RichEditorPaintMethod>(WeakClaim(this), &paragraphs_, baselineOffset_, contentMod_, overlayMod_);
9671 }
9672 
9673 int32_t RichEditorPattern::GetHandleIndex(const Offset& offset) const
9674 {
9675     CHECK_NULL_RETURN(!isShowPlaceholder_, 0);
9676     return paragraphs_.GetIndex(Offset(offset.GetX() + contentRect_.GetX() - richTextRect_.GetX(),
9677         offset.GetY() + contentRect_.GetY() - richTextRect_.GetY()));
9678 }
9679 
9680 std::vector<RectF> RichEditorPattern::GetTextBoxes()
9681 {
9682     std::vector<RectF> selectedRects;
9683     if (IsAiSelected()) {
9684         selectedRects = paragraphs_.GetRects(textSelector_.aiStart.value(), textSelector_.aiStart.value());
9685     } else {
9686         selectedRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
9687     }
9688     std::vector<RectF> res;
9689     res.reserve(selectedRects.size());
9690     for (auto&& rect : selectedRects) {
9691         res.emplace_back(rect);
9692     }
9693     if (!res.empty() && paragraphs_.IsSelectLineHeadAndUseLeadingMargin(textSelector_.GetTextStart())) {
9694         // To make drag screenshot include LeadingMarginPlaceholder when not single line
9695         if (res.front().GetY() != res.back().GetY()) {
9696             res.front().SetLeft(0.0f);
9697         }
9698     }
9699     return res;
9700 }
9701 
9702 float RichEditorPattern::GetLineHeight() const
9703 {
9704     auto selectedRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
9705     CHECK_NULL_RETURN(selectedRects.size(), 0.0f);
9706     return selectedRects.front().Height();
9707 }
9708 
9709 size_t RichEditorPattern::GetLineCount() const
9710 {
9711     CHECK_NULL_RETURN(!spans_.empty() || NeedShowPlaceholder(), 0);
9712     return paragraphs_.GetLineCount();
9713 }
9714 
9715 TextLineMetrics RichEditorPattern::GetLineMetrics(int32_t lineNumber)
9716 {
9717     if (lineNumber < 0 || GetLineCount() == 0 || static_cast<uint32_t>(lineNumber) > GetLineCount() - 1) {
9718         TAG_LOGE(AceLogTag::ACE_RICH_TEXT,
9719                 "GetLineMetrics failed, lineNumber not between 0 and max lines:%{public}d", lineNumber);
9720         return TextLineMetrics();
9721     }
9722     auto lineMetrics = paragraphs_.GetLineMetrics(lineNumber);
9723     const auto& textRect = GetTextRect();
9724     lineMetrics.x += textRect.GetX();
9725     lineMetrics.y += textRect.GetY();
9726     lineMetrics.baseline += textRect.GetY();
9727     return lineMetrics;
9728 }
9729 
9730 std::vector<ParagraphManager::TextBox> RichEditorPattern::GetRectsForRange(
9731     int32_t start, int32_t end, RectHeightStyle heightStyle, RectWidthStyle widthStyle)
9732 {
9733     if (start < 0 || end < 0 || start > end) {
9734         return {};
9735     }
9736     std::vector<ParagraphManager::TextBox> textBoxes =
9737         paragraphs_.GetRectsForRange(start, end, heightStyle, widthStyle);
9738     const auto& textRect = richTextRect_;
9739     std::vector<ParagraphManager::TextBox> adjustedTextBoxes;
9740     for (auto& textBox : textBoxes) {
9741         ParagraphManager::TextBox adjustedTextBox = textBox;
9742         adjustedTextBox.rect_.SetLeft(textBox.rect_.Left() + textRect.Left());
9743         adjustedTextBox.rect_.SetTop(textBox.rect_.Top() + textRect.Top());
9744         adjustedTextBoxes.push_back(adjustedTextBox);
9745     }
9746     return adjustedTextBoxes;
9747 }
9748 
9749 float RichEditorPattern::GetLetterSpacing() const
9750 {
9751     auto selectedRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
9752     CHECK_NULL_RETURN(!selectedRects.empty(), 0.0f);
9753     return selectedRects.front().Width();
9754 }
9755 
9756 void RichEditorPattern::UpdateSelectMenuInfo(SelectMenuInfo& menuInfo)
9757 {
9758     bool isSupportCameraInput = false;
9759 #if defined(ENABLE_STANDARD_INPUT)
9760     auto inputMethod = MiscServices::InputMethodController::GetInstance();
9761     isSupportCameraInput =
9762         inputMethod && inputMethod->IsInputTypeSupported(MiscServices::InputType::CAMERA_INPUT);
9763 #endif
9764     menuInfo.showCameraInput = !IsSelected() && isSupportCameraInput && !customKeyboardBuilder_;
9765     if (textResponseType_.has_value()) {
9766         menuInfo.responseType = static_cast<int32_t>(textResponseType_.value());
9767     }
9768 
9769     if (IsShowAIMenuOption() && !GetAIItemOption().empty()) {
9770         auto firstSpanItem = GetAIItemOption().begin()->second;
9771         menuInfo.aiMenuOptionType = firstSpanItem.type;
9772         return;
9773     }
9774     menuInfo.aiMenuOptionType = TextDataDetectType::INVALID;
9775 }
9776 
9777 RectF RichEditorPattern::GetCaretRect() const
9778 {
9779     RectF rect;
9780     CHECK_NULL_RETURN(overlayMod_, rect);
9781     auto richEditorOverlay = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
9782     CHECK_NULL_RETURN(richEditorOverlay, rect);
9783     rect.SetOffset(richEditorOverlay->GetCaretOffset());
9784     rect.SetHeight(richEditorOverlay->GetCaretHeight());
9785     return rect;
9786 }
9787 
9788 void RichEditorPattern::ScrollToSafeArea() const
9789 {
9790     auto pipeline = GetContext();
9791     CHECK_NULL_VOID(pipeline);
9792     if (pipeline->UsingCaretAvoidMode()) {
9793         // using TriggerAvoidOnCaretChange instead in CaretAvoidMode
9794         return;
9795     }
9796     auto textFieldManager = DynamicCast<TextFieldManagerNG>(pipeline->GetTextFieldManager());
9797     CHECK_NULL_VOID(textFieldManager);
9798     textFieldManager->ScrollTextFieldToSafeArea();
9799 }
9800 
9801 void RichEditorPattern::InitScrollablePattern()
9802 {
9803     auto layoutProperty = GetLayoutProperty<RichEditorLayoutProperty>();
9804     CHECK_NULL_VOID(layoutProperty);
9805     auto barState = layoutProperty->GetDisplayModeValue(DisplayMode::AUTO);
9806     CHECK_NULL_VOID(!barDisplayMode_.has_value() || barDisplayMode_.value() != barState);
9807     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "setBarState=%{public}d", barState);
9808     barDisplayMode_ = barState;
9809     if (!GetScrollableEvent()) {
9810         AddScrollEvent();
9811     }
9812     SetAxis(Axis::VERTICAL);
9813     if (barState != DisplayMode::AUTO) {
9814         barState = DisplayMode::ON;
9815     }
9816     SetScrollBar(barState);
9817     auto scrollBar = GetScrollBar();
9818     if (scrollBar) {
9819         auto richEditorTheme = GetTheme<RichEditorTheme>();
9820         CHECK_NULL_VOID(richEditorTheme);
9821         scrollBar->SetMinHeight(richEditorTheme->GetScrollbarMinHeight());
9822     }
9823     if (overlayMod_) {
9824         UpdateScrollBarOffset();
9825     }
9826     auto& paddingProperty = layoutProperty->GetPaddingProperty();
9827     if (paddingProperty) {
9828         auto offsetY = paddingProperty->top.has_value() ? paddingProperty->top->GetDimension().ConvertToPx() : 0.0f;
9829         auto offsetX = paddingProperty->left.has_value() ? paddingProperty->left->GetDimension().ConvertToPx() : 0.0f;
9830         richTextRect_.SetOffset(OffsetF(offsetX, offsetY));
9831     }
9832 }
9833 
9834 void RichEditorPattern::ProcessInnerPadding()
9835 {
9836     auto theme = GetTheme<RichEditorTheme>();
9837     CHECK_NULL_VOID(theme);
9838     auto host = GetHost();
9839     CHECK_NULL_VOID(host);
9840     auto layoutProperty = host->GetLayoutProperty<RichEditorLayoutProperty>();
9841     CHECK_NULL_VOID(layoutProperty);
9842     auto themePadding = theme->GetPadding();
9843     auto& paddingProp = layoutProperty->GetPaddingProperty();
9844     auto left = !paddingProp ? CalcLength(themePadding.Left()).GetDimension()
9845                              : paddingProp->left.value_or(CalcLength(themePadding.Left())).GetDimension();
9846     auto top = !paddingProp ? CalcLength(themePadding.Top()).GetDimension()
9847                             : paddingProp->top.value_or(CalcLength(themePadding.Top())).GetDimension();
9848     auto bottom = !paddingProp ? CalcLength(themePadding.Bottom()).GetDimension()
9849                                : paddingProp->bottom.value_or(CalcLength(themePadding.Bottom())).GetDimension();
9850     auto right = !paddingProp ? CalcLength(themePadding.Right()).GetDimension()
9851                               : paddingProp->right.value_or(CalcLength(themePadding.Right())).GetDimension();
9852     PaddingProperty paddings;
9853     paddings.top = NG::CalcLength(top);
9854     paddings.bottom = NG::CalcLength(bottom);
9855     paddings.left = NG::CalcLength(left);
9856     paddings.right = NG::CalcLength(right);
9857     layoutProperty->UpdatePadding(paddings);
9858 }
9859 
9860 void RichEditorPattern::UpdateScrollStateAfterLayout(bool shouldDisappear)
9861 {
9862     bool hasTextOffsetChanged = false;
9863     if (GreatNotEqual(richTextRect_.GetY(), contentRect_.GetY())) {
9864         auto offset = richTextRect_.GetOffset();
9865         offset.AddY(contentRect_.GetY() - richTextRect_.GetY());
9866         richTextRect_.SetOffset(offset);
9867         hasTextOffsetChanged = true;
9868     }
9869     if (GreatNotEqual(richTextRect_.Height(), contentRect_.Height()) &&
9870         LessNotEqual(richTextRect_.Bottom(), contentRect_.Bottom())) {
9871         auto offset = richTextRect_.GetOffset();
9872         offset.AddY(contentRect_.Bottom() - richTextRect_.Bottom());
9873         richTextRect_.SetOffset(offset);
9874         hasTextOffsetChanged = true;
9875     }
9876     if (LessOrEqual(richTextRect_.Height(), contentRect_.Height()) &&
9877         LessNotEqual(richTextRect_.GetY(), contentRect_.GetY())) {
9878         richTextRect_.SetOffset(contentRect_.GetOffset());
9879         hasTextOffsetChanged = true;
9880     }
9881     if (hasTextOffsetChanged) {
9882         UpdateChildrenOffset();
9883     }
9884     StopScrollable();
9885     CheckScrollable();
9886     if (overlayMod_) {
9887         UpdateScrollBarOffset();
9888     }
9889     auto scrollBar = GetScrollBar();
9890     CHECK_NULL_VOID(scrollBar);
9891 
9892     if (isFirstCallOnReady_) {
9893         isFirstCallOnReady_ = false;
9894         scrollBar->ScheduleDisappearDelayTask();
9895         return;
9896     }
9897     if (shouldDisappear) {
9898         scrollBar->ScheduleDisappearDelayTask();
9899     }
9900 }
9901 
9902 bool RichEditorPattern::OnScrollCallback(float offset, int32_t source)
9903 {
9904     auto scrollBar = GetScrollBar();
9905     if (source == SCROLL_FROM_START) {
9906         IF_PRESENT(scrollBar, PlayScrollBarAppearAnimation());
9907         if (SelectOverlayIsOn()) {
9908             selectOverlay_->HideMenu(true);
9909         }
9910         CloseAIMenu();
9911         ScrollablePattern::RecordScrollEvent(Recorder::EventType::SCROLL_START);
9912         UIObserverHandler::GetInstance().NotifyScrollEventStateChange(
9913             AceType::WeakClaim(this), ScrollEventType::SCROLL_START);
9914         return true;
9915     }
9916     if (IsReachedBoundary(offset)) {
9917         return false;
9918     }
9919     if (scrollBar && source == SCROLL_FROM_JUMP) {
9920         scrollBar->PlayScrollBarAppearAnimation();
9921         scrollBar->ScheduleDisappearDelayTask();
9922     }
9923     auto newOffset = MoveTextRect(offset);
9924     MoveFirstHandle(newOffset);
9925     MoveSecondHandle(newOffset);
9926     dataDetectorAdapter_->aiSpanRects_.clear();
9927     return true;
9928 }
9929 
9930 float RichEditorPattern::GetCrossOverHeight() const
9931 {
9932     if (!keyboardAvoidance_ || !contentChange_ || AceApplicationInfo::GetInstance().
9933         GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_FOURTEEN)) {
9934         return 0.0f;
9935     }
9936     auto pipeline = GetContext();
9937     CHECK_NULL_RETURN(pipeline, 0.0f);
9938     auto rootHeight = pipeline->GetRootHeight();
9939     auto keyboardY = rootHeight - pipeline->GetSafeAreaManager()->GetKeyboardInset().Length();
9940     if (GreatOrEqual(keyboardY, rootHeight)) {
9941         return 0.0f;
9942     }
9943     float height = contentRect_.Bottom();
9944     float frameY = parentGlobalOffset_.GetY() + contentRect_.GetY();
9945     float bottom = frameY + height;
9946     auto crossOverHeight = bottom - keyboardY;
9947     if (LessOrEqual(crossOverHeight, 0.0f)) {
9948         return 0.0f;
9949     }
9950     return crossOverHeight;
9951 }
9952 
9953 float RichEditorPattern::MoveTextRect(float offset)
9954 {
9955     auto keyboardOffset = GetCrossOverHeight();
9956     if (GreatNotEqual(richTextRect_.Height(), contentRect_.Height() - keyboardOffset)) {
9957         if (GreatNotEqual(richTextRect_.GetY() + offset, contentRect_.GetY())) {
9958             offset = contentRect_.GetY() - richTextRect_.GetY();
9959         } else if (LessNotEqual(richTextRect_.Bottom() + offset, contentRect_.Bottom() - keyboardOffset)) {
9960             offset = contentRect_.Bottom() - keyboardOffset - richTextRect_.Bottom();
9961         }
9962     } else if (!NearEqual(richTextRect_.GetY(), contentRect_.GetY())) {
9963         offset = contentRect_.GetY() - richTextRect_.GetY();
9964     } else {
9965         return 0.0f;
9966     }
9967     if (NearEqual(offset, 0.0f)) {
9968         return offset;
9969     }
9970     scrollOffset_ = richTextRect_.GetY() + offset;
9971     richTextRect_.SetOffset(OffsetF(richTextRect_.GetX(), scrollOffset_));
9972     UpdateScrollBarOffset();
9973     UpdateChildrenOffset();
9974     if (auto host = GetContentHost(); host) {
9975         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
9976     }
9977     return offset;
9978 }
9979 
9980 void RichEditorPattern::MoveFirstHandle(float offset)
9981 {
9982     if (SelectOverlayIsOn() && !NearEqual(offset, 0.0f)) {
9983         textSelector_.selectionBaseOffset.AddY(offset);
9984         auto firstHandleOffset = textSelector_.firstHandle.GetOffset();
9985         firstHandleOffset.AddY(offset);
9986         textSelector_.firstHandle.SetOffset(firstHandleOffset);
9987         selectOverlay_->UpdateFirstHandleOffset();
9988     }
9989 }
9990 
9991 void RichEditorPattern::MoveSecondHandle(float offset)
9992 {
9993     if (SelectOverlayIsOn() && !NearEqual(offset, 0.0f)) {
9994         textSelector_.selectionDestinationOffset.AddY(offset);
9995         auto secondHandleOffset = textSelector_.secondHandle.GetOffset();
9996         secondHandleOffset.AddY(offset);
9997         textSelector_.secondHandle.SetOffset(secondHandleOffset);
9998         selectOverlay_->UpdateSecondHandleOffset();
9999     }
10000 }
10001 
10002 void RichEditorPattern::SetNeedMoveCaretToContentRect()
10003 {
10004     CHECK_NULL_VOID(isRichEditorInit_);
10005     needMoveCaretToContentRect_ = true;
10006 }
10007 
10008 void RichEditorPattern::MoveCaretToContentRect()
10009 {
10010     auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
10011     MoveCaretToContentRect(caretOffset, caretHeight);
10012 }
10013 
10014 void RichEditorPattern::MoveCaretToContentRect(const OffsetF& caretOffset, float caretHeight)
10015 {
10016     auto keyboardOffset = GetCrossOverHeight();
10017     auto contentRect = GetTextContentRect();
10018     auto textRect = GetTextRect();
10019     auto scrollBar = GetScrollBar();
10020     if (scrollBar) {
10021         scrollBar->PlayScrollBarAppearAnimation();
10022         scrollBar->ScheduleDisappearDelayTask();
10023     }
10024     if (LessOrEqual(textRect.Height(), contentRect.Height() - keyboardOffset) || isShowPlaceholder_) {
10025         return;
10026     }
10027     if (LessNotEqual(contentRect.GetSize().Height(), caretHeight) &&
10028         !NearEqual(caretOffset.GetY() + caretHeight, contentRect.Bottom() - keyboardOffset)) {
10029         OnScrollCallback(contentRect.Bottom() - keyboardOffset - caretOffset.GetY() - caretHeight, SCROLL_FROM_NONE);
10030     }
10031     if (LessNotEqual(contentRect.GetSize().Height(), caretHeight)) {
10032         return;
10033     }
10034     if (LessNotEqual(caretOffset.GetY(), contentRect.GetY())) {
10035         if (LessOrEqual(caretOffset.GetX(), GetTextRect().GetX())) {
10036             OnScrollCallback(contentRect.GetY() - caretOffset.GetY() + caretHeight, SCROLL_FROM_NONE);
10037         } else {
10038             OnScrollCallback(contentRect.GetY() - caretOffset.GetY(), SCROLL_FROM_NONE);
10039         }
10040     } else if (GreatNotEqual(caretOffset.GetY() + caretHeight, contentRect.Bottom() - keyboardOffset)) {
10041         auto distance = contentRect.Bottom() - keyboardOffset - caretOffset.GetY() - caretHeight -
10042             CARET_BOTTOM_DISTANCE.ConvertToPx();
10043         OnScrollCallback(distance, SCROLL_FROM_NONE);
10044     }
10045 }
10046 
10047 void RichEditorPattern::MoveCaretToContentRect(float offset, int32_t source)
10048 {
10049     float caretHeight = 0.0f;
10050     auto caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight);
10051     auto keyboardOffset = GetCrossOverHeight();
10052     auto contentRect = GetTextContentRect();
10053     auto distance = contentRect.Bottom() - keyboardOffset - caretOffset.GetY() - caretHeight - offset;
10054     OnScrollCallback(distance, source);
10055 }
10056 
10057 bool RichEditorPattern::IsCaretInContentArea()
10058 {
10059     float caretHeight = 0.0f;
10060     auto caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight);
10061     auto keyboardOffset = GetCrossOverHeight();
10062     auto contentRect = GetTextContentRect();
10063     return GreatNotEqual(caretOffset.GetY() + caretHeight, contentRect.GetY())
10064         && LessNotEqual(caretOffset.GetY(), contentRect.Bottom() - keyboardOffset);
10065 }
10066 
10067 void RichEditorPattern::UpdateScrollBarOffset()
10068 {
10069     if (!GetScrollBar() && !GetScrollBarProxy()) {
10070         return;
10071     }
10072     Size size(frameRect_.Width(), frameRect_.Height());
10073     auto verticalGap = frameRect_.Height() - contentRect_.Height();
10074     UpdateScrollBarRegion(
10075         contentRect_.GetY() - richTextRect_.GetY(), richTextRect_.Height() + verticalGap, size, Offset(0.0, 0.0));
10076     auto tmpHost = GetHost();
10077     CHECK_NULL_VOID(tmpHost);
10078     tmpHost->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
10079 }
10080 
10081 void RichEditorPattern::OnScrollEndCallback()
10082 {
10083     auto scrollBar = GetScrollBar();
10084     if (scrollBar) {
10085         scrollBar->ScheduleDisappearDelayTask();
10086     }
10087     CHECK_NULL_VOID(!selectOverlay_->GetIsHandleMoving());
10088     if (IsSelectAreaVisible()) {
10089         auto info = selectOverlay_->GetSelectOverlayInfo();
10090         if (info && info->menuInfo.menuBuilder) {
10091             selectOverlay_->ProcessOverlay({ .animation = true });
10092         } else {
10093             selectOverlay_->UpdateMenuOffset();
10094             selectOverlay_->ShowMenu();
10095         }
10096     }
10097     if (AnimateStoped()) {
10098         ScrollablePattern::RecordScrollEvent(Recorder::EventType::SCROLL_STOP);
10099         UIObserverHandler::GetInstance().NotifyScrollEventStateChange(
10100             AceType::WeakClaim(this), ScrollEventType::SCROLL_STOP);
10101     }
10102 }
10103 
10104 bool RichEditorPattern::IsSelectAreaVisible()
10105 {
10106     auto pipeline = GetContext();
10107     CHECK_NULL_RETURN(pipeline, false);
10108     auto safeAreaManager = pipeline->GetSafeAreaManager();
10109     CHECK_NULL_RETURN(safeAreaManager, false);
10110     auto keyboardInsert = safeAreaManager->GetKeyboardInset();
10111     auto selectArea = GetSelectArea(SelectRectsType::ALL_LINES);
10112 
10113     return !selectArea.IsEmpty() && LessNotEqual(selectArea.Top(), keyboardInsert.start);
10114 }
10115 
10116 bool RichEditorPattern::IsReachedBoundary(float offset)
10117 {
10118     auto keyboardOffset = GetCrossOverHeight();
10119     return (NearEqual(richTextRect_.GetY(), contentRect_.GetY()) && GreatNotEqual(offset, 0.0f)) ||
10120            (NearEqual(richTextRect_.GetY() + richTextRect_.Height(),
10121                 contentRect_.GetY() + contentRect_.Height() - keyboardOffset) &&
10122                LessNotEqual(offset, 0.0f));
10123 }
10124 
10125 void RichEditorPattern::CheckScrollable()
10126 {
10127     auto gestureHub = GetGestureEventHub();
10128     CHECK_NULL_VOID(gestureHub);
10129     scrollable_ = GetTextContentLength() > 0 && GreatNotEqual(richTextRect_.Height(), contentRect_.Height());
10130     SetScrollEnabled(scrollable_);
10131 }
10132 
10133 void RichEditorPattern::UpdateChildrenOffset()
10134 {
10135     auto host = GetContentHost();
10136     CHECK_NULL_VOID(host);
10137     std::vector<int32_t> placeholderIndex;
10138     for (const auto& child : spans_) {
10139         if (!child) {
10140             continue;
10141         }
10142         if (AceType::InstanceOf<ImageSpanItem>(child) || AceType::InstanceOf<PlaceholderSpanItem>(child)) {
10143             placeholderIndex.emplace_back(child->placeholderIndex);
10144         }
10145     }
10146     if (spans_.empty() || placeholderIndex.empty()) {
10147         return;
10148     }
10149     size_t index = 0;
10150     std::vector<RectF> rectsForPlaceholders = paragraphs_.GetPlaceholderRects();
10151     auto childrenNodes = host->GetChildren();
10152     auto textOffset = GetTextRect().GetOffset();
10153     for (const auto& child : childrenNodes) {
10154         auto childNode = AceType::DynamicCast<FrameNode>(child);
10155         if (!childNode) {
10156             continue;
10157         }
10158         if (!(childNode->GetPattern<ImagePattern>() || childNode->GetPattern<PlaceholderSpanPattern>())) {
10159             continue;
10160         }
10161         if (isSpanStringMode_) {
10162             auto imageSpanNode = AceType::DynamicCast<ImageSpanNode>(child);
10163             if (imageSpanNode && imageSpanNode->GetSpanItem()) {
10164                 index = static_cast<uint32_t>(imageSpanNode->GetSpanItem()->placeholderIndex);
10165             }
10166         }
10167         if (index >= rectsForPlaceholders.size()) {
10168             break;
10169         }
10170         auto rect = rectsForPlaceholders.at(index);
10171         auto geometryNode = childNode->GetGeometryNode();
10172         if (geometryNode) {
10173             geometryNode->SetMarginFrameOffset(textOffset + OffsetF(rect.Left(), rect.Top()));
10174             childNode->ForceSyncGeometryNode();
10175             childNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
10176         }
10177         ++index;
10178     }
10179 }
10180 
10181 void RichEditorPattern::AutoScrollByEdgeDetection(AutoScrollParam param, OffsetF offset, EdgeDetectionStrategy strategy)
10182 {
10183     if (NearEqual(prevAutoScrollOffset_.GetY(), offset.GetY())) {
10184         return;
10185     }
10186     prevAutoScrollOffset_ = offset;
10187     auto contentRect = GetTextContentRect();
10188     auto isDragging = param.autoScrollEvent == AutoScrollEvent::DRAG;
10189     float edgeThreshold = isDragging ? AUTO_SCROLL_DRAG_EDGE_DISTANCE.ConvertToPx()
10190                                      : AUTO_SCROLL_EDGE_DISTANCE.ConvertToPx();
10191     auto maxHeight = isDragging ? frameRect_.Height() : contentRect.Height();
10192     if (GreatNotEqual(edgeThreshold * 2, maxHeight)) {
10193         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AutoScrollByEdgeDetection: hot area height is great than max height.");
10194         return;
10195     }
10196     float topEdgeThreshold = isDragging ? edgeThreshold : edgeThreshold + contentRect.GetY();
10197     float bottomThreshold = isDragging ? frameRect_.Height() - edgeThreshold : contentRect.Bottom() - edgeThreshold;
10198     if (param.autoScrollEvent == AutoScrollEvent::HANDLE) {
10199         auto handleTopOffset = offset;
10200         auto handleBottomOffset = OffsetF(offset.GetX(), offset.GetY() + param.handleRect.Height());
10201         if (GreatNotEqual(handleBottomOffset.GetY(), bottomThreshold)) {
10202             param.offset = bottomThreshold - handleBottomOffset.GetY();
10203             ScheduleAutoScroll(param);
10204         } else if (LessNotEqual(handleTopOffset.GetY(), topEdgeThreshold)) {
10205             param.offset = topEdgeThreshold - handleTopOffset.GetY();
10206             ScheduleAutoScroll(param);
10207         } else {
10208             StopAutoScroll();
10209         }
10210         return;
10211     }
10212     // drag and mouse
10213     if (GreatNotEqual(offset.GetY(), bottomThreshold)) {
10214         param.offset = isDragging ? -CalcDragSpeed(bottomThreshold, frameRect_.Height(), offset.GetY())
10215                                   : bottomThreshold - offset.GetY();
10216         ScheduleAutoScroll(param);
10217     } else if (LessNotEqual(offset.GetY(), topEdgeThreshold)) {
10218         param.offset = isDragging ? CalcDragSpeed(topEdgeThreshold, 0, offset.GetY())
10219                                   : topEdgeThreshold - offset.GetY();
10220         ScheduleAutoScroll(param);
10221     } else {
10222         StopAutoScroll();
10223     }
10224 }
10225 
10226 float RichEditorPattern::CalcDragSpeed(float hotAreaStart, float hotAreaEnd, float point)
10227 {
10228     auto distanceRatio = (point - hotAreaStart) / (hotAreaEnd - hotAreaStart);
10229     auto speedFactor = Curves::SHARP->MoveInternal(distanceRatio);
10230     return ((MAX_DRAG_SCROLL_SPEED * speedFactor) / TIME_UNIT) * AUTO_SCROLL_INTERVAL;
10231 }
10232 
10233 void RichEditorPattern::ScheduleAutoScroll(AutoScrollParam param)
10234 {
10235     if (GreatNotEqual(param.offset, 0.0f) && IsReachTop()) {
10236         return;
10237     }
10238     if (LessNotEqual(param.offset, 0.0f) && IsReachBottom()) {
10239         return;
10240     }
10241     auto context = GetContext();
10242     CHECK_NULL_VOID(context);
10243     auto taskExecutor = context->GetTaskExecutor();
10244     CHECK_NULL_VOID(taskExecutor);
10245     if (param.isFirstRun_) {
10246         param.isFirstRun_ = false;
10247         currentScrollParam_ = param;
10248         if (isAutoScrollRunning_) {
10249             return;
10250         }
10251     }
10252     autoScrollTask_.Reset([weak = WeakClaim(this)]() {
10253         auto client = weak.Upgrade();
10254         CHECK_NULL_VOID(client);
10255         client->OnAutoScroll(client->currentScrollParam_);
10256         if (client->IsReachTop() || client->IsReachBottom()) {
10257             client->StopAutoScroll();
10258         }
10259     });
10260     isAutoScrollRunning_ = true;
10261     taskExecutor->PostDelayedTask(autoScrollTask_, TaskExecutor::TaskType::UI, AUTO_SCROLL_INTERVAL,
10262         "ArkUIRichEditorScheduleAutoScroll");
10263 }
10264 
10265 void RichEditorPattern::OnAutoScroll(AutoScrollParam param)
10266 {
10267     if (param.showScrollbar) {
10268         auto scrollBar = GetScrollBar();
10269         IF_PRESENT(scrollBar, PlayScrollBarAppearAnimation());
10270         param.showScrollbar = false;
10271     }
10272     CHECK_NULL_VOID(param.autoScrollEvent != AutoScrollEvent::NONE);
10273     auto newOffset = MoveTextRect(param.offset);
10274     switch (param.autoScrollEvent) {
10275         case AutoScrollEvent::CARET:
10276             break;
10277         case AutoScrollEvent::HANDLE: {
10278             param.isFirstHandle ? MoveSecondHandle(newOffset) : MoveFirstHandle(newOffset);
10279             selectOverlay_->OnHandleMove(param.handleRect, param.isFirstHandle);
10280             break;
10281         }
10282         case AutoScrollEvent::DRAG:
10283             break;
10284         case AutoScrollEvent::MOUSE: {
10285             auto textOffset = ConvertTouchOffsetToTextOffset(param.eventOffset);
10286             int32_t extend = (GetTextContentLength() == 0) ? 0 : paragraphs_.GetIndex(textOffset);
10287             UpdateSelector(textSelector_.baseOffset, extend);
10288             SetCaretPosition(std::max(textSelector_.baseOffset, extend));
10289             break;
10290         }
10291         default:
10292             TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "Unsupported auto scroll event type");
10293             return;
10294     }
10295     CHECK_NULL_VOID(!NearEqual(newOffset, 0.0f));
10296     ScheduleAutoScroll(param);
10297 }
10298 
10299 void RichEditorPattern::StopAutoScroll()
10300 {
10301     isAutoScrollRunning_ = false;
10302     autoScrollTask_.Cancel();
10303     prevAutoScrollOffset_ = OffsetF(0.0f, 0.0f);
10304     auto scrollBar = GetScrollBar();
10305     IF_PRESENT(scrollBar, ScheduleDisappearDelayTask());
10306 }
10307 
10308 bool RichEditorPattern::NeedAiAnalysis(
10309     const CaretUpdateType targeType, const int32_t pos, const int32_t& spanStart, const std::string& content)
10310 {
10311     if (spanStart < 0) {
10312         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "NeedAiAnalysis -spanStart:%{public}d, return!", spanStart);
10313         return false;
10314     }
10315 
10316     if (!InputAIChecker::NeedAIAnalysis(content.empty(), targeType, lastClickTimeStamp_ - lastAiPosTimeStamp_)) {
10317         return false;
10318     }
10319 
10320     if (IsClickBoundary(pos) && targeType == CaretUpdateType::PRESSED) {
10321         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "NeedAiAnalysis IsClickBoundary, return!");
10322         return false;
10323     }
10324     EmojiRelation relation = GetEmojiRelation(pos);
10325     if (relation == EmojiRelation::IN_EMOJI || relation == EmojiRelation::MIDDLE_EMOJI ||
10326         relation == EmojiRelation::BEFORE_EMOJI) {
10327         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "NeedAiAnalysis emoji relation=%{public}d, return!", relation);
10328         return false;
10329     }
10330     return true;
10331 }
10332 
10333 void RichEditorPattern::AdjustCursorPosition(int32_t& pos)
10334 {
10335     // the rich text has some spans, the pos is belong to the whole richtext content, should use (pos - spanStarint)
10336     int32_t spanStart = -1;
10337     // get the span text by the position, maybe text is empty
10338     std::string content = GetPositionSpansText(pos, spanStart);
10339 
10340     if (NeedAiAnalysis(CaretUpdateType::PRESSED, pos, spanStart, content)) {
10341         int32_t aiPos = pos - spanStart;
10342         DataDetectorMgr::GetInstance().AdjustCursorPosition(aiPos, content, lastAiPosTimeStamp_, lastClickTimeStamp_);
10343         if (aiPos < 0) {
10344             return;
10345         }
10346         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "get ai pos:%{public}d--spanStart%{public}d", aiPos, spanStart);
10347         pos = aiPos + spanStart;
10348     }
10349 }
10350 
10351 bool RichEditorPattern::AdjustWordSelection(int32_t& start, int32_t& end)
10352 {
10353     // the rich text has some spans, the pos is belong to the whole richtext content, should use (pos - spanStarint)
10354     int32_t spanStart = -1;
10355     // get the span text by the position, maybe text is empty
10356     std::string content = GetPositionSpansText(start, spanStart);
10357     if (NeedAiAnalysis(CaretUpdateType::DOUBLE_CLICK, start, spanStart, content)) {
10358         int32_t aiPosStart = start - spanStart;
10359         int32_t aiPosEnd = end - spanStart;
10360         DataDetectorMgr::GetInstance().AdjustWordSelection(aiPosStart, content, aiPosStart, aiPosEnd);
10361         if (aiPosStart < 0 || aiPosEnd < 0) {
10362             return false;
10363         }
10364 
10365         start = std::min(aiPosStart + spanStart, GetTextContentLength());
10366         end = std::min(aiPosEnd + spanStart, GetTextContentLength());
10367         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "get ai selector [%{public}d--%{public}d]", start, end);
10368         return true;
10369     }
10370     return false;
10371 }
10372 
10373 void RichEditorPattern::AdjustPlaceholderSelection(int32_t& start, int32_t& end, const Offset& touchPos)
10374 {
10375     CHECK_NULL_VOID(!spans_.empty());
10376     if (!IsTouchBeforeCaret(start, touchPos)) {
10377         return;
10378     }
10379     auto it = std::find_if(spans_.begin(), spans_.end(), [start](const RefPtr<SpanItem>& spanItem) {
10380         return spanItem->position == start;
10381     });
10382     if (it != spans_.end()) {
10383         // adjust selection if touch right of image or placeholder
10384         auto spanIndex = std::distance(spans_.begin(), it);
10385         auto spanNodeBefore = DynamicCast<FrameNode>(GetChildByIndex(spanIndex));
10386         if (spanNodeBefore && (spanNodeBefore->GetTag() == V2::IMAGE_ETS_TAG ||
10387             spanNodeBefore->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG)) {
10388             end = start;
10389             --start;
10390             TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "get placeholder selector [%{public}d--%{public}d]", start, end);
10391         }
10392     }
10393 }
10394 
10395 bool RichEditorPattern::IsTouchAtLineEnd(int32_t caretPos, const Offset& textOffset)
10396 {
10397     auto positionWithAffinity = paragraphs_.GetGlyphPositionAtCoordinate(textOffset);
10398     TextAffinity currentAffinity = positionWithAffinity.affinity_;
10399     return currentAffinity == TextAffinity::UPSTREAM && !IsTouchBeforeCaret(caretPos, textOffset);
10400 }
10401 
10402 bool RichEditorPattern::IsTouchBeforeCaret(int32_t caretPos, const Offset& textOffset) {
10403     CHECK_NULL_RETURN(!spans_.empty(), false);
10404     float selectLineHeight = 0.0f;
10405     OffsetF caretOffsetUp = paragraphs_.ComputeCursorOffset(caretPos, selectLineHeight);
10406     auto needAdjustRect = RectF{ 0, caretOffsetUp.GetY(), caretOffsetUp.GetX(), selectLineHeight };
10407     return needAdjustRect.IsInRegion(PointF{ textOffset.GetX(), textOffset.GetY() });
10408 }
10409 
10410 bool RichEditorPattern::IsClickBoundary(const int32_t position)
10411 {
10412     if (InputAIChecker::IsSingleClickAtBoundary(position, GetTextContentLength())) {
10413         return true;
10414     }
10415 
10416     float height = 0;
10417     auto handleOffset = CalcCursorOffsetByPosition(position, height);
10418     if (InputAIChecker::IsMultiClickAtBoundary(handleOffset, TextPattern::GetTextRect())) {
10419         return true;
10420     }
10421     return false;
10422 }
10423 
10424 std::string RichEditorPattern::GetPositionSpansText(int32_t position, int32_t& startSpan)
10425 {
10426     int32_t start = position - AI_TEXT_RANGE_LEFT;
10427     int32_t end = position + AI_TEXT_RANGE_RIGHT;
10428 
10429     start = std::clamp(start, 0, GetTextContentLength());
10430     end = std::clamp(end, 0, GetTextContentLength());
10431     AdjustSelector(start, end);
10432     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "caret=%{public}d, range=[%{public}d,%{public}d]", position, start, end);
10433 
10434     // get all the spans between start and end, then filter the valid text
10435     auto infos = GetSpansInfo(start, end, GetSpansMethod::ONSELECT);
10436     if (infos.GetSelection().resultObjects.empty()) {
10437         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "get spans text is null pos:%{public}d,return", position);
10438         return "";
10439     }
10440     auto list = infos.GetSelection().resultObjects;
10441 
10442     std::stringstream sstream;
10443     for (const auto& obj : list) {
10444         if (obj.type != SelectSpanType::TYPESPAN) {
10445             if (obj.spanPosition.spanRange[0] == position) {
10446                 startSpan = -1;
10447                 return "";
10448             } else if (obj.spanPosition.spanRange[1] <= position) {
10449                 sstream.str("");
10450                 startSpan = -1;
10451             } else {
10452                 break;
10453             }
10454         } else {
10455             if (startSpan < 0) {
10456                 startSpan = obj.spanPosition.spanRange[0] + obj.offsetInSpan[0];
10457             }
10458             // we should use the wide string deal to avoid crash
10459             auto wideText = obj.valueString;
10460             int32_t textLen = static_cast<int32_t>(wideText.length());
10461             if (obj.offsetInSpan[0] < textLen && obj.offsetInSpan[1] <= textLen) {
10462                 sstream << UtfUtils::Str16ToStr8(
10463                     wideText.substr(obj.offsetInSpan[0], obj.offsetInSpan[1] - obj.offsetInSpan[0]));
10464             }
10465         }
10466     }
10467 
10468     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "get spans text ret spanStart:%{public}d", startSpan);
10469     return sstream.str();
10470 }
10471 
10472 bool RichEditorPattern::IsShowSelectMenuUsingMouse()
10473 {
10474     auto host = GetHost();
10475     CHECK_NULL_RETURN(host, false);
10476     auto pipeline = host->GetContext();
10477     CHECK_NULL_RETURN(pipeline, false);
10478     auto selectOverlayManager = pipeline->GetSelectOverlayManager();
10479     CHECK_NULL_RETURN(selectOverlayManager, false);
10480     return selectOverlayManager->GetSelectOverlayInfo().isUsingMouse;
10481 }
10482 
10483 RefPtr<FocusHub> RichEditorPattern::GetFocusHub() const
10484 {
10485     auto host = GetHost();
10486     CHECK_NULL_RETURN(host, nullptr);
10487     auto focusHub = host->GetOrCreateFocusHub();
10488     return focusHub;
10489 }
10490 
10491 void RichEditorPattern::HandleCursorOnDragMoved(const RefPtr<NotifyDragEvent>& notifyDragEvent)
10492 {
10493     auto host = GetHost();
10494     CHECK_NULL_VOID(host);
10495     if (HasFocus()) {
10496         if (!isCursorAlwaysDisplayed_) {
10497             isCursorAlwaysDisplayed_ = true;
10498             StartTwinkling();
10499         }
10500         if (SystemProperties::GetDebugEnabled()) {
10501             TAG_LOGD(AceLogTag::ACE_RICH_TEXT,
10502                 "In OnDragMoved, the cursor has always Displayed in the textField, id:" SEC_PLD(%{public}d),
10503                     SEC_PARAM(host->GetId()));
10504         }
10505         return;
10506     }
10507     TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
10508         "In OnDragMoved, the dragging node is moving in the richEditor, id:" SEC_PLD(%{public}d),
10509             SEC_PARAM(host->GetId()));
10510     auto focusHub = GetFocusHub();
10511     CHECK_NULL_VOID(focusHub);
10512     isOnlyRequestFocus_ = true;
10513     focusHub->RequestFocusImmediately();
10514     if (focusHub->IsCurrentFocus()) {
10515         ShowCaretWithoutTwinkling();
10516     }
10517 };
10518 
10519 void RichEditorPattern::HandleCursorOnDragLeaved(const RefPtr<NotifyDragEvent>& notifyDragEvent)
10520 {
10521     auto host = GetHost();
10522     CHECK_NULL_VOID(host);
10523     TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
10524         "In OnDragLeaved, the dragging node has left from richEditor, id:" SEC_PLD(%{public}d),
10525             SEC_PARAM(host->GetId()));
10526     auto focusHub = GetFocusHub();
10527     CHECK_NULL_VOID(focusHub);
10528     focusHub->LostFocusToViewRoot();
10529     StopTwinkling();
10530 };
10531 
10532 void RichEditorPattern::HandleCursorOnDragEnded(const RefPtr<NotifyDragEvent>& notifyDragEvent)
10533 {
10534     auto host = GetHost();
10535     CHECK_NULL_VOID(host);
10536     auto focusHub = GetFocusHub();
10537     CHECK_NULL_VOID(focusHub);
10538     StopAutoScroll();
10539     if (!isCursorAlwaysDisplayed_) {
10540         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "In OnDragEnded,"
10541             " the released location is not in the current richEditor, id:" SEC_PLD(%{public}d),
10542                 SEC_PARAM(host->GetId()));
10543         IF_TRUE(HasFocus(), CloseKeyboard(false));
10544         focusHub->LostFocus();
10545         StopTwinkling();
10546         return;
10547     }
10548     TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
10549         "In OnDragEnded, the released location is in the current richEditor, id:" SEC_PLD(%{public}d),
10550             SEC_PARAM(host->GetId()));
10551     focusHub->LostFocusToViewRoot();
10552     isCursorAlwaysDisplayed_ = false;
10553     StopTwinkling();
10554 };
10555 
10556 void RichEditorPattern::HandleOnDragStatusCallback(
10557     const DragEventType& dragEventType, const RefPtr<NotifyDragEvent>& notifyDragEvent)
10558 {
10559     ScrollablePattern::HandleOnDragStatusCallback(dragEventType, notifyDragEvent);
10560     switch (dragEventType) {
10561         case DragEventType::MOVE:
10562             HandleCursorOnDragMoved(notifyDragEvent);
10563             break;
10564         case DragEventType::LEAVE:
10565             HandleCursorOnDragLeaved(notifyDragEvent);
10566             break;
10567         case DragEventType::DROP:
10568             HandleCursorOnDragEnded(notifyDragEvent);
10569             break;
10570         default:
10571             break;
10572     }
10573 }
10574 
10575 void RichEditorPattern::HandleOnCameraInput()
10576 {
10577     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnCameraInput");
10578 #if defined(ENABLE_STANDARD_INPUT)
10579     if (richEditTextChangeListener_ == nullptr) {
10580         richEditTextChangeListener_ = new OnTextChangedListenerImpl(WeakClaim(this));
10581     }
10582     auto inputMethod = MiscServices::InputMethodController::GetInstance();
10583     if (!inputMethod) {
10584         return;
10585     }
10586     StartTwinkling();
10587 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
10588     if (imeShown_) {
10589         inputMethod->StartInputTypeAsync(MiscServices::InputType::CAMERA_INPUT);
10590     } else {
10591         HandleOnEditChanged(true);
10592         auto optionalTextConfig = GetMiscTextConfig();
10593         CHECK_NULL_VOID(optionalTextConfig.has_value());
10594         MiscServices::TextConfig textConfig = optionalTextConfig.value();
10595         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnCameraInput set calling window id is : %{public}u",
10596             textConfig.windowId);
10597 #ifdef WINDOW_SCENE_SUPPORTED
10598         auto systemWindowId = GetSCBSystemWindowId();
10599         if (systemWindowId) {
10600             TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "windowId From %{public}u to %{public}u.", textConfig.windowId,
10601                 systemWindowId);
10602             textConfig.windowId = systemWindowId;
10603         }
10604 #endif
10605         auto ret = inputMethod->Attach(richEditTextChangeListener_, false, textConfig);
10606         if (ret == MiscServices::ErrorCode::NO_ERROR) {
10607             auto pipeline = GetContext();
10608             CHECK_NULL_VOID(pipeline);
10609             auto textFieldManager = AceType::DynamicCast<TextFieldManagerNG>(pipeline->GetTextFieldManager());
10610             CHECK_NULL_VOID(textFieldManager);
10611             textFieldManager->SetIsImeAttached(true);
10612         }
10613         inputMethod->StartInputType(MiscServices::InputType::CAMERA_INPUT);
10614         inputMethod->ShowTextInput();
10615     }
10616     CloseSelectOverlay();
10617 #endif
10618 #endif
10619 }
10620 
10621 bool RichEditorPattern::CanStartAITask() const
10622 {
10623     return !isEditing_ && !spans_.empty() && TextPattern::CanStartAITask();
10624 }
10625 
10626 bool RichEditorPattern::NeedShowAIDetect()
10627 {
10628     return !isEditing_ && !isShowPlaceholder_ && !spans_.empty() && TextPattern::NeedShowAIDetect();
10629 }
10630 
10631 void RichEditorPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
10632 {
10633     /* no fixed attr below, just return */
10634     if (filter.IsFastFilter()) {
10635         return;
10636     }
10637     json->PutExtAttr("enableDataDetector", textDetectEnable_ ? "true" : "false", filter);
10638     json->PutExtAttr("dataDetectorConfig", dataDetectorAdapter_->textDetectConfigStr_.c_str(), filter);
10639     json->PutExtAttr("placeholder", GetPlaceHolderInJson().c_str(), filter);
10640     json->PutExtAttr("customKeyboard", GetCustomKeyboardInJson().c_str(), filter);
10641     json->PutExtAttr("bindSelectionMenu", GetBindSelectionMenuInJson().c_str(), filter);
10642     json->PutExtAttr("copyOptions", static_cast<int32_t>(copyOption_), filter);
10643     json->PutExtAttr("enablePreviewText", isTextPreviewSupported_ ? "true" : "false", filter);
10644     json->PutExtAttr("caretColor", GetCaretColor().ColorToString().c_str(), filter);
10645     json->PutExtAttr("selectedBackgroundColor", GetSelectedBackgroundColor().ColorToString().c_str(), filter);
10646     auto enterKeyType = static_cast<int32_t>(GetTextInputActionValue(GetDefaultTextInputAction()));
10647     json->PutExtAttr("enterKeyType", enterKeyType, filter);
10648     json->PutExtAttr("stopBackPress", isStopBackPress_ ? "true" : "false", filter);
10649     json->PutExtAttr("keyboardAppearance", static_cast<int32_t>(keyboardAppearance_), filter);
10650     json->PutExtAttr("maxLength", maxLength_.value_or(INT_MAX), filter);
10651     json->PutExtAttr("enableHapticFeedback", isEnableHapticFeedback_ ? "true" : "false", filter);
10652     json->PutExtAttr("barState", static_cast<int32_t>(GetBarDisplayMode()), filter);
10653     json->PutExtAttr("enableKeyboardOnFocus", needToRequestKeyboardOnFocus_ ? "true" : "false", filter);
10654     auto undoStyle = isStyledUndoSupported_ ? OHOS::Ace::UndoStyle::KEEP_STYLE : OHOS::Ace::UndoStyle::CLEAR_STYLE;
10655     json->PutExtAttr("undoStyle", static_cast<int32_t>(undoStyle), filter);
10656     json->PutExtAttr("enableAutoSpacing", isEnableAutoSpacing_ ? "true" : "false", filter);
10657 }
10658 
10659 std::string RichEditorPattern::GetCustomKeyboardInJson() const
10660 {
10661     auto jsonValue = JsonUtil::Create(true);
10662     jsonValue->Put("supportAvoidance", keyboardAvoidance_ ? "true" : "false");
10663     return StringUtils::RestoreBackslash(jsonValue->ToString());
10664 }
10665 
10666 void RichEditorPattern::FillPreviewMenuInJson(const std::unique_ptr<JsonValue>& jsonValue) const
10667 {
10668     IF_PRESENT(oneStepDragController_, FillJsonValue(jsonValue));
10669 }
10670 
10671 std::string RichEditorPattern::GetPlaceHolderInJson() const
10672 {
10673     auto host = GetHost();
10674     CHECK_NULL_RETURN(host, "");
10675     auto layoutProperty = host->GetLayoutProperty<RichEditorLayoutProperty>();
10676     bool hasPlaceHolder = layoutProperty && layoutProperty->HasPlaceholder()
10677         && !layoutProperty->GetPlaceholder().value().empty();
10678     CHECK_NULL_RETURN(hasPlaceHolder, "");
10679     auto jsonValue = JsonUtil::Create(true);
10680     jsonValue->Put("value", UtfUtils::Str16ToStr8(layoutProperty->GetPlaceholderValue(u"")).c_str());
10681     auto jsonFont = JsonUtil::Create(true);
10682     jsonFont->Put("size", GetFontSizeWithThemeInJson(layoutProperty->GetPlaceholderFontSize()).c_str());
10683     jsonFont->Put("weight", GetFontWeightInJson(layoutProperty->GetPlaceholderFontWeight()).c_str());
10684     jsonFont->Put("family", GetFontFamilyInJson(layoutProperty->GetPlaceholderFontFamily()).c_str());
10685     jsonFont->Put("style", GetFontStyleInJson(layoutProperty->GetPlaceholderItalicFontStyle()).c_str());
10686     auto jsonStyle = JsonUtil::Create(true);
10687     jsonStyle->Put("font", jsonFont->ToString().c_str());
10688     jsonStyle->Put("fontColor", GetTextColorInJson(layoutProperty->GetPlaceholderTextColor()).c_str());
10689     jsonValue->Put("style", jsonStyle->ToString().c_str());
10690     return StringUtils::RestoreBackslash(jsonValue->ToString());
10691 }
10692 
10693 std::string RichEditorPattern::GetTextColorInJson(const std::optional<Color>& value) const
10694 {
10695     CHECK_NULL_RETURN(!value, value->ColorToString());
10696     auto host = GetHost();
10697     CHECK_NULL_RETURN(host, "");
10698     auto pipeline = host->GetContext();
10699     CHECK_NULL_RETURN(pipeline, "");
10700     auto richEditorTheme = pipeline->GetTheme<RichEditorTheme>();
10701     CHECK_NULL_RETURN(richEditorTheme, "");
10702     Color textColor = richEditorTheme->GetTextStyle().GetTextColor();
10703     return textColor.ColorToString();
10704 }
10705 
10706 void RichEditorPattern::GetCaretMetrics(CaretMetricsF& caretCaretMetric)
10707 {
10708     float caretHeight = 0.0f;
10709     OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight);
10710     auto host = GetHost();
10711     CHECK_NULL_VOID(host);
10712     auto offset = host->GetPaintRectOffsetNG(false, true);
10713     caretOffset += offset;
10714     caretCaretMetric.offset = caretOffset;
10715     caretCaretMetric.height = caretHeight;
10716 }
10717 
10718 void RichEditorPattern::OnVirtualKeyboardAreaChanged()
10719 {
10720     CHECK_NULL_VOID(SelectOverlayIsOn() && !selectOverlay_->GetIsHandleMoving() && !selectOverlay_->GetIsHandleHidden());
10721     float selectLineHeight = 0.0f;
10722     textSelector_.selectionBaseOffset.SetX(
10723         CalcCursorOffsetByPosition(textSelector_.GetStart(), selectLineHeight).GetX());
10724     textSelector_.selectionDestinationOffset.SetX(
10725         CalcCursorOffsetByPosition(textSelector_.GetEnd(), selectLineHeight).GetX());
10726     CreateHandles();
10727 }
10728 
10729 void RichEditorPattern::ResetDragOption()
10730 {
10731     auto gestureEventHub = GetGestureEventHub();
10732     CHECK_NULL_VOID(gestureEventHub);
10733     if (gestureEventHub->GetIsTextDraggable()) {
10734         CloseSelectOverlay();
10735         ResetSelection();
10736     }
10737 }
10738 
10739 void RichEditorPattern::AdjustSelectRects(SelectRectsType pos, std::vector<RectF>& selectRects)
10740 {
10741     if (pos == SelectRectsType::LEFT_TOP_POINT) {
10742         selectRects.erase(std::next(selectRects.begin()), selectRects.end());
10743         selectRects.front().SetSize({0, 0});
10744     } else if (pos == SelectRectsType::RIGHT_BOTTOM_POINT) {
10745         selectRects.erase(selectRects.begin(), std::prev(selectRects.end()));
10746         selectRects.front().SetRect({selectRects.front().Right(), selectRects.front().Bottom()}, {0, 0});
10747     }
10748 }
10749 
10750 RectF RichEditorPattern::GetSelectArea(SelectRectsType pos)
10751 {
10752     RectF rect;
10753     auto paintOffset = selectOverlay_->GetPaintOffsetWithoutTransform();
10754     auto selectRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
10755     auto contentRect = contentRect_;
10756     contentRect.SetOffset(contentRect.GetOffset() + paintOffset);
10757     auto host = GetHost();
10758     CHECK_NULL_RETURN(host, rect);
10759     auto parent = host->GetAncestorNodeOfFrame(false);
10760     contentRect = GetVisibleContentRect(parent, contentRect);
10761     AppendSelectRect(selectRects);
10762     if (selectRects.empty()) {
10763         CHECK_NULL_RETURN(overlayMod_, rect);
10764         auto richEditorOverlay = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
10765         CHECK_NULL_RETURN(richEditorOverlay, rect);
10766         auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
10767         auto caretWidth = GetCaretWidth();
10768         auto selectRect = RectF(caretOffset + paintOffset, SizeF(caretWidth, caretHeight));
10769         return selectRect.IntersectRectT(contentRect);
10770     }
10771     AdjustSelectRects(pos, selectRects);
10772     auto frontRect = selectRects.front();
10773     auto backRect = selectRects.back();
10774     float selectAreaRight = frontRect.Right();
10775     float selectAreaLeft = frontRect.Left();
10776     if (selectRects.size() != 1) {
10777         std::unordered_map<float, RectF> selectLineRect;
10778         for (const auto& box : selectRects) {
10779             auto combineLineRect = box;
10780             auto top = box.Top();
10781             if (selectLineRect.find(top) == selectLineRect.end()) {
10782                 selectLineRect.insert({ top, combineLineRect });
10783             } else {
10784                 combineLineRect = combineLineRect.CombineRectT(selectLineRect[top]);
10785                 selectLineRect.insert({ top, combineLineRect });
10786             }
10787             selectAreaRight = std::max(selectAreaRight, combineLineRect.Right());
10788             selectAreaLeft = std::min(selectAreaLeft, combineLineRect.Left());
10789         }
10790     }
10791     RectF res = { selectAreaLeft + richTextRect_.GetX() + paintOffset.GetX(),
10792         frontRect.GetY() + richTextRect_.GetY() + paintOffset.GetY(), selectAreaRight - selectAreaLeft,
10793         backRect.Bottom() - frontRect.Top() };
10794     return res.IntersectRectT(contentRect);
10795 }
10796 
10797 void RichEditorPattern::AppendSelectRect(std::vector<RectF>& selectRects)
10798 {
10799     CHECK_NULL_VOID(!selectOverlay_->IsSingleHandle());
10800     auto startPosition = textSelector_.GetTextStart();
10801     auto endPosition = textSelector_.GetTextEnd();
10802     CHECK_NULL_VOID(GetParagraphEndPosition(startPosition) != GetParagraphEndPosition(endPosition));
10803     if (paragraphs_.IsIndexAtParagraphEnd(startPosition + 1)) {
10804         selectRects.insert(selectRects.begin(), CreateNewLineRect(startPosition, false));
10805     }
10806     if (paragraphs_.IsIndexAtParagraphEnd(endPosition)) {
10807         selectRects.emplace_back(CreateNewLineRect(endPosition, true));
10808     }
10809 }
10810 
10811 RectF RichEditorPattern::CreateNewLineRect(const int32_t position, const bool downStreamFirst)
10812 {
10813     auto paragraphInfo = paragraphs_.GetParagraphInfo(position);
10814     auto& style = paragraphInfo.paragraphStyle;
10815     auto height = 0.0f;
10816     auto offset = paragraphs_.ComputeCursorOffset(position, height, downStreamFirst, true);
10817     auto caretWidth = GetCaretWidth();
10818     RectF rect = RectF(offset.GetX(), offset.GetY(), 0, height);
10819     auto textAlign = TextBase::CheckTextAlignByDirection(style.align, style.direction);
10820     TextBase::UpdateSelectedBlankLineRect(rect, caretWidth, textAlign, GetTextContentRect().Width());
10821     return rect;
10822 }
10823 
10824 bool RichEditorPattern::NeedShowPlaceholder() const
10825 {
10826     auto host = GetHost();
10827     CHECK_NULL_RETURN(host, false);
10828     auto layoutProperty = host->GetLayoutProperty<RichEditorLayoutProperty>();
10829     CHECK_NULL_RETURN(layoutProperty, false);
10830     return layoutProperty->HasPlaceholder() && !layoutProperty->GetPlaceholder().value().empty();
10831 }
10832 
10833 bool RichEditorPattern::IsTouchInFrameArea(const PointF& touchPoint)
10834 {
10835     auto host = GetHost();
10836     CHECK_NULL_RETURN(host, false);
10837     auto viewPort = RectF(parentGlobalOffset_, frameRect_.GetSize());
10838     auto parent = host->GetAncestorNodeOfFrame(false);
10839     viewPort = GetVisibleContentRect(parent, viewPort);
10840     return viewPort.IsInRegion(touchPoint);
10841 }
10842 
10843 bool RichEditorPattern::SetPlaceholder(std::vector<std::list<RefPtr<SpanItem>>>& spanItemList)
10844 {
10845     if (!spans_.empty()) {
10846         isShowPlaceholder_ = false;
10847         return false;
10848     }
10849     auto host = GetHost();
10850     CHECK_NULL_RETURN(host, false);
10851     auto layoutProperty = host->GetLayoutProperty<RichEditorLayoutProperty>();
10852     CHECK_NULL_RETURN(layoutProperty, false);
10853     if (!layoutProperty->HasPlaceholder() || layoutProperty->GetPlaceholder().value().empty()) {
10854         isShowPlaceholder_ = false;
10855         return false;
10856     }
10857     auto placeholderValue = layoutProperty->GetPlaceholder().value();
10858     auto placeholderNode = SpanNode::GetOrCreateSpanNode(ElementRegister::GetInstance()->MakeUniqueId());
10859     CHECK_NULL_RETURN(placeholderNode, false);
10860     if (layoutProperty->HasPlaceholderFontSize()) {
10861         placeholderNode->UpdateFontSize(layoutProperty->GetPlaceholderFontSize().value());
10862     }
10863     if (layoutProperty->HasPlaceholderFontWeight()) {
10864         placeholderNode->UpdateFontWeight(layoutProperty->GetPlaceholderFontWeight().value());
10865     }
10866     if (layoutProperty->HasPlaceholderFontFamily()) {
10867         placeholderNode->UpdateFontFamily(layoutProperty->GetPlaceholderFontFamily().value());
10868     }
10869     if (layoutProperty->HasPlaceholderItalicFontStyle()) {
10870         placeholderNode->UpdateItalicFontStyle(layoutProperty->GetPlaceholderItalicFontStyle().value());
10871     }
10872     if (layoutProperty->HasPlaceholderTextColor()) {
10873         placeholderNode->UpdateTextColorWithoutCheck(layoutProperty->GetPlaceholderTextColor().value());
10874     } else {
10875         auto theme = GetTheme<RichEditorTheme>();
10876         placeholderNode->UpdateTextColorWithoutCheck(theme ? theme->GetPlaceholderColor() : Color());
10877     }
10878 
10879     auto spanItem = placeholderNode->GetSpanItem();
10880     CHECK_NULL_RETURN(spanItem, false);
10881     spanItem->content = placeholderValue;
10882     spanItemList.clear();
10883     spanItemList.push_back({ { {spanItem} } });
10884     isShowPlaceholder_ = true;
10885     return true;
10886 }
10887 
10888 std::string RichEditorPattern::GetPlaceHolder() const
10889 {
10890     auto host = GetHost();
10891     CHECK_NULL_RETURN(host, "");
10892     auto layoutProperty = host->GetLayoutProperty<RichEditorLayoutProperty>();
10893     CHECK_NULL_RETURN(layoutProperty, "");
10894     return UtfUtils::Str16ToStr8(layoutProperty->GetPlaceholderValue(u""));
10895 }
10896 
10897 Color RichEditorPattern::GetCaretColor() const
10898 {
10899     if (caretColor_.has_value()) {
10900         return caretColor_.value();
10901     }
10902     auto richEditorTheme = GetTheme<RichEditorTheme>();
10903     CHECK_NULL_RETURN(richEditorTheme, SYSTEM_CARET_COLOR);
10904     return richEditorTheme->GetCaretColor();
10905 }
10906 
10907 Color RichEditorPattern::GetSelectedBackgroundColor() const
10908 {
10909     Color selectedBackgroundColor;
10910     if (selectedBackgroundColor_.has_value()) {
10911         selectedBackgroundColor = selectedBackgroundColor_.value();
10912     } else {
10913         auto richEditorTheme = GetTheme<RichEditorTheme>();
10914         CHECK_NULL_RETURN(richEditorTheme, SYSTEM_SELECT_BACKGROUND_COLOR);
10915         selectedBackgroundColor = richEditorTheme->GetSelectedBackgroundColor();
10916     }
10917     // Alpha == 255 Means completely opaque
10918     if (selectedBackgroundColor.GetAlpha() == COLOR_OPAQUE) {
10919         selectedBackgroundColor = selectedBackgroundColor.ChangeOpacity(DEFAILT_OPACITY);
10920     }
10921     return selectedBackgroundColor;
10922 }
10923 
10924 void RichEditorPattern::HandleOnDragDropStyledString(const RefPtr<OHOS::Ace::DragEvent>& event, bool isCopy)
10925 {
10926     CHECK_NULL_VOID(event);
10927     auto data = event->GetData();
10928     CHECK_NULL_VOID(data);
10929     auto arr = UdmfClient::GetInstance()->GetSpanStringRecord(data);
10930     if (!arr.empty()) {
10931         auto spanStr = SpanString::DecodeTlv(arr);
10932         if (!spanStr->GetSpanItems().empty()) {
10933             if (isSpanStringMode_) {
10934                 HandleOnDragInsertStyledString(spanStr, isCopy);
10935                 return;
10936             }
10937             AddSpanByPasteData(spanStr, TextChangeReason::DRAG);
10938             return;
10939         }
10940     }
10941 
10942     auto records = UdmfClient::GetInstance()->GetPlainTextRecords(data);
10943     if (records.empty()) {
10944         return;
10945     }
10946     std::string str;
10947     for (const auto& record : records) {
10948         str += record;
10949     }
10950     if (str.empty()) {
10951         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "text is empty.");
10952         return;
10953     }
10954     if (isSpanStringMode_) {
10955         InsertValueInStyledString(UtfUtils::Str8ToStr16(str), true);
10956     } else {
10957         HandleOnDragDropTextOperation(UtfUtils::Str8ToStr16(str), isDragSponsor_, isCopy);
10958     }
10959 }
10960 
10961 void RichEditorPattern::HandleOnDragDrop(const RefPtr<OHOS::Ace::DragEvent>& event, bool isCopy)
10962 {
10963     auto host = GetHost();
10964     CHECK_NULL_VOID(host);
10965     auto eventHub = host->GetOrCreateEventHub<RichEditorEventHub>();
10966     CHECK_NULL_VOID(eventHub);
10967     TextCommonEvent textCommonEvent;
10968     if (textCommonEvent.IsPreventDefault()) {
10969         CloseSelectOverlay();
10970         ResetSelection();
10971         StartTwinkling();
10972         return;
10973     }
10974     HandleOnDragDropStyledString(event, isCopy);
10975     if (textSelector_.IsValid()) {
10976         CloseSelectOverlay();
10977         ResetSelection();
10978     }
10979     auto focusHub = host->GetOrCreateFocusHub();
10980     CHECK_NULL_VOID(focusHub);
10981     if (focusHub->IsCurrentFocus()) {
10982         StartTwinkling();
10983     }
10984     afterDragSelect_ = isMouseOrTouchPad(sourceTool_);
10985     releaseInDrop_ = true;
10986 }
10987 
10988 void RichEditorPattern::DeleteForward(int32_t currentPosition, int32_t length)
10989 {
10990     RichEditorDeleteValue info;
10991     info.SetOffset(currentPosition);
10992     info.SetRichEditorDeleteDirection(RichEditorDeleteDirection::FORWARD);
10993     info.SetLength(length);
10994     CalcDeleteValueObj(currentPosition, length, info);
10995     DeleteByDeleteValueInfo(info);
10996     OnReportRichEditorEvent("onDeleteComplete");
10997 }
10998 
10999 int32_t RichEditorPattern::HandleOnDragDeleteForward(int32_t currentPosition)
11000 {
11001     int32_t allDelLength = 0;
11002     SelectionInfo textSelectInfo = GetSpansInfo(dragRange_.first, dragRange_.second, GetSpansMethod::ONSELECT);
11003     std::list<ResultObject> dragResultObjects = textSelectInfo.GetSelection().resultObjects;
11004     auto dragLength = dragRange_.second - dragRange_.first;
11005     UndoRedoRecord styledRecord;
11006     styledRecord.restoreBuilderSpan = true;
11007     undoManager_->RecordSelectionBefore(TextRange{ currentPosition, currentPosition });
11008     undoManager_->UpdateRecordBeforeChange(dragRange_.first, dragLength, styledRecord);
11009     for (auto ri = dragResultObjects.rbegin(); ri != dragResultObjects.rend(); ++ri) {
11010         if (SelectSpanType::TYPESPAN == ri->type || (SelectSpanType::TYPEIMAGE == ri->type && ri->valueString != u" ")) {
11011             int32_t spanStart = ri->offsetInSpan[RichEditorSpanRange::RANGESTART];
11012             int32_t spanEnd = ri->offsetInSpan[RichEditorSpanRange::RANGEEND];
11013             int32_t reStart = ri->spanPosition.spanRange[RichEditorSpanRange::RANGESTART];
11014             int32_t delStart = reStart;
11015             if (spanStart > 0) {
11016                 delStart += spanStart;
11017             }
11018             int32_t delLength = spanEnd - spanStart;
11019             DeleteForward(delStart, delLength);
11020             allDelLength += delLength;
11021         }
11022     }
11023     undoManager_->RecordOperationAfterChange(dragRange_.first, dragLength - allDelLength, styledRecord);
11024     return allDelLength;
11025 }
11026 
11027 void RichEditorPattern::HandleOnDragDropTextOperation(const std::u16string& insertValue, bool isDeleteSelect, bool isCopy)
11028 {
11029     insertValueLength_ = static_cast<int32_t>(insertValue.length());
11030     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "DragDropText, len=%{public}d, del=%{public}d, copy=%{public}d, dragRange="
11031         "[%{public}d,%{public}d]", insertValueLength_, isDeleteSelect, isCopy, dragRange_.first, dragRange_.second);
11032     if (!isDeleteSelect || isCopy) {
11033         InsertValueByOperationType(insertValue, OperationType::DRAG);
11034         return;
11035     }
11036     int32_t currentPosition = caretPosition_;
11037     int32_t strLength = static_cast<int32_t>(insertValue.length());
11038     OperationRecord record;
11039     record.addText = insertValue;
11040     record.beforeCaretPosition = dragRange_.first;
11041     RichEditorChangeValue changeValue(TextChangeReason::DRAG);
11042     // drag not move, do not fire contentChange
11043     if (dragRange_.first <= currentPosition && currentPosition <= dragRange_.second) {
11044         lastCaretPosition_ = dragRange_.first;
11045         return;
11046     }
11047     CHECK_NULL_VOID(BeforeChangeText(changeValue, record, RecordType::DRAG));
11048     if (currentPosition < dragRange_.first) {
11049         InsertValueByOperationType(insertValue, OperationType::DRAG);
11050         dragRange_.first += strLength;
11051         dragRange_.second += strLength;
11052         HandleOnDragDeleteForward(currentPosition);
11053         lastCaretPosition_ = currentPosition;
11054     } else if (currentPosition > dragRange_.second) {
11055         InsertValueByOperationType(insertValue, OperationType::DRAG);
11056         int32_t delLength = HandleOnDragDeleteForward(currentPosition);
11057         caretPosition_ -= delLength;
11058         lastCaretPosition_ = currentPosition - strLength;
11059     }
11060 
11061     AfterContentChange(changeValue);
11062 }
11063 
11064 void RichEditorPattern::UndoDrag(const OperationRecord& record)
11065 {
11066     if (!record.addText.has_value() || record.deleteCaretPosition == -1) {
11067         return;
11068     }
11069     const auto& str = record.addText.value();
11070     int32_t length = static_cast<int32_t>(str.length());
11071     DeleteForward(record.beforeCaretPosition, length);
11072 
11073     lastCaretPosition_ = caretPosition_;
11074     caretPosition_ = record.deleteCaretPosition;
11075     InsertValueOperation(str, nullptr, OperationType::DEFAULT);
11076 }
11077 
11078 void RichEditorPattern::RedoDrag(const OperationRecord& record)
11079 {
11080     if (!record.addText.has_value() || record.deleteCaretPosition == -1) {
11081         return;
11082     }
11083     RichEditorChangeValue changeValue(TextChangeReason::REDO);
11084     CHECK_NULL_VOID(BeforeChangeText(changeValue, record, RecordType::REDO));
11085     const auto& str = record.addText.value();
11086     int32_t length = static_cast<int32_t>(str.length());
11087     DeleteForward(record.deleteCaretPosition, length);
11088     lastCaretPosition_ = caretPosition_;
11089     caretPosition_ = record.beforeCaretPosition;
11090     InsertValueOperation(str, nullptr, OperationType::DRAG);
11091     AfterContentChange(changeValue);
11092 }
11093 
11094 bool RichEditorPattern::IsEditing()
11095 {
11096     return isEditing_;
11097 }
11098 
11099 void RichEditorPattern::HandleOnEditChanged(bool isEditing)
11100 {
11101     CHECK_NULL_VOID(isEditing_ != isEditing);
11102     auto host = GetHost();
11103     CHECK_NULL_VOID(host);
11104     auto eventHub = host->GetOrCreateEventHub<RichEditorEventHub>();
11105     CHECK_NULL_VOID(eventHub);
11106 
11107     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "editState->%{public}d", isEditing);
11108     isEditing_ = isEditing;
11109     eventHub->FireOnEditingChange(isEditing);
11110 
11111     if (CanStartAITask()) {
11112         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "leave edit state, start AI task");
11113         dataDetectorAdapter_->StartAITask();
11114     } else {
11115         if (isEditing) {
11116             TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "enter edit state, reset previewLongPress_");
11117             previewLongPress_ = false;
11118         }
11119         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
11120     }
11121 }
11122 
11123 void RichEditorPattern::ResetKeyboardIfNeed()
11124 {
11125     bool needToResetKeyboard = false;
11126     auto currentAction = GetTextInputActionValue(GetDefaultTextInputAction());
11127     // When the enter key type changes, the keyboard needs to be reset.
11128     if (action_ != TextInputAction::UNSPECIFIED) {
11129         needToResetKeyboard = action_ != currentAction;
11130     }
11131     action_ = currentAction;
11132 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
11133     if (needToResetKeyboard) {
11134         // if keyboard attached or keyboard is shown, pull up keyboard again
11135         if (imeShown_ || isCustomKeyboardAttached_) {
11136             if (HasFocus()) {
11137                 RequestKeyboard(false, true, true);
11138             }
11139             return;
11140         }
11141 #if defined(ENABLE_STANDARD_INPUT)
11142         auto inputMethod = MiscServices::InputMethodController::GetInstance();
11143         CHECK_NULL_VOID(inputMethod);
11144         MiscServices::Configuration config;
11145         config.SetEnterKeyType(static_cast<MiscServices::EnterKeyType>(action_));
11146         config.SetTextInputType(static_cast<MiscServices::TextInputType>(keyboard_));
11147         inputMethod->OnConfigurationChange(config);
11148 #endif
11149     }
11150 #else
11151     if (needToResetKeyboard && HasConnection()) {
11152         CloseSelectOverlay();
11153         ResetSelection();
11154         CloseKeyboard(false);
11155         RequestKeyboard(false, true, true);
11156     }
11157 #endif
11158 }
11159 
11160 void RichEditorPattern::OnTextInputActionUpdate(TextInputAction value) {}
11161 
11162 void RichEditorPattern::PerformAction(TextInputAction action, bool forceCloseKeyboard)
11163 {
11164     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "PerformAction, action=%{public}d", action);
11165     auto host = GetHost();
11166     CHECK_NULL_VOID(host);
11167     // When the Enter key is triggered, perform a line feed operation.
11168     if (action == TextInputAction::NEW_LINE) {
11169         InsertValue(u"\n", true);
11170     }
11171     // Enter key type callback
11172     TextFieldCommonEvent event;
11173     auto eventHub = host->GetOrCreateEventHub<RichEditorEventHub>();
11174     eventHub->FireOnSubmit(static_cast<int32_t>(action), event);
11175     // If the developer wants to keep editing, editing will not stop
11176     if (event.IsKeepEditable() || action == TextInputAction::NEW_LINE) {
11177         return;
11178     }
11179     // Exit the editing state
11180     StopEditing();
11181 }
11182 
11183 void RichEditorPattern::StopEditing()
11184 {
11185     CHECK_NULL_VOID(HasFocus());
11186     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "StopEditing");
11187 
11188 #ifdef IOS_PLATFORM
11189     CloseKeyboard(false);
11190 #endif
11191     // In order to avoid the physical keyboard being able to type, you need to make sure that you lose focus
11192     FocusHub::LostFocusToViewRoot();
11193 }
11194 
11195 TextInputAction RichEditorPattern::GetDefaultTextInputAction() const
11196 {
11197     // As with TextInput, it is a line break by default
11198     return TextInputAction::NEW_LINE;
11199 }
11200 
11201 void RichEditorPattern::GetChangeSpanStyle(RichEditorChangeValue& changeValue, std::optional<TextStyle>& spanTextStyle,
11202     std::optional<struct UpdateParagraphStyle>& spanParaStyle, std::optional<std::u16string>& urlAddress,
11203     const RefPtr<SpanNode>& spanNode, int32_t spanIndex, bool useTypingParaStyle)
11204 {
11205     auto originalSpans = changeValue.GetRichEditorOriginalSpans();
11206     if (spanIndex == 0 && originalSpans.size()) {
11207         const RichEditorAbstractSpanResult& firstInfo = originalSpans.front();
11208         const RichEditorAbstractSpanResult& lastInfo = originalSpans.back();
11209         int32_t firstLength = static_cast<int32_t>(firstInfo.GetValue().length());
11210         int32_t lastLength = static_cast<int32_t>(lastInfo.GetValue().length());
11211         if (firstInfo.GetEraseLength() == firstLength && lastInfo.GetEraseLength() == lastLength) {
11212             if (spans_.size() == originalSpans.size() ||
11213                 static_cast<int32_t>(spans_.size()) == (lastInfo.GetSpanIndex() + 1)) {
11214                 urlAddress.reset();
11215                 return; // all spanNode be deleted, set default style
11216             }
11217             spanIndex = lastInfo.GetSpanIndex() + 1;
11218         } else if (firstInfo.GetEraseLength() == firstLength) {
11219             spanIndex = lastInfo.GetSpanIndex();
11220         }
11221         auto it = spans_.begin();
11222         std::advance(it, spanIndex);
11223         if ((*it)->unicode != 0 || DynamicCast<PlaceholderSpanItem>(*it)) {
11224             return; // is not a textSpan(Image/Symbol/other)
11225         }
11226         spanTextStyle = (*it)->GetTextStyle();
11227         if (!useTypingParaStyle) {
11228             struct UpdateParagraphStyle paraStyle;
11229             paraStyle.textAlign = (*it)->textLineStyle->GetTextAlign();
11230             paraStyle.leadingMargin = (*it)->textLineStyle->GetLeadingMargin();
11231             paraStyle.wordBreak = (*it)->textLineStyle->GetWordBreak();
11232             paraStyle.lineBreakStrategy = (*it)->textLineStyle->GetLineBreakStrategy();
11233             paraStyle.paragraphSpacing = (*it)->textLineStyle->GetParagraphSpacing();
11234             paraStyle.textVerticalAlign = (*it)->textLineStyle->GetTextVerticalAlign();
11235             spanParaStyle = paraStyle;
11236         }
11237     } else if (spanNode && spanNode->GetSpanItem()) {
11238         spanTextStyle = spanNode->GetSpanItem()->GetTextStyle();
11239         if (!useTypingParaStyle) {
11240             struct UpdateParagraphStyle paraStyle;
11241             paraStyle.textAlign = spanNode->GetTextAlign();
11242             paraStyle.leadingMargin = spanNode->GetLeadingMarginValue({});
11243             paraStyle.wordBreak = spanNode->GetWordBreak();
11244             paraStyle.lineBreakStrategy = spanNode->GetLineBreakStrategy();
11245             paraStyle.paragraphSpacing = spanNode->GetParagraphSpacing();
11246             paraStyle.textVerticalAlign = spanNode->GetTextVerticalAlign();
11247             spanParaStyle = paraStyle;
11248         }
11249     }
11250 }
11251 
11252 void RichEditorPattern::GetReplacedSpan(RichEditorChangeValue& changeValue, int32_t& innerPosition,
11253     const std::u16string& insertValue, int32_t textIndex, std::optional<TextStyle> textStyle,
11254     std::optional<struct UpdateParagraphStyle> paraStyle, std::optional<std::u16string> urlAddress,
11255     bool isCreate, bool fixDel)
11256 {
11257     bool useTypingParaStyle = styleManager_->UseTypingParaStyle(spans_, changeValue);
11258     IF_TRUE(useTypingParaStyle, paraStyle = styleManager_->GetTypingParagraphStyle());
11259 
11260     std::u16string originalStr;
11261     int32_t originalPos = 0;
11262     RefPtr<SpanItem> spanItem = fixDel ? GetDelPartiallySpanItem(changeValue, originalStr, originalPos) : nullptr;
11263     TextInsertValueInfo info;
11264     CalcInsertValueObj(info, textIndex, isCreate);
11265     int32_t spanIndex = info.GetSpanIndex();
11266     int32_t offsetInSpan = info.GetOffsetInSpan();
11267     auto host = GetContentHost();
11268     CHECK_NULL_VOID(host);
11269     auto uiNode = host->GetChildAtIndex(spanIndex);
11270     RefPtr<SpanNode> spanNode = DynamicCast<SpanNode>(uiNode);
11271     if (!isCreate && textIndex && uiNode && uiNode->GetTag() != V2::SPAN_ETS_TAG) {
11272         spanNode = nullptr;
11273         ++spanIndex; // select/create a new span When the span is not a textSpan(Image/Symbol/other)
11274         offsetInSpan = 0;
11275         spanNode = DynamicCast<SpanNode>(host->GetChildAtIndex(spanIndex));
11276     }
11277 
11278     changeValue.SetRangeAfter({ innerPosition, innerPosition + insertValue.length()});
11279     std::u16string textTemp = insertValue;
11280     if (!textStyle && !isCreate && spanNode) {
11281         if (typingStyle_ && !HasSameTypingStyle(spanNode)) {
11282             textStyle = typingTextStyle_; // create a new span When have a different typingStyle
11283             bool insertInSpan = textIndex && offsetInSpan;
11284             spanIndex = insertInSpan ? spanIndex + 1 : spanIndex;
11285             offsetInSpan = 0;
11286         } else {
11287             textTemp = spanNode->GetSpanItem()->content;
11288             textTemp.insert(offsetInSpan, insertValue);
11289             urlAddress = spanNode->GetSpanItem()->urlAddress;
11290         }
11291     }
11292 
11293     auto it = textTemp.find(LINE_SEPARATOR);
11294     bool containNextLine = it != std::u16string::npos && it != textTemp.size() - 1;
11295 
11296     if (textStyle || containNextLine) { // SpanNode Fission
11297         GetReplacedSpanFission(changeValue, innerPosition, textTemp, spanIndex, offsetInSpan, textStyle, paraStyle,
11298             urlAddress);
11299     } else {
11300         std::optional<TextStyle> spanTextStyle = textStyle ? textStyle : typingTextStyle_;
11301         GetChangeSpanStyle(changeValue, spanTextStyle, paraStyle, urlAddress, spanNode, spanIndex, useTypingParaStyle);
11302         CreateSpanResult(changeValue, innerPosition, spanIndex, offsetInSpan, offsetInSpan + insertValue.length(),
11303             textTemp, spanTextStyle, paraStyle, urlAddress);
11304         innerPosition += static_cast<int32_t>(insertValue.length());
11305     }
11306 
11307     if (spanItem) {
11308         spanItem->content = originalStr;
11309         spanItem->position = originalPos;
11310     }
11311 }
11312 
11313 void RichEditorPattern::GetReplacedSpanFission(RichEditorChangeValue& changeValue, int32_t& innerPosition,
11314     std::u16string& content, int32_t startSpanIndex, int32_t offsetInSpan, std::optional<TextStyle> textStyle,
11315     std::optional<struct UpdateParagraphStyle> paraStyle, const std::optional<std::u16string>& urlAddress)
11316 {
11317     int spanIndex = startSpanIndex;
11318     auto index = content.find(LINE_SEPARATOR);
11319     while (index != std::u16string::npos) {
11320         auto textAfter = content.substr(index + 1);
11321         if (textAfter.empty()) {
11322             break;
11323         }
11324         auto textBefore = content.substr(0, index + 1);
11325         if (offsetInSpan != static_cast<int32_t>(textBefore.length())) {
11326             CreateSpanResult(changeValue, innerPosition, spanIndex, offsetInSpan, textBefore.length(),
11327                 textBefore, textStyle, paraStyle, urlAddress);
11328             innerPosition += textBefore.length() - offsetInSpan;
11329         }
11330         content = textAfter;
11331         index = content.find(LINE_SEPARATOR);
11332         offsetInSpan = 0;
11333         ++spanIndex;
11334     }
11335     CreateSpanResult(changeValue, innerPosition, spanIndex, offsetInSpan, content.length(),
11336         content, textStyle, paraStyle, urlAddress);
11337     innerPosition += static_cast<int32_t>(content.length());
11338 }
11339 
11340 void RichEditorPattern::CreateSpanResult(RichEditorChangeValue& changeValue, int32_t& innerPosition, int32_t spanIndex,
11341     int32_t offsetInSpan, int32_t endInSpan, std::u16string content, std::optional<TextStyle> textStyle,
11342     std::optional<struct UpdateParagraphStyle> paraStyle, const std::optional<std::u16string>& urlAddress)
11343 {
11344     RichEditorAbstractSpanResult retInfo;
11345     if (textStyle) {
11346         SetTextStyleToRet(retInfo, *textStyle);
11347     } else {
11348         SetThemeTextStyleToRet(retInfo);
11349     }
11350     IF_TRUE(urlAddress.has_value(), retInfo.SetUrlAddress(urlAddress.value()));
11351     retInfo.SetSpanIndex(spanIndex);
11352     if (!previewTextRecord_.newPreviewContent.empty()) {
11353         retInfo.SetPreviewText(previewTextRecord_.newPreviewContent);
11354     } else {
11355         retInfo.SetValue(content);
11356     }
11357     int32_t rangStart = std::max(0, innerPosition - offsetInSpan);
11358     retInfo.SetSpanRangeStart(rangStart);
11359     retInfo.SetSpanRangeEnd(rangStart + content.length());
11360     retInfo.SetOffsetInSpan(offsetInSpan);
11361     retInfo.SetEraseLength(endInSpan - offsetInSpan);
11362     SetParaStyleToRet(retInfo, paraStyle);
11363     changeValue.SetRichEditorReplacedSpans(retInfo);
11364 }
11365 
11366 void RichEditorPattern::SetTextStyleToRet(RichEditorAbstractSpanResult& retInfo, const TextStyle& textStyle)
11367 {
11368     retInfo.SetTextDecoration(textStyle.GetTextDecorationFirst());
11369     retInfo.SetFontColor(textStyle.GetTextColor().ColorToString());
11370     retInfo.SetColor(textStyle.GetTextDecorationColor().ColorToString());
11371     retInfo.SetTextDecorationStyle(textStyle.GetTextDecorationStyle());
11372     retInfo.SetLineThicknessScale(textStyle.GetLineThicknessScale());
11373     retInfo.SetFontSize(textStyle.GetFontSize().ConvertToVp());
11374     retInfo.SetFontStyle(textStyle.GetFontStyle());
11375     TextStyleResult textStyleResult;
11376     textStyleResult.lineHeight = textStyle.GetLineHeight().ConvertToVp();
11377     textStyleResult.halfLeading = textStyle.GetHalfLeading();
11378     textStyleResult.letterSpacing = textStyle.GetLetterSpacing().ConvertToVp();
11379     textStyleResult.textShadows = textStyle.GetTextShadows();
11380     textStyleResult.textBackgroundStyle = textStyle.GetTextBackgroundStyle();
11381     retInfo.SetTextStyle(textStyleResult);
11382     retInfo.SetLineHeight(textStyle.GetLineHeight().ConvertToVp());
11383     retInfo.SetHalfLeading(textStyle.GetHalfLeading());
11384     retInfo.SetLetterspacing(textStyle.GetLetterSpacing().ConvertToVp());
11385     retInfo.SetFontFeature(textStyle.GetFontFeatures());
11386     std::string fontFamilyValue;
11387     auto fontFamily = textStyle.GetFontFamilies();
11388     for (const auto& str : fontFamily) {
11389         fontFamilyValue += str;
11390     }
11391     retInfo.SetFontFamily(fontFamilyValue);
11392     retInfo.SetFontWeight((int32_t)textStyle.GetFontWeight());
11393 }
11394 
11395 void RichEditorPattern::SetThemeTextStyleToRet(RichEditorAbstractSpanResult& retInfo)
11396 {
11397     auto theme = GetTheme<RichEditorTheme>();
11398     TextStyle style = theme ? theme->GetTextStyle() : TextStyle();
11399     retInfo.SetFontColor(style.GetTextColor().ColorToString());
11400     retInfo.SetFontSize(Dimension(DEFAULT_TEXT_SIZE, DimensionUnit::FP).ConvertToVp());
11401     retInfo.SetFontStyle(OHOS::Ace::FontStyle::NORMAL);
11402     retInfo.SetFontWeight(static_cast<int32_t>(FontWeight::NORMAL));
11403     retInfo.SetTextDecoration(TextDecoration::NONE);
11404     retInfo.SetColor(style.GetTextColor().ColorToString());
11405     retInfo.SetFontFamily("HarmonyOS Sans");
11406 }
11407 
11408 void RichEditorPattern::SetParaStyleToRet(RichEditorAbstractSpanResult& retInfo,
11409     std::optional<struct UpdateParagraphStyle> paraStyle)
11410 {
11411     CHECK_NULL_VOID(paraStyle);
11412     TextStyleResult textStyleResult = retInfo.GetTextStyle();
11413     textStyleResult.textAlign = static_cast<int32_t>(paraStyle->textAlign.value_or(TextAlign::START));
11414     if (paraStyle->leadingMargin) {
11415         textStyleResult.leadingMarginSize[0] = paraStyle->leadingMargin->size.Width().ToString();
11416         textStyleResult.leadingMarginSize[1] = paraStyle->leadingMargin->size.Height().ToString();
11417     }
11418     IF_TRUE(paraStyle->wordBreak.has_value(),
11419         textStyleResult.wordBreak = static_cast<int32_t>(paraStyle->wordBreak.value()));
11420     IF_TRUE(paraStyle->lineBreakStrategy.has_value(),
11421         textStyleResult.lineBreakStrategy = static_cast<int32_t>(paraStyle->lineBreakStrategy.value()));
11422     IF_TRUE(paraStyle->paragraphSpacing.has_value(), textStyleResult.paragraphSpacing =
11423         Dimension(paraStyle->paragraphSpacing.value().ConvertToFp(), DimensionUnit::FP));
11424     IF_TRUE(paraStyle->textVerticalAlign.has_value(), textStyleResult.textVerticalAlign =
11425         static_cast<int32_t>(paraStyle->textVerticalAlign.value()));
11426     retInfo.SetTextStyle(textStyleResult);
11427 }
11428 
11429 void RichEditorPattern::CalcInsertValueObj(TextInsertValueInfo& info, int textIndex, bool isCreate)
11430 {
11431     if (spans_.empty()) {
11432         info.SetSpanIndex(0);
11433         info.SetOffsetInSpan(0);
11434         return;
11435     }
11436     auto it = std::find_if(
11437         spans_.begin(), spans_.end(), [caretPosition = textIndex](const RefPtr<SpanItem>& spanItem) {
11438             auto spanLength = static_cast<int32_t>(spanItem->content.length());
11439             if (spanLength == 0) {
11440                 return spanItem->position == caretPosition;
11441             }
11442             return (spanItem->position - spanLength <= caretPosition) && (caretPosition <= spanItem->position);
11443         });
11444     if (it == spans_.end()) {
11445         info.SetSpanIndex(static_cast<int32_t>(spans_.size()) - 1);
11446         info.SetOffsetInSpan((*spans_.rbegin())->content.length());
11447         return;
11448     }
11449     if (textIndex && isCreate) {
11450         info.SetSpanIndex(std::distance(spans_.begin(), it) + 1);
11451         info.SetOffsetInSpan(0);
11452         return;
11453     }
11454     if ((*it)->content.back() == '\n' && (*it)->position == textIndex) { // next line/span begin
11455         info.SetSpanIndex(std::distance(spans_.begin(), it) + 1);
11456         info.SetOffsetInSpan(0);
11457     } else {
11458         info.SetSpanIndex(std::distance(spans_.begin(), it));
11459         int32_t spanStart = (*it)->position - static_cast<int32_t>((*it)->content.length());
11460         info.SetOffsetInSpan(textIndex - spanStart);
11461     }
11462 }
11463 
11464 void RichEditorPattern::GetDeletedSpan(RichEditorChangeValue& changeValue, int32_t& innerPosition,
11465     int32_t length, RichEditorDeleteDirection direction)
11466 {
11467     RichEditorDeleteValue info;
11468     if (!textSelector_.SelectNothing()) {
11469         length = textSelector_.GetTextEnd() - textSelector_.GetTextStart();
11470         innerPosition = std::min(textSelector_.GetStart(), textSelector_.GetEnd());
11471     } else if (previewTextRecord_.NeedReplace()) {
11472         length = previewTextRecord_.replacedRange.end - previewTextRecord_.replacedRange.start;
11473         innerPosition = previewTextRecord_.replacedRange.start;
11474     } else {
11475         int32_t emojiLength = CalculateDeleteLength(length, (direction == RichEditorDeleteDirection::BACKWARD));
11476         if (direction == RichEditorDeleteDirection::BACKWARD) {
11477             innerPosition -= emojiLength;
11478         }
11479         if (length < emojiLength) {
11480             length = emojiLength;
11481         }
11482     }
11483 
11484     info.SetOffset(innerPosition);
11485     info.SetRichEditorDeleteDirection(direction);
11486     info.SetLength(length);
11487     if (!spans_.empty()) {
11488         CalcDeleteValueObj(innerPosition, length, info);
11489     }
11490     if (!spans_.empty() || isAPI14Plus) {
11491         changeValue.SetRangeBefore({ innerPosition, innerPosition + length });
11492         changeValue.SetRangeAfter({ innerPosition, innerPosition });
11493     }
11494     const std::list<RichEditorAbstractSpanResult>& resultList = info.GetRichEditorDeleteSpans();
11495     for (auto& it : resultList) {
11496         if (it.GetType() == SpanResultType::TEXT) {
11497             changeValue.SetRichEditorOriginalSpans(it);
11498         } else if (it.GetType() == SpanResultType::SYMBOL && textSelector_.SelectNothing() &&
11499             previewTextRecord_.previewContent.empty()) {
11500             int32_t symbolStart = it.GetSpanRangeStart();
11501             changeValue.SetRichEditorOriginalSpans(it);
11502             changeValue.SetRangeBefore({ symbolStart, symbolStart + SYMBOL_SPAN_LENGTH });
11503             changeValue.SetRangeAfter({ symbolStart, symbolStart });
11504         }
11505     }
11506 }
11507 
11508 RefPtr<SpanItem> RichEditorPattern::GetDelPartiallySpanItem(
11509     RichEditorChangeValue& changeValue, std::u16string& originalStr, int32_t& originalPos)
11510 {
11511     RefPtr<SpanItem> retItem = nullptr;
11512     if (changeValue.GetRichEditorOriginalSpans().size() == 0) {
11513         return retItem;
11514     }
11515     std::u16string textTemp;
11516     auto originalSpans = changeValue.GetRichEditorOriginalSpans();
11517     const RichEditorAbstractSpanResult& firstResult = originalSpans.front();
11518     auto it = spans_.begin();
11519     std::advance(it, firstResult.GetSpanIndex());
11520     retItem = *it;
11521     originalStr = retItem->content;
11522     originalPos = retItem->position;
11523     retItem->content.erase(firstResult.OffsetInSpan(), firstResult.GetEraseLength());
11524     retItem->position -= firstResult.GetEraseLength();
11525     if (firstResult.GetEraseLength() != static_cast<int32_t>(firstResult.GetValue().length())) {
11526         return retItem;
11527     }
11528 
11529     if (firstResult.GetSpanIndex() == 0) {
11530         int32_t spanIndex = 0;
11531         for (auto& orgIt : originalSpans) {
11532             spanIndex = orgIt.GetSpanIndex();
11533             if (orgIt.GetEraseLength() != static_cast<int32_t>(orgIt.GetValue().length())) {
11534                 // find the deleted(Partially) spanItem
11535                 auto findIt = spans_.begin();
11536                 std::advance(findIt, spanIndex);
11537                 textTemp = (*findIt)->content;
11538                 textTemp.erase(orgIt.OffsetInSpan(), orgIt.GetEraseLength());
11539                 retItem->content = textTemp;
11540                 retItem->position = textTemp.length();
11541                 return retItem;
11542             }
11543         }
11544         if (spans_.size() == originalSpans.size() || static_cast<int32_t>(spans_.size()) == (spanIndex + 1)) {
11545             return retItem; // all spanNode be deleted
11546         }
11547         auto nextIt = spans_.begin();
11548         std::advance(nextIt, spanIndex + 1);
11549         if ((*nextIt)->unicode != 0 || DynamicCast<PlaceholderSpanItem>(*nextIt)) {
11550             return retItem; // is not a textSpan(Image/Symbol/other)
11551         }
11552         retItem->content = (*nextIt)->content;
11553         retItem->position = static_cast<int32_t>(retItem->content.length());
11554     }
11555     return retItem;
11556 }
11557 
11558 bool RichEditorPattern::BeforeChangeText(RichEditorChangeValue& changeValue, const TextSpanOptions& options)
11559 {
11560     auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
11561     CHECK_NULL_RETURN(eventHub, false);
11562     if (!eventHub->HasOnWillChange() && !eventHub->HasOnDidChange()) {
11563         return true;
11564     }
11565     int32_t innerPosition = caretPosition_;
11566 
11567    // AddTextSpan
11568     std::optional<TextStyle> textStyle = std::nullopt;
11569     if (options.style.has_value()) {
11570         textStyle = options.style;
11571     }
11572     if (options.offset.has_value()) {
11573         if (spans_.empty() || options.offset.value() < 0) {
11574             innerPosition = 0;
11575         } else if (options.offset.value() > GetTextContentLength()) {
11576             innerPosition = GetTextContentLength();
11577         } else {
11578             innerPosition = options.offset.value();
11579         }
11580     } else {
11581         innerPosition = GetTextContentLength();
11582     }
11583     // only add, do not delete
11584     changeValue.SetRangeBefore({ innerPosition, innerPosition });
11585     GetReplacedSpan(changeValue, innerPosition, options.value, innerPosition, textStyle, options.paraStyle,
11586         options.urlAddress, true);
11587 
11588     CHECK_NULL_RETURN(eventHub->HasOnWillChange(), true);
11589     auto ret = eventHub->FireOnWillChange(changeValue);
11590     return ret;
11591 }
11592 
11593 bool RichEditorPattern::BeforeAddImage(RichEditorChangeValue& changeValue,
11594     const ImageSpanOptions& options, int32_t insertIndex)
11595 {
11596     auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
11597     CHECK_NULL_RETURN(eventHub, false);
11598     if (!eventHub->HasOnWillChange() && !eventHub->HasOnDidChange()) {
11599         return true;
11600     }
11601     changeValue.SetRangeBefore({ insertIndex, insertIndex });
11602     changeValue.SetRangeAfter({ insertIndex, insertIndex + 1});
11603     RichEditorAbstractSpanResult retInfo;
11604     TextInsertValueInfo info;
11605     CalcInsertValueObj(info, insertIndex, true);
11606     int32_t spanIndex = info.GetSpanIndex();
11607     retInfo.SetSpanIndex(spanIndex);
11608     UpdateImageSpanResultByOptions(retInfo, options);
11609     retInfo.SetOffsetInSpan(0);
11610     retInfo.SetEraseLength(1);
11611     retInfo.SetSpanRangeStart(insertIndex);
11612     retInfo.SetSpanRangeEnd(insertIndex + 1);
11613     changeValue.SetRichEditorReplacedImageSpans(retInfo);
11614     CHECK_NULL_RETURN(eventHub->HasOnWillChange(), true);
11615     return eventHub->FireOnWillChange(changeValue);
11616 }
11617 
11618 void RichEditorPattern::UpdateImageSpanResultByOptions(RichEditorAbstractSpanResult& retInfo,
11619     const ImageSpanOptions& options)
11620 {
11621     retInfo.SetSpanType(SpanResultType::IMAGE);
11622     if (options.image) {
11623         retInfo.SetValueResourceStr(*options.image);
11624     }
11625     if (options.imagePixelMap) {
11626         retInfo.SetValuePixelMap(*options.imagePixelMap);
11627     }
11628     if (options.imageAttribute.has_value()) {
11629         auto imgAttr = options.imageAttribute.value();
11630         if (imgAttr.size.has_value()) {
11631             retInfo.SetSizeWidth(imgAttr.size->width.value_or(CalcDimension()).ConvertToPx());
11632             retInfo.SetSizeHeight(imgAttr.size->height.value_or(CalcDimension()).ConvertToPx());
11633         }
11634         if (imgAttr.verticalAlign.has_value()) {
11635             retInfo.SetVerticalAlign(imgAttr.verticalAlign.value());
11636         }
11637         if (imgAttr.objectFit.has_value()) {
11638             retInfo.SetImageFit(imgAttr.objectFit.value());
11639         }
11640         if (imgAttr.marginProp.has_value()) {
11641             retInfo.SetMargin(imgAttr.marginProp.value().ToString());
11642         }
11643         if (imgAttr.borderRadius.has_value()) {
11644             retInfo.SetBorderRadius(imgAttr.borderRadius.value().ToString());
11645         }
11646     }
11647 }
11648 
11649 bool RichEditorPattern::BeforeSpansChange(const UndoRedoRecord& record, bool isUndo)
11650 {
11651     auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
11652     CHECK_NULL_RETURN(eventHub, false);
11653     CHECK_NULL_RETURN(eventHub->HasOnWillChange(), true);
11654     RichEditorChangeValue changeValue(isUndo ? TextChangeReason::UNDO : TextChangeReason::REDO);
11655     auto rangeBefore = isUndo ? record.rangeAfter : record.rangeBefore;
11656     changeValue.SetRangeBefore(rangeBefore);
11657     auto rangeAfter = isUndo ? record.rangeBefore : record.rangeAfter;
11658     changeValue.SetRangeAfter(rangeAfter);
11659     auto optionsList = isUndo ? record.optionsListBefore : record.optionsListAfter;
11660     TextInsertValueInfo info;
11661     CalcInsertValueObj(info, rangeBefore.start, true);
11662     int32_t spanIndex = info.GetSpanIndex();
11663     for (const auto& option : optionsList.value_or(OptionsList{})) {
11664         std::visit([weak = WeakClaim(this), &changeValue, &spanIndex](const auto& specificOption) {
11665             auto pattern = weak.Upgrade();
11666             CHECK_NULL_VOID(pattern);
11667             using T = std::decay_t<decltype(specificOption)>;
11668             if constexpr (std::is_same_v<T, ImageSpanOptions>) {
11669                 auto retInfo = pattern->GetResultByImageSpanOptions(specificOption, spanIndex);
11670                 changeValue.SetRichEditorReplacedImageSpans(retInfo);
11671             } else if constexpr (std::is_same_v<T, TextSpanOptions>) {
11672                 auto retInfo = pattern->GetResultByTextSpanOptions(specificOption, spanIndex);
11673                 changeValue.SetRichEditorReplacedSpans(retInfo);
11674             } else if constexpr (std::is_same_v<T, SymbolSpanOptions>) {
11675                 auto retInfo = pattern->GetResultBySymbolSpanOptions(specificOption, spanIndex);
11676                 changeValue.SetRichEditorReplacedSymbolSpans(retInfo);
11677             } else if constexpr (std::is_same_v<T, BuilderSpanOptions>) {
11678                 auto textOptions = TextSpanOptions{ .offset = specificOption.offset, .value = u" " };
11679                 auto retInfo = pattern->GetResultByTextSpanOptions(textOptions, spanIndex);
11680                 changeValue.SetRichEditorReplacedSpans(retInfo);
11681             }
11682             spanIndex++;
11683         }, option);
11684     }
11685     return eventHub->FireOnWillChange(changeValue);
11686 }
11687 
11688 RichEditorAbstractSpanResult RichEditorPattern::GetResultByImageSpanOptions(const ImageSpanOptions& options,
11689     int32_t spanIndex)
11690 {
11691     RichEditorAbstractSpanResult retInfo;
11692     auto spanRangeStart = options.offset.value_or(GetTextContentLength());
11693     UpdateSpanResultRange(retInfo, spanIndex, { spanRangeStart, spanRangeStart + IMAGE_SPAN_LENGTH });
11694     UpdateImageSpanResultByOptions(retInfo, options);
11695     return retInfo;
11696 }
11697 
11698 void RichEditorPattern::UpdateSpanResultRange(RichEditorAbstractSpanResult& retInfo, int32_t spanIndex,
11699     TextRange spanRange)
11700 {
11701     retInfo.SetSpanIndex(spanIndex);
11702     retInfo.SetOffsetInSpan(0);
11703     retInfo.SetEraseLength(spanRange.GetLength());
11704     retInfo.SetSpanRangeStart(spanRange.start);
11705     retInfo.SetSpanRangeEnd(spanRange.end);
11706 }
11707 
11708 RichEditorAbstractSpanResult RichEditorPattern::GetResultByTextSpanOptions(const TextSpanOptions& options,
11709     int32_t spanIndex)
11710 {
11711     RichEditorAbstractSpanResult retInfo;
11712     auto spanRangeStart = options.offset.value_or(GetTextContentLength());
11713     UpdateSpanResultRange(retInfo, spanIndex, { spanRangeStart, spanRangeStart + options.value.length() });
11714     UpdateTextSpanResultByOptions(retInfo, options);
11715     return retInfo;
11716 }
11717 
11718 void RichEditorPattern::UpdateTextSpanResultByOptions(RichEditorAbstractSpanResult& retInfo,
11719     const TextSpanOptions& options)
11720 {
11721     if (options.style.has_value()) {
11722         SetTextStyleToRet(retInfo, options.style.value());
11723     } else {
11724         SetThemeTextStyleToRet(retInfo);
11725     }
11726     auto urlAddress = options.urlAddress;
11727     IF_TRUE(urlAddress.has_value(), retInfo.SetUrlAddress(urlAddress.value()));
11728     retInfo.SetValue(options.value);
11729     SetParaStyleToRet(retInfo, options.paraStyle);
11730 }
11731 
11732 RichEditorAbstractSpanResult RichEditorPattern::GetResultBySymbolSpanOptions(const SymbolSpanOptions& options,
11733     int32_t spanIndex)
11734 {
11735     RichEditorAbstractSpanResult retInfo;
11736     auto spanRangeStart = options.offset.value_or(GetTextContentLength());
11737     UpdateSpanResultRange(retInfo, spanIndex, { spanRangeStart, spanRangeStart + SYMBOL_SPAN_LENGTH });
11738     UpdateSymbolSpanResultByOptions(retInfo, options);
11739     return retInfo;
11740 }
11741 
11742 void RichEditorPattern::UpdateSymbolSpanResultByOptions(RichEditorAbstractSpanResult& retInfo,
11743     const SymbolSpanOptions& options)
11744 {
11745     retInfo.SetValueString(std::to_string(options.symbolId));
11746     retInfo.SetSpanType(SpanResultType::SYMBOL);
11747     TextStyle style = options.style.value_or(TextStyle());
11748     retInfo.SetSymbolSpanStyle(SymbolSpanStyle(style));
11749     retInfo.SetValueResource(options.resourceObject);
11750 }
11751 
11752 void RichEditorPattern::AfterSpansChange(const UndoRedoRecord& record, bool isUndo)
11753 {
11754     auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
11755     CHECK_NULL_VOID(eventHub);
11756     CHECK_NULL_VOID(eventHub->HasOnDidChange());
11757     RichEditorChangeValue changeValue(isUndo ? TextChangeReason::UNDO : TextChangeReason::REDO);
11758     auto rangeBefore = isUndo ? record.rangeAfter : record.rangeBefore;
11759     changeValue.SetRangeBefore(rangeBefore);
11760     auto rangeAfter = isUndo ? record.rangeBefore : record.rangeAfter;
11761     changeValue.SetRangeAfter(rangeAfter);
11762     eventHub->FireOnDidChange(changeValue);
11763     ReportAfterContentChangeEvent();
11764 }
11765 
11766 void RichEditorPattern::FixMoveDownChange(RichEditorChangeValue& changeValue, int32_t delLength)
11767 {
11768     int32_t delSpanCount = 0;
11769     for (auto& it : changeValue.GetRichEditorOriginalSpans()) {
11770         if (it.GetEraseLength() == static_cast<int32_t>(it.GetValue().length())) {
11771             ++delSpanCount;
11772         }
11773     }
11774     for (auto& it : const_cast<std::vector<RichEditorAbstractSpanResult>&>(changeValue.GetRichEditorReplacedSpans())) {
11775         if (delSpanCount) {
11776             it.SetSpanIndex(it.GetSpanIndex() - delSpanCount);
11777         }
11778     }
11779 }
11780 
11781 void RichEditorPattern::BeforeUndo(
11782     RichEditorChangeValue& changeValue, int32_t& innerPosition, const OperationRecord& record)
11783 {
11784     innerPosition = record.afterCaretPosition;
11785     if (record.addText.has_value() && record.deleteCaretPosition != -1) { // UndoDrag
11786         GetDeletedSpan(changeValue, innerPosition, record.addText.value_or(u"").length(),
11787             RichEditorDeleteDirection::FORWARD);
11788         innerPosition = record.deleteCaretPosition;
11789         GetReplacedSpan(changeValue, innerPosition, record.addText.value(), innerPosition, std::nullopt, std::nullopt);
11790     } else if (record.addText.has_value() && record.deleteText.has_value()) {
11791         GetDeletedSpan(changeValue, innerPosition, record.addText.value_or(u"").length(),
11792             RichEditorDeleteDirection::BACKWARD);
11793         GetReplacedSpan(
11794             changeValue, innerPosition, record.deleteText.value(), innerPosition, std::nullopt, std::nullopt);
11795     } else if (record.deleteText.has_value()) {
11796         GetReplacedSpan(
11797             changeValue, innerPosition, record.deleteText.value(), innerPosition, std::nullopt, std::nullopt);
11798     } else if (record.addText.has_value()) {
11799         GetDeletedSpan(changeValue, innerPosition, record.addText.value_or(u"").length(),
11800             RichEditorDeleteDirection::BACKWARD);
11801     }
11802 }
11803 
11804 void RichEditorPattern::BeforeRedo(
11805     RichEditorChangeValue& changeValue, int32_t& innerPosition, const OperationRecord& record)
11806 {
11807     innerPosition = record.beforeCaretPosition - record.addText.value_or(u"").length();
11808     if (record.addText.has_value() && record.deleteCaretPosition != -1) { // RedoDrag
11809         innerPosition = record.deleteCaretPosition;
11810         GetDeletedSpan(changeValue, innerPosition, record.addText.value_or(u"").length(),
11811             RichEditorDeleteDirection::FORWARD);
11812         innerPosition = record.beforeCaretPosition;
11813         GetReplacedSpan(changeValue, innerPosition, record.addText.value(), innerPosition, std::nullopt, std::nullopt);
11814     } else if (record.addText.has_value() && record.deleteText.has_value()) {
11815         GetDeletedSpan(changeValue, innerPosition, record.deleteText.value_or(u"").length(),
11816             RichEditorDeleteDirection::FORWARD);
11817         GetReplacedSpan(changeValue, innerPosition, record.addText.value(), innerPosition, std::nullopt, std::nullopt);
11818     } else if (record.deleteText.has_value()) {
11819         innerPosition = record.beforeCaretPosition - record.deleteText.value_or(u"").length();
11820         GetDeletedSpan(changeValue, innerPosition, record.deleteText.value_or(u"").length(),
11821             RichEditorDeleteDirection::FORWARD);
11822     } else if (record.addText.has_value()) {
11823         innerPosition = std::min(innerPosition, record.afterCaretPosition);
11824         int32_t innerAddPosition = record.afterCaretPosition - static_cast<int32_t>(record.addText.value().length());
11825         if (changeValue.GetRichEditorOriginalSpans().empty()) {
11826             innerPosition = caretPosition_;
11827             innerAddPosition = caretPosition_;
11828         }
11829         GetReplacedSpan(changeValue, innerAddPosition, record.addText.value(), innerPosition,
11830             std::nullopt, std::nullopt);
11831     }
11832 }
11833 
11834 void RichEditorPattern::BeforeDrag(
11835     RichEditorChangeValue& changeValue, int32_t& innerPosition, const OperationRecord& record)
11836 {
11837     std::u16string recordAddText = record.addText.value_or(u"");
11838     int length = recordAddText.length();
11839     int32_t nowPosition = innerPosition;
11840     std::optional<TextStyle> style = std::nullopt;
11841     if (typingStyle_.has_value() && typingTextStyle_.has_value()) {
11842         style = typingTextStyle_.value();
11843     }
11844     if (!isDragSponsor_) { // drag from outside
11845         GetReplacedSpan(
11846             changeValue, innerPosition, recordAddText, innerPosition, style, std::nullopt, std::nullopt, true, false);
11847     } else if (nowPosition < record.beforeCaretPosition + length) { // move up
11848         innerPosition = record.beforeCaretPosition;
11849         GetDeletedSpan(changeValue, innerPosition, length, RichEditorDeleteDirection::FORWARD);
11850         innerPosition = nowPosition;
11851         GetReplacedSpan(
11852             changeValue, innerPosition, recordAddText, nowPosition, style, std::nullopt, std::nullopt, true, false);
11853     } else { // move down
11854         innerPosition = record.beforeCaretPosition;
11855         GetDeletedSpan(changeValue, innerPosition, length, RichEditorDeleteDirection::FORWARD);
11856         innerPosition = nowPosition - length;
11857         GetReplacedSpan(
11858             changeValue, innerPosition, recordAddText, nowPosition, style, std::nullopt, std::nullopt, true, false);
11859         FixMoveDownChange(changeValue, length);
11860     }
11861 }
11862 
11863 bool RichEditorPattern::BeforeChangeText(
11864     RichEditorChangeValue& changeValue, const OperationRecord& record, RecordType type, int32_t delLength)
11865 {
11866     int32_t innerPosition = caretPosition_;
11867     auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
11868     CHECK_NULL_RETURN(eventHub, false);
11869     if (!eventHub->HasOnWillChange() && !eventHub->HasOnDidChange()) {
11870         return true;
11871     }
11872 
11873     if (RecordType::INSERT == type) {
11874         if (textSelector_.IsValid()) {
11875             GetDeletedSpan(changeValue, innerPosition,
11876                 static_cast<int32_t>(textSelector_.GetTextEnd() - textSelector_.GetTextStart()));
11877         } else if (previewTextRecord_.NeedReplace()) {
11878             GetDeletedSpan(changeValue, innerPosition,
11879                 static_cast<int32_t>(previewTextRecord_.replacedRange.end - previewTextRecord_.replacedRange.start));
11880         }
11881         if (record.addText.has_value()) {
11882             GetReplacedSpan(changeValue, innerPosition, record.addText.value(), innerPosition, std::nullopt, std::nullopt);
11883         }
11884     }
11885     if (RecordType::DEL_FORWARD == type) {
11886         innerPosition = record.beforeCaretPosition;
11887         GetDeletedSpan(changeValue, innerPosition, delLength, RichEditorDeleteDirection::FORWARD);
11888     }
11889     if (RecordType::DEL_BACKWARD == type) {
11890         innerPosition = record.beforeCaretPosition;
11891         GetDeletedSpan(changeValue, innerPosition, delLength, RichEditorDeleteDirection::BACKWARD);
11892     }
11893     if (RecordType::UNDO == type) {
11894         BeforeUndo(changeValue, innerPosition, record);
11895     }
11896     if (RecordType::REDO == type) {
11897         BeforeRedo(changeValue, innerPosition, record);
11898     }
11899     if (RecordType::DRAG == type) {
11900         BeforeDrag(changeValue, innerPosition, record);
11901     }
11902     bool isDelete = RecordType::DEL_FORWARD == type || RecordType::DEL_BACKWARD == type;
11903     if (changeValue.GetRichEditorOriginalSpans().empty() && !isDelete) {
11904         // only add, do not delete
11905         changeValue.SetRangeBefore({ caretPosition_, caretPosition_ });
11906     }
11907 
11908     CHECK_NULL_RETURN(eventHub->HasOnWillChange(), true);
11909     auto ret = eventHub->FireOnWillChange(changeValue);
11910     return ret;
11911 }
11912 
11913 OffsetF RichEditorPattern::GetTextPaintOffset() const
11914 {
11915     if (selectOverlay_->HasRenderTransform()) {
11916         return selectOverlay_->GetPaintRectOffsetWithTransform();
11917     }
11918     return GetPaintRectGlobalOffset();
11919 }
11920 
11921 OffsetF RichEditorPattern::GetPaintRectGlobalOffset() const
11922 {
11923     auto host = GetHost();
11924     CHECK_NULL_RETURN(host, OffsetF(0.0f, 0.0f));
11925     auto pipeline = host->GetContextRefPtr();
11926     CHECK_NULL_RETURN(pipeline, OffsetF(0.0f, 0.0f));
11927     auto rootOffset = pipeline->GetRootRect().GetOffset();
11928     auto textPaintOffset = host->GetPaintRectOffsetNG(false, true);
11929     return textPaintOffset - rootOffset;
11930 }
11931 
11932 void RichEditorPattern::HandlePointWithTransform(OffsetF& point)
11933 {
11934     auto host = GetHost();
11935     CHECK_NULL_VOID(host);
11936     PointF convertPoint = { point.GetX(), point.GetY() };
11937     auto parent = host;
11938     while (parent && (parent->GetTag() != V2::WINDOW_SCENE_ETS_TAG)) {
11939         auto renderContext = parent->GetRenderContext();
11940         CHECK_NULL_VOID(renderContext);
11941         auto paintOffset = renderContext->GetPaintRectWithoutTransform().GetOffset();
11942         if (parent != host) {
11943             convertPoint = convertPoint + paintOffset;
11944         }
11945         renderContext->GetPointTransform(convertPoint);
11946         parent = parent->GetAncestorNodeOfFrame(true);
11947     }
11948     point = { convertPoint.GetX(), convertPoint.GetY() };
11949 }
11950 
11951 CaretOffsetInfo RichEditorPattern::GetCaretOffsetInfoByPosition(int32_t position)
11952 {
11953     CaretOffsetInfo caretInfo;
11954     int32_t currrentPosition = 0;
11955     if (position == -1) {
11956         currrentPosition = caretPosition_;
11957     } else {
11958         currrentPosition = position;
11959     }
11960     caretInfo.caretOffsetUp = CalcCursorOffsetByPosition(currrentPosition, caretInfo.caretHeightUp, false, false);
11961     caretInfo.caretOffsetDown = CalcCursorOffsetByPosition(currrentPosition, caretInfo.caretHeightDown, true, false);
11962     caretInfo.caretOffsetLine = CalcCursorOffsetByPosition(currrentPosition, caretInfo.caretHeightLine);
11963     return caretInfo;
11964 }
11965 
11966 void RichEditorPattern::CalcLineSidesIndexByPosition(int32_t& startIndex, int32_t& endIndex)
11967 {
11968     Offset textStartOffset;
11969     Offset textEndOffset;
11970 
11971     CHECK_NULL_VOID(overlayMod_);
11972     auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
11973     auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize());
11974     float textOffsetY = richTextRect_.GetY() - (minDet / 2.0);
11975     auto currentCaretOffsetOverlay = overlayMod->GetCaretOffset();
11976     textStartOffset = Offset(0, currentCaretOffsetOverlay.GetY() - textOffsetY);
11977     textEndOffset = Offset(richTextRect_.Width(), currentCaretOffsetOverlay.GetY() - textOffsetY);
11978     startIndex = paragraphs_.GetIndex(textStartOffset);
11979     endIndex = paragraphs_.GetIndex(textEndOffset);
11980 }
11981 
11982 RectF RichEditorPattern::CalcLineInfoByPosition()
11983 {
11984     int32_t startIndex = 0;
11985     int32_t endIndex = 0;
11986 
11987     CalcLineSidesIndexByPosition(startIndex, endIndex);
11988     if (startIndex == endIndex) {
11989         endIndex += 1;
11990     }
11991     auto selectedRects = paragraphs_.GetRects(startIndex, endIndex);
11992     CHECK_NULL_RETURN(selectedRects.size(), {});
11993     return selectedRects.front();
11994 }
11995 
11996 int32_t RichEditorPattern::CalcMoveUpPos(float& leadingMarginOffset)
11997 {
11998     int32_t caretPosition;
11999     CaretOffsetInfo caretInfo;
12000     float textOffsetDownY = 0.0f;
12001     int32_t startIndex = 0;
12002     int32_t endIndex = 0;
12003     Offset textOffset;
12004 
12005     caretInfo = GetCaretOffsetInfoByPosition();
12006     auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize());
12007     CHECK_NULL_RETURN(overlayMod_, 0);
12008     auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
12009     auto caretOffsetOverlay = overlayMod->GetCaretOffset();
12010     auto caretOffsetWidth = overlayMod->GetCaretWidth();
12011     bool cursorNotAtLineStart = NearEqual(caretOffsetOverlay.GetX(), caretInfo.caretOffsetUp.GetX(), caretOffsetWidth);
12012     float textOffsetY = richTextRect_.GetY() + (minDet / 2.0); // 2.0 Cursor one half at the center position
12013     CalcLineSidesIndexByPosition(startIndex, endIndex);
12014     auto rectLineInfo = CalcLineInfoByPosition();
12015     leadingMarginOffset = rectLineInfo.GetX();
12016     if (cursorNotAtLineStart) {
12017         textOffsetDownY = caretInfo.caretOffsetLine.GetY() - textOffsetY;
12018         // lm mean leadingMargin abbr
12019         auto lmSizeOffset = (endIndex - startIndex <= 1 && NearEqual(rectLineInfo.Width(), richTextRect_.Width()))
12020                                 ? rectLineInfo.GetX()
12021                                 : 0;
12022         textOffset = Offset(caretInfo.caretOffsetLine.GetX() - richTextRect_.GetX() + lmSizeOffset, textOffsetDownY);
12023     } else {
12024         textOffsetDownY = caretInfo.caretOffsetLine.GetY() + caretInfo.caretHeightLine - textOffsetY;
12025         textOffset = Offset(caretOffsetOverlay.GetX() - richTextRect_.GetX(), textOffsetDownY);
12026     }
12027     caretPosition = paragraphs_.GetIndex(textOffset);
12028     return caretPosition;
12029 }
12030 
12031 int32_t RichEditorPattern::CalcMoveDownPos(float& leadingMarginOffset)
12032 {
12033     CaretOffsetInfo caretInfo;
12034     float textOffsetDownY = 0.0f;
12035     Offset textOffset;
12036     int32_t caretPositionEnd;
12037 
12038     caretInfo = GetCaretOffsetInfoByPosition();
12039     auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize());
12040     CHECK_NULL_RETURN(overlayMod_, 0);
12041     auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
12042     auto caretOffsetOverlay = overlayMod->GetCaretOffset();
12043     auto caretOffsetWidth = overlayMod->GetCaretWidth();
12044     float textOffsetX = richTextRect_.GetX();
12045     float textOffsetY = richTextRect_.GetY() - (minDet / 2.0);
12046     bool cursorNotAtLineStart = NearEqual(caretOffsetOverlay.GetX(), caretInfo.caretOffsetUp.GetX(), caretOffsetWidth);
12047     // midle or enter
12048     auto rectLineInfo = CalcLineInfoByPosition();
12049     leadingMarginOffset = rectLineInfo.GetX();
12050     auto lineHeightDis = rectLineInfo.Height();
12051     // midle or end, first line start position,end line end position
12052     textOffsetDownY = caretInfo.caretOffsetLine.GetY() + caretInfo.caretHeightLine - textOffsetY;
12053     float lastLineTop = 0.0f;
12054     float paragraphSpacing = 0.0f;
12055     HandleCurrentPositionParagraphInfo(lastLineTop, paragraphSpacing);
12056     if (cursorNotAtLineStart || caretPosition_ == 0) {
12057         IF_TRUE(NearEqual(std::floor(caretInfo.caretOffsetLine.GetY()), std::floor(lastLineTop)),
12058             textOffsetDownY += paragraphSpacing);
12059         textOffset = Offset(caretInfo.caretOffsetLine.GetX() - textOffsetX, textOffsetDownY);
12060     } else {
12061         IF_TRUE(NearEqual(std::floor(caretInfo.caretOffsetLine.GetY() + lineHeightDis), std::floor(lastLineTop)),
12062             textOffsetDownY += paragraphSpacing);
12063         textOffsetDownY += lineHeightDis;
12064         textOffset = Offset(caretOffsetOverlay.GetX() - textOffsetX, textOffsetDownY);
12065     }
12066     caretPositionEnd = paragraphs_.GetIndex(textOffset);
12067     return caretPositionEnd;
12068 }
12069 
12070 int32_t RichEditorPattern::CalcLineBeginPosition()
12071 {
12072     float caretHeight = 0.0f;
12073     OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight, false, false);
12074     auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
12075     auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize());
12076     auto textOffsetY = caretOffset.GetY() - textPaintOffset.GetY() + (minDet / 2.0);
12077     Offset textOffset = { 0, textOffsetY };
12078     auto newPos = paragraphs_.GetIndex(textOffset);
12079     return newPos;
12080 }
12081 
12082 float RichEditorPattern::GetTextThemeFontSize()
12083 {
12084     auto theme = GetTheme<TextTheme>();
12085     CHECK_NULL_RETURN(theme, 0.0f);
12086     auto textStyle = theme->GetTextStyle();
12087     return textStyle.GetFontSize().ConvertToPx();
12088 }
12089 
12090 int32_t RichEditorPattern::CalcLineEndPosition(int32_t index)
12091 {
12092     CaretOffsetInfo caretInfo;
12093     int32_t realCaretOffsetY = 0;
12094     int32_t realLastClickOffsetY = 0;
12095 
12096     caretInfo = GetCaretOffsetInfoByPosition(index);
12097     if (NearEqual(richTextRect_.GetY(), contentRect_.GetY())) {
12098         realLastClickOffsetY = lastClickOffset_.GetY();
12099         realCaretOffsetY = caretInfo.caretOffsetDown.GetY();
12100     } else {
12101         auto scrollOffset =
12102             caretInfo.caretOffsetDown.GetY() - caretInfo.caretOffsetUp.GetY() + caretInfo.caretOffsetLine.GetY();
12103         realLastClickOffsetY = lastClickOffset_.GetY() + std::abs(richTextRect_.GetY()) + contentRect_.GetY();
12104         realCaretOffsetY = scrollOffset + std::abs(richTextRect_.GetY()) + contentRect_.GetY();
12105     }
12106     auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
12107     auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize());
12108     Offset textOffset;
12109     auto rectLineInfo = CalcLineInfoByPosition();
12110     float textWidth = richTextRect_.Width() + rectLineInfo.GetX();
12111     float textPaintOffsetY = textPaintOffset.GetY() - (minDet / 2.0);
12112     float textOffsetClickY = realLastClickOffsetY - textPaintOffsetY;
12113     float textOffsetDownY = realCaretOffsetY - textPaintOffsetY;
12114     if (lastClickOffset_.NonNegative()) {
12115         textOffset = { textWidth, textOffsetClickY };
12116     } else {
12117         textOffset = { textWidth, textOffsetDownY };
12118     }
12119     auto position = paragraphs_.GetIndex(textOffset);
12120     return position;
12121 }
12122 
12123 int32_t RichEditorPattern::CalcSingleLineBeginPosition(int32_t fixedPos)
12124 {
12125     float caretHeightDown = 0.0f;
12126     OffsetF caretOffsetDown = CalcCursorOffsetByPosition(fixedPos, caretHeightDown, true, false);
12127     float caretHeightUp = 0.0f;
12128     OffsetF caretOffsetUp = CalcCursorOffsetByPosition(fixedPos, caretHeightUp, false, false);
12129     bool isCaretPosInLineEnd = !NearEqual(caretOffsetDown.GetX(), caretOffsetUp.GetX(), 0.5f);
12130 
12131     auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
12132     CHECK_NULL_RETURN(overlayMod_, false);
12133     auto caretOffsetOverlay = overlayMod->GetCaretOffset();
12134     bool cursorNotAtLineStart = NearEqual(caretOffsetOverlay.GetX(), caretOffsetUp.GetX(), 0.5f);
12135 
12136     Offset textOffset;
12137     if (!cursorNotAtLineStart && !lastClickOffset_.IsNegative()) {
12138         return fixedPos;
12139     } else if (isCaretPosInLineEnd && lastClickOffset_.IsNegative()) {
12140         return lastSelectionRange_.start_;
12141     } else {
12142         float caretHeight = 0.0f;
12143         OffsetF caretOffsetFixed = CalcCursorOffsetByPosition(fixedPos, caretHeight, false, false);
12144         textOffset = { 0, caretOffsetFixed.GetY() };
12145     }
12146     auto position = paragraphs_.GetIndex(textOffset);
12147     return position;
12148 }
12149 
12150 int32_t RichEditorPattern::CalcSingleLineEndPosition(int32_t fixedPos)
12151 {
12152     auto rectLineInfo = CalcLineInfoByPosition();
12153     float textWidth = richTextRect_.Width() + rectLineInfo.GetX();
12154 
12155     float caretHeightDown = 0.0f;
12156     OffsetF caretOffsetDown = CalcCursorOffsetByPosition(fixedPos, caretHeightDown, true, false);
12157     float caretHeightUp = 0.0f;
12158     OffsetF caretOffsetUp = CalcCursorOffsetByPosition(fixedPos, caretHeightUp, false, false);
12159     bool isCaretPosInLineEnd = !NearEqual(caretOffsetDown.GetX(), caretOffsetUp.GetX(), 0.5f);
12160 
12161     auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
12162     CHECK_NULL_RETURN(overlayMod_, false);
12163     auto caretOffsetOverlay = overlayMod->GetCaretOffset();
12164     bool cursorNotAtLineStart = NearEqual(caretOffsetOverlay.GetX(), caretOffsetUp.GetX(), 0.5f);
12165 
12166     Offset textOffset;
12167     if (isCaretPosInLineEnd && lastClickOffset_.IsNegative() && lastSelectionRange_.end_ > fixedPos) {
12168         return lastSelectionRange_.end_;
12169     } else if (!cursorNotAtLineStart && isCaretPosInLineEnd && fixedPos == caretPosition_) {
12170         textOffset = { textWidth, caretOffsetOverlay.GetY() };
12171     } else if (isCaretPosInLineEnd && lastSelectionRange_.end_ <= fixedPos) {
12172         CursorMoveLineEnd();
12173         return fixedPos;
12174     } else if (cursorNotAtLineStart && isCaretPosInLineEnd) {
12175         return fixedPos;
12176     } else {
12177         float caretHeight = 0.0f;
12178         OffsetF caretOffsetFixed = CalcCursorOffsetByPosition(fixedPos, caretHeight, false, false);
12179         textOffset = { textWidth, caretOffsetFixed.GetY() };
12180     }
12181     auto position = paragraphs_.GetIndex(textOffset);
12182     return position;
12183 }
12184 
12185 bool RichEditorPattern::CursorMoveLineBegin()
12186 {
12187     int32_t currentPositionIndex = 0;
12188     if (textSelector_.SelectNothing()) {
12189         currentPositionIndex = caretPosition_;
12190     } else {
12191         currentPositionIndex = textSelector_.GetTextStart();
12192     }
12193     CloseSelectOverlay();
12194     ResetSelection();
12195     float caretHeightDown = 0.0f;
12196     Offset textOffset;
12197 
12198     if (0 == currentPositionIndex) {
12199         SetCaretPosition(currentPositionIndex);
12200         StartTwinkling();
12201         return false;
12202     }
12203     OffsetF caretOffsetDown = CalcCursorOffsetByPosition(currentPositionIndex, caretHeightDown, true, false);
12204     auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
12205     auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize());
12206     float textPaintOffsetY = textPaintOffset.GetY() - (minDet / 2.0);
12207     if (lastClickOffset_.NonNegative()) {
12208         textOffset = { 0, lastClickOffset_.GetY() - textPaintOffsetY };
12209     } else {
12210         textOffset = { 0, caretOffsetDown.GetY() - textPaintOffsetY };
12211     }
12212     auto position = paragraphs_.GetIndex(textOffset);
12213     AdjustCursorPosition(position);
12214     SetCaretPosition(position);
12215     MoveCaretToContentRect();
12216     StartTwinkling();
12217     auto host = GetHost();
12218     IF_PRESENT(host, MarkDirtyNode(PROPERTY_UPDATE_RENDER));
12219     return true;
12220 }
12221 
12222 bool RichEditorPattern::CursorMoveLineEnd()
12223 {
12224     int32_t position = 0;
12225     if (!textSelector_.SelectNothing()) {
12226         position = textSelector_.GetTextEnd();
12227         CaretOffsetInfo caretInfo = GetCaretOffsetInfoByPosition(position);
12228         bool cursorAtLineEnd = !NearEqual(caretInfo.caretOffsetUp.GetX(), caretInfo.caretOffsetDown.GetX(), 0.5f);
12229         bool cursorAfterNewLine = (position - 1) == GetParagraphEndPosition(position - 1);
12230         if (cursorAfterNewLine) {
12231             --position;
12232         } else if (!cursorAtLineEnd) {
12233             position = CalcLineEndPosition(textSelector_.GetTextEnd());
12234         }
12235     } else {
12236         position = CalcLineEndPosition();
12237     }
12238     CloseSelectOverlay();
12239     ResetSelection();
12240     float caretHeight = 0.0f;
12241     CHECK_NULL_RETURN(overlayMod_, false);
12242     auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
12243     SetCaretPosition(position);
12244     StartTwinkling();
12245     OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight, false, false);
12246     overlayMod->SetCaretOffsetAndHeight(caretOffset, caretHeight);
12247     SetLastClickOffset(caretOffset);
12248     caretAffinityPolicy_ = CaretAffinityPolicy::UPSTREAM_FIRST;
12249     MoveCaretToContentRect(caretOffset, caretHeight);
12250     auto host = GetHost();
12251     CHECK_NULL_RETURN(host, false);
12252     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
12253     return true;
12254 }
12255 
12256 void RichEditorPattern::HandleSelectFontStyle(KeyCode code)
12257 {
12258     auto start = textSelector_.GetTextStart();
12259     auto end = textSelector_.GetTextEnd();
12260     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Change range:[%{public}d-%{public}d] font by key:%{public}d mode:%{public}d",
12261         start, end, code, isSpanStringMode_);
12262     CHECK_NULL_VOID(!textSelector_.SelectNothing());
12263     auto host = GetHost();
12264     CHECK_NULL_VOID(host);
12265     if (isSpanStringMode_) {
12266         UpdateSelectStyledStringStyle(start, end, code);
12267     } else {
12268         UpdateSelectSpanStyle(start, end, code);
12269     }
12270     StopTwinkling();
12271     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
12272 }
12273 
12274 void RichEditorPattern::HandleOnShowMenu()
12275 {
12276     auto isSelectAreaVisible = IsSelectAreaVisible();
12277     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnShowMenu, selectAreaVisible=%{public}d,"
12278         "previewTextInputting=%{public}d,isDragging=%{public}d,isMoveCaret=%{public}d,isHandleMoving=%{public}d,"
12279         "isTouchSelecting=%{public}d,mouseStatus=%{public}d",
12280         isSelectAreaVisible, IsPreviewTextInputting(), IsDragging(), moveCaretState_.isMoveCaret,
12281         selectOverlay_->GetIsHandleMoving(), isTouchSelecting_, mouseStatus_);
12282     CHECK_NULL_VOID(isSelectAreaVisible && !IsPreviewTextInputting() && !IsDragging());
12283     CHECK_NULL_VOID(!moveCaretState_.isMoveCaret && !selectOverlay_->GetIsHandleMoving());
12284     CHECK_NULL_VOID(!isTouchSelecting_ && mouseStatus_ != MouseStatus::MOVE);
12285 
12286     if (sourceType_ == SourceType::MOUSE) {
12287         if (!IsSelected()) {
12288             selectedType_ = TextSpanType::TEXT;
12289         }
12290         textResponseType_ = TextResponseType::RIGHT_CLICK;
12291         selectionMenuOffsetByMouse_ = GetCaretRect().GetOffset() + GetParentGlobalOffset();
12292         selectOverlay_->ProcessOverlay({ .animation = true });
12293         return;
12294     }
12295     if (!IsSelected()) {
12296         CreateAndShowSingleHandle();
12297         return;
12298     }
12299     if (SelectOverlayIsOn()) {
12300         selectOverlay_->SwitchToOverlayMode();
12301         if (selectOverlay_->NeedRefreshMenu()) {
12302             selectOverlay_->ProcessOverlay({ .animation = true, .requestCode = REQUEST_RECREATE });
12303             return;
12304         }
12305         selectOverlay_->UpdateMenuOffset();
12306         selectOverlay_->ShowMenu();
12307         return;
12308     }
12309     CalculateHandleOffsetAndShowOverlay();
12310     selectOverlay_->ProcessOverlay({ .animation = true });
12311 }
12312 
12313 PositionType RichEditorPattern::GetPositionTypeFromLine()
12314 {
12315     auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
12316     CHECK_NULL_RETURN(overlayMod, PositionType::DEFAULT);
12317     auto currentCaretOffsetOverlay = overlayMod->GetCaretOffset();
12318     auto caretOffsetWidth = overlayMod->GetCaretWidth();
12319     int32_t currentParagraphStart = GetParagraphBeginPosition(caretPosition_);
12320     bool isParagraphStart = caretPosition_ == currentParagraphStart;
12321     CHECK_NULL_RETURN(!isParagraphStart, PositionType::PARAGRAPH_START);
12322     int32_t currentParagraphEnd = GetParagraphEndPosition(caretPosition_);
12323     bool isParagraphEnd = caretPosition_ == currentParagraphEnd;
12324     CHECK_NULL_RETURN(!isParagraphEnd, PositionType::PARAGRAPH_END);
12325     CaretOffsetInfo caretInfo = GetCaretOffsetInfoByPosition();
12326     bool isCaretAtLineMiddle = NearEqual(caretInfo.caretOffsetDown.GetX(), caretInfo.caretOffsetUp.GetX(), 0.5f);
12327     CHECK_NULL_RETURN(!isCaretAtLineMiddle, PositionType::DEFAULT);
12328     bool isCaretAtLineEnd =
12329         NearEqual(currentCaretOffsetOverlay.GetX(), caretInfo.caretOffsetUp.GetX(), caretOffsetWidth);
12330     CHECK_NULL_RETURN(!isCaretAtLineEnd, PositionType::LINE_END);
12331     return PositionType::LINE_START;
12332 }
12333 
12334 int32_t RichEditorPattern::HandleSelectWrapper(CaretMoveIntent direction, int32_t fixedPos)
12335 {
12336     int32_t index = GetCaretPosition();
12337     switch (direction) {
12338         case CaretMoveIntent::Left:
12339             return CaretPositionSelectEmoji(CaretMoveIntent::Left);
12340         case CaretMoveIntent::Right:
12341             return CaretPositionSelectEmoji(CaretMoveIntent::Right);
12342         case CaretMoveIntent::Up:
12343             return HandleKbVerticalSelection(true);
12344         case CaretMoveIntent::Down:
12345             return HandleKbVerticalSelection(false);
12346         case CaretMoveIntent::LeftWord: {
12347             return GetLeftWordIndex(index);
12348         }
12349         case CaretMoveIntent::RightWord: {
12350             return GetRightWordIndex(index);
12351         }
12352         case CaretMoveIntent::ParagraghBegin:
12353             return paragraphs_.GetParagraphInfo(index - 1).start;
12354         case CaretMoveIntent::ParagraghEnd:
12355             return paragraphs_.GetParagraphInfo(index).end;
12356         case CaretMoveIntent::LineBegin:
12357             return CalcSingleLineBeginPosition(fixedPos);
12358         case CaretMoveIntent::LineEnd:
12359             return CalcSingleLineEndPosition(fixedPos);
12360         default:
12361             return NONE_SELECT_TYPE;
12362     }
12363 }
12364 
12365 int32_t RichEditorPattern::HandleKbVerticalSelection(bool isUp)
12366 {
12367     float caretHeight = 0.0f;
12368     float newCaretHeight = 0.0f;
12369     float careOffsetY = 0.0f;
12370     int32_t newPos;
12371     Offset textOffset;
12372     OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight);
12373     auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize()) / 2.0;
12374     auto positionType = GetPositionTypeFromLine();
12375     if (isUp) {
12376         float selectStartHeight = 0.0f;
12377         OffsetF selectStartOffset = CalcCursorOffsetByPosition(textSelector_.GetTextStart(), selectStartHeight);
12378         careOffsetY = caretOffset.GetY() - GetTextRect().GetY() - minDet;
12379         textOffset = Offset(caretOffset.GetX() - GetTextRect().GetX(), careOffsetY);
12380         CHECK_NULL_RETURN(GreatNotEqual(textOffset.GetY(), 0), 0);
12381         newPos = paragraphs_.GetIndex(textOffset, true);
12382         OffsetF newCaretOffset = CalcCursorOffsetByPosition(newPos, newCaretHeight, true);
12383         CHECK_EQUAL_RETURN(!textSelector_.SelectNothing() && textSelector_.GetTextEnd() == caretPosition_ &&
12384             selectStartOffset.GetY() == newCaretOffset.GetY(), true, textSelector_.GetTextStart());
12385     } else {
12386         float selectEndHeight = 0.0f;
12387         OffsetF selectEndOffset = CalcCursorOffsetByPosition(textSelector_.GetEnd(), selectEndHeight);
12388         careOffsetY = caretOffset.GetY() - GetTextRect().GetY() + caretHeight + (minDet / 2.0);
12389         float lastLineTop = 0.0f;
12390         float paragraphSpacing = 0.0f;
12391         HandleCurrentPositionParagraphInfo(lastLineTop, paragraphSpacing);
12392         if (positionType == PositionType::LINE_START) {
12393             auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
12394             CHECK_NULL_RETURN(overlayMod, 0);
12395             auto caretOffsetOverlay = overlayMod->GetCaretOffset();
12396             auto rectLineInfo = CalcLineInfoByPosition();
12397             careOffsetY += rectLineInfo.Height();
12398             IF_TRUE(NearEqual(std::floor(caretOffset.GetY() + rectLineInfo.Height()), std::floor(lastLineTop)),
12399                 careOffsetY += paragraphSpacing);
12400             textOffset = Offset(caretOffsetOverlay.GetX() - GetTextRect().GetX(), careOffsetY);
12401         } else {
12402             IF_TRUE(NearEqual(std::floor(caretOffset.GetY()), std::floor(lastLineTop)),
12403                 careOffsetY += paragraphSpacing);
12404             textOffset = Offset(caretOffset.GetX() - GetTextRect().GetX(), careOffsetY);
12405         }
12406         auto height = paragraphs_.GetHeight();
12407         CHECK_NULL_RETURN(LessNotEqual(textOffset.GetY(), height), GetTextContentLength());
12408         newPos = paragraphs_.GetIndex(textOffset, true);
12409         OffsetF newCaretOffset = CalcCursorOffsetByPosition(newPos, newCaretHeight);
12410         CHECK_EQUAL_RETURN(!textSelector_.SelectNothing() && textSelector_.GetTextStart() == caretPosition_ &&
12411             selectEndOffset.GetY() == newCaretOffset.GetY(), true, textSelector_.GetTextEnd());
12412     }
12413     return newPos;
12414 }
12415 
12416 void RichEditorPattern::HandleSelectFontStyleWrapper(KeyCode code, TextStyle& spanStyle)
12417 {
12418     switch (code) {
12419         case KeyCode::KEY_B:
12420             if (spanStyle.GetFontWeight() == Ace::FontWeight::BOLD) {
12421                 spanStyle.SetFontWeight(Ace::FontWeight::NORMAL);
12422             } else {
12423                 spanStyle.SetFontWeight(Ace::FontWeight::BOLD);
12424             }
12425             break;
12426         case KeyCode::KEY_I:
12427             if (spanStyle.GetFontStyle() == OHOS::Ace::FontStyle::ITALIC) {
12428                 spanStyle.SetFontStyle(OHOS::Ace::FontStyle::NORMAL);
12429             } else {
12430                 spanStyle.SetFontStyle(OHOS::Ace::FontStyle::ITALIC);
12431             }
12432             break;
12433         case KeyCode::KEY_U:
12434             if (spanStyle.GetTextDecorationFirst() == TextDecoration::UNDERLINE) {
12435                 spanStyle.SetTextDecoration(TextDecoration::NONE);
12436             } else {
12437                 spanStyle.SetTextDecoration(TextDecoration::UNDERLINE);
12438             }
12439             break;
12440         default:
12441             LOGW("Unsupported select operation for HandleSelectFrontStyle");
12442             return;
12443     }
12444 }
12445 
12446 void RichEditorPattern::AIDeleteComb(int32_t start, int32_t end, int32_t& aiPosition, bool direction)
12447 {
12448     std::u16string selectTextContent;
12449     GetContentBySpans(selectTextContent);
12450     // get select content
12451     std::u16string selectData16 = selectTextContent.substr(static_cast<int32_t>(start), static_cast<int32_t>(end - start));
12452     std::string selectData = StringUtils::Str16ToStr8(selectData16);
12453     int32_t aiPosStart;
12454     int32_t aiPosEnd;
12455     int32_t caretPosition;
12456     int32_t size = 1;
12457 
12458     if (direction) {
12459         caretPosition = end - start - size;
12460         DataDetectorMgr::GetInstance().AdjustWordSelection(caretPosition, selectData, aiPosStart, aiPosEnd);
12461         aiPosition = aiPosStart + start;
12462     } else {
12463         caretPosition = 0;
12464         DataDetectorMgr::GetInstance().AdjustWordSelection(caretPosition, selectData, aiPosStart, aiPosEnd);
12465         aiPosition = aiPosEnd + start;
12466     }
12467     if (aiPosStart < 0 || aiPosEnd < 0) {
12468         aiPosition = GetCaretPosition();
12469     }
12470 }
12471 
12472 bool RichEditorPattern::HandleOnDeleteComb(bool backward)
12473 {
12474     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnDeleteComb backward=%{public}d", backward);
12475     undoManager_->RecordSelectionBefore();
12476     if (textSelector_.IsValid()) {
12477         CloseSelectOverlay();
12478         SetCaretPosition(textSelector_.GetTextStart());
12479         ResetSelection();
12480     }
12481     if (backward) {
12482         DeleteBackwardWord();
12483     } else {
12484         DeleteForwardWord();
12485     }
12486     undoManager_->ClearSelectionBefore();
12487     MoveCaretToContentRect();
12488     StartTwinkling();
12489     auto host = GetHost();
12490     CHECK_NULL_RETURN(host, false);
12491     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
12492     return true;
12493 }
12494 
12495 void RichEditorPattern::DeleteBackwardWord()
12496 {
12497     CHECK_NULL_VOID(caretPosition_ != 0);
12498     int32_t startIndex = caretPosition_;
12499     int32_t spaceEndIndex = startIndex;
12500     AdjustIndexSkipSpace(spaceEndIndex, MoveDirection::BACKWARD);
12501     int32_t wordEndIndex = std::max(0, spaceEndIndex - 1);
12502     AdjustSelectorForSymbol(wordEndIndex, HandleType::FIRST, SelectorAdjustPolicy::INCLUDE);
12503     AdjustWordSelection(wordEndIndex, spaceEndIndex);
12504     DeleteBackward(startIndex - wordEndIndex, TextChangeReason::INPUT);
12505 }
12506 
12507 void RichEditorPattern::DeleteForwardWord()
12508 {
12509     CHECK_NULL_VOID(caretPosition_ != GetTextContentLength());
12510     int32_t startIndex = caretPosition_;
12511     int32_t spaceEndIndex = startIndex;
12512     AdjustIndexSkipSpace(spaceEndIndex, MoveDirection::FORWARD);
12513     int32_t wordEndIndex = std::min(spaceEndIndex + 1, GetTextContentLength());
12514     AdjustWordSelection(spaceEndIndex, wordEndIndex);
12515     DeleteForward(wordEndIndex - startIndex, TextChangeReason::INPUT);
12516 }
12517 
12518 const std::list<RefPtr<UINode>>& RichEditorPattern::GetAllChildren() const
12519 {
12520     childNodes_.clear();
12521     auto host = GetContentHost();
12522     CHECK_NULL_RETURN(host, childNodes_);
12523     auto children = host->GetChildren();
12524     for (const auto& child: children) {
12525         childNodes_.push_back(child);
12526     }
12527     return childNodes_;
12528 }
12529 
12530 void RichEditorPattern::HandleTripleClickEvent(OHOS::Ace::GestureEvent& info)
12531 {
12532     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleTripleClickEvent");
12533     CHECK_EQUAL_VOID(IsPreviewTextInputting(), true);
12534     CHECK_EQUAL_VOID(IsDragging(), true);
12535     bool isMouseClickWithShift = shiftFlag_ && info.GetSourceDevice() == SourceType::MOUSE;
12536     CHECK_EQUAL_VOID(isMouseClickWithShift, true);
12537     auto focusHub = GetFocusHub();
12538     CHECK_NULL_VOID(focusHub);
12539     CHECK_EQUAL_VOID(focusHub->IsFocusable(), false);
12540     auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
12541     Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
12542         info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
12543     int32_t pos = paragraphs_.GetIndex(textOffset);
12544 
12545     int32_t start = 0;
12546     int32_t end = 0;
12547     auto& paragraphInfoList = paragraphs_.GetParagraphs();
12548     if (!paragraphInfoList.empty() && pos == paragraphInfoList.back().end) {
12549         start = paragraphInfoList.back().start;
12550         end = paragraphInfoList.back().end;
12551     } else {
12552         for (const auto& paragraph : paragraphInfoList) {
12553             if (pos >= paragraph.start && pos < paragraph.end) {
12554                 start = paragraph.start;
12555                 end = paragraph.end;
12556                 break;
12557             }
12558         }
12559     }
12560     if (!paragraphInfoList.empty() && paragraphInfoList.back().end != end) {
12561         --end;
12562     }
12563     end = std::min(GetTextContentLength(), end);
12564     start = std::min(GetTextContentLength(), start);
12565     CHECK_EQUAL_VOID(start > end, true);
12566     TripleClickSection(info, start, end, pos);
12567 }
12568 
12569 void RichEditorPattern::UpdateSelectionByTouchMove(const Offset& touchOffset)
12570 {
12571     // While previewing + long press and move, then shall select content.
12572     auto host = GetHost();
12573     CHECK_NULL_VOID(host);
12574 
12575     Offset textOffset = ConvertTouchOffsetToTextOffset(touchOffset);
12576     auto positionWithAffinity = paragraphs_.GetGlyphPositionAtCoordinate(textOffset);
12577     SetCaretPositionWithAffinity(positionWithAffinity);
12578     MoveCaretToContentRect();
12579     int32_t currentPosition = GreatNotEqual(textOffset.GetY(), paragraphs_.GetHeight())
12580                                 ? GetTextContentLength()
12581                                 : caretPosition_;
12582     IF_TRUE(GetTextContentLength() > 0, SetMagnifierLocalOffset(touchOffset));
12583     auto [initSelectStart, initSelectEnd] = initSelector_;
12584     int32_t start = std::min(initSelectStart, currentPosition);
12585     int32_t end = std::max(initSelectEnd, currentPosition);
12586     if (start == textSelector_.GetTextStart()) {
12587         StartVibratorByIndexChange(end, textSelector_.GetTextEnd());
12588     } else {
12589         StartVibratorByIndexChange(start, textSelector_.GetTextStart());
12590     }
12591     HandleSelectionChange(start, end);
12592     TriggerAvoidOnCaretChange();
12593     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
12594 }
12595 
12596 void RichEditorPattern::HideMenu()
12597 {
12598     selectOverlay_->HideMenu();
12599 }
12600 
12601 void RichEditorPattern::OnSelectionMenuOptionsUpdate(const NG::OnCreateMenuCallback&& onCreateMenuCallback,
12602     const NG::OnMenuItemClickCallback&& onMenuItemClick, const NG::OnPrepareMenuCallback&& onPrepareMenuCallback)
12603 {
12604     selectOverlay_->OnSelectionMenuOptionsUpdate(
12605         std::move(onCreateMenuCallback), std::move(onMenuItemClick), std::move(onPrepareMenuCallback));
12606 }
12607 
12608 bool RichEditorPattern::CheckTripClickEvent(GestureEvent& info)
12609 {
12610     clickInfo_.push_back(info.GetTimeStamp());
12611     if (clickInfo_.size() > MAX_CLICK) {
12612         clickInfo_.erase(clickInfo_.begin());
12613     }
12614     if (clickInfo_.size() == MAX_CLICK) {
12615         std::chrono::duration<float, std::ratio<1, InputAIChecker::SECONDS_TO_MILLISECONDS>>
12616             clickTimeIntervalOne = clickInfo_[1] - clickInfo_[0];
12617         std::chrono::duration<float, std::ratio<1, InputAIChecker::SECONDS_TO_MILLISECONDS>>
12618             clickTimeIntervalTwo = clickInfo_[2] - clickInfo_[1];
12619         if (clickTimeIntervalOne.count() < DOUBLE_CLICK_INTERVAL_MS
12620             && clickTimeIntervalTwo.count() < DOUBLE_CLICK_INTERVAL_MS) {
12621             return true;
12622         }
12623     }
12624     return false;
12625 }
12626 
12627 void RichEditorPattern::TripleClickSection(GestureEvent& info, int32_t start, int32_t end, int32_t pos)
12628 {
12629     auto host = GetHost();
12630     CHECK_NULL_VOID(host);
12631     textSelector_.Update(start, end);
12632     if (IsShowHandle()) {
12633         SetCaretPositionWithAffinity({ end, TextAffinity::UPSTREAM });
12634         MoveCaretToContentRect();
12635     }
12636     UpdateSelectionType(GetSpansInfo(start, end, GetSpansMethod::ONSELECT));
12637     if (info.GetSourceDevice() == SourceType::TOUCH) {
12638         showSelect_ = true;
12639         RequestKeyboard(false, true, true);
12640         HandleOnEditChanged(true);
12641         CalculateHandleOffsetAndShowOverlay();
12642         selectOverlay_->ProcessOverlay({ .menuIsShow = !selectOverlay_->GetIsHandleMoving(), .animation = true });
12643     }
12644     if (info.GetSourceDevice() == SourceType::TOUCH && start == end) {
12645         selectOverlay_->SetIsSingleHandle(true);
12646     }
12647     if (textSelector_.SelectNothing()) {
12648         textSelector_.Update(pos, pos);
12649     } else {
12650         StopTwinkling();
12651     }
12652     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
12653 }
12654 
12655 void RichEditorPattern::RequestKeyboardToEdit()
12656 {
12657     CHECK_NULL_VOID(!isEditing_ && HasFocus());
12658     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "request keyboard and enter edit");
12659     RequestKeyboard(false, true, true);
12660     HandleOnEditChanged(true);
12661 }
12662 
12663 bool RichEditorPattern::IsResponseRegionExpandingNeededForStylus(const TouchEvent& touchEvent) const
12664 {
12665     if (touchEvent.sourceTool != SourceTool::PEN || touchEvent.type != TouchType::DOWN) {
12666         return false;
12667     }
12668     auto host = GetHost();
12669     CHECK_NULL_RETURN(host, false);
12670     auto focusHub = host->GetFocusHub();
12671     CHECK_NULL_RETURN(focusHub, false);
12672     if (!focusHub->IsFocusable() || !host->IsVisible()) {
12673         return false;
12674     }
12675     auto renderContext = host->GetRenderContext();
12676     CHECK_NULL_RETURN(renderContext, false);
12677     auto opacity = renderContext->GetOpacity();
12678     // if opacity is 0.0f, no need to hit frameNode.
12679     if (NearZero(opacity.value_or(1.0f))) {
12680         return false;
12681     }
12682     return true;
12683 }
12684 
12685 RectF RichEditorPattern::ExpandDefaultResponseRegion(RectF& rect)
12686 {
12687     return rect + NG::SizeF(0, OHOS::Ace::HOT_AREA_ADJUST_SIZE.ConvertToPx() * OHOS::Ace::HOT_AREA_EXPAND_TIME) +
12688            NG::OffsetF(0, -OHOS::Ace::HOT_AREA_ADJUST_SIZE.ConvertToPx());
12689 }
12690 
12691 bool RichEditorPattern::InsertOrDeleteSpace(int32_t index)
12692 {
12693     // delete or insert space
12694     if (index < 0 || index >= GetTextContentLength()) {
12695         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "index is invalid, index=%{public}d", index);
12696         return false;
12697     }
12698     bool success = SetCaretPosition(index);
12699     CHECK_NULL_RETURN(success, false);
12700     CloseSelectOverlay();
12701     ResetSelection();
12702 
12703     auto curIt = GetSpanIter(index);
12704     if (curIt != spans_.end()) {
12705         std::u16string curText = (*curIt)->content;
12706         if ((*curIt)->spanItemType == SpanItemType::NORMAL
12707             && index >= (*curIt)->rangeStart && curText[index - (*curIt)->rangeStart] == u' ') {
12708             TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "delete forward");
12709             DeleteForward(1, TextChangeReason::STYLUS);
12710             return true;
12711         }
12712     }
12713 
12714     auto preIt = GetSpanIter(index - 1);
12715     if (preIt != spans_.end()) {
12716         std::u16string preText = (*preIt)->content;
12717         if ((*preIt)->spanItemType == SpanItemType::NORMAL
12718             && index - 1 >= (*preIt)->rangeStart && preText[index - 1 - (*preIt)->rangeStart] == u' ') {
12719             TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "delete backward");
12720             DeleteBackward(1, TextChangeReason::STYLUS);
12721             return true;
12722         }
12723     }
12724 
12725     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "insert value");
12726     InsertValueByOperationType(u" ", OperationType::STYLUS);
12727     return true;
12728 }
12729 
12730 // only called by IME
12731 void RichEditorPattern::DeleteRange(int32_t start, int32_t end, bool isIME)
12732 {
12733     DeleteRange(start, end, isIME, TextChangeReason::INPUT);
12734 }
12735 
12736 void RichEditorPattern::DeleteRange(int32_t start, int32_t end, bool isIME, TextChangeReason reason)
12737 {
12738     if (start > end) {
12739         std::swap(start, end);
12740     }
12741     start = std::max(0, start);
12742     end = std::min(GetTextContentLength(), end);
12743     if (start > GetTextContentLength() || end < 0 || start == end) {
12744         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "start=%{public}d, end=%{public}d, not in the range", start, end);
12745         return;
12746     }
12747     CHECK_NULL_VOID(!IsPreviewTextInputting());
12748     SetCaretPosition(start);
12749     auto length = end - start;
12750     if (isSpanStringMode_) {
12751         DeleteValueInStyledString(start, length, true, false);
12752         return;
12753     }
12754     OperationRecord record;
12755     record.beforeCaretPosition = caretPosition_;
12756     RichEditorChangeValue changeValue(reason);
12757     CHECK_NULL_VOID(BeforeChangeText(changeValue, record, RecordType::DEL_FORWARD, length));
12758     std::u16string textContent;
12759     GetContentBySpans(textContent);
12760     auto realEnd = std::clamp(caretPosition_ + length, 0, static_cast<int32_t>(textContent.length()));
12761     std::u16string deleteText = textContent.substr(
12762         static_cast<uint32_t>(std::clamp(caretPosition_, 0, static_cast<int32_t>(textContent.length()))),
12763         static_cast<uint32_t>(realEnd - caretPosition_));
12764     if (caretPosition_ != GetTextContentLength()) {
12765         RichEditorDeleteValue info;
12766         info.SetOffset(caretPosition_);
12767         info.SetRichEditorDeleteDirection(RichEditorDeleteDirection::FORWARD);
12768         info.SetLength(length);
12769         int32_t currentPosition = caretPosition_;
12770         if (!spans_.empty()) {
12771             CalcDeleteValueObj(currentPosition, length, info);
12772             bool doDelete = DoDeleteActions(currentPosition, length, info);
12773             CHECK_NULL_VOID(doDelete);
12774         }
12775     }
12776     CHECK_NULL_VOID(deleteText.length() != 0);
12777     ClearRedoOperationRecords();
12778     record.deleteText = deleteText;
12779     record.afterCaretPosition = caretPosition_;
12780     AddOperationRecord(record);
12781     AfterContentChange(changeValue);
12782 }
12783 
12784 void RichEditorPattern::HandleOnPageUp()
12785 {
12786     HandlePageScroll(true);
12787 }
12788 
12789 void RichEditorPattern::HandleOnPageDown()
12790 {
12791     HandlePageScroll(false);
12792 }
12793 
12794 void RichEditorPattern::HandlePageScroll(bool isPageUp)
12795 {
12796     auto visibleRect = selectOverlay_->GetVisibleRect();
12797     float distance = isPageUp ? visibleRect.Height() : -visibleRect.Height();
12798     RectF curCaretRect = GetCaretRect();
12799     auto height = paragraphs_.GetHeight();
12800     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "PageScroll isPageUp:%{public}d distance:%{public}f paragraphsHeight:%{public}f",
12801         isPageUp, distance, height);
12802     CloseSelectOverlay();
12803     ResetSelection();
12804     OnScrollCallback(distance, SCROLL_FROM_JUMP);
12805     auto paintOffset = selectOverlay_->GetPaintOffsetWithoutTransform();
12806     float offsetY = isPageUp ? visibleRect.Top() : visibleRect.Bottom();
12807     auto localOffset = Offset(curCaretRect.GetX(), offsetY - paintOffset.GetY());
12808     auto textOffset = ConvertTouchOffsetToTextOffset(localOffset);
12809     auto positionWithAffinity = paragraphs_.GetGlyphPositionAtCoordinate(textOffset);
12810     // If scrolling to the first or last line, move the cursor to the beginning or end of the line
12811     if (isPageUp && LessOrEqual(textOffset.GetY(), 0)) {
12812         positionWithAffinity = PositionWithAffinity(0, TextAffinity::DOWNSTREAM);
12813     } else if (!isPageUp && GreatOrEqual(textOffset.GetY(), height)) {
12814         positionWithAffinity = PositionWithAffinity(GetTextContentLength(), TextAffinity::UPSTREAM);
12815     }
12816     SetCaretPositionWithAffinity(positionWithAffinity);
12817     IF_TRUE(isEditing_, StartTwinkling());
12818 }
12819 
12820 TextStyle RichEditorPattern::GetDefaultTextStyle()
12821 {
12822     auto theme = GetTheme<RichEditorTheme>();
12823     TextStyle style = theme ? theme->GetTextStyle() : TextStyle();
12824     style.SetFontSize(Dimension(DEFAULT_TEXT_SIZE, DimensionUnit::FP));
12825     style.SetFontFeatures(ParseFontFeatureSettings("\"pnum\" 1"));
12826     style.SetFontFamilies({ "HarmonyOS Sans" });
12827     return style;
12828 }
12829 
12830 bool RichEditorPattern::IsShowTranslate()
12831 {
12832     auto richEditorTheme = GetTheme<RichEditorTheme>();
12833     CHECK_NULL_RETURN(richEditorTheme, false);
12834     return richEditorTheme->GetTranslateIsSupport();
12835 }
12836 
12837 bool RichEditorPattern::IsShowSearch()
12838 {
12839     auto richEditorTheme = GetTheme<RichEditorTheme>();
12840     CHECK_NULL_RETURN(richEditorTheme, false);
12841     return richEditorTheme->GetSearchIsSupport();
12842 }
12843 
12844 bool RichEditorPattern::IsShowAIWrite()
12845 {
12846     CHECK_NULL_RETURN(!textSelector_.SelectNothing(), false);
12847     auto container = Container::Current();
12848     if (container && container->IsSceneBoardWindow()) {
12849         return false;
12850     }
12851 
12852     if (copyOption_ == CopyOptions::None) {
12853         return false;
12854     }
12855     auto theme = GetTheme<RichEditorTheme>();
12856     CHECK_NULL_RETURN(theme, false);
12857     auto bundleName = theme->GetAIWriteBundleName();
12858     auto abilityName = theme->GetAIWriteAbilityName();
12859     if (bundleName.empty() || abilityName.empty()) {
12860         TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "Failed to obtain AI write package name!");
12861         return false;
12862     }
12863     aiWriteAdapter_->SetBundleName(bundleName);
12864     aiWriteAdapter_->SetAbilityName(abilityName);
12865 
12866     auto isAISupport = false;
12867     if (theme->GetAIWriteIsSupport() == "true") {
12868         isAISupport = true;
12869     }
12870 
12871     auto host = GetHost();
12872     CHECK_NULL_RETURN(host, false);
12873     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Whether the device supports AI write: %{public}d, nodeId: %{public}d",
12874         isAISupport, host->GetId());
12875     return isAISupport;
12876 }
12877 
12878 void RichEditorPattern::GetAIWriteInfo(AIWriteInfo& info)
12879 {
12880     CHECK_NULL_VOID(!textSelector_.SelectNothing());
12881     info.firstHandle = textSelector_.firstHandle.ToString();
12882     info.secondHandle = textSelector_.secondHandle.ToString();
12883     info.selectStart = textSelector_.GetTextStart();
12884     info.selectEnd = textSelector_.GetTextEnd();
12885     auto host = GetHost();
12886     CHECK_NULL_VOID(host);
12887     info.componentType = host->GetTag();
12888 
12889     // serialize the sentenced-level text
12890     auto textSize = static_cast<int32_t>(GetTextForDisplay().length()) + placeholderCount_;
12891     RefPtr<SpanString> spanString = ToStyledString(0, textSize);
12892     auto contentAll = spanString->GetU16string();
12893     auto sentenceStart = 0;
12894     auto sentenceEnd = textSize;
12895     for (int32_t i = info.selectStart; i >= 0; --i) {
12896         if (aiWriteAdapter_->IsSentenceBoundary(contentAll[i])) {
12897             sentenceStart = i + 1;
12898             break;
12899         }
12900     }
12901     for (int32_t i = info.selectEnd; i < textSize; i++) {
12902         if (aiWriteAdapter_->IsSentenceBoundary(contentAll[i])) {
12903             sentenceEnd = i;
12904             break;
12905         }
12906     }
12907     info.start = info.selectStart - sentenceStart;
12908     info.end = info.selectEnd - sentenceStart;
12909     spanString = ToStyledString(sentenceStart, sentenceEnd);
12910     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "Sentence range=[%{public}d-%{public}d], content=" SEC_PLD(%{public}s),
12911         sentenceStart, sentenceEnd, SEC_PARAM(spanString->GetString().c_str()));
12912     spanString->EncodeTlv(info.sentenceBuffer);
12913 
12914     // serialize the selected text
12915     spanString = ToStyledString(info.selectStart, info.selectEnd);
12916     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "Selected range=[%{public}d-%{public}d], content=" SEC_PLD(%{public}s),
12917         info.selectStart, info.selectEnd, SEC_PARAM(spanString->GetString().c_str()));
12918     spanString->EncodeTlv(info.selectBuffer);
12919     info.selectLength = static_cast<int32_t>(aiWriteAdapter_->GetSelectLengthOnlyText(spanString->GetU16string()));
12920 }
12921 
12922 void RichEditorPattern::HandleOnAIWrite()
12923 {
12924     aiWriteAdapter_->SetAIWrite(true);
12925     AIWriteInfo info;
12926     GetAIWriteInfo(info);
12927     CloseSelectOverlay();
12928     ResetSelection();
12929     CloseKeyboard(false);
12930 
12931     auto callback = [weak = WeakClaim(this), info](std::vector<uint8_t>& buffer) {
12932         auto pattern = weak.Upgrade();
12933         CHECK_NULL_VOID(pattern);
12934         pattern->HandleAIWriteResult(info.selectStart, info.selectEnd, buffer);
12935         auto aiWriteAdapter = pattern->aiWriteAdapter_;
12936         CHECK_NULL_VOID(aiWriteAdapter);
12937         aiWriteAdapter->CloseModalUIExtension();
12938     };
12939     auto pipeline = GetContext();
12940     CHECK_NULL_VOID(pipeline);
12941     aiWriteAdapter_->SetPipelineContext(WeakClaim(pipeline));
12942     aiWriteAdapter_->ShowModalUIExtension(info, callback);
12943 }
12944 
12945 SymbolSpanOptions RichEditorPattern::GetSymbolSpanOptions(const RefPtr<SpanItem>& spanItem)
12946 {
12947     CHECK_NULL_RETURN(spanItem, {});
12948     TextStyle textStyle = GetDefaultTextStyle();
12949     UseSelfStyle(spanItem->fontStyle, spanItem->textLineStyle, textStyle, true);
12950     SymbolSpanOptions options;
12951     options.style = textStyle;
12952     options.offset = caretPosition_;
12953     options.resourceObject = spanItem->GetResourceObject();
12954     options.symbolId = spanItem->GetSymbolId();
12955     return options;
12956 }
12957 
12958 void RichEditorPattern::ReplacePlaceholderWithCustomSpan(
12959     const RefPtr<SpanItem>& spanItem, size_t& index, size_t& textIndex)
12960 {
12961     if (isSpanStringMode_) {
12962         auto customSpanItem = DynamicCast<CustomSpanItem>(spanItem);
12963         auto customSpan = MakeRefPtr<CustomSpan>();
12964         if (customSpanItem->onMeasure.has_value()) {
12965             customSpan->SetOnMeasure(customSpanItem->onMeasure.value());
12966         }
12967         if (customSpanItem->onDraw.has_value()) {
12968             customSpan->SetOnDraw(customSpanItem->onDraw.value());
12969         }
12970         auto spanString = MakeRefPtr<MutableSpanString>(customSpan);
12971         InsertStyledStringByPaste(spanString);
12972     } else {
12973         auto customSpanItem = DynamicCast<PlaceholderSpanItem>(spanItem);
12974         CHECK_NULL_VOID(customSpanItem);
12975         auto customNode = customSpanItem->GetCustomNode();
12976         SpanOptionBase options;
12977         options.offset = caretPosition_;
12978         AddPlaceholderSpan(customNode, options, TextChangeReason::AI_WRITE);
12979     }
12980     textIndex = index + PLACEHOLDER_LENGTH;
12981 }
12982 
12983 void RichEditorPattern::ReplacePlaceholderWithSymbolSpan(
12984     const RefPtr<SpanItem>& spanItem, size_t& index, size_t& textIndex)
12985 {
12986     auto options = GetSymbolSpanOptions(spanItem);
12987     options.offset = caretPosition_;
12988     AddSymbolSpan(options, TextChangeReason::AI_WRITE, false, caretPosition_);
12989     textIndex = index + PLACEHOLDER_LENGTH;
12990 }
12991 
12992 void RichEditorPattern::ReplacePlaceholderWithImageSpan(
12993     const RefPtr<SpanItem>& spanItem, size_t& index, size_t& textIndex)
12994 {
12995     auto imageSpanItem = DynamicCast<ImageSpanItem>(spanItem);
12996     CHECK_NULL_VOID(imageSpanItem);
12997     auto options = imageSpanItem->options;
12998     options.offset = caretPosition_;
12999     if (isSpanStringMode_) {
13000         auto spanString = MakeRefPtr<SpanString>(options);
13001         InsertStyledStringByPaste(spanString);
13002     } else {
13003         AddImageSpan(options, TextChangeReason::AI_WRITE, true, caretPosition_, true);
13004     }
13005     textIndex = index + PLACEHOLDER_LENGTH;
13006 }
13007 
13008 void RichEditorPattern::ReplacePlaceholderWithRawSpans(
13009     const RefPtr<SpanItem>& spanItem, size_t& index, size_t& textIndex)
13010 {
13011     switch (spanItem->spanItemType) {
13012         case SpanItemType::SYMBOL:
13013             ReplacePlaceholderWithSymbolSpan(spanItem, index, textIndex);
13014             return;
13015         case SpanItemType::CustomSpan:
13016             ReplacePlaceholderWithCustomSpan(spanItem, index, textIndex);
13017             return;
13018         case SpanItemType::IMAGE:
13019             ReplacePlaceholderWithImageSpan(spanItem, index, textIndex);
13020             return;
13021         default:
13022             return;
13023     }
13024 }
13025 
13026 void RichEditorPattern::AddSpansAndReplacePlaceholder(RefPtr<SpanString>& spanString)
13027 {
13028     auto content = spanString->GetU16string();
13029     size_t textIndex = 0;
13030     size_t index = content.find(PLACEHOLDER_MARK);
13031 
13032     while (index != std::u16string::npos) {
13033         if (textIndex < index) {
13034             auto subSpan = spanString->GetSubSpanString(textIndex, index - textIndex);
13035             AddSpanByPasteData(subSpan, TextChangeReason::AI_WRITE);
13036         }
13037         auto key = content.substr(index, PLACEHOLDER_LENGTH);
13038         if (placeholderSpansMap_.find(key) == placeholderSpansMap_.end()) {
13039             index = content.find(PLACEHOLDER_MARK, index + 1);
13040             continue;
13041         }
13042         auto spanItem = placeholderSpansMap_[key];
13043         if (!spanItem) {
13044             index = content.find(PLACEHOLDER_MARK, index + 1);
13045             continue;
13046         }
13047         ReplacePlaceholderWithRawSpans(spanItem, index, textIndex);
13048         index = content.find(PLACEHOLDER_MARK, index + 1);
13049     }
13050     if (textIndex < content.length()) {
13051         auto subSpan = spanString->GetSubSpanString(textIndex, content.length() - textIndex);
13052         AddSpanByPasteData(subSpan, TextChangeReason::AI_WRITE);
13053     }
13054 }
13055 
13056 void RichEditorPattern::InsertSpanByBackData(RefPtr<SpanString>& spanString)
13057 {
13058     CHECK_NULL_VOID(spanString);
13059     if (textSelector_.IsValid()) {
13060         SetCaretPosition(textSelector_.GetTextStart());
13061         DeleteForward(textSelector_.GetTextStart(), textSelector_.GetTextEnd() - textSelector_.GetTextStart());
13062         ResetSelection();
13063     }
13064     if (placeholderSpansMap_.empty()) {
13065         AddSpanByPasteData(spanString, TextChangeReason::AI_WRITE);
13066     } else {
13067         AddSpansAndReplacePlaceholder(spanString);
13068     }
13069     StartTwinkling();
13070     auto host = GetHost();
13071     CHECK_NULL_VOID(host);
13072     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
13073     host->MarkModifyDone();
13074 }
13075 
13076 void RichEditorPattern::HandleAIWriteResult(int32_t start, int32_t end, std::vector<uint8_t>& buffer)
13077 {
13078     RefPtr<SpanString> spanString = SpanString::DecodeTlv(buffer);
13079     if (spanString->GetSpanItems().empty()) {
13080         return;
13081     }
13082     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "Backfilling results range=[%{public}d--%{public}d], content=" SEC_PLD(%{public}s),
13083         start, end, SEC_PARAM(spanString->GetString().c_str()));
13084 
13085     textSelector_.Update(start, end);
13086     auto length = end - start;
13087     CHECK_NULL_VOID(length > 0);
13088     DeleteBackward(length, TextChangeReason::AI_WRITE);
13089     InsertSpanByBackData(spanString);
13090     BeforeIMEInsertValue(UtfUtils::Str8ToStr16(spanString->GetString()));
13091     InsertValueByOperationType(u"", OperationType::AI_WRITE);
13092 }
13093 
13094 bool RichEditorPattern::IsTextEditableForStylus() const
13095 {
13096     CHECK_NULL_RETURN(!customKeyboardBuilder_, false);
13097     auto host = GetHost();
13098     CHECK_NULL_RETURN(host, false);
13099     auto focusHub = host->GetFocusHub();
13100     CHECK_NULL_RETURN(focusHub, false);
13101     if (!focusHub->IsFocusable() || !host->IsVisible()) {
13102         return false;
13103     }
13104     auto renderContext = host->GetRenderContext();
13105     CHECK_NULL_RETURN(renderContext, false);
13106     auto opacity = renderContext->GetOpacity();
13107     // if opacity is 0.0f, no need to hit frameNode.
13108     if (NearZero(opacity.value_or(1.0f))) {
13109         return false;
13110     }
13111     return true;
13112 }
13113 
13114 void RichEditorPattern::DumpInfo(std::unique_ptr<JsonValue>& json)
13115 {
13116     if (customKeyboardBuilder_) {
13117         json->Put("CustomKeyboard, Attached", std::to_string(isCustomKeyboardAttached_).c_str());
13118     }
13119     auto host = GetHost();
13120     CHECK_NULL_VOID(host);
13121     auto richEditorTheme = GetTheme<RichEditorTheme>();
13122     CHECK_NULL_VOID(richEditorTheme);
13123     json->Put("caret offset", GetCaretRect().GetOffset().ToString().c_str());
13124     json->Put("caret height",
13125         std::to_string(NearZero(GetCaretRect().Height()) ? richEditorTheme->GetDefaultCaretHeight().ConvertToPx()
13126                                                          : GetCaretRect().Height())
13127             .c_str());
13128     json->Put("text rect", richTextRect_.ToString().c_str());
13129     json->Put("content rect", contentRect_.ToString().c_str());
13130     auto richEditorPaintOffset = host->GetPaintRectOffsetNG(false, true);
13131     bool hasRenderTransform = selectOverlay_->HasRenderTransform();
13132     if (hasRenderTransform) {
13133         richEditorPaintOffset = selectOverlay_->GetPaintOffsetWithoutTransform();
13134     }
13135     json->Put("hasRenderTransform", std::to_string(hasRenderTransform).c_str());
13136     json->Put("richEditorPaintOffset", richEditorPaintOffset.ToString().c_str());
13137     auto selectOverlayInfo = selectOverlay_->GetSelectOverlayInfo();
13138     CHECK_NULL_VOID(selectOverlayInfo);
13139     json->Put("selectOverlay info", selectOverlayInfo->ToString().c_str());
13140 }
13141 
13142 RectF RichEditorPattern::GetCaretRelativeRect()
13143 {
13144     CHECK_NULL_RETURN(caretTwinkling_, RectF(-1, -1, -1, -1));
13145     auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
13146     CHECK_NULL_RETURN(overlayMod_, RectF(0, 0, 0, 0));
13147     auto caretWidth = DynamicCast<RichEditorOverlayModifier>(overlayMod_)->GetCaretWidth();
13148     return RectF(caretOffset.GetX(), caretOffset.GetY(), caretWidth, caretHeight);
13149 }
13150 
13151 void RichEditorPattern::OnReportRichEditorEvent(const std::string& event)
13152 {
13153     std::string value = RICHEDITOR + event;
13154     UiSessionManager::GetInstance()->ReportComponentChangeEvent(EVENT, value);
13155     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "nodeId:[%{public}d] RichEditor reportComponentChangeEvent %{public}s", frameId_,
13156         event.c_str());
13157 }
13158 
13159 float RichEditorPattern::GetCaretWidth()
13160 {
13161     return static_cast<float>(CARET_WIDTH.ConvertToPx());
13162 }
13163 
13164 #if defined(IOS_PLATFORM)
13165 const TextEditingValue& RichEditorPattern::GetInputEditingValue() const
13166 {
13167     static TextEditingValue value;
13168     value.text.clear();
13169     if (!spans_.empty()) {
13170         std::string text;
13171         for (const auto& span : spans_) {
13172             if (!span) {
13173                 continue;
13174             }
13175             if (span->spanItemType == SpanItemType::NORMAL) {
13176                 text.append(UtfUtils::Str16ToStr8(span->content));
13177             } else {
13178                 text.append(" ");
13179             }
13180         }
13181         value.text = text;
13182     }
13183     if (textSelector_.IsValid()) {
13184         value.selection.Update(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
13185     } else {
13186         value.MoveToPosition(caretPosition_);
13187     }
13188     return value;
13189 }
13190 #endif
13191 
13192 bool RichEditorPattern::IsSelectAll()
13193 {
13194     return textSelector_.GetTextStart() == 0 && textSelector_.GetTextEnd() == GetTextContentLength();
13195 }
13196 
13197 void RichEditorPattern::OnAccessibilityEventTextChange(const std::string& changeType, const std::string& changeString)
13198 {
13199     auto pipeline = GetContext();
13200     CHECK_NULL_VOID(pipeline);
13201     auto host = GetHost();
13202     CHECK_NULL_VOID(host);
13203     AccessibilityEvent event;
13204     event.type = AccessibilityEventType::TEXT_CHANGE;
13205     event.nodeId = host->GetAccessibilityId();
13206     event.extraEventInfo.insert({changeType, changeString});
13207     pipeline->SendEventToAccessibilityWithNode(event, GetHost());
13208 }
13209 
13210 void RichEditorPattern::ReportComponentChangeEvent() {
13211 #if !defined(PREVIEW) && !defined(ACE_UNITTEST) && defined(OHOS_PLATFORM)
13212     std::string str;
13213     if (isSpanStringMode_) {
13214         CHECK_NULL_VOID(styledString_);
13215         str = styledString_->GetString();
13216     } else {
13217         std::u16string u16Str;
13218         GetContentBySpans(u16Str);
13219         str = UtfUtils::Str16DebugToStr8(u16Str);
13220     }
13221     auto value = InspectorJsonUtil::Create();
13222     CHECK_NULL_VOID(value);
13223     value->Put("text", str.c_str());
13224     UiSessionManager::GetInstance()->ReportComponentChangeEvent(frameId_, "event", value);
13225     SEC_TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "nodeId:[%{public}d] RichEditor reportComponentChangeEvent %{public}zu",
13226         frameId_, str.length());
13227 #endif
13228 }
13229 } // namespace OHOS::Ace::NG
13230