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