1 /*
2 * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #define NAPI_VERSION 8
17
18 #include "core/components_ng/pattern/rich_editor/rich_editor_pattern.h"
19
20 #include <algorithm>
21 #include <chrono>
22 #include <cstddef>
23 #include <cstdint>
24 #include <functional>
25 #include <future>
26 #include <iterator>
27 #include <sstream>
28 #include <string>
29 #include <utility>
30
31 #include "interfaces/inner_api/ui_session/ui_session_manager.h"
32 #ifdef ANDROID_PLATFORM
33 #include "adapter/android/capability/java/jni/editing/text_input_client_handler.h"
34 #endif
35 #ifdef IOS_PLATFORM
36 #include "adapter/ios/capability/editing/text_input_client_handler.h"
37 #endif
38 #include "adapter/ohos/capability/clipboard/clipboard_impl.h"
39 #include "base/geometry/offset.h"
40 #include "base/i18n/localization.h"
41 #include "base/log/ace_trace.h"
42 #include "base/log/dump_log.h"
43 #include "base/log/log_wrapper.h"
44 #include "base/memory/ace_type.h"
45 #include "base/utils/string_utils.h"
46 #include "base/utils/utf_helper.h"
47 #include "base/utils/utils.h"
48 #include "core/common/ai/data_detector_mgr.h"
49 #include "core/common/clipboard/paste_data.h"
50 #include "core/common/container.h"
51 #include "core/common/container_scope.h"
52 #include "core/common/ime/text_input_client.h"
53 #include "core/common/share/text_share_adapter.h"
54 #include "core/common/stylus/stylus_detector_mgr.h"
55 #include "core/common/vibrator/vibrator_utils.h"
56 #include "core/components/common/layout/constants.h"
57 #include "core/components/common/properties/text_style_parser.h"
58 #include "core/components_ng/base/inspector_filter.h"
59 #include "core/components_ng/base/observer_handler.h"
60 #include "core/components_ng/base/view_stack_processor.h"
61 #include "core/components_ng/event/event_hub.h"
62 #include "core/components_ng/event/gesture_event_hub.h"
63 #include "core/components_ng/event/long_press_event.h"
64 #include "core/components_ng/pattern/image/image_pattern.h"
65 #include "core/components_ng/pattern/overlay/keyboard_base_pattern.h"
66 #include "core/components_ng/pattern/rich_editor/color_mode_processor.h"
67 #include "core/components_ng/pattern/rich_editor/one_step_drag_controller.h"
68 #include "core/components_ng/pattern/rich_editor/rich_editor_content_pattern.h"
69 #include "core/components_ng/pattern/rich_editor/rich_editor_event_hub.h"
70 #include "core/components_ng/pattern/rich_editor/rich_editor_layout_property.h"
71 #include "core/components_ng/pattern/rich_editor/rich_editor_model.h"
72 #include "core/components_ng/pattern/rich_editor/rich_editor_undo_manager.h"
73 #include "core/components_ng/pattern/rich_editor/rich_editor_utils.h"
74 #include "core/components_ng/pattern/rich_editor/style_manager.h"
75 #include "core/components_ng/pattern/rich_editor_drag/rich_editor_drag_pattern.h"
76 #include "core/components_ng/pattern/text_field/text_field_manager.h"
77 #include "core/components_ng/pattern/text_field/text_input_ai_checker.h"
78 #include "core/text/html_utils.h"
79
80 #ifndef ACE_UNITTEST
81 #ifdef ENABLE_STANDARD_INPUT
82 #include "refbase.h"
83 #include "adapter/ohos/entrance/ace_container.h"
84 #include "core/components_ng/pattern/text_field/on_text_changed_listener_impl.h"
85 #endif
86 #endif
87
88 #include "core/common/udmf/udmf_client.h"
89
90 #ifdef WINDOW_SCENE_SUPPORTED
91 #include "core/components_ng/pattern/window_scene/helper/window_scene_helper.h"
92 #endif
93
94 #ifdef ENABLE_ROSEN_BACKEND
95 #include "core/components/custom_paint/rosen_render_custom_paint.h"
96 #endif
97
98 namespace OHOS::Ace::NG {
99
100 namespace {
101 #if defined(ENABLE_STANDARD_INPUT)
102 // should be moved to theme
103 constexpr float DEFAULT_CARET_HEIGHT = 18.5f;
104 constexpr Dimension KEYBOARD_AVOID_OFFSET = 24.0_vp;
105 constexpr size_t MAX_PLACEHOLDER_SIZE = 255;
106 constexpr size_t MAX_ABILITY_NAME_SIZE = 127;
107 #endif
108 constexpr Dimension CARET_WIDTH = 2.0_vp;
109 constexpr int32_t IMAGE_SPAN_LENGTH = 1;
110 constexpr int32_t SYMBOL_SPAN_LENGTH = 2;
111 constexpr uint32_t RICH_EDITOR_TWINKLING_INTERVAL_MS = 500;
112 constexpr uint32_t RICH_EDITOR_TWINKLING_INTERVAL_MS_DEBUG = 3000;
113 constexpr int32_t AUTO_SCROLL_INTERVAL = 15;
114 constexpr Dimension CARET_BOTTOM_DISTANCE = 16.0_vp;
115 constexpr Dimension AUTO_SCROLL_EDGE_DISTANCE = 15.0_vp;
116 constexpr Dimension AUTO_SCROLL_DRAG_EDGE_DISTANCE = 58.0_vp;
117 constexpr float MAX_DRAG_SCROLL_SPEED = 2400.0f;
118 constexpr float TIME_UNIT = 1000.0f;
119 constexpr float DOUBLE_CLICK_INTERVAL_MS = 300.0f;
120 constexpr uint32_t RECORD_MAX_LENGTH = 20;
121 constexpr float DEFAILT_OPACITY = 0.2f;
122 constexpr int64_t COLOR_OPAQUE = 255;
123 constexpr int32_t MAX_CLICK = 3;
124
125 constexpr Color SYSTEM_CARET_COLOR = Color(0xff007dff);
126 constexpr Color SYSTEM_SELECT_BACKGROUND_COLOR = Color(0x33007dff);
127
128 const auto MAGNIFIER_ANIMATION_CURVE = AceType::MakeRefPtr<InterpolatingSpring>(0.0f, 1.0f, 228.0f, 30.0f);
129 constexpr int32_t MAGNIFIER_ANIMATION_DURATION = 100;
130
131 constexpr int32_t ERROR_BAD_PARAMETERS = -1;
132 constexpr char PREVIEW_STYLE_NORMAL[] = "normal";
133 constexpr char PREVIEW_STYLE_UNDERLINE[] = "underline";
134 const std::u16string LINE_SEPARATOR = u"\n";
135 // hen do ai anaylsis, we should limit the left an right limit of the string
136 constexpr static int32_t AI_TEXT_RANGE_LEFT = 50;
137 constexpr static int32_t AI_TEXT_RANGE_RIGHT = 50;
138 constexpr static int32_t NONE_SELECT_TYPE = -1;
139
140 constexpr float RICH_DEFAULT_SHADOW_COLOR = 0x33000000;
141 constexpr float RICH_DEFAULT_ELEVATION = 120.0f;
142 constexpr int32_t CUSTOM_CONTENT_LENGTH = 1;
143 constexpr int32_t SYMBOL_CONTENT_LENGTH = 2;
144 constexpr int32_t PLACEHOLDER_LENGTH = 6;
145 const std::u16string PLACEHOLDER_MARK = u"{ return (span->urlOnRelease); };
151 } // namespace
152
RichEditorPattern(bool isStyledStringMode)153 RichEditorPattern::RichEditorPattern(bool isStyledStringMode) :
154 #ifndef ACE_UNITTEST
155 isAPI14Plus(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_FOURTEEN)),
156 #else
157 isAPI14Plus(true),
158 #endif
159 isAPI16Plus(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_SIXTEEN)),
160 isAPI18Plus(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)),
161 isAPI20Plus(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWENTY))
162 {
163 SetSpanStringMode(isStyledStringMode);
164 selectOverlay_ = AceType::MakeRefPtr<RichEditorSelectOverlay>(WeakClaim(this));
165 magnifierController_ = MakeRefPtr<MagnifierController>(WeakClaim(this));
166 styledString_ = MakeRefPtr<MutableSpanString>(u"");
167 styledString_->SetSpanWatcher(WeakClaim(this));
168 twinklingInterval_ = SystemProperties::GetDebugEnabled()
169 ? RICH_EDITOR_TWINKLING_INTERVAL_MS_DEBUG : RICH_EDITOR_TWINKLING_INTERVAL_MS;
170 floatingCaretState_.UpdateOriginCaretColor(GetDisplayColorMode());
171 undoManager_ = RichEditorUndoManager::Create(isSpanStringMode_, WeakClaim(this));
172 styleManager_ = std::make_unique<StyleManager>(WeakClaim(this));
173 if (!dataDetectorAdapter_) {
174 dataDetectorAdapter_ = MakeRefPtr<DataDetectorAdapter>();
175 }
176 }
177
~RichEditorPattern()178 RichEditorPattern::~RichEditorPattern()
179 {
180 if (isCustomKeyboardAttached_) {
181 CloseCustomKeyboard();
182 }
183 #ifdef CROSS_PLATFORM
184 if (HasConnection()) {
185 connection_->Close(GetInstanceId());
186 connection_ = nullptr;
187 }
188 #endif
189 }
190
SetStyledString(const RefPtr<SpanString> & value)191 void RichEditorPattern::SetStyledString(const RefPtr<SpanString>& value)
192 {
193 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "SetStyledString, len=%{public}d", value->GetLength());
194 SEC_TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "SetStyledString, str=%{public}s", value->GetString().c_str());
195 if (GetTextContentLength() > maxLength_.value_or(INT_MAX)) {
196 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "SetStyledString: Reach the maxLength. maxLength=%{public}d", maxLength_.value_or(INT_MAX));
197 return;
198 }
199 CHECK_NULL_VOID(value && styledString_);
200 auto subValue = value;
201 if (value->GetLength() != styledString_->GetLength() && value->GetLength() > maxLength_.value_or(INT_MAX)) {
202 auto subLength = CalculateTruncationLength(value->GetU16string(), maxLength_.value_or(INT_MAX));
203 if (subLength == 0) {
204 IF_TRUE(IsPreviewTextInputting() && !previewTextRecord_.previewTextExiting, NotifyExitTextPreview(true));
205 return;
206 }
207 subValue = value->GetSubSpanString(0, subLength);
208 }
209 IF_TRUE(IsPreviewTextInputting() && !previewTextRecord_.previewTextExiting, NotifyExitTextPreview(true));
210 auto length = styledString_->GetLength();
211 UndoRedoRecord record;
212 undoManager_->ApplyOperationToRecord(0, length, subValue, record);
213 CloseSelectOverlay();
214 ResetSelection();
215 styledString_->RemoveCustomSpan();
216 styledString_->ReplaceSpanString(0, length, subValue);
217 SetCaretPosition(styledString_->GetLength());
218 SetNeedMoveCaretToContentRect();
219 auto host = GetContentHost();
220 CHECK_NULL_VOID(host);
221 styledString_->AddCustomSpan();
222 styledString_->SetFramNode(host);
223 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
224 ForceTriggerAvoidOnCaretChange();
225 undoManager_->RecordOperation(record);
226 ReportAfterContentChangeEvent();
227 }
228
UpdateSpanItems(const std::list<RefPtr<NG::SpanItem>> & spanItems)229 void RichEditorPattern::UpdateSpanItems(const std::list<RefPtr<NG::SpanItem>>& spanItems)
230 {
231 SetSpanItemChildren(spanItems);
232 ProcessStyledString();
233 }
234
ProcessStyledString()235 void RichEditorPattern::ProcessStyledString()
236 {
237 auto host = GetContentHost();
238 CHECK_NULL_VOID(host);
239 std::u16string textCache = textForDisplay_;
240 textForDisplay_.clear();
241 dataDetectorAdapter_->textForAI_.clear();
242 host->Clean();
243 RemoveEmptySpanItems();
244 hasUrlSpan_ = false;
245 for (const auto& span : spans_) {
246 if (!span) {
247 continue;
248 }
249 auto imageSpan = DynamicCast<ImageSpanItem>(span);
250 if (imageSpan) {
251 MountImageNode(imageSpan);
252 dataDetectorAdapter_->textForAI_ += u'\n';
253 } else {
254 dataDetectorAdapter_->textForAI_ += span->content;
255 }
256 textForDisplay_ += span->content;
257 auto [spanStart, spanEnd] = span->interval;
258 span->rangeStart = spanStart;
259 span->position = spanEnd;
260
261 if (span->urlOnRelease) {
262 hasUrlSpan_ = true;
263 }
264 }
265 if (textForDisplay_ != textCache) {
266 dataDetectorAdapter_->aiDetectInitialized_ = false;
267 }
268 if (CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
269 dataDetectorAdapter_->StartAITask();
270 }
271 }
272
MountImageNode(const RefPtr<ImageSpanItem> & imageItem)273 void RichEditorPattern::MountImageNode(const RefPtr<ImageSpanItem>& imageItem)
274 {
275 auto host = GetContentHost();
276 CHECK_NULL_VOID(host);
277 CHECK_NULL_VOID(imageItem);
278 auto options = imageItem->options;
279 auto imageNode = ImageSpanNode::GetOrCreateSpanNode(V2::IMAGE_ETS_TAG,
280 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<ImagePattern>(); });
281 auto pattern = imageNode->GetPattern<ImagePattern>();
282 CHECK_NULL_VOID(pattern);
283 if (options.imagePixelMap.has_value()) {
284 pattern->SetSyncLoad(true);
285 } else if (options.imageAttribute.has_value()) {
286 pattern->SetSyncLoad(options.imageAttribute.value().syncLoad);
287 }
288 auto index = host->GetChildren().size();
289 imageNodes.push_back(imageNode);
290 imageNode->MountToParent(host, index);
291 HandleImageDrag(imageNode);
292 SetImageLayoutProperty(imageNode, options);
293 imageItem->nodeId_ = imageNode->GetId();
294 imageNode->SetImageItem(imageItem);
295 }
296
SetImageLayoutProperty(RefPtr<ImageSpanNode> imageNode,const ImageSpanOptions & options)297 void RichEditorPattern::SetImageLayoutProperty(RefPtr<ImageSpanNode> imageNode, const ImageSpanOptions& options)
298 {
299 CHECK_NULL_VOID(imageNode);
300 auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
301 CHECK_NULL_VOID(imageLayoutProperty);
302 std::function<ImageSourceInfo()> createSourceInfoFunc = CreateImageSourceInfo(options);
303 imageLayoutProperty->UpdateImageSourceInfo(createSourceInfoFunc());
304 if (options.imageAttribute.has_value()) {
305 auto imgAttr = options.imageAttribute.value();
306 if (imgAttr.size.has_value()) {
307 imageLayoutProperty->UpdateUserDefinedIdealSize(imgAttr.size->GetSize());
308 }
309 if (imgAttr.verticalAlign.has_value()) {
310 imageLayoutProperty->UpdateVerticalAlign(imgAttr.verticalAlign.value());
311 }
312 if (imgAttr.objectFit.has_value()) {
313 imageLayoutProperty->UpdateImageFit(imgAttr.objectFit.value());
314 }
315 if (imgAttr.marginProp.has_value()) {
316 imageLayoutProperty->UpdateMargin(imgAttr.marginProp.value());
317 }
318 if (imgAttr.paddingProp.has_value()) {
319 imageLayoutProperty->UpdatePadding(imgAttr.paddingProp.value());
320 }
321 if (imgAttr.borderRadius.has_value()) {
322 auto imageRenderCtx = imageNode->GetRenderContext();
323 imageRenderCtx->UpdateBorderRadius(imgAttr.borderRadius.value());
324 imageRenderCtx->SetClipToBounds(true);
325 }
326 auto paintProperty = imageNode->GetPaintProperty<ImageRenderProperty>();
327 if (imgAttr.colorFilterMatrix.has_value() && paintProperty) {
328 paintProperty->UpdateColorFilter(imgAttr.colorFilterMatrix.value());
329 paintProperty->ResetDrawingColorFilter();
330 } else if (imgAttr.drawingColorFilter.has_value() && paintProperty) {
331 paintProperty->UpdateDrawingColorFilter(imgAttr.drawingColorFilter.value());
332 paintProperty->ResetColorFilter();
333 }
334 }
335 imageNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
336 imageNode->MarkModifyDone();
337 IF_PRESENT(oneStepDragController_, MarkDirtyNode(WeakClaim(RawPtr(imageNode))));
338 }
339
HandleStyledStringInsertion(RefPtr<SpanString> insertStyledString,const UndoRedoRecord & record,std::u16string & subValue,bool needReplaceInTextPreview,bool shouldCommitInput)340 void RichEditorPattern::HandleStyledStringInsertion(RefPtr<SpanString> insertStyledString, const UndoRedoRecord& record,
341 std::u16string& subValue, bool needReplaceInTextPreview, bool shouldCommitInput)
342 {
343 auto changeStart = record.rangeBefore.start;
344 auto changeLength = record.rangeBefore.GetLength();
345 if (changeLength > 0 && (subValue.length() > 0 || !shouldCommitInput)) {
346 auto start = needReplaceInTextPreview ? previewTextRecord_.replacedRange.start : caretPosition_;
347 auto isUpdateCaret = !needReplaceInTextPreview;
348 DeleteValueInStyledString(start, changeLength, false, isUpdateCaret);
349 }
350 bool isSingleHandleMoving = selectOverlay_->IsSingleHandleMoving();
351 if (textSelector_.IsValid()) {
352 ResetSelection();
353 }
354 CloseSelectOverlay();
355 if (insertStyledString) {
356 styledString_->InsertSpanString(changeStart, insertStyledString);
357 } else {
358 styledString_->InsertString(changeStart, subValue);
359 }
360 SetCaretPosition(changeStart + static_cast<int32_t>(subValue.length()), !needReplaceInTextPreview);
361 IF_TRUE((!caretVisible_ || isSingleHandleMoving) && HasFocus(), StartTwinkling());
362 auto host = GetContentHost();
363 CHECK_NULL_VOID(host);
364 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
365 host->MarkModifyDone();
366 }
367
InsertValueInStyledString(const std::u16string & insertValue,bool shouldCommitInput,bool isPaste)368 void RichEditorPattern::InsertValueInStyledString(
369 const std::u16string& insertValue, bool shouldCommitInput, bool isPaste)
370 {
371 CHECK_NULL_VOID(styledString_);
372 IF_TRUE(shouldCommitInput && previewTextRecord_.IsValid(), FinishTextPreviewInner());
373 int32_t changeStart = caretPosition_;
374 int32_t changeLength = 0;
375 if (textSelector_.IsValid()) {
376 changeStart = textSelector_.GetTextStart();
377 changeLength = textSelector_.GetTextEnd() - changeStart;
378 IF_TRUE(!shouldCommitInput, undoManager_->RecordPreviewInputtingStart(changeStart, changeLength));
379 }
380 auto subValue = insertValue;
381 if (!ProcessTextTruncationOperation(subValue, shouldCommitInput)) {
382 return;
383 }
384 auto needReplaceInTextPreview = (previewTextRecord_.needReplacePreviewText || previewTextRecord_.needReplaceText) &&
385 previewTextRecord_.replacedRange.end - previewTextRecord_.replacedRange.start > 0;
386 if (needReplaceInTextPreview) {
387 changeStart= previewTextRecord_.replacedRange.start;
388 changeLength = previewTextRecord_.replacedRange.end - previewTextRecord_.replacedRange.start;
389 }
390 UndoRedoRecord record;
391 bool isPreventChange = false;
392 auto insertStyledString = styleManager_->CreateStyledStringByTypingStyle(subValue, styledString_, changeStart, changeLength);
393 if (insertStyledString) {
394 undoManager_->ApplyOperationToRecord(changeStart, changeLength, insertStyledString, record);
395 } else {
396 undoManager_->ApplyOperationToRecord(changeStart, changeLength, subValue, record);
397 }
398 isPreventChange = !BeforeStyledStringChange(record);
399 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "InsertValueInSS isPreventChange=%{public}d, needReplacePreviewText=%{public}d",
400 isPreventChange, previewTextRecord_.needReplacePreviewText);
401 if (isPreventChange && !previewTextRecord_.needReplacePreviewText) {
402 IF_TRUE(shouldCommitInput, undoManager_->ClearPreviewInputRecord());
403 return;
404 }
405 HandleStyledStringInsertion(insertStyledString, record, subValue, needReplaceInTextPreview, shouldCommitInput);
406 IF_TRUE(shouldCommitInput, undoManager_->RecordInsertOperation(record));
407 AfterStyledStringChange(record);
408 IF_TRUE(!isPaste, OnReportRichEditorEvent("onIMEInputComplete"));
409 }
410
DeleteBackwardInStyledString(int32_t length)411 void RichEditorPattern::DeleteBackwardInStyledString(int32_t length)
412 {
413 DeleteValueInStyledString(caretPosition_ - length, length);
414 }
415
DeleteForwardInStyledString(int32_t length,bool isIME)416 void RichEditorPattern::DeleteForwardInStyledString(int32_t length, bool isIME)
417 {
418 DeleteValueInStyledString(caretPosition_, length, isIME);
419 }
420
DeleteValueInStyledString(int32_t start,int32_t length,bool isIME,bool isUpdateCaret)421 void RichEditorPattern::DeleteValueInStyledString(int32_t start, int32_t length, bool isIME, bool isUpdateCaret)
422 {
423 CHECK_NULL_VOID(styledString_);
424 if (!textSelector_.SelectNothing()) {
425 start = textSelector_.GetTextStart();
426 length = textSelector_.GetTextEnd() - textSelector_.GetTextStart();
427 }
428 auto range = TextEmojiProcessor::CalSubU16stringRange(start, length, styledString_->GetU16string(), true, true);
429 start = range.startIndex;
430 length = range.endIndex - range.startIndex;
431 UndoRedoRecord record;
432 undoManager_->ApplyOperationToRecord(start, length, u"", record);
433 bool isPreventChange = isIME && !BeforeStyledStringChange(record);
434 TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
435 "deleteInSS, start=%{public}d, length=%{public}d, isPreventChange=%{public}d, "
436 "isPreviewTextInputting=%{public}d",
437 start, length, isPreventChange, IsPreviewTextInputting());
438 CHECK_NULL_VOID(!isPreventChange || IsPreviewTextInputting());
439 IF_TRUE(isIME && !IsPreviewTextInputting(), undoManager_->RecordOperation(record));
440 bool isSingleHandleMoving = selectOverlay_->IsSingleHandleMoving();
441 if (textSelector_.IsValid()) {
442 CloseSelectOverlay();
443 ResetSelection();
444 }
445 styledString_->RemoveString(start, length);
446 if (isUpdateCaret) {
447 SetCaretPosition(start, !isModifyingContent_);
448 }
449 if ((!caretVisible_ || isSingleHandleMoving) && HasFocus()) {
450 StartTwinkling();
451 if (!isEditing_ && isIME) {
452 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "previewLongPress_ is true, before RequestKeyboard");
453 RequestKeyboard(false, true, true);
454 HandleOnEditChanged(true);
455 previewLongPress_ = false;
456 }
457 }
458 if (isIME) {
459 AfterStyledStringChange(record);
460 }
461 OnReportRichEditorEvent("onDeleteComplete");
462 auto host = GetHost();
463 CHECK_NULL_VOID(host);
464 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
465 host->MarkModifyDone();
466 }
467
CreateStyledStringByStyleBefore(int32_t start,const std::u16string & string)468 RefPtr<SpanString> RichEditorPattern::CreateStyledStringByStyleBefore(int32_t start, const std::u16string& string)
469 {
470 auto styledString = AceType::MakeRefPtr<SpanString>(string);
471 CHECK_NULL_RETURN(styledString_, styledString);
472 auto stringLength = styledString->GetLength();
473 CHECK_NULL_RETURN(stringLength != 0, styledString);
474 auto lastStyles = styledString_->GetSpans(start - 1, 1);
475 for (auto && style : lastStyles) {
476 CHECK_NULL_CONTINUE(style);
477 auto spanType = style->GetSpanType();
478 CHECK_NULL_CONTINUE(spanType != SpanType::Image && spanType != SpanType::CustomSpan);
479 auto span = style->GetSubSpan(0, stringLength);
480 styledString->AddSpan(span);
481 }
482 return styledString;
483 }
484
BeforeStyledStringChange(const UndoRedoRecord & record,bool isUndo)485 bool RichEditorPattern::BeforeStyledStringChange(const UndoRedoRecord& record, bool isUndo)
486 {
487 CHECK_NULL_RETURN(!record.isOnlyStyleChange, true);
488 auto changeStart = isUndo ? record.rangeAfter.start : record.rangeBefore.start;
489 auto changeLength = isUndo ? record.rangeAfter.GetLength() : record.rangeBefore.GetLength();
490 auto styledString = isUndo ? record.styledStringBefore : record.styledStringAfter;
491 return BeforeStyledStringChange(changeStart, changeLength, styledString);
492 }
493
BeforeStyledStringChange(int32_t start,int32_t length,const std::u16string & string)494 bool RichEditorPattern::BeforeStyledStringChange(int32_t start, int32_t length, const std::u16string& string)
495 {
496 auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
497 CHECK_NULL_RETURN(eventHub, true);
498 CHECK_NULL_RETURN(eventHub->HasOnStyledStringWillChange(), true);
499 auto changeStart = std::clamp(start, 0, GetTextContentLength());
500 auto styledString = CreateStyledStringByStyleBefore(changeStart, string);
501 return BeforeStyledStringChange(changeStart, length, styledString);
502 }
503
BeforeStyledStringChange(int32_t start,int32_t length,const RefPtr<SpanString> & styledString)504 bool RichEditorPattern::BeforeStyledStringChange(int32_t start, int32_t length, const RefPtr<SpanString>& styledString)
505 {
506 auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
507 CHECK_NULL_RETURN(eventHub, true);
508 CHECK_NULL_RETURN(eventHub->HasOnStyledStringWillChange(), true);
509 auto replaceMentString = AceType::MakeRefPtr<MutableSpanString>(u"");
510 replaceMentString->AppendSpanString(styledString);
511 StyledStringChangeValue changeValue;
512 auto changeStart = std::clamp(start, 0, GetTextContentLength());
513 auto changeEnd = std::clamp(changeStart + length, 0, GetTextContentLength());
514 changeValue.SetRangeBefore({ changeStart, changeEnd });
515 changeValue.SetReplacementString(replaceMentString);
516 if (!previewTextRecord_.newPreviewContent.empty()) {
517 auto previewTextStyledString = AceType::MakeRefPtr<MutableSpanString>(previewTextRecord_.newPreviewContent);
518 changeValue.SetPreviewText(previewTextStyledString);
519 }
520 return eventHub->FireOnStyledStringWillChange(changeValue);
521 }
522
AfterStyledStringChange(int32_t start,int32_t length,const std::u16string & string)523 void RichEditorPattern::AfterStyledStringChange(int32_t start, int32_t length, const std::u16string& string)
524 {
525 auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
526 CHECK_NULL_VOID(eventHub);
527 if (eventHub->HasOnStyledStringDidChange()) {
528 StyledStringChangeValue changeValue;
529 auto changeStart = std::clamp(start, 0, GetTextContentLength());
530 auto changeEnd = changeStart + length;
531 auto stringLength = static_cast<int32_t>(string.length());
532 auto stringEnd = changeStart + stringLength;
533 changeValue.SetRangeBefore({ changeStart, changeEnd });
534 changeValue.SetRangeAfter({ changeStart, stringEnd });
535 eventHub->FireOnStyledStringDidChange(changeValue);
536 }
537 ForceTriggerAvoidOnCaretChange();
538 ReportAfterContentChangeEvent();
539 }
540
AfterStyledStringChange(const UndoRedoRecord & record,bool isUndo)541 void RichEditorPattern::AfterStyledStringChange(const UndoRedoRecord& record, bool isUndo)
542 {
543 CHECK_NULL_VOID(!record.isOnlyStyleChange);
544 auto start = isUndo ? record.rangeAfter.start : record.rangeBefore.start;
545 auto length = isUndo ? record.rangeAfter.GetLength() : record.rangeBefore.GetLength();
546 auto styledString = isUndo ? record.styledStringBefore : record.styledStringAfter;
547 CHECK_NULL_VOID(styledString);
548 AfterStyledStringChange(start, length, styledString->GetU16string());
549 }
550
ProcessStyledUndo(const UndoRedoRecord & record)551 void RichEditorPattern::ProcessStyledUndo(const UndoRedoRecord& record)
552 {
553 CHECK_NULL_VOID(IsSupportStyledUndo());
554 auto host = GetHost();
555 CHECK_NULL_VOID(host);
556 IF_PRESENT(magnifierController_, RemoveMagnifierFrameNode());
557 ResetTouchAndMoveCaretState(false);
558 auto undoRecord = record;
559 undoRecord.Reverse();
560 isSpanStringMode_ ? ApplyRecordInStyledString(undoRecord) : ApplyRecordInSpans(undoRecord, true);
561 if (record.selectionBefore.GetLength() == 0) {
562 IF_TRUE(isEditing_, StartTwinkling());
563 } else {
564 HandleSelectionChange(undoRecord.selectionBefore.start, undoRecord.selectionBefore.end);
565 FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
566 }
567 SetCaretPosition(undoRecord.selectionBefore.end);
568 caretAffinityPolicy_ = undoRecord.caretAffinityBefore;
569 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
570 host->MarkModifyDone();
571 }
572
ProcessStyledRedo(const UndoRedoRecord & record)573 void RichEditorPattern::ProcessStyledRedo(const UndoRedoRecord& record)
574 {
575 CHECK_NULL_VOID(IsSupportStyledUndo());
576 auto host = GetHost();
577 CHECK_NULL_VOID(host);
578 isSpanStringMode_ ? ApplyRecordInStyledString(record) : ApplyRecordInSpans(record, false);
579 SetCaretPosition(record.rangeAfter.end);
580 IF_TRUE(isEditing_, StartTwinkling());
581 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
582 host->MarkModifyDone();
583 }
584
ApplyRecordInStyledString(const UndoRedoRecord & record)585 void RichEditorPattern::ApplyRecordInStyledString(const UndoRedoRecord& record)
586 {
587 CHECK_NULL_VOID(styledString_);
588 auto start = record.rangeBefore.start;
589 auto length = record.rangeBefore.GetLength();
590 auto styledString = record.styledStringAfter;
591 auto startBefore = record.rangeAfter.start;
592 auto lengthBefore = record.rangeAfter.GetLength();
593 CloseSelectOverlay();
594 ResetSelection();
595 if (record.isOnlyStyleChange) {
596 std::vector<RefPtr<SpanBase>> updateSpans;
597 for (const auto& spanType : record.updateSpanTypes) {
598 styledString_->RemoveSpan(start, length, spanType);
599 auto spansBefore = styledString->GetSpans(0, lengthBefore, spanType);
600 for (auto& span : spansBefore) {
601 CHECK_NULL_CONTINUE(span);
602 auto spanStart = span->GetStartIndex() + startBefore;
603 auto spanEnd = span->GetEndIndex() + startBefore;
604 updateSpans.push_back(span->GetSubSpan(spanStart, spanEnd));
605 }
606 }
607 paragraphCache_.Clear();
608 styledString_->BindWithSpans(updateSpans);
609 styledString_->NotifySpanWatcher();
610 } else {
611 styledString_->ReplaceSpanString(start, length, styledString);
612 }
613 }
614
ApplyRecordInSpans(const UndoRedoRecord & record,bool isUndo)615 void RichEditorPattern::ApplyRecordInSpans(const UndoRedoRecord& record, bool isUndo)
616 {
617 CloseSelectOverlay();
618 ResetSelection();
619 StopTwinkling();
620 DeleteForward(record.rangeBefore.start, record.rangeBefore.GetLength());
621 ApplyOptions(record.optionsListAfter.value_or(OptionsList{}), record.IsRestoreBuilderSpan(), isUndo);
622 }
623
ApplyOptions(const OptionsList & optionsList,bool restoreBuilderSpan,bool isUndo)624 void RichEditorPattern::ApplyOptions(const OptionsList& optionsList, bool restoreBuilderSpan, bool isUndo)
625 {
626 int32_t optionsLength = 0;
627 for (const auto& option : optionsList) {
628 std::visit([weak = WeakClaim(this), restoreBuilderSpan, &optionsLength, isUndo](const auto& specificOption) {
629 using T = std::decay_t<decltype(specificOption)>;
630 auto pattern = weak.Upgrade();
631 CHECK_NULL_VOID(pattern);
632 auto reason = isUndo ? TextChangeReason::UNDO : TextChangeReason::REDO;
633 if constexpr (std::is_same_v<T, ImageSpanOptions>) {
634 ImageSpanOptions options = specificOption;
635 options.optionSource = OptionSource::UNDO_REDO;
636 pattern->AddImageSpan(options, reason, false, pattern->GetCaretIndex(), false);
637 optionsLength++;
638 } else if constexpr (std::is_same_v<T, TextSpanOptions>) {
639 TextSpanOptions options = specificOption;
640 options.optionSource = OptionSource::UNDO_REDO;
641 pattern->AddTextSpan(options, reason, false, pattern->GetCaretIndex());
642 optionsLength += static_cast<int32_t>(options.value.length());
643 } else if constexpr (std::is_same_v<T, SymbolSpanOptions>) {
644 SymbolSpanOptions options = specificOption;
645 options.optionSource = OptionSource::UNDO_REDO;
646 pattern->AddSymbolSpan(options, reason, false, pattern->GetCaretIndex());
647 optionsLength += SYMBOL_SPAN_LENGTH;
648 } else if constexpr (std::is_same_v<T, BuilderSpanOptions>) {
649 pattern->AddPlaceholderSpan(specificOption, restoreBuilderSpan, reason);
650 optionsLength++;
651 }
652 }, option);
653 }
654 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "ApplyOptions length=%{public}d", optionsLength);
655 }
656
AddPlaceholderSpan(const BuilderSpanOptions & options,bool restoreBuilderSpan,TextChangeReason reason)657 void RichEditorPattern::AddPlaceholderSpan(const BuilderSpanOptions& options, bool restoreBuilderSpan,
658 TextChangeReason reason)
659 {
660 if (!restoreBuilderSpan || !options.customNode) {
661 auto textOptions = TextSpanOptions{ .offset = options.offset, .value = u" " };
662 textOptions.optionSource = OptionSource::UNDO_REDO;
663 AddTextSpan(textOptions, reason, false, caretPosition_);
664 undoManager_->RemoveBuilderSpanOptions(options.customNode);
665 return;
666 }
667 auto baseOption = SpanOptionBase { .offset = options.offset, .optionSource = OptionSource::UNDO_REDO };
668 AddPlaceholderSpan(options.customNode, baseOption, reason);
669 }
670
OnModifyDone()671 void RichEditorPattern::OnModifyDone()
672 {
673 Pattern::CheckLocalized();
674 auto host = GetHost();
675 CHECK_NULL_VOID(host);
676 auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
677 copyOption_ = layoutProperty->GetCopyOption().value_or(CopyOptions::Local);
678 auto context = host->GetContext();
679 CHECK_NULL_VOID(context);
680 ResetKeyboardIfNeed();
681 context->AddOnAreaChangeNode(host->GetId());
682 if (!clipboard_ && context) {
683 clipboard_ = ClipboardProxy::GetInstance()->GetClipboard(context->GetTaskExecutor());
684 }
685 instanceId_ = context->GetInstanceId();
686 InitMouseEvent();
687 InitAISpanHoverEvent();
688 auto focusHub = host->GetOrCreateFocusHub();
689 CHECK_NULL_VOID(focusHub);
690 InitFocusEvent(focusHub);
691 auto gestureEventHub = host->GetOrCreateGestureEventHub();
692 InitClickEvent(gestureEventHub);
693 InitLongPressEvent(gestureEventHub);
694 InitTouchEvent();
695 InitPanEvent();
696 HandleEnabled();
697 ProcessInnerPadding();
698 InitScrollablePattern();
699 SetAccessibilityAction();
700 selectOverlay_->SetMenuTranslateIsSupport(IsShowTranslate());
701 selectOverlay_->SetIsSupportMenuSearch(IsShowSearch());
702 if (host->IsDraggable()) {
703 InitDragDropEvent();
704 } else {
705 ClearDragDropEvent();
706 }
707 Register2DragDropManager();
708 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
709
710 auto eventHub = host->GetOrCreateEventHub<EventHub>();
711 CHECK_NULL_VOID(eventHub);
712 bool enabledCache = eventHub->IsEnabled();
713 if (textDetectEnable_ && enabledCache != enabled_) {
714 enabled_ = enabledCache;
715 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
716 }
717 SetIsEnableSubWindowMenu();
718 }
719
HandleEnabled()720 void RichEditorPattern::HandleEnabled()
721 {
722 auto host = GetHost();
723 CHECK_NULL_VOID(host);
724 auto renderContext = host->GetRenderContext();
725 CHECK_NULL_VOID(renderContext);
726 if (IsDisabled()) {
727 auto richEditorTheme = GetTheme<RichEditorTheme>();
728 CHECK_NULL_VOID(richEditorTheme);
729 auto disabledAlpha = richEditorTheme->GetDisabledAlpha();
730 renderContext->OnOpacityUpdate(disabledAlpha);
731 } else {
732 auto opacity = renderContext->GetOpacity().value_or(1.0);
733 renderContext->OnOpacityUpdate(opacity);
734 }
735 }
736
BeforeCreateLayoutWrapper()737 void RichEditorPattern::BeforeCreateLayoutWrapper()
738 {
739 ACE_SCOPED_TRACE("RichEditorBeforeCreateLayoutWrapper");
740 if (!isSpanStringMode_) {
741 TextPattern::PreCreateLayoutWrapper();
742 hasUrlSpan_ = std::any_of(spans_.begin(), spans_.end(), URL_SPAN_FILTER);
743 } else if (contentMod_) {
744 contentMod_->ContentChange();
745 }
746 }
747
UpdateMagnifierStateAfterLayout(bool frameSizeChange)748 void RichEditorPattern::UpdateMagnifierStateAfterLayout(bool frameSizeChange)
749 {
750 CHECK_NULL_VOID(!selectOverlay_->GetIsHandleMoving());
751 if (frameSizeChange && magnifierController_ && magnifierController_->GetMagnifierNodeExist()) {
752 ResetTouchSelectState();
753 ResetTouchAndMoveCaretState();
754 magnifierController_->RemoveMagnifierFrameNode();
755 }
756 }
757
UpdateGestureHotZone(const RefPtr<LayoutWrapper> & dirty)758 void RichEditorPattern::UpdateGestureHotZone(const RefPtr<LayoutWrapper>& dirty)
759 {
760 const auto& geometryNode = dirty->GetGeometryNode();
761 auto paddingSize = geometryNode->GetPaddingSize();
762 auto paddingOffset = geometryNode->GetPaddingOffset() - geometryNode->GetFrameOffset();
763
764 auto hotZoneWidth = Dimension(paddingSize.Width());
765 auto hotZoneHeight = Dimension(paddingSize.Height());
766 auto hotZoneOffset = DimensionOffset(Offset(paddingOffset.GetX(), paddingOffset.GetY()));
767
768 auto gestureHub = GetGestureEventHub();
769 CHECK_NULL_VOID(gestureHub);
770 gestureHub->SetResponseRegion({ { hotZoneWidth, hotZoneHeight, hotZoneOffset } });
771 }
772
ClearOnFocusTextField(FrameNode * node)773 void RichEditorPattern::ClearOnFocusTextField(FrameNode* node)
774 {
775 CHECK_NULL_VOID(isAPI14Plus && node);
776 auto context = node->GetContextRefPtr();
777 CHECK_NULL_VOID(context);
778 auto textFieldManager = DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
779 IF_PRESENT(textFieldManager, ClearOnFocusTextField(node->GetId()));
780 }
781
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)782 bool RichEditorPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
783 {
784 CHECK_NULL_RETURN(!config.skipMeasure && !dirty->SkipMeasureContent(), false);
785 auto originalFrameRect = frameRect_;
786 frameRect_ = dirty->GetGeometryNode()->GetFrameRect();
787 auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
788 CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
789 auto layoutAlgorithm = DynamicCast<RichEditorLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
790 CHECK_NULL_RETURN(layoutAlgorithm, false);
791 UpdateParentOffsetAndOverlay();
792 const auto& richTextRectOpt = layoutAlgorithm->GetTextRect();
793 IF_TRUE(richTextRectOpt.has_value(), richTextRect_ = richTextRectOpt.value());
794 UpdateTextFieldManager(Offset(parentGlobalOffset_.GetX(), parentGlobalOffset_.GetY()), frameRect_.Height());
795 bool ret = TextPattern::OnDirtyLayoutWrapperSwap(dirty, config);
796 UpdateScrollStateAfterLayout(config.frameSizeChange);
797 UpdateMagnifierStateAfterLayout(config.frameSizeChange);
798 IF_TRUE(!isRichEditorInit_, FireOnReady());
799 MoveCaretOnLayoutSwap();
800 HandleTasksOnLayoutSwap();
801 HandleSelectOverlayOnLayoutSwap();
802 IF_TRUE(originalFrameRect.GetSize() != frameRect_.GetSize(), {
803 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "frame size change");
804 TriggerAvoidOnCaretChangeNextFrame();
805 });
806 IF_TRUE(!isModifyingContent_, UpdateCaretInfoToController());
807 auto host = GetHost();
808 CHECK_NULL_RETURN(host, ret);
809 auto context = host->GetRenderContext();
810 CHECK_NULL_RETURN(context, ret);
811 if (context->GetClipEdge().has_value()) {
812 auto geometryNode = host->GetGeometryNode();
813 auto frameOffset = geometryNode->GetFrameOffset();
814 auto frameSize = geometryNode->GetFrameSize();
815 auto height = static_cast<float>(paragraphs_.GetHeight() + std::fabs(baselineOffset_));
816 if (!context->GetClipEdge().value() && LessNotEqual(frameSize.Height(), height)) {
817 RectF boundsRect(frameOffset.GetX(), frameOffset.GetY(), frameSize.Width(), height);
818 CHECK_NULL_RETURN(overlayMod_, ret);
819 overlayMod_->SetBoundsRect(boundsRect);
820 }
821 }
822 caretUpdateType_ = CaretUpdateType::NONE;
823 IF_PRESENT(oneStepDragController_, HandleDirtyNodes());
824 UpdateGestureHotZone(dirty);
825 if (afterDragSelect_) {
826 UpdateSelectionAndHandleVisibility();
827 afterDragSelect_ = false;
828 }
829 releaseInDrop_ = false;
830 return ret;
831 }
832
UpdateSelectionAndHandleVisibility()833 void RichEditorPattern::UpdateSelectionAndHandleVisibility()
834 {
835 auto start = recoverStart_;
836 auto end = recoverEnd_;
837 if (textSelector_.GetStart() == start && textSelector_.GetEnd() == end) {
838 return;
839 }
840 auto host = GetHost();
841 CHECK_NULL_VOID(host);
842 if (isMouseOrTouchPad(sourceTool_) && releaseInDrop_) {
843 if (isSpanStringMode_) {
844 start = caretPosition_ - insertValueLength_;
845 end = caretPosition_;
846 } else {
847 start = lastCaretPosition_;
848 end = insertValueLength_ + lastCaretPosition_;
849 }
850 }
851 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "UpdateSelectionAndHandleVisibility range=[%{public}d--%{public}d]", start, end);
852 textSelector_.Update(start, end);
853
854 if (!isMouseOrTouchPad(sourceTool_)) {
855 if (!selectOverlay_->IsBothHandlesShow() && !selectOverlay_->SelectOverlayIsCreating()) {
856 showSelect_ = true;
857 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
858 CalculateHandleOffsetAndShowOverlay();
859 selectOverlay_->ProcessOverlay({.menuIsShow = false, .animation = false});
860 }
861 }
862 FireOnSelectionChange(start, end, true);
863 }
864
HandleSelectOverlayOnLayoutSwap()865 void RichEditorPattern::HandleSelectOverlayOnLayoutSwap()
866 {
867 bool needToRefreshSelectOverlay = textSelector_.IsValid() && SelectOverlayIsOn() && !IsPreviewTextInputting();
868 CHECK_NULL_VOID(needToRefreshSelectOverlay);
869 auto overlayTask = [weak = WeakClaim(this)]() {
870 auto pattern = weak.Upgrade();
871 CHECK_NULL_VOID(pattern);
872 auto selectOverlay = pattern->selectOverlay_;
873 IF_PRESENT(selectOverlay, UpdateSelectOverlayOnAreaChanged());
874 };
875 if (AnimationUtils::IsImplicitAnimationOpen()) {
876 auto pipeline = GetContext();
877 CHECK_NULL_VOID(pipeline);
878 pipeline->AddAfterRenderTask(overlayTask);
879 } else {
880 overlayTask();
881 }
882 }
883
FireOnReady()884 void RichEditorPattern::FireOnReady()
885 {
886 auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
887 CHECK_NULL_VOID(eventHub);
888 eventHub->FireOnReady();
889 ClearOperationRecords();
890 isFirstCallOnReady_ = true;
891 isRichEditorInit_ = true;
892 }
893
MoveCaretOnLayoutSwap()894 void RichEditorPattern::MoveCaretOnLayoutSwap()
895 {
896 MoveCaretAfterTextChange();
897 if (needMoveCaretToContentRect_ || isEditing_) {
898 MoveCaretToContentRect();
899 needMoveCaretToContentRect_ = false;
900 }
901 }
902
CreateImageSourceInfo(const ImageSpanOptions & options)903 std::function<ImageSourceInfo()> RichEditorPattern::CreateImageSourceInfo(const ImageSpanOptions& options)
904 {
905 std::string src;
906 RefPtr<PixelMap> pixMap = nullptr;
907 std::string bundleName;
908 std::string moduleName;
909 if (options.image.has_value()) {
910 src = options.image.value();
911 }
912 if (options.imagePixelMap.has_value()) {
913 pixMap = options.imagePixelMap.value();
914 }
915 if (options.bundleName.has_value()) {
916 bundleName = options.bundleName.value();
917 }
918 if (options.moduleName.has_value()) {
919 moduleName = options.moduleName.value();
920 }
921 auto createSourceInfoFunc = [src, noPixMap = !options.imagePixelMap.has_value(),
922 isUriPureNumber = options.isUriPureNumber.value_or(false), pixMap, bundleName,
923 moduleName]() -> ImageSourceInfo {
924 ImageSourceInfo info;
925 #if defined(PIXEL_MAP_SUPPORTED)
926 if (noPixMap) {
927 info = ImageSourceInfo { src, bundleName, moduleName };
928 } else {
929 info = ImageSourceInfo(pixMap);
930 }
931 #else
932 info = ImageSourceInfo { src, bundleName, moduleName };
933 #endif
934 info.SetIsUriPureNumber(isUriPureNumber);
935 return info;
936 };
937 return std::move(createSourceInfoFunc);
938 }
939
GetTextContentLength()940 int32_t RichEditorPattern::GetTextContentLength()
941 {
942 if (isSpanStringMode_ && styledString_) {
943 return styledString_->GetLength();
944 }
945 if (!spans_.empty()) {
946 auto it = spans_.rbegin();
947 return (*it)->position;
948 }
949 return 0;
950 }
951
SetPreviewMenuParam(TextSpanType spanType,std::function<void ()> & builder,const SelectMenuParam & menuParam)952 void RichEditorPattern::SetPreviewMenuParam(TextSpanType spanType, std::function<void()>& builder, const SelectMenuParam& menuParam)
953 {
954 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "SetPreviewMenuParam, spanType=%{public}d, builder=%{public}d",
955 spanType, !!builder);
956 if (!oneStepDragController_) {
957 oneStepDragController_ = std::make_unique<OneStepDragController>(WeakClaim(this));
958 }
959 oneStepDragController_->SetMenuParam(spanType, builder, menuParam);
960 oneStepDragController_->SetEnableEventResponse(textSelector_, imageNodes, builderNodes);
961 }
962
HandleImageDrag(const RefPtr<ImageSpanNode> & imageNode)963 void RichEditorPattern::HandleImageDrag(const RefPtr<ImageSpanNode>& imageNode)
964 {
965 DisableDrag(imageNode);
966 IF_PRESENT(oneStepDragController_, EnableOneStepDrag(TextSpanType::IMAGE, imageNode));
967 }
968
DisableDrag(const RefPtr<ImageSpanNode> & imageNode)969 void RichEditorPattern::DisableDrag(const RefPtr<ImageSpanNode>& imageNode)
970 {
971 // Disable the image itself event
972 imageNode->SetDraggable(false);
973 auto gesture = imageNode->GetOrCreateGestureEventHub();
974 CHECK_NULL_VOID(gesture);
975 gesture->InitDragDropEvent();
976 gesture->SetDragEvent(nullptr, { PanDirection::DOWN }, 0, Dimension(0));
977 }
978
SetGestureOptions(UserGestureOptions options,RefPtr<SpanItem> spanItem)979 void RichEditorPattern::SetGestureOptions(UserGestureOptions options, RefPtr<SpanItem> spanItem)
980 {
981 IF_TRUE(options.onClick, spanItem->SetOnClickEvent(std::move(options.onClick)));
982 IF_TRUE(options.onLongPress, spanItem->SetLongPressEvent(std::move(options.onLongPress)));
983 IF_TRUE(options.onDoubleClick, spanItem->SetDoubleClickEvent(std::move(options.onDoubleClick)));
984 }
985
AddSpanHoverEvent(RefPtr<SpanItem> spanItem,const RefPtr<FrameNode> & frameNode,const SpanOptionBase & options)986 void RichEditorPattern::AddSpanHoverEvent(
987 RefPtr<SpanItem> spanItem, const RefPtr<FrameNode>& frameNode, const SpanOptionBase& options)
988 {
989 auto onHoverFunc = options.userMouseOption.onHover;
990 CHECK_NULL_VOID(spanItem && frameNode && onHoverFunc);
991 auto tag = frameNode->GetTag();
992 spanItem->SetHoverEvent(std::move(onHoverFunc));
993 auto eventHub = frameNode->GetOrCreateEventHub<EventHub>();
994 CHECK_NULL_VOID(eventHub);
995 auto inputHub = eventHub->GetOrCreateInputEventHub();
996 CHECK_NULL_VOID(inputHub);
997 auto hoverTask = [weak = WeakClaim(Referenced::RawPtr(spanItem)), tag](bool isHover, HoverInfo& info) {
998 auto item = weak.Upgrade();
999 if (item && item->onHover) {
1000 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "[%{public}s] onHover status :[%{public}d]", tag.c_str(), isHover);
1001 item->onHover(isHover, info);
1002 }
1003 };
1004 auto hoverEvent = MakeRefPtr<InputEvent>(std::move(hoverTask));
1005 inputHub->AddOnHoverEvent(hoverEvent);
1006 }
1007
AddImageSpanFromCollaboration(const ImageSpanOptions & options,bool updateCaret)1008 int32_t RichEditorPattern::AddImageSpanFromCollaboration(const ImageSpanOptions& options, bool updateCaret)
1009 {
1010 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AddImageSpanFromCollaboration, updateCaret=%{public}d, ssMode=%{public}d",
1011 updateCaret, !!isSpanStringMode_);
1012 if (isSpanStringMode_) {
1013 auto insertStyledString = MakeRefPtr<SpanString>(options);
1014 auto index = options.offset.value_or(caretPosition_);
1015 InsertStyledString(insertStyledString, index, updateCaret);
1016 return 0;
1017 }
1018 return AddImageSpan(options, TextChangeReason::COLLABORATION, false, 0, updateCaret);
1019 }
1020
AddImageSpan(const ImageSpanOptions & options,TextChangeReason reason,bool isPaste,int32_t index,bool updateCaret)1021 int32_t RichEditorPattern::AddImageSpan(const ImageSpanOptions& options, TextChangeReason reason, bool isPaste,
1022 int32_t index, bool updateCaret)
1023 {
1024 if (GetTextContentLength() >= maxLength_.value_or(INT_MAX)) {
1025 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "AddImageSpan: Reach the maxLength. maxLength=%{public}d", maxLength_.value_or(INT_MAX));
1026 return 0;
1027 }
1028 auto host = GetContentHost();
1029 CHECK_NULL_RETURN(host, -1);
1030 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AddImageSpan, opts=%{public}s, updateCaret=%{public}d",
1031 options.ToString().c_str(), updateCaret);
1032 NotifyExitTextPreview(false);
1033 auto imageNode = ImageSpanNode::GetOrCreateSpanNode(V2::IMAGE_ETS_TAG,
1034 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<ImagePattern>(); });
1035 auto pattern = imageNode->GetPattern<ImagePattern>();
1036 CHECK_NULL_RETURN(pattern, -1);
1037 pattern->SetSyncLoad(true);
1038 int32_t insertIndex = options.offset.value_or(GetTextContentLength());
1039 insertIndex = std::min(insertIndex, GetTextContentLength());
1040 AdjustSelectorForSymbol(insertIndex, HandleType::SECOND, SelectorAdjustPolicy::EXCLUDE);
1041 RichEditorChangeValue changeValue(reason);
1042 bool isUndoRedo = options.optionSource == OptionSource::UNDO_REDO;
1043 CHECK_NULL_RETURN(isUndoRedo || BeforeAddImage(changeValue, options, insertIndex), -1);
1044
1045 HandleImageDrag(imageNode);
1046 AddOprationWhenAddImage(insertIndex);
1047 int32_t spanIndex = TextSpanSplit(insertIndex);
1048 IF_TRUE(spanIndex == -1, spanIndex = static_cast<int32_t>(host->GetChildren().size()));
1049
1050 imageNodes.push_back(imageNode);
1051 imageNode->MountToParent(host, spanIndex);
1052 auto renderContext = imageNode->GetRenderContext();
1053 IF_PRESENT(renderContext, SetNeedAnimateFlag(false));
1054 SetImageLayoutProperty(imageNode, options);
1055 auto spanItem = imageNode->GetSpanItem();
1056 // The length of the imageSpan defaults to the length of a character to calculate the position
1057 spanItem->content = u" ";
1058 spanItem->SetImageSpanOptions(options);
1059 spanItem->spanItemType = SpanItemType::IMAGE;
1060 AddSpanItem(spanItem, spanIndex);
1061 SetGestureOptions(options.userGestureOption, spanItem);
1062 auto userMouseOption = options.userMouseOption;
1063 if (userMouseOption.onHover) {
1064 spanItem->onHover_ = std::move(userMouseOption.onHover);
1065 hoverableNodes.push_back(imageNode);
1066 }
1067 placeholderCount_++;
1068 IF_TRUE(!isUndoRedo, undoManager_->RecordAddSpanOperation(spanItem, SpanOptionsType::IMAGE));
1069 if (updateCaret) {
1070 SetCaretPosition(insertIndex + spanItem->content.length());
1071 SetNeedMoveCaretToContentRect();
1072 }
1073 ResetSelectionAfterAddSpan(isPaste);
1074 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1075 host->MarkModifyDone();
1076 IF_TRUE(!isUndoRedo, AfterContentChange(changeValue));
1077 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "end");
1078 return spanIndex;
1079 }
1080
CreateHoverInfo(const MouseInfo & info)1081 HoverInfo RichEditorPattern::CreateHoverInfo(const MouseInfo& info)
1082 {
1083 HoverInfo hoverInfo;
1084 hoverInfo.SetType(info.GetType());
1085 hoverInfo.SetTimeStamp(info.GetTimeStamp());
1086 hoverInfo.SetTarget(info.GetTarget());
1087 hoverInfo.SetSourceDevice(info.GetSourceDevice());
1088 hoverInfo.SetForce(info.GetForce());
1089 IF_TRUE(info.GetTiltX(), hoverInfo.SetTiltX(info.GetTiltX().value()));
1090 IF_TRUE(info.GetTiltY(), hoverInfo.SetTiltY(info.GetTiltY().value()));
1091 hoverInfo.SetSourceTool(info.GetSourceTool());
1092 hoverInfo.SetDeviceId(info.GetDeviceId());
1093 hoverInfo.SetTargetDisplayId(info.GetTargetDisplayId());
1094 hoverInfo.SetStopPropagation(info.IsStopPropagation());
1095 hoverInfo.SetPreventDefault(info.IsPreventDefault());
1096 hoverInfo.SetPatternName(info.GetPatternName());
1097 hoverInfo.SetPressedKeyCodes(info.GetPressedKeyCodes());
1098 hoverInfo.SetIsPostEventResult(info.GetIsPostEventResult());
1099 hoverInfo.SetPostEventNodeId(info.GetPostEventNodeId());
1100 return hoverInfo;
1101 }
1102
HandleImageHoverEvent(const MouseInfo & mouseInfo)1103 void RichEditorPattern::HandleImageHoverEvent(const MouseInfo& mouseInfo)
1104 {
1105 CHECK_NULL_VOID(mouseInfo.GetAction() == MouseAction::MOVE && !isMousePressed_);
1106 ACE_SCOPED_TRACE("RichEditorHandleImageHoverEvent");
1107 PointF mouseOffset = { mouseInfo.GetLocalLocation().GetX(), mouseInfo.GetLocalLocation().GetY() };
1108 HoverInfo info = CreateHoverInfo(mouseInfo);
1109 for (auto it = hoverableNodes.begin(); it != hoverableNodes.end();) {
1110 auto spanNode = it->Upgrade();
1111 if (!spanNode) {
1112 it = hoverableNodes.erase(it);
1113 continue;
1114 }
1115 const auto& imageSpanItem = spanNode->GetSpanItem();
1116 if (!imageSpanItem || !imageSpanItem->onHover_) {
1117 it = hoverableNodes.erase(it);
1118 continue;
1119 }
1120 const auto& geoNode = spanNode->GetGeometryNode();
1121 CHECK_NULL_CONTINUE(geoNode);
1122 const auto& imageRect = geoNode->GetFrameRect();
1123 if (!imageRect.IsInRegion(mouseOffset)) {
1124 ++it;
1125 continue;
1126 }
1127 if (!lastHoverSpanItem_) {
1128 imageSpanItem->onHover_(true, info);
1129 lastHoverSpanItem_ = imageSpanItem;
1130 lastHoverInfo_ = info;
1131 return;
1132 }
1133 CHECK_NULL_VOID(Referenced::RawPtr(lastHoverSpanItem_) != Referenced::RawPtr(imageSpanItem));
1134 imageSpanItem->onHover_(true, info);
1135 lastHoverSpanItem_->onHover_(false, info);
1136 lastHoverSpanItem_ = imageSpanItem;
1137 lastHoverInfo_ = info;
1138 return;
1139 }
1140
1141 if (lastHoverSpanItem_) {
1142 lastHoverSpanItem_->onHover_(false, info);
1143 lastHoverSpanItem_.Reset();
1144 }
1145 }
1146
AddOprationWhenAddImage(int32_t beforeCaretPos)1147 void RichEditorPattern::AddOprationWhenAddImage(int32_t beforeCaretPos)
1148 {
1149 OperationRecord record;
1150 record.beforeCaretPosition = beforeCaretPos;
1151 record.addText = u" ";
1152 ClearRedoOperationRecords();
1153 record.afterCaretPosition = record.beforeCaretPosition + 1;
1154 AddOperationRecord(record);
1155 }
1156
ResetSelectionAfterAddSpan(bool isPaste)1157 void RichEditorPattern::ResetSelectionAfterAddSpan(bool isPaste)
1158 {
1159 if (isPaste || !textSelector_.IsValid()) {
1160 return;
1161 }
1162 CloseSelectOverlay();
1163 ResetSelection();
1164 if (isEditing_ && !caretVisible_) {
1165 StartTwinkling();
1166 }
1167 }
1168
AddSpanItem(const RefPtr<SpanItem> & item,int32_t offset)1169 void RichEditorPattern::AddSpanItem(const RefPtr<SpanItem>& item, int32_t offset)
1170 {
1171 auto host = GetContentHost();
1172 CHECK_NULL_VOID(host);
1173 if (offset == -1) {
1174 offset = static_cast<int32_t>(host->GetChildren().size());
1175 }
1176 offset = std::clamp(offset, 0, static_cast<int32_t>(host->GetChildren().size()) - 1);
1177 auto it = spans_.begin();
1178 std::advance(it, offset);
1179 spans_.insert(it, item);
1180 UpdateSpanPosition();
1181 ReportAfterContentChangeEvent();
1182 }
1183
SetContentPattern(const RefPtr<RichEditorContentPattern> & contentPattern)1184 void RichEditorPattern::SetContentPattern(const RefPtr<RichEditorContentPattern>& contentPattern)
1185 {
1186 contentPattern_ = contentPattern;
1187 }
1188
GetContentHost() const1189 RefPtr<FrameNode> RichEditorPattern::GetContentHost() const
1190 {
1191 if (!contentPattern_) {
1192 TAG_LOGE(AceLogTag::ACE_RICH_TEXT, "GetContentHost, contentPattern is null");
1193 return nullptr;
1194 }
1195 return contentPattern_->GetHost();
1196 }
1197
OnAttachToFrameNode()1198 void RichEditorPattern::OnAttachToFrameNode()
1199 {
1200 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "OnAttachToFrameNode");
1201 TextPattern::OnAttachToFrameNode();
1202 richEditorInstanceId_ = Container::CurrentIdSafely();
1203 auto frameNode = GetHost();
1204 CHECK_NULL_VOID(frameNode);
1205 frameId_ = frameNode->GetId();
1206 StylusDetectorMgr::GetInstance()->AddTextFieldFrameNode(frameNode, WeakClaim(this));
1207 auto context = GetContext();
1208 CHECK_NULL_VOID(context);
1209 context->AddWindowSizeChangeCallback(frameId_);
1210
1211 auto patternCreator = [weak = WeakClaim(this)]() { return AceType::MakeRefPtr<RichEditorContentPattern>(weak); };
1212 auto nodeId = ElementRegister::GetInstance()->MakeUniqueId();
1213 auto contentNode = FrameNode::GetOrCreateFrameNode(V2::RICH_EDITOR_CONTENT_ETS_TAG, nodeId, patternCreator);
1214 frameNode->AddChild(contentNode);
1215 SetContentPattern(contentNode->GetPattern<RichEditorContentPattern>());
1216 }
1217
OnDetachFromFrameNode(FrameNode * node)1218 void RichEditorPattern::OnDetachFromFrameNode(FrameNode* node)
1219 {
1220 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "OnAttachToFrameNode");
1221 TextPattern::OnDetachFromFrameNode(node);
1222 ScrollablePattern::OnDetachFromFrameNode(node);
1223 ClearOnFocusTextField(node);
1224 auto context = pipeline_.Upgrade();
1225 IF_PRESENT(context, RemoveWindowSizeChangeCallback(frameId_));
1226 }
1227
AddPlaceholderSpan(const RefPtr<UINode> & customNode,const SpanOptionBase & options,TextChangeReason reason)1228 int32_t RichEditorPattern::AddPlaceholderSpan(const RefPtr<UINode>& customNode, const SpanOptionBase& options,
1229 TextChangeReason reason)
1230 {
1231 if (GetTextContentLength() >= maxLength_.value_or(INT_MAX)) {
1232 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "AddPlaceholderSpan: Reach the maxLength. maxLength=%{public}d", maxLength_.value_or(INT_MAX));
1233 return 0;
1234 }
1235 CHECK_NULL_RETURN(customNode, 0);
1236 auto host = GetContentHost();
1237 CHECK_NULL_RETURN(host, 0);
1238 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AddPlaceholderSpan");
1239 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "options=%{public}s", options.ToString().c_str());
1240 NotifyExitTextPreview(false);
1241 auto placeholderSpanNode = PlaceholderSpanNode::GetOrCreateSpanNode(V2::PLACEHOLDER_SPAN_ETS_TAG,
1242 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<PlaceholderSpanPattern>(); });
1243 CHECK_NULL_RETURN(placeholderSpanNode, 0);
1244 customNode->MountToParent(placeholderSpanNode);
1245 SetSelfAndChildDraggableFalse(customNode);
1246 IF_PRESENT(oneStepDragController_, EnableOneStepDrag(TextSpanType::BUILDER, placeholderSpanNode));
1247 auto focusHub = placeholderSpanNode->GetOrCreateFocusHub();
1248 focusHub->SetFocusable(false);
1249 int32_t insertIndex = options.offset.value_or(GetTextContentLength());
1250 AdjustSelectorForSymbol(insertIndex, HandleType::SECOND, SelectorAdjustPolicy::EXCLUDE);
1251 int32_t spanIndex = TextSpanSplit(insertIndex);
1252 if (spanIndex == -1) {
1253 spanIndex = static_cast<int32_t>(host->GetChildren().size());
1254 }
1255 builderNodes.push_back(placeholderSpanNode);
1256 placeholderSpanNode->MountToParent(host, spanIndex);
1257 auto renderContext = placeholderSpanNode->GetRenderContext();
1258 IF_PRESENT(renderContext, SetNeedAnimateFlag(false));
1259 auto spanItem = placeholderSpanNode->GetSpanItem();
1260 spanItem->content = u" ";
1261 spanItem->SetCustomNode(customNode);
1262 spanItem->dragBackgroundColor_ = options.dragBackgroundColor;
1263 spanItem->isDragShadowNeeded_ = options.isDragShadowNeeded;
1264 AddSpanItem(spanItem, spanIndex);
1265 IF_TRUE(options.optionSource != OptionSource::UNDO_REDO,
1266 undoManager_->RecordAddSpanOperation(spanItem, SpanOptionsType::BUILDER));
1267 placeholderCount_++;
1268 SetCaretPosition(insertIndex + spanItem->content.length());
1269 ResetSelectionAfterAddSpan(false);
1270 auto placeholderPipelineContext = placeholderSpanNode->GetContext();
1271 IF_PRESENT(placeholderPipelineContext, SetDoKeyboardAvoidAnimate(false));
1272 SetNeedMoveCaretToContentRect();
1273 AddOnPlaceholderHoverEvent(placeholderSpanNode);
1274 placeholderSpanNode->MarkModifyDone();
1275 placeholderSpanNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1276 host->MarkModifyDone();
1277 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1278 return spanIndex;
1279 }
1280
AddOnPlaceholderHoverEvent(const RefPtr<PlaceholderSpanNode> & placeholderSpanNode)1281 void RichEditorPattern::AddOnPlaceholderHoverEvent(const RefPtr<PlaceholderSpanNode>& placeholderSpanNode)
1282 {
1283 CHECK_NULL_VOID(placeholderSpanNode);
1284 auto inputHub = placeholderSpanNode->GetOrCreateInputEventHub();
1285 CHECK_NULL_VOID(inputHub);
1286 auto hoverTask = [weak = WeakClaim(this)](bool isHover) {
1287 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "placeholder, on hover event isHover=%{public}d", isHover);
1288 auto pattern = weak.Upgrade();
1289 CHECK_NULL_VOID(pattern);
1290 pattern->OnPlaceholderHover(isHover);
1291 };
1292 auto hoverEvent = MakeRefPtr<InputEvent>(std::move(hoverTask));
1293 inputHub->AddOnHoverEvent(hoverEvent);
1294 }
1295
OnPlaceholderHover(bool isHover)1296 void RichEditorPattern::OnPlaceholderHover(bool isHover)
1297 {
1298 auto host = GetHost();
1299 CHECK_NULL_VOID(host);
1300 auto pipeline = GetContext();
1301 CHECK_NULL_VOID(pipeline);
1302 auto nodeId = host->GetId();
1303 if (isHover) {
1304 pipeline->FreeMouseStyleHoldNode(nodeId);
1305 } else {
1306 pipeline->FreeMouseStyleHoldNode();
1307 ChangeMouseStyle(MouseFormat::TEXT_CURSOR);
1308 }
1309 }
1310
SetSelfAndChildDraggableFalse(const RefPtr<UINode> & customNode)1311 void RichEditorPattern::SetSelfAndChildDraggableFalse(const RefPtr<UINode>& customNode)
1312 {
1313 CHECK_NULL_VOID(customNode);
1314 auto frameNode = DynamicCast<FrameNode>(customNode);
1315 if (frameNode) {
1316 auto gestureEventHub = frameNode->GetOrCreateGestureEventHub();
1317 IF_PRESENT(gestureEventHub, SetDragForbiddenForcely(true));
1318 }
1319 for (const auto& child : customNode->GetChildren()) {
1320 SetSelfAndChildDraggableFalse(child);
1321 }
1322 }
1323
AddTextSpan(TextSpanOptions options,TextChangeReason reason,bool isPaste,int32_t index)1324 int32_t RichEditorPattern::AddTextSpan(TextSpanOptions options, TextChangeReason reason, bool isPaste, int32_t index)
1325 {
1326 if (GetTextContentLength() >= maxLength_.value_or(INT_MAX)) {
1327 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "AddTextSpan: Reach the maxLength. maxLength=%{public}d", maxLength_.value_or(INT_MAX));
1328 return 0;
1329 }
1330 auto length = CalculateTruncationLength(options.value, maxLength_.value_or(INT_MAX) - GetTextContentLength());
1331 if (length == 0) {
1332 return -1;
1333 }
1334 options.value = options.value.substr(0, length);
1335 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AddTextSpan, opts=%{public}s", ToBriefString(options).c_str());
1336 SEC_TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AddTextSpan, opts=%{public}s", options.ToString().c_str());
1337 AdjustAddPosition(options);
1338 NotifyExitTextPreview();
1339 OperationRecord record;
1340 auto textContentLength = GetTextContentLength();
1341 if (options.offset.has_value()) {
1342 options.offset = std::clamp(options.offset.value(), 0, textContentLength);
1343 AdjustSelectorForSymbol(options.offset.value(), HandleType::SECOND, SelectorAdjustPolicy::EXCLUDE);
1344 }
1345 record.beforeCaretPosition = std::clamp(options.offset.value_or(textContentLength), 0, textContentLength);
1346 record.addText = options.value;
1347 RichEditorChangeValue changeValue(reason);
1348 bool isUndoRedo = options.optionSource == OptionSource::UNDO_REDO;
1349 CHECK_NULL_RETURN(isUndoRedo || BeforeChangeText(changeValue, options), -1);
1350 ClearRedoOperationRecords();
1351 record.afterCaretPosition = record.beforeCaretPosition + static_cast<int32_t>(options.value.length());
1352 AddOperationRecord(record);
1353 auto ret = AddTextSpanOperation(options, isPaste, index, false);
1354 SetNeedMoveCaretToContentRect();
1355 if (!previewTextRecord_.IsValid() && !isUndoRedo) {
1356 AfterContentChange(changeValue);
1357 }
1358 return ret;
1359 }
1360
AdjustAddPosition(TextSpanOptions & options)1361 void RichEditorPattern::AdjustAddPosition(TextSpanOptions& options)
1362 {
1363 CHECK_NULL_VOID(IsPreviewTextInputting() && options.offset.has_value());
1364 auto& offset = options.offset.value();
1365 auto delta = offset - previewTextRecord_.startOffset;
1366 offset -= std::min(std::max(0, delta), previewTextRecord_.endOffset - previewTextRecord_.startOffset);
1367 }
1368
AddTextSpanOperation(const TextSpanOptions & options,bool isPaste,int32_t index,bool needLeadingMargin,bool updateCaretPosition)1369 int32_t RichEditorPattern::AddTextSpanOperation(
1370 const TextSpanOptions& options, bool isPaste, int32_t index, bool needLeadingMargin, bool updateCaretPosition)
1371 {
1372 auto host = GetContentHost();
1373 CHECK_NULL_RETURN(host, -1);
1374
1375 auto spanNode = SpanNode::GetOrCreateSpanNode(ElementRegister::GetInstance()->MakeUniqueId());
1376
1377 int32_t spanIndex = 0;
1378 int32_t offset = -1;
1379 if (options.offset.has_value()) {
1380 offset = TextSpanSplit(options.offset.value(), needLeadingMargin);
1381 if (offset == -1) {
1382 spanIndex = static_cast<int32_t>(host->GetChildren().size());
1383 } else {
1384 spanIndex = offset;
1385 }
1386 spanNode->MountToParent(host, offset);
1387 } else if (index != -1) {
1388 spanNode->MountToParent(host, index);
1389 spanIndex = index;
1390 } else {
1391 spanIndex = static_cast<int32_t>(host->GetChildren().size());
1392 spanNode->MountToParent(host);
1393 }
1394 auto textStyle = options.style;
1395 if (options.urlAddress.has_value() && options.useThemeFontColor && textStyle.has_value()) {
1396 textStyle.value().SetTextColor(GetUrlSpanColor());
1397 }
1398 auto spanItem = spanNode->GetSpanItem();
1399 spanItem->SetTextStyle(textStyle);
1400 spanItem->useThemeFontColor = options.useThemeFontColor;
1401 spanItem->useThemeDecorationColor = options.useThemeDecorationColor;
1402 UpdateSpanNode(spanNode, options);
1403 AddSpanItem(spanItem, offset);
1404 if (!options.style.has_value()) {
1405 SetDefaultColor(spanNode);
1406 }
1407 if (options.paraStyle) {
1408 UpdateParagraphStyle(spanNode, *options.paraStyle);
1409 }
1410 SetGestureOptions(options.userGestureOption, spanItem);
1411 bool needRecord =
1412 options.optionSource != OptionSource::UNDO_REDO && options.optionSource != OptionSource::IME_INSERT;
1413 IF_TRUE(needRecord, undoManager_->RecordAddSpanOperation(spanItem, SpanOptionsType::TEXT));
1414 if (updateCaretPosition && !previewTextRecord_.IsValid()) {
1415 if (options.offset.has_value()) {
1416 SetCaretPosition(options.offset.value() + options.value.length());
1417 } else {
1418 SetCaretPosition(GetTextContentLength());
1419 }
1420 }
1421 ResetSelectionAfterAddSpan(isPaste);
1422 SpanNodeFission(spanNode);
1423 return spanIndex;
1424 }
1425
UpdateSpanNode(RefPtr<SpanNode> spanNode,const TextSpanOptions & options)1426 void RichEditorPattern::UpdateSpanNode(RefPtr<SpanNode> spanNode, const TextSpanOptions& options)
1427 {
1428 spanNode->UpdateContent(options.value);
1429 if (options.style.has_value()) {
1430 const TextStyle& textStyle = options.style.value();
1431 spanNode->UpdateTextColorWithoutCheck(textStyle.GetTextColor());
1432 spanNode->UpdateFontSize(textStyle.GetFontSize());
1433 spanNode->UpdateItalicFontStyle(textStyle.GetFontStyle());
1434 spanNode->UpdateFontWeight(textStyle.GetFontWeight());
1435 spanNode->UpdateFontFamily(textStyle.GetFontFamilies());
1436 spanNode->UpdateTextDecoration(std::vector<TextDecoration> { textStyle.GetTextDecorationFirst() });
1437 spanNode->UpdateTextDecorationColorWithoutCheck(textStyle.GetTextDecorationColor());
1438 spanNode->UpdateTextDecorationStyle(textStyle.GetTextDecorationStyle());
1439 spanNode->UpdateLineThicknessScale(textStyle.GetLineThicknessScale());
1440 spanNode->UpdateTextShadow(textStyle.GetTextShadows());
1441 spanNode->UpdateHalfLeading(textStyle.GetHalfLeading());
1442 spanNode->UpdateLineHeight(textStyle.GetLineHeight());
1443 spanNode->UpdateLetterSpacing(textStyle.GetLetterSpacing());
1444 spanNode->UpdateFontFeature(textStyle.GetFontFeatures());
1445 UpdateTextBackgroundStyle(spanNode, textStyle.GetTextBackgroundStyle());
1446 }
1447 UpdateUrlStyle(spanNode, options.urlAddress);
1448 }
1449
UpdateTextBackgroundStyle(RefPtr<SpanNode> & spanNode,const std::optional<TextBackgroundStyle> & style)1450 void RichEditorPattern::UpdateTextBackgroundStyle(
1451 RefPtr<SpanNode>& spanNode, const std::optional<TextBackgroundStyle>& style)
1452 {
1453 CHECK_NULL_VOID(style.has_value());
1454 TextBackgroundStyle backgroundStyle = style.value();
1455 backgroundStyle.needCompareGroupId = false;
1456 if (backgroundStyle == spanNode->GetTextBackgroundStyle()) {
1457 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "text background style is same");
1458 return;
1459 }
1460 backgroundStyle.needCompareGroupId = true;
1461 spanNode->SetTextBackgroundStyle(backgroundStyle);
1462 }
1463
UpdateUrlStyle(RefPtr<SpanNode> & spanNode,const std::optional<std::u16string> & urlAddressOpt)1464 void RichEditorPattern::UpdateUrlStyle(RefPtr<SpanNode>& spanNode, const std::optional<std::u16string>& urlAddressOpt)
1465 {
1466 CHECK_NULL_VOID(spanNode && urlAddressOpt.has_value());
1467 auto& spanItem = spanNode->GetSpanItem();
1468 CHECK_NULL_VOID(spanItem);
1469 auto& urlAddress = urlAddressOpt.value();
1470
1471 // handle url span callback
1472 std::function<void()> urlOnRelease;
1473 if (!urlAddress.empty()) {
1474 urlOnRelease = [add = UtfUtils::Str16ToStr8(urlAddress)]() {
1475 auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
1476 CHECK_NULL_VOID(pipelineContext);
1477 pipelineContext->HyperlinkStartAbility(add);
1478 };
1479 }
1480 spanItem->SetUrlOnReleaseEvent(std::move(urlOnRelease));
1481 spanItem->urlAddress = urlAddress;
1482
1483 // handle url span color
1484 CHECK_NULL_VOID(spanItem->useThemeFontColor);
1485 if (urlAddress.empty()) {
1486 auto theme = GetTheme<RichEditorTheme>();
1487 CHECK_NULL_VOID(theme);
1488 const auto& themeTextStyle = theme->GetTextStyle();
1489 const auto& textColor = themeTextStyle.GetTextColor();
1490 spanNode->UpdateTextColor(textColor);
1491 IF_TRUE(spanItem->useThemeDecorationColor, spanNode->UpdateTextDecorationColor(textColor));
1492 } else {
1493 const auto& urlSpanColor = GetUrlSpanColor();
1494 spanNode->UpdateTextColor(urlSpanColor);
1495 IF_TRUE(spanItem->useThemeDecorationColor, spanNode->UpdateTextDecorationColor(urlSpanColor));
1496 }
1497 }
1498
AddSymbolSpan(SymbolSpanOptions options,TextChangeReason reason,bool isPaste,int32_t index)1499 int32_t RichEditorPattern::AddSymbolSpan(SymbolSpanOptions options, TextChangeReason reason, bool isPaste, int32_t index)
1500 {
1501 if (GetTextContentLength() >= maxLength_.value_or(INT_MAX) - 1) {
1502 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "AddSymbolSpan: Reach the maxLength. maxLength=%{public}d", maxLength_.value_or(INT_MAX));
1503 return 0;
1504 }
1505 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AddSymbolSpan, opts=%{public}s isPaste=%{public}d, index=%{public}d",
1506 ToBriefString(options).c_str(), isPaste, index);
1507 SEC_TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AddSymbolSpan, opts=%{public}s", options.ToString().c_str());
1508
1509 NotifyExitTextPreview(false);
1510 if (options.offset.has_value()) {
1511 options.offset = std::clamp(options.offset.value(), 0, GetTextContentLength());
1512 AdjustSelectorForSymbol(options.offset.value(), HandleType::SECOND, SelectorAdjustPolicy::EXCLUDE);
1513 }
1514 RichEditorChangeValue changeValue(reason);
1515 bool isUndoRedo = options.optionSource == OptionSource::UNDO_REDO;
1516 CHECK_NULL_RETURN(isUndoRedo || BeforeAddSymbol(changeValue, options), -1);
1517 OperationRecord record;
1518 record.beforeCaretPosition = options.offset.value_or(static_cast<int32_t>(GetTextContentLength()));
1519 record.addText = u" ";
1520 ClearRedoOperationRecords();
1521 record.afterCaretPosition = record.beforeCaretPosition + 1;
1522 AddOperationRecord(record);
1523 auto ret = AddSymbolSpanOperation(options, isPaste, index);
1524 SetNeedMoveCaretToContentRect();
1525 IF_TRUE(!isUndoRedo, AfterContentChange(changeValue));
1526 return ret;
1527 }
1528
AddSymbolSpanOperation(const SymbolSpanOptions & options,bool isPaste,int32_t index)1529 int32_t RichEditorPattern::AddSymbolSpanOperation(const SymbolSpanOptions& options, bool isPaste, int32_t index)
1530 {
1531 auto host = GetContentHost();
1532 CHECK_NULL_RETURN(host, -1);
1533
1534 auto spanNode = SpanNode::GetOrCreateSpanNode(V2::SYMBOL_SPAN_ETS_TAG,
1535 ElementRegister::GetInstance()->MakeUniqueId());
1536
1537 int32_t insertIndex = options.offset.value_or(GetTextContentLength());
1538 int32_t spanIndex = TextSpanSplit(insertIndex);
1539 if (spanIndex == -1) {
1540 spanIndex = static_cast<int32_t>(host->GetChildren().size());
1541 }
1542 spanNode->MountToParent(host, spanIndex);
1543 spanNode->UpdateContent(options.symbolId);
1544 if (options.style.has_value()) {
1545 spanNode->UpdateFontSize(options.style.value().GetFontSize());
1546 spanNode->UpdateFontWeight(options.style.value().GetFontWeight());
1547 spanNode->UpdateSymbolColorList(options.style.value().GetSymbolColorList());
1548 spanNode->UpdateSymbolRenderingStrategy(options.style.value().GetRenderStrategy());
1549 spanNode->UpdateSymbolEffectStrategy(options.style.value().GetEffectStrategy());
1550 spanNode->UpdateSymbolType(options.style.value().GetSymbolType());
1551 spanNode->UpdateFontFamily(options.style.value().GetFontFamilies());
1552 }
1553 bool isUndoRedo = (options.optionSource == OptionSource::UNDO_REDO);
1554 IF_TRUE(isUndoRedo && options.paraStyle.has_value(), UpdateParagraphStyle(spanNode, options.paraStyle.value()));
1555 auto spanItem = spanNode->GetSpanItem();
1556 spanItem->content = u" ";
1557 spanItem->spanItemType = SpanItemType::SYMBOL;
1558 spanItem->SetSymbolId(options.symbolId);
1559 spanItem->SetTextStyle(options.style);
1560 spanItem->SetResourceObject(options.resourceObject);
1561 AddSpanItem(spanItem, spanIndex);
1562 IF_TRUE(!isUndoRedo, undoManager_->RecordAddSpanOperation(spanItem, SpanOptionsType::SYMBOL));
1563 SetCaretPosition(insertIndex + spanItem->content.length());
1564 ResetSelectionAfterAddSpan(false);
1565 SpanNodeFission(spanNode);
1566 return spanIndex;
1567 }
1568
BeforeAddSymbol(RichEditorChangeValue & changeValue,const SymbolSpanOptions & options)1569 bool RichEditorPattern::BeforeAddSymbol(RichEditorChangeValue& changeValue, const SymbolSpanOptions& options)
1570 {
1571 auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
1572 CHECK_NULL_RETURN(eventHub, false);
1573 CHECK_NULL_RETURN(eventHub->HasOnWillChange(), true);
1574
1575 int32_t contentLength = GetTextContentLength();
1576 int32_t insertIndex = options.offset.value_or(contentLength);
1577 insertIndex = std::clamp(insertIndex, 0, contentLength);
1578
1579 changeValue.SetRangeBefore({ insertIndex, insertIndex });
1580 changeValue.SetRangeAfter({ insertIndex, insertIndex + SYMBOL_SPAN_LENGTH });
1581 RichEditorAbstractSpanResult retInfo;
1582 TextInsertValueInfo info;
1583 CalcInsertValueObj(info, insertIndex, true);
1584 int32_t spanIndex = info.GetSpanIndex();
1585 retInfo.SetSpanIndex(spanIndex);
1586 retInfo.SetOffsetInSpan(0);
1587 retInfo.SetValueString(std::to_string(options.symbolId));
1588 retInfo.SetEraseLength(SYMBOL_SPAN_LENGTH);
1589 retInfo.SetSpanRangeStart(insertIndex);
1590 retInfo.SetSpanRangeEnd(insertIndex + SYMBOL_SPAN_LENGTH);
1591 retInfo.SetSpanType(SpanResultType::SYMBOL);
1592
1593 TextStyle style = options.style.value_or(TextStyle());
1594 retInfo.SetSymbolSpanStyle(SymbolSpanStyle(style));
1595 retInfo.SetValueResource(options.resourceObject);
1596
1597 changeValue.SetRichEditorReplacedSymbolSpans(retInfo);
1598 auto ret = eventHub->FireOnWillChange(changeValue);
1599 return ret;
1600 }
1601
AfterContentChange(RichEditorChangeValue & changeValue)1602 void RichEditorPattern::AfterContentChange(RichEditorChangeValue& changeValue)
1603 {
1604 auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
1605 if (eventHub && eventHub->HasOnDidChange()) {
1606 eventHub->FireOnDidChange(changeValue);
1607 }
1608 ForceTriggerAvoidOnCaretChange();
1609 ReportAfterContentChangeEvent();
1610 }
1611
ReportAfterContentChangeEvent()1612 void RichEditorPattern::ReportAfterContentChangeEvent()
1613 {
1614 std::string currentContent;
1615 if (isSpanStringMode_) {
1616 IF_TRUE(styledString_, currentContent = styledString_->GetString());
1617 } else {
1618 std::u16string u16Str;
1619 GetContentBySpans(u16Str);
1620 currentContent = UtfUtils::Str16DebugToStr8(u16Str);
1621 }
1622
1623 auto weakThis = AceType::WeakClaim(this);
1624 auto callback = [weakThis](const std::string& type, const std::string& content) {
1625 auto strongThis = weakThis.Upgrade();
1626 CHECK_NULL_VOID(strongThis);
1627 strongThis->OnAccessibilityEventTextChange(type, content);
1628 };
1629
1630 ProcessAccessibilityTextChange(
1631 currentContent,
1632 std::move(callback),
1633 AceLogTag::ACE_RICH_TEXT
1634 );
1635 }
1636
SpanNodeFission(RefPtr<SpanNode> & spanNode,bool needLeadingMargin)1637 void RichEditorPattern::SpanNodeFission(RefPtr<SpanNode>& spanNode, bool needLeadingMargin)
1638 {
1639 auto spanItem = spanNode->GetSpanItem();
1640 auto wContent = spanItem->content;
1641 auto spanStart = spanItem->position - wContent.length();
1642 for (size_t i = 0; i < wContent.length(); i++) {
1643 if (wContent[i] == '\n') {
1644 TextSpanSplit(static_cast<int32_t>(spanStart + i + 1), needLeadingMargin);
1645 }
1646 }
1647 UpdateSpanPosition();
1648 }
1649
DeleteSpans(const RangeOptions & options,TextChangeReason reason)1650 void RichEditorPattern::DeleteSpans(const RangeOptions& options, TextChangeReason reason)
1651 {
1652 IF_TRUE(previewTextRecord_.previewTextExiting, TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "exiting preview"));
1653 IF_TRUE(IsPreviewTextInputting() && !previewTextRecord_.previewTextExiting, NotifyExitTextPreview());
1654 auto length = GetTextContentLength();
1655 int32_t start = options.start.value_or(0);
1656 int32_t end = options.end.value_or(length);
1657 if (start > end) {
1658 std::swap(start, end);
1659 }
1660 start = std::max(0, start);
1661 end = std::min(length, end);
1662 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "delete spans range=[%{public}d, %{public}d]", start, end);
1663 if (start > length || end < 0 || start == end) {
1664 return;
1665 }
1666 AdjustSelector(start, end);
1667 OperationRecord record;
1668 record.beforeCaretPosition = start;
1669 std::u16string u16valueString;
1670 GetContentBySpans(u16valueString);
1671 record.deleteText = u16valueString.substr(start, end - start);
1672 RichEditorChangeValue changeValue(reason);
1673 changeValue.SetRangeBefore({ start, end });
1674 changeValue.SetRangeAfter({ start, start });
1675 if (auto eventHub = GetOrCreateEventHub<RichEditorEventHub>(); eventHub) {
1676 CHECK_NULL_VOID(eventHub->FireOnWillChange(changeValue));
1677 }
1678 ClearRedoOperationRecords();
1679 record.afterCaretPosition = start;
1680 AddOperationRecord(record);
1681 UndoRedoRecord styledRecord;
1682 undoManager_->UpdateRecordBeforeChange(start, end - start, styledRecord);
1683 DeleteSpansOperation(start, end);
1684 undoManager_->RecordOperationAfterChange(start, 0, styledRecord);
1685 AfterContentChange(changeValue);
1686 }
1687
DeleteSpansOperation(int32_t start,int32_t end)1688 void RichEditorPattern::DeleteSpansOperation(int32_t start, int32_t end)
1689 {
1690 auto startInfo = GetSpanPositionInfo(start);
1691 auto endInfo = GetSpanPositionInfo(end - 1);
1692 if (startInfo.spanIndex_ == endInfo.spanIndex_) {
1693 DeleteSpanByRange(start, end, startInfo);
1694 } else {
1695 DeleteSpansByRange(start, end, startInfo, endInfo);
1696 }
1697 RemoveEmptySpanItems();
1698 if (textSelector_.IsValid()) {
1699 SetCaretPosition(textSelector_.GetTextStart());
1700 CloseSelectOverlay();
1701 ResetSelection();
1702 }
1703 SetCaretOffset(start);
1704 auto host = GetContentHost();
1705 CHECK_NULL_VOID(host);
1706 if (host->GetChildren().empty() || GetTextContentLength() == 0) {
1707 SetCaretPosition(0);
1708 textForDisplay_.clear();
1709 }
1710 UpdateSpanPosition();
1711 }
1712
RemoveEmptySpanItems()1713 void RichEditorPattern::RemoveEmptySpanItems()
1714 {
1715 for (auto it = spans_.begin(); it != spans_.end();) {
1716 if ((*it)->content.empty()) {
1717 it = spans_.erase(it);
1718 } else {
1719 ++it;
1720 }
1721 }
1722 }
1723
RemoveEmptySpanNodes()1724 void RichEditorPattern::RemoveEmptySpanNodes()
1725 {
1726 auto host = GetContentHost();
1727 CHECK_NULL_VOID(host);
1728 auto& spanNodes = host->GetChildren();
1729 for (auto it = spanNodes.begin(); it != spanNodes.end();) {
1730 auto spanNode = AceType::DynamicCast<SpanNode>(*it);
1731 if (!spanNode) {
1732 ++it;
1733 continue;
1734 }
1735 if (spanNode->GetSpanItem()->content.empty()) {
1736 it = host->RemoveChild(spanNode);
1737 } else {
1738 ++it;
1739 }
1740 }
1741 }
1742
RemoveEmptySpans()1743 void RichEditorPattern::RemoveEmptySpans()
1744 {
1745 RemoveEmptySpanItems();
1746 RemoveEmptySpanNodes();
1747 }
1748
DeleteSpanByRange(int32_t start,int32_t end,SpanPositionInfo info)1749 void RichEditorPattern::DeleteSpanByRange(int32_t start, int32_t end, SpanPositionInfo info)
1750 {
1751 auto host = GetContentHost();
1752 CHECK_NULL_VOID(host);
1753 auto childrens = host->GetChildren();
1754 auto it = childrens.begin();
1755 std::advance(it, info.spanIndex_);
1756 CHECK_NULL_VOID(it != childrens.end());
1757 if (start == info.spanStart_ && end == info.spanEnd_) {
1758 ClearContent(*it);
1759 host->RemoveChild(*it);
1760 } else {
1761 auto spanNode = DynamicCast<SpanNode>(*it);
1762 CHECK_NULL_VOID(spanNode);
1763 auto spanItem = spanNode->GetSpanItem();
1764 auto beforStr = spanItem->content.substr(0, start - info.spanStart_);
1765 auto endStr = spanItem->content.substr(end - info.spanStart_);
1766 spanNode->UpdateContent(beforStr + endStr);
1767 }
1768 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1769 host->MarkModifyDone();
1770 }
1771
DeleteSpansByRange(int32_t start,int32_t end,SpanPositionInfo startInfo,SpanPositionInfo endInfo)1772 void RichEditorPattern::DeleteSpansByRange(
1773 int32_t start, int32_t end, SpanPositionInfo startInfo, SpanPositionInfo endInfo)
1774 {
1775 auto host = GetContentHost();
1776 CHECK_NULL_VOID(host);
1777 auto childrens = host->GetChildren();
1778 CHECK_NULL_VOID(!childrens.empty());
1779
1780 auto itStart = childrens.begin();
1781 if (startInfo.spanIndex_ >= static_cast<int32_t>(childrens.size())) {
1782 std::advance(itStart, static_cast<int32_t>(childrens.size()) - 1);
1783 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "startInfo.spanIndex_ is larger than childrens size");
1784 } else {
1785 std::advance(itStart, startInfo.spanIndex_);
1786 }
1787 CHECK_NULL_VOID(itStart != childrens.end());
1788 auto saveStartSpan = (start == startInfo.spanStart_) ? 0 : 1;
1789 if (saveStartSpan) {
1790 auto spanNodeStart = DynamicCast<SpanNode>(*itStart);
1791 CHECK_NULL_VOID(spanNodeStart);
1792 auto spanItemStart = spanNodeStart->GetSpanItem();
1793 auto beforStr = spanItemStart->content.substr(0, start - startInfo.spanStart_);
1794 spanNodeStart->UpdateContent(beforStr);
1795 }
1796 auto itEnd = childrens.begin();
1797 std::advance(itEnd, endInfo.spanIndex_);
1798 auto delEndSpan = (end == endInfo.spanEnd_) ? 1 : 0;
1799 if (!delEndSpan) {
1800 auto spanNodeEnd = DynamicCast<SpanNode>(*itEnd);
1801 CHECK_NULL_VOID(spanNodeEnd);
1802 auto spanItemEnd = spanNodeEnd->GetSpanItem();
1803 auto endStr = spanItemEnd->content.substr(end - endInfo.spanStart_, endInfo.spanEnd_ - end);
1804 spanNodeEnd->UpdateContent(endStr);
1805 }
1806 auto startIter = childrens.begin();
1807 std::advance(startIter, startInfo.spanIndex_ + saveStartSpan);
1808 auto endIter = childrens.begin();
1809 std::advance(endIter, endInfo.spanIndex_);
1810 for (auto iter = startIter; iter != endIter; ++iter) {
1811 ClearContent(*iter);
1812 host->RemoveChild(*iter);
1813 }
1814 if (endIter != childrens.end() && delEndSpan) {
1815 ClearContent(*endIter);
1816 host->RemoveChild(*endIter);
1817 }
1818 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1819 host->MarkModifyDone();
1820 }
1821
GetLeftTextOfCursor(int32_t number)1822 std::u16string RichEditorPattern::GetLeftTextOfCursor(int32_t number)
1823 {
1824 if (number > caretPosition_) {
1825 number = caretPosition_;
1826 }
1827 auto start = caretPosition_;
1828 if (IsSelected()) {
1829 start = std::min(textSelector_.GetStart(), textSelector_.GetEnd());
1830 }
1831 return GetSelectedText(start - number, start);
1832 }
1833
GetRightTextOfCursor(int32_t number)1834 std::u16string RichEditorPattern::GetRightTextOfCursor(int32_t number)
1835 {
1836 auto end = caretPosition_;
1837 if (IsSelected()) {
1838 end = std::max(textSelector_.GetStart(), textSelector_.GetEnd());
1839 }
1840 return GetSelectedText(end, end + number);
1841 }
1842
GetTextIndexAtCursor()1843 int32_t RichEditorPattern::GetTextIndexAtCursor()
1844 {
1845 return caretPosition_;
1846 }
1847
ClearContent(const RefPtr<UINode> & child)1848 void RichEditorPattern::ClearContent(const RefPtr<UINode>& child)
1849 {
1850 CHECK_NULL_VOID(child);
1851 if (child->GetTag() == V2::SPAN_ETS_TAG) {
1852 auto spanNode = DynamicCast<SpanNode>(child);
1853 if (spanNode) {
1854 spanNode->UpdateContent(u"");
1855 spanNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1856 }
1857 return;
1858 }
1859 auto imageSpanNode = DynamicCast<ImageSpanNode>(child);
1860 if (imageSpanNode) {
1861 imageSpanNode->GetSpanItem()->content.clear();
1862 imageSpanNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1863 return;
1864 }
1865 auto placeholderSpanNode = DynamicCast<PlaceholderSpanNode>(child);
1866 if (placeholderSpanNode) {
1867 placeholderSpanNode->GetSpanItem()->content.clear();
1868 placeholderSpanNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1869 }
1870 }
1871
GetSpanPositionInfo(int32_t position)1872 SpanPositionInfo RichEditorPattern::GetSpanPositionInfo(int32_t position)
1873 {
1874 SpanPositionInfo spanPositionInfo(-1, -1, -1, -1);
1875 CHECK_NULL_RETURN(!spans_.empty(), spanPositionInfo);
1876 position = std::clamp(position, 0, GetTextContentLength());
1877 // find the spanItem where the position is
1878 auto it = std::find_if(spans_.begin(), spans_.end(), [position](const RefPtr<SpanItem>& spanItem) {
1879 return (spanItem->position - static_cast<int32_t>(spanItem->content.length()) <= position) &&
1880 (position < spanItem->position);
1881 });
1882 if (it != spans_.end() && (*it)->unicode != 0 && (*it)->position - caretPosition_ + moveLength_ == 1) {
1883 it++;
1884 moveLength_++;
1885 position++;
1886 }
1887
1888 // the position is at the end
1889 if (it == spans_.end()) {
1890 return spanPositionInfo;
1891 }
1892
1893 spanPositionInfo.spanIndex_ = std::distance(spans_.begin(), it);
1894 int32_t contentLen = static_cast<int32_t>((*it)->content.length());
1895 spanPositionInfo.spanStart_ = (*it)->position - contentLen;
1896 spanPositionInfo.spanEnd_ = (*it)->position;
1897 spanPositionInfo.spanOffset_ = position - spanPositionInfo.spanStart_;
1898 return spanPositionInfo;
1899 }
1900
CopyTextSpanStyle(RefPtr<SpanNode> & source,RefPtr<SpanNode> & target,bool needLeadingMargin)1901 void RichEditorPattern::CopyTextSpanStyle(RefPtr<SpanNode>& source, RefPtr<SpanNode>& target, bool needLeadingMargin)
1902 {
1903 CopyTextSpanFontStyle(source, target);
1904 CopyTextSpanLineStyle(source, target, needLeadingMargin);
1905 CopyTextSpanUrlStyle(source, target);
1906 }
1907
CopyTextSpanFontStyle(RefPtr<SpanNode> & source,RefPtr<SpanNode> & target)1908 void RichEditorPattern::CopyTextSpanFontStyle(RefPtr<SpanNode>& source, RefPtr<SpanNode>& target)
1909 {
1910 CHECK_NULL_VOID(source);
1911 CHECK_NULL_VOID(source->GetTag() == V2::SPAN_ETS_TAG);
1912 CHECK_NULL_VOID(target);
1913 COPY_SPAN_STYLE_IF_PRESENT(source, target, FontSize);
1914 COPY_SPAN_STYLE_IF_PRESENT(source, target, TextColor);
1915 COPY_SPAN_STYLE_IF_PRESENT(source, target, ItalicFontStyle);
1916 COPY_SPAN_STYLE_IF_PRESENT(source, target, FontWeight);
1917 COPY_SPAN_STYLE_IF_PRESENT(source, target, FontFamily);
1918 COPY_SPAN_STYLE_IF_PRESENT(source, target, TextDecoration);
1919 COPY_SPAN_STYLE_IF_PRESENT(source, target, TextDecorationColor);
1920 COPY_SPAN_STYLE_IF_PRESENT(source, target, TextDecorationStyle);
1921 COPY_SPAN_STYLE_IF_PRESENT(source, target, LineThicknessScale);
1922 COPY_SPAN_STYLE_IF_PRESENT(source, target, TextCase);
1923 COPY_SPAN_STYLE_IF_PRESENT(source, target, LineHeight);
1924 COPY_SPAN_STYLE_IF_PRESENT(source, target, HalfLeading);
1925 COPY_SPAN_STYLE_IF_PRESENT(source, target, LetterSpacing);
1926 COPY_SPAN_STYLE_IF_PRESENT(source, target, FontFeature);
1927 COPY_SPAN_STYLE_IF_PRESENT(source, target, TextShadow);
1928 target->GetSpanItem()->useThemeFontColor = source->GetSpanItem()->useThemeFontColor;
1929 target->GetSpanItem()->useThemeDecorationColor = source->GetSpanItem()->useThemeDecorationColor;
1930 UpdateTextBackgroundStyle(target, source->GetTextBackgroundStyle());
1931 }
1932
CopyTextSpanLineStyle(RefPtr<SpanNode> & source,RefPtr<SpanNode> & target,bool needLeadingMargin)1933 void RichEditorPattern::CopyTextSpanLineStyle(
1934 RefPtr<SpanNode>& source, RefPtr<SpanNode>& target, bool needLeadingMargin)
1935 {
1936 CHECK_NULL_VOID(source);
1937 CHECK_NULL_VOID(target);
1938 COPY_SPAN_STYLE_IF_PRESENT(source, target, TextAlign);
1939 COPY_SPAN_STYLE_IF_PRESENT(source, target, WordBreak);
1940 COPY_SPAN_STYLE_IF_PRESENT(source, target, LineBreakStrategy);
1941 COPY_SPAN_STYLE_IF_PRESENT(source, target, ParagraphSpacing);
1942 COPY_SPAN_STYLE_IF_PRESENT(source, target, TextVerticalAlign);
1943 if (source->HasLeadingMargin()) {
1944 auto leadingMargin = source->GetLeadingMarginValue({});
1945 if (!needLeadingMargin) {
1946 leadingMargin.pixmap.Reset();
1947 }
1948 target->UpdateLeadingMargin(leadingMargin);
1949 }
1950 }
1951
CopyTextSpanUrlStyle(RefPtr<SpanNode> & source,RefPtr<SpanNode> & target)1952 void RichEditorPattern::CopyTextSpanUrlStyle(RefPtr<SpanNode>& source, RefPtr<SpanNode>& target)
1953 {
1954 CHECK_NULL_VOID(source && source->GetTag() == V2::SPAN_ETS_TAG);
1955 CHECK_NULL_VOID(target && target->GetTag() == V2::SPAN_ETS_TAG);
1956 const auto& sourceSpanItem = source->GetSpanItem();
1957 const auto& targetSpanItem = target->GetSpanItem();
1958 CHECK_NULL_VOID(sourceSpanItem && targetSpanItem);
1959 targetSpanItem->urlOnRelease = sourceSpanItem->urlOnRelease;
1960 targetSpanItem->urlAddress = sourceSpanItem->urlAddress;
1961 }
1962
CopyGestureOption(const RefPtr<SpanNode> & source,RefPtr<SpanNode> & target)1963 void RichEditorPattern::CopyGestureOption(const RefPtr<SpanNode>& source, RefPtr<SpanNode>& target)
1964 {
1965 CHECK_NULL_VOID(source);
1966 CHECK_NULL_VOID(target);
1967 auto sourceItem = source->GetSpanItem();
1968 CHECK_NULL_VOID(sourceItem);
1969 auto targetItem = target->GetSpanItem();
1970 CHECK_NULL_VOID(targetItem);
1971
1972 if (sourceItem->onClick) {
1973 auto tmpClickFunc = sourceItem->onClick;
1974 targetItem->SetOnClickEvent(std::move(tmpClickFunc));
1975 }
1976 if (sourceItem->onLongPress) {
1977 auto tmpLongPressFunc = sourceItem->onLongPress;
1978 targetItem->SetLongPressEvent(std::move(tmpLongPressFunc));
1979 }
1980 if (sourceItem->onDoubleClick) {
1981 auto tmpDoubleClickFunc = sourceItem->onDoubleClick;
1982 targetItem->SetDoubleClickEvent(std::move(tmpDoubleClickFunc));
1983 }
1984 if (sourceItem->onHover) {
1985 auto tmpHoverFunc = sourceItem->onHover;
1986 targetItem->SetHoverEvent(std::move(tmpHoverFunc));
1987 }
1988 if (sourceItem->onTouch) {
1989 auto tmpTouchFunc = sourceItem->onTouch;
1990 targetItem->SetTouchEvent(std::move(tmpTouchFunc));
1991 }
1992 }
1993
HandleUserTouchEvent(TouchEventInfo & info)1994 void RichEditorPattern::HandleUserTouchEvent(TouchEventInfo& info)
1995 {
1996 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleUserTouchEvent");
1997 TextPattern::HandleSpanStringTouchEvent(info);
1998 }
1999
TextSpanSplit(int32_t position,bool needLeadingMargin)2000 int32_t RichEditorPattern::TextSpanSplit(int32_t position, bool needLeadingMargin)
2001 {
2002 CHECK_NULL_RETURN(!spans_.empty(), -1);
2003
2004 SpanPositionInfo positionInfo = GetSpanPositionInfo(position);
2005 int32_t spanIndex = positionInfo.spanIndex_;
2006 int32_t spanStart = positionInfo.spanStart_;
2007 int32_t spanEnd = positionInfo.spanEnd_;
2008 int32_t offsetInSpan = positionInfo.spanOffset_;
2009 TAG_LOGD(AceLogTag::ACE_RICH_TEXT,
2010 "position=%{public}d, spanIndex=%{public}d, spanRange=[%{public}d,%{public}d], offsetInSpan=%{public}d",
2011 position, spanIndex, spanStart, spanEnd, offsetInSpan);
2012
2013 CHECK_NULL_RETURN((offsetInSpan > 0), spanIndex);
2014
2015 auto host = GetContentHost();
2016 CHECK_NULL_RETURN(host, -1);
2017 auto spanNode = DynamicCast<SpanNode>(host->GetChildAtIndex(spanIndex));
2018 CHECK_NULL_RETURN(spanNode, -1);
2019
2020 auto spanItem = spanNode->GetSpanItem();
2021 auto spanItemContent = spanItem->content;
2022 offsetInSpan = std::min(offsetInSpan, static_cast<int32_t>(spanItemContent.length()));
2023
2024 spanNode->UpdateContent(spanItemContent.substr(0, offsetInSpan));
2025 spanItem->position = spanStart + offsetInSpan;
2026
2027 auto newSpanNode = SpanNode::GetOrCreateSpanNode(ElementRegister::GetInstance()->MakeUniqueId());
2028 CHECK_NULL_RETURN(newSpanNode, -1);
2029
2030 CopyTextSpanStyle(spanNode, newSpanNode, needLeadingMargin);
2031 CopyGestureOption(spanNode, newSpanNode);
2032 newSpanNode->UpdateContent(spanItemContent.substr(offsetInSpan));
2033 newSpanNode->MountToParent(host, spanIndex + 1);
2034
2035 auto newSpanItem = newSpanNode->GetSpanItem();
2036 newSpanItem->rangeStart = spanStart + offsetInSpan;
2037 newSpanItem->position = spanEnd;
2038
2039 auto spanIter = spans_.begin();
2040 std::advance(spanIter, spanIndex + 1);
2041 spans_.insert(spanIter, newSpanItem);
2042
2043 return spanIndex + 1;
2044 }
2045
GetCaretPosition()2046 int32_t RichEditorPattern::GetCaretPosition()
2047 {
2048 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "GetCaretPosition");
2049 return caretPosition_;
2050 }
2051
SetCaretOffset(int32_t caretPosition)2052 bool RichEditorPattern::SetCaretOffset(int32_t caretPosition)
2053 {
2054 if (IsPreviewTextInputting()) {
2055 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "intercept operation in previewText state");
2056 return false;
2057 }
2058 int32_t inputCaretPosition = caretPosition;
2059 AdjustSelector(caretPosition, HandleType::SECOND);
2060 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "setCaretOffset, in=%{public}d, afterAdjust=%{public}d",
2061 inputCaretPosition, caretPosition);
2062 bool success = SetCaretPosition(caretPosition);
2063 auto host = GetHost();
2064 CHECK_NULL_RETURN(host, false);
2065 auto focusHub = host->GetOrCreateFocusHub();
2066 CHECK_NULL_RETURN(focusHub, false);
2067 if (focusHub->IsCurrentFocus()) {
2068 isCursorAlwaysDisplayed_ = false;
2069 StartTwinkling();
2070 }
2071 CloseSelectOverlay();
2072 ResetSelection();
2073 return success;
2074 }
2075
CalcCursorOffsetByPosition(int32_t position,float & selectLineHeight,bool downStreamFirst,bool needLineHighest)2076 OffsetF RichEditorPattern::CalcCursorOffsetByPosition(
2077 int32_t position, float& selectLineHeight, bool downStreamFirst, bool needLineHighest)
2078 {
2079 selectLineHeight = 0.0f;
2080 auto host = GetHost();
2081 auto contentHost = GetContentHost();
2082 CHECK_NULL_RETURN(host && contentHost, OffsetF(0, 0));
2083 auto pipeline = host->GetContext();
2084 CHECK_NULL_RETURN(pipeline, OffsetF(0, 0));
2085 auto rootOffset = pipeline->GetRootRect().GetOffset();
2086 auto textPaintOffset = richTextRect_.GetOffset();
2087 needLineHighest |= IsCustomSpanInCaretPos(position, downStreamFirst);
2088 auto startOffset = paragraphs_.ComputeCursorOffset(position, selectLineHeight, downStreamFirst, needLineHighest);
2089 auto children = contentHost->GetChildren();
2090 if (NearZero(selectLineHeight)) {
2091 if (children.empty() || GetTextContentLength() == 0) {
2092 return textPaintOffset - rootOffset;
2093 }
2094 if (std::all_of(children.begin(), children.end(), [](RefPtr<UINode>& node) {
2095 CHECK_NULL_RETURN(node, false);
2096 return (node->GetTag() == V2::IMAGE_ETS_TAG || node->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG);
2097 })) {
2098 bool isTail = false;
2099 auto it = children.begin();
2100 if (position >= static_cast<int32_t>(children.size())) {
2101 std::advance(it, (static_cast<int32_t>(children.size()) - 1));
2102 isTail = true;
2103 } else {
2104 std::advance(it, position);
2105 }
2106 if (it == children.end()) {
2107 return startOffset;
2108 }
2109 auto imageNode = DynamicCast<FrameNode>(*it);
2110 if (imageNode) {
2111 auto geometryNode = imageNode->GetGeometryNode();
2112 CHECK_NULL_RETURN(geometryNode, OffsetF(0.0f, 0.0f));
2113 startOffset = geometryNode->GetMarginFrameOffset();
2114 selectLineHeight = geometryNode->GetMarginFrameSize().Height();
2115 startOffset += isTail ? OffsetF(geometryNode->GetMarginFrameSize().Width(), 0.0f) : OffsetF(0.0f, 0.0f);
2116 }
2117 return startOffset;
2118 }
2119 }
2120 auto caretOffset = startOffset + textPaintOffset + rootOffset;
2121 CHECK_NULL_RETURN(overlayMod_, caretOffset);
2122 caretOffset.SetX(std::clamp(caretOffset.GetX(), 0.0f, richTextRect_.Right()));
2123 return caretOffset;
2124 }
2125
HandleCurrentPositionParagraphInfo(float & lastLineTop,float & paragraphSpacing)2126 void RichEditorPattern::HandleCurrentPositionParagraphInfo(float& lastLineTop, float& paragraphSpacing)
2127 {
2128 auto paragraphInfo = paragraphs_.GetParagraphInfo(caretPosition_);
2129 paragraphSpacing = paragraphInfo.paragraphStyle.paragraphSpacing.ConvertToPx();
2130 float lastLineHeight = 0.0f;
2131 CHECK_EQUAL_VOID(paragraphInfo.end - 1 >= paragraphInfo.start, false);
2132 lastLineTop = CalcCursorOffsetByPosition(paragraphInfo.end -1, lastLineHeight, true, true).GetY();
2133 }
2134
IsCustomSpanInCaretPos(int32_t position,bool downStreamFirst)2135 bool RichEditorPattern::IsCustomSpanInCaretPos(int32_t position, bool downStreamFirst)
2136 {
2137 CHECK_NULL_RETURN((isSpanStringMode_ && styledString_), false);
2138 auto start = downStreamFirst ? position : position - 1;
2139 start = std::clamp(start, 0, GetTextContentLength());
2140 auto lastStyles = styledString_->GetSpans(start, 1);
2141 for (auto& style : lastStyles) {
2142 if (style && style->GetSpanType() == SpanType::CustomSpan) {
2143 return true;
2144 }
2145 }
2146 return false;
2147 }
2148
SetCaretPositionWithAffinity(PositionWithAffinity positionWithAffinity)2149 void RichEditorPattern::SetCaretPositionWithAffinity(PositionWithAffinity positionWithAffinity)
2150 {
2151 auto currentPosition = static_cast<int32_t>(positionWithAffinity.position_);
2152 SetCaretPosition(currentPosition);
2153 caretAffinityPolicy_ = (positionWithAffinity.affinity_ == TextAffinity::UPSTREAM)
2154 ? CaretAffinityPolicy::UPSTREAM_FIRST
2155 : CaretAffinityPolicy::DOWNSTREAM_FIRST;
2156 }
2157
SetCaretPosition(int32_t pos,bool needNotifyImf)2158 bool RichEditorPattern::SetCaretPosition(int32_t pos, bool needNotifyImf)
2159 {
2160 auto correctPos = std::clamp(pos, 0, GetTextContentLength());
2161 IF_TRUE(!isModifyingContent_, AdjustSelector(correctPos, HandleType::SECOND));
2162 ResetLastClickOffset();
2163 caretAffinityPolicy_ = CaretAffinityPolicy::DEFAULT;
2164 CHECK_NULL_RETURN((pos == correctPos), false);
2165 if (caretPosition_ != correctPos) {
2166 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "caret:%{public}d->%{public}d", caretPosition_, correctPos);
2167 lastCaretPosition_ = caretPosition_;
2168 caretPosition_ = correctPos;
2169 FireOnSelectionChange(caretPosition_);
2170 if (caretChangeListener_) {
2171 caretChangeListener_(caretPosition_);
2172 }
2173 }
2174 if (needNotifyImf) {
2175 UpdateCaretInfoToController();
2176 }
2177 return true;
2178 }
2179
FireOnSelectionChange(const int32_t caretPosition)2180 void RichEditorPattern::FireOnSelectionChange(const int32_t caretPosition)
2181 {
2182 if (!textSelector_.SelectNothing() || !caretTwinkling_) {
2183 return;
2184 }
2185 FireOnSelectionChange(caretPosition, caretPosition);
2186 }
2187
FireOnSelectionChange(const TextSelector & selector)2188 void RichEditorPattern::FireOnSelectionChange(const TextSelector& selector)
2189 {
2190 if (selector.SelectNothing()) {
2191 return;
2192 }
2193 FireOnSelectionChange(selector.GetStart(), selector.GetEnd());
2194 }
2195
FireOnSelectionChange(int32_t start,int32_t end,bool isForced)2196 void RichEditorPattern::FireOnSelectionChange(int32_t start, int32_t end, bool isForced)
2197 {
2198 auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
2199 CHECK_NULL_VOID(eventHub);
2200 CHECK_NULL_VOID(isForced || HasFocus() || dataDetectorAdapter_->hasClickedMenuOption_);
2201 bool isSingleHandle = selectOverlay_->IsSingleHandle();
2202 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "onSelectionChange, range=[%{public}d,%{public}d], isTwinkling=%{public}d, "
2203 "isSingleHandle=%{public}d", start, end, caretTwinkling_, isSingleHandle);
2204 if (start < 0 || end < 0) {
2205 return;
2206 }
2207 if (start == end && !caretTwinkling_ && !isSingleHandle) {
2208 return;
2209 }
2210 if (start > end) {
2211 std::swap(start, end);
2212 }
2213 auto range = SelectionRangeInfo(start, end);
2214 if (range == lastSelectionRange_) {
2215 return;
2216 }
2217 lastSelectionRange_ = std::move(range);
2218 eventHub->FireOnSelectionChange(&range);
2219 }
2220
GetCaretVisible() const2221 bool RichEditorPattern::GetCaretVisible() const
2222 {
2223 return caretVisible_;
2224 }
2225
OnWindowHide()2226 void RichEditorPattern::OnWindowHide()
2227 {
2228 ScrollablePattern::OnWindowHide();
2229 }
2230
SetUpdateSpanStyle(struct UpdateSpanStyle updateSpanStyle)2231 void RichEditorPattern::SetUpdateSpanStyle(struct UpdateSpanStyle updateSpanStyle)
2232 {
2233 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "SetUpdateSpanStyle");
2234 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "updateSpanStyle=%{public}s", updateSpanStyle.ToString().c_str());
2235 updateSpanStyle_ = updateSpanStyle;
2236 }
2237
GetUpdateSpanStyle()2238 UpdateSpanStyle RichEditorPattern::GetUpdateSpanStyle()
2239 {
2240 return updateSpanStyle_;
2241 }
2242
MarkAISpanStyleChanged()2243 void RichEditorPattern::MarkAISpanStyleChanged()
2244 {
2245 std::for_each(spans_.begin(), spans_.end(), [](const auto& span) {
2246 if (span->aiSpanResultCount != 0) {
2247 span->needReLayout = true;
2248 }
2249 });
2250 TextPattern::MarkAISpanStyleChanged();
2251 }
2252
HandleOnAskCelia()2253 void RichEditorPattern::HandleOnAskCelia()
2254 {
2255 TextPattern::HandleOnAskCelia();
2256 if (IsUsingMouse()) {
2257 CloseSelectOverlay();
2258 } else {
2259 selectOverlay_->HideMenu();
2260 }
2261 }
2262
UpdateCaretStyleByTypingStyle(bool isReset)2263 void RichEditorPattern::UpdateCaretStyleByTypingStyle(bool isReset)
2264 {
2265 bool empty = spans_.empty();
2266 bool hasPreviewContent = !previewTextRecord_.previewContent.empty();
2267 bool lastNewLine = !empty && styleManager_->HasTypingParagraphStyle() && spans_.back()->content.back() == u'\n';
2268 CHECK_NULL_VOID(empty || hasPreviewContent || lastNewLine || isReset);
2269 auto host = GetHost();
2270 CHECK_NULL_VOID(host);
2271 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2272 }
2273
SetTypingStyle(std::optional<struct UpdateSpanStyle> typingStyle,std::optional<TextStyle> textStyle)2274 void RichEditorPattern::SetTypingStyle(std::optional<struct UpdateSpanStyle> typingStyle,
2275 std::optional<TextStyle> textStyle)
2276 {
2277 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "SetTypingStyle, %{public}d", typingStyle.has_value());
2278 bool isReset = typingStyle_.has_value() && !typingStyle.has_value();
2279 typingStyle_ = typingStyle;
2280 typingTextStyle_ = textStyle;
2281 styleManager_->SetTypingStyle(typingStyle, textStyle);
2282 if (typingStyle_.has_value() && typingTextStyle_.has_value()) {
2283 IF_TRUE(typingStyle_->updateTextBackgroundStyle,
2284 typingStyle_->updateTextBackgroundStyle->needCompareGroupId = false);
2285 auto textBackgroundStyle = typingTextStyle_->GetTextBackgroundStyle();
2286 if (textBackgroundStyle) {
2287 textBackgroundStyle->needCompareGroupId = false;
2288 typingTextStyle_->SetTextBackgroundStyle(textBackgroundStyle);
2289 }
2290 }
2291 UpdateCaretStyleByTypingStyle(isReset);
2292 }
2293
SetTypingParagraphStyle(std::optional<struct UpdateParagraphStyle> typingParagraphStyle)2294 void RichEditorPattern::SetTypingParagraphStyle(std::optional<struct UpdateParagraphStyle> typingParagraphStyle)
2295 {
2296 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "SetTypingParagraphStyle, %{public}d", typingParagraphStyle.has_value());
2297 bool isReset = styleManager_->HasTypingParagraphStyle() && !typingParagraphStyle.has_value();
2298 styleManager_->SetTypingParagraphStyle(typingParagraphStyle);
2299 UpdateCaretStyleByTypingStyle(isReset);
2300 }
2301
GetTypingStyle()2302 std::optional<struct UpdateSpanStyle> RichEditorPattern::GetTypingStyle()
2303 {
2304 return typingStyle_;
2305 }
2306
UpdateFontFeatureTextStyle(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle & updateSpanStyle,TextStyle & textStyle)2307 void RichEditorPattern::UpdateFontFeatureTextStyle(
2308 RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle& updateSpanStyle, TextStyle& textStyle)
2309 {
2310 if (updateSpanStyle.updateFontFeature.has_value()) {
2311 spanNode->UpdateFontFeature(textStyle.GetFontFeatures());
2312 }
2313 }
2314
UpdateTextStyle(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle updateSpanStyle,TextStyle textStyle)2315 void RichEditorPattern::UpdateTextStyle(
2316 RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle updateSpanStyle, TextStyle textStyle)
2317 {
2318 CHECK_NULL_VOID(spanNode->GetTag() == V2::SPAN_ETS_TAG);
2319 auto host = GetHost();
2320 CHECK_NULL_VOID(host);
2321 UpdateFontFeatureTextStyle(spanNode, updateSpanStyle, textStyle);
2322 if (updateSpanStyle.updateTextColor.has_value()) {
2323 spanNode->UpdateTextColorWithoutCheck(textStyle.GetTextColor());
2324 spanNode->GetSpanItem()->useThemeFontColor = false;
2325 }
2326 if (updateSpanStyle.updateLineHeight.has_value()) {
2327 spanNode->UpdateLineHeight(textStyle.GetLineHeight());
2328 }
2329 if (updateSpanStyle.updateHalfLeading.has_value()) {
2330 spanNode->UpdateHalfLeading(textStyle.GetHalfLeading());
2331 }
2332 if (updateSpanStyle.updateLetterSpacing.has_value()) {
2333 spanNode->UpdateLetterSpacing(textStyle.GetLetterSpacing());
2334 }
2335 if (updateSpanStyle.updateFontSize.has_value()) {
2336 spanNode->UpdateFontSize(textStyle.GetFontSize());
2337 }
2338 if (updateSpanStyle.updateItalicFontStyle.has_value()) {
2339 spanNode->UpdateItalicFontStyle(textStyle.GetFontStyle());
2340 }
2341 if (updateSpanStyle.updateFontWeight.has_value()) {
2342 spanNode->UpdateFontWeight(textStyle.GetFontWeight());
2343 }
2344 if (updateSpanStyle.updateFontFamily.has_value()) {
2345 spanNode->UpdateFontFamily(textStyle.GetFontFamilies());
2346 }
2347 UpdateDecoration(spanNode, updateSpanStyle, textStyle);
2348 if (updateSpanStyle.updateTextShadows.has_value()) {
2349 spanNode->UpdateTextShadow(textStyle.GetTextShadows());
2350 }
2351 if (updateSpanStyle.updateTextBackgroundStyle.has_value()) {
2352 UpdateTextBackgroundStyle(spanNode, textStyle.GetTextBackgroundStyle());
2353 }
2354 UpdateUrlStyle(spanNode, updateSpanStyle.updateUrlAddress);
2355
2356 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2357 host->MarkModifyDone();
2358 }
2359
UpdateDecoration(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle & updateSpanStyle,TextStyle & textStyle)2360 void RichEditorPattern::UpdateDecoration(
2361 RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle& updateSpanStyle, TextStyle& textStyle)
2362 {
2363 if (updateSpanStyle.updateTextDecoration.has_value()) {
2364 spanNode->UpdateTextDecoration(textStyle.GetTextDecoration());
2365 }
2366 if (updateSpanStyle.updateTextDecorationColor.has_value()) {
2367 spanNode->UpdateTextDecorationColorWithoutCheck(textStyle.GetTextDecorationColor());
2368 spanNode->GetSpanItem()->useThemeDecorationColor = false;
2369 }
2370 if (updateSpanStyle.updateTextDecorationStyle.has_value()) {
2371 spanNode->UpdateTextDecorationStyle(textStyle.GetTextDecorationStyle());
2372 }
2373 if (updateSpanStyle.updateLineThicknessScale.has_value()) {
2374 spanNode->UpdateLineThicknessScale(textStyle.GetLineThicknessScale());
2375 }
2376 }
2377
UpdateSymbolStyle(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle updateSpanStyle,TextStyle textStyle)2378 void RichEditorPattern::UpdateSymbolStyle(
2379 RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle updateSpanStyle, TextStyle textStyle)
2380 {
2381 CHECK_NULL_VOID(spanNode->GetTag() == V2::SYMBOL_SPAN_ETS_TAG);
2382 auto host = GetHost();
2383 CHECK_NULL_VOID(host);
2384 UpdateFontFeatureTextStyle(spanNode, updateSpanStyle, textStyle);
2385 if (updateSpanStyle.updateSymbolFontSize.has_value()) {
2386 spanNode->UpdateFontSize(updateSpanStyle.updateSymbolFontSize.value());
2387 }
2388 if (updateSpanStyle.updateSymbolFontWeight.has_value()) {
2389 spanNode->UpdateFontWeight(updateSpanStyle.updateSymbolFontWeight.value());
2390 }
2391 if (updateSpanStyle.updateSymbolColor.has_value()) {
2392 spanNode->UpdateSymbolColorList(updateSpanStyle.updateSymbolColor.value());
2393 }
2394 if (updateSpanStyle.updateSymbolRenderingStrategy.has_value()) {
2395 spanNode->UpdateSymbolRenderingStrategy(updateSpanStyle.updateSymbolRenderingStrategy.value());
2396 }
2397 if (updateSpanStyle.updateSymbolEffectStrategy.has_value()) {
2398 spanNode->UpdateSymbolEffectStrategy(updateSpanStyle.updateSymbolEffectStrategy.value());
2399 }
2400 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2401 host->MarkModifyDone();
2402 }
2403
HasSameTypingStyle(const RefPtr<SpanNode> & spanNode)2404 bool RichEditorPattern::HasSameTypingStyle(const RefPtr<SpanNode>& spanNode)
2405 {
2406 auto spanItem = spanNode->GetSpanItem();
2407 CHECK_NULL_RETURN(spanItem, false);
2408 auto spanTextStyle = spanItem->GetTextStyle();
2409 if (spanTextStyle.has_value() && typingTextStyle_.has_value()) {
2410 return spanTextStyle.value() == typingTextStyle_.value();
2411 } else {
2412 return !(spanTextStyle.has_value() || typingTextStyle_.has_value());
2413 }
2414 }
2415
UpdateImageStyle(RefPtr<FrameNode> & imageNode,const ImageSpanAttribute & imageStyle)2416 void RichEditorPattern::UpdateImageStyle(RefPtr<FrameNode>& imageNode, const ImageSpanAttribute& imageStyle)
2417 {
2418 CHECK_NULL_VOID(imageNode);
2419 CHECK_NULL_VOID(imageNode->GetTag() == V2::IMAGE_ETS_TAG);
2420 auto host = GetHost();
2421 CHECK_NULL_VOID(host);
2422 auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
2423 CHECK_NULL_VOID(imageLayoutProperty);
2424 if (updateSpanStyle_.updateImageWidth.has_value() || updateSpanStyle_.updateImageHeight.has_value()) {
2425 imageLayoutProperty->UpdateUserDefinedIdealSize(imageStyle.size->GetSize());
2426 }
2427 if (updateSpanStyle_.updateImageFit.has_value()) {
2428 imageLayoutProperty->UpdateImageFit(imageStyle.objectFit.value());
2429 }
2430 if (updateSpanStyle_.updateImageVerticalAlign.has_value()) {
2431 imageLayoutProperty->UpdateVerticalAlign(imageStyle.verticalAlign.value());
2432 }
2433 if (updateSpanStyle_.borderRadius.has_value()) {
2434 auto imageRenderCtx = imageNode->GetRenderContext();
2435 imageRenderCtx->UpdateBorderRadius(imageStyle.borderRadius.value());
2436 imageRenderCtx->SetClipToBounds(true);
2437 }
2438 if (updateSpanStyle_.marginProp.has_value()) {
2439 imageLayoutProperty->UpdateMargin(imageStyle.marginProp.value());
2440 }
2441 UpdateImageAttribute(imageNode, imageStyle);
2442 imageNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2443 imageNode->MarkModifyDone();
2444 IF_PRESENT(oneStepDragController_, MarkDirtyNode(WeakClaim((ImageSpanNode*) RawPtr(imageNode))));
2445 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2446 host->MarkModifyDone();
2447 }
2448
UpdateImageAttribute(RefPtr<FrameNode> & imageNode,const ImageSpanAttribute & imageStyle)2449 void RichEditorPattern::UpdateImageAttribute(RefPtr<FrameNode>& imageNode, const ImageSpanAttribute& imageStyle)
2450 {
2451 CHECK_NULL_VOID(imageNode);
2452 auto node = DynamicCast<ImageSpanNode>(imageNode);
2453 CHECK_NULL_VOID(node);
2454 auto imageSpanItem = DynamicCast<ImageSpanItem>(node->GetSpanItem());
2455 CHECK_NULL_VOID(imageSpanItem);
2456 auto& imageAttribute = imageSpanItem->options.imageAttribute;
2457 IF_TRUE(!imageAttribute.has_value(), imageAttribute = imageStyle);
2458 if (updateSpanStyle_.updateImageWidth.has_value() || updateSpanStyle_.updateImageHeight.has_value()) {
2459 imageAttribute->size = imageStyle.size;
2460 }
2461 if (updateSpanStyle_.updateImageFit.has_value()) {
2462 imageAttribute->objectFit = imageStyle.objectFit;
2463 }
2464 if (updateSpanStyle_.updateImageVerticalAlign.has_value()) {
2465 imageAttribute->verticalAlign = imageStyle.verticalAlign;
2466 }
2467 if (updateSpanStyle_.borderRadius.has_value()) {
2468 imageAttribute->borderRadius = imageStyle.borderRadius;
2469 }
2470 if (updateSpanStyle_.marginProp.has_value()) {
2471 imageAttribute->marginProp = imageStyle.marginProp;
2472 }
2473 imageSpanItem->MarkDirty();
2474 }
2475
SymbolSpanUpdateStyle(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle updateSpanStyle,TextStyle textStyle)2476 bool RichEditorPattern::SymbolSpanUpdateStyle(
2477 RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle updateSpanStyle, TextStyle textStyle)
2478 {
2479 if (spanNode->GetTag() == V2::SYMBOL_SPAN_ETS_TAG) {
2480 UpdateSymbolStyle(spanNode, updateSpanStyle_, textStyle);
2481 return true;
2482 }
2483 return false;
2484 }
2485
UpdateSpanStyle(int32_t start,int32_t end,const TextStyle & textStyle,const ImageSpanAttribute & imageStyle,bool isExternal)2486 void RichEditorPattern::UpdateSpanStyle(
2487 int32_t start, int32_t end, const TextStyle& textStyle, const ImageSpanAttribute& imageStyle, bool isExternal)
2488 {
2489 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "updateSpanStyle, [%{public}d,%{public}d], %{public}s",
2490 start, end, ToBriefString(textStyle, imageStyle, updateSpanStyle_).c_str());
2491 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "textStyle=%{public}s, imageStyle=%{public}s",
2492 textStyle.ToString().c_str(), imageStyle.ToString().c_str());
2493 auto host = GetContentHost();
2494 CHECK_NULL_VOID(host);
2495 AdjustSelector(start, end);
2496 UndoRedoRecord styledRecord;
2497 IF_TRUE(isExternal, undoManager_->UpdateRecordBeforeChange(start, end - start, styledRecord, true));
2498 int32_t spanStart = 0;
2499 int32_t spanEnd = 0;
2500 for (auto it = host->GetChildren().begin(); it != host->GetChildren().end(); ++it) {
2501 auto spanNode = DynamicCast<SpanNode>(*it);
2502 auto imageNode = DynamicCast<FrameNode>(*it);
2503 if (!spanNode) {
2504 if (spanEnd != 0) {
2505 spanStart = spanEnd;
2506 }
2507 spanEnd = spanStart + 1;
2508 } else {
2509 spanNode->GetSpanItem()->GetIndex(spanStart, spanEnd);
2510 }
2511 if (spanEnd < start) {
2512 continue;
2513 }
2514
2515 if (spanStart >= start && spanEnd <= end) {
2516 if (spanNode) {
2517 UpdateSymbolStyle(spanNode, updateSpanStyle_, textStyle);
2518 UpdateTextStyle(spanNode, updateSpanStyle_, textStyle);
2519 } else {
2520 UpdateImageStyle(imageNode, imageStyle);
2521 }
2522 if (spanEnd == end) {
2523 break;
2524 }
2525 } else if ((spanStart < start && start < spanEnd) || (spanStart < end && end < spanEnd)) {
2526 if (SymbolSpanUpdateStyle(spanNode, updateSpanStyle_, textStyle)) {
2527 continue;
2528 }
2529 auto index = spanStart < start && start < spanEnd ? start : end;
2530 TextSpanSplit(index, true);
2531 --it;
2532 } else if (spanStart >= end) {
2533 break;
2534 }
2535 }
2536 IF_TRUE(isExternal, undoManager_->RecordOperationAfterChange(start, end - start, styledRecord));
2537 }
2538
GetChildByIndex(int32_t index) const2539 RefPtr<UINode> RichEditorPattern::GetChildByIndex(int32_t index) const
2540 {
2541 auto host = GetContentHost();
2542 CHECK_NULL_RETURN(host, nullptr);
2543 return host->GetChildAtIndex(index);
2544 }
2545
SetResultObjectText(ResultObject & resultObject,const RefPtr<SpanItem> & spanItem)2546 void RichEditorPattern::SetResultObjectText(ResultObject& resultObject, const RefPtr<SpanItem>& spanItem)
2547 {
2548 CHECK_NULL_VOID(spanItem);
2549 resultObject.valueString = spanItem->content;
2550 if (spanItem->rangeStart <= previewTextRecord_.startOffset && spanItem->position >= previewTextRecord_.endOffset) {
2551 resultObject.previewText = previewTextRecord_.previewContent;
2552 }
2553 resultObject.urlAddress = spanItem->GetUrlAddress();
2554 }
2555
GetContentBySpans(std::u16string & u16Str)2556 void RichEditorPattern::GetContentBySpans(std::u16string& u16Str)
2557 {
2558 uint32_t length = 1;
2559 for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
2560 length += (*iter)->content.length();
2561 }
2562 u16Str.reserve(length);
2563
2564 for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
2565 u16Str.append((*iter)->content);
2566 }
2567 }
2568
SetSelectSpanStyle(int32_t start,int32_t end,KeyCode code,bool isStart)2569 void RichEditorPattern::SetSelectSpanStyle(int32_t start, int32_t end, KeyCode code, bool isStart)
2570 {
2571 TextStyle spanStyle;
2572 struct UpdateSpanStyle updateSpanStyle;
2573 ImageSpanAttribute imageStyle;
2574 auto it = std::find_if(spans_.begin(), spans_.end(), [start](const RefPtr<SpanItem>& spanItem) {
2575 return (spanItem->rangeStart <= start) && (start < spanItem->position);
2576 });
2577 if (it == spans_.end()) {
2578 return;
2579 }
2580 std::optional<TextStyle> spanTextStyle = (*it)->GetTextStyle();
2581 if (spanTextStyle.has_value()) {
2582 spanStyle = spanTextStyle.value();
2583 }
2584 HandleSelectFontStyleWrapper(code, spanStyle);
2585 IF_TRUE(!(*it)->useThemeFontColor, updateSpanStyle.updateTextColor = spanStyle.GetTextColor());
2586 updateSpanStyle.updateFontSize = spanStyle.GetFontSize();
2587 updateSpanStyle.updateItalicFontStyle = spanStyle.GetFontStyle();
2588 updateSpanStyle.updateFontWeight = spanStyle.GetFontWeight();
2589 updateSpanStyle.updateFontFamily = spanStyle.GetFontFamilies();
2590 updateSpanStyle.updateTextDecoration = spanStyle.GetTextDecorationFirst();
2591 if (!isStart) {
2592 auto updateSpanStyle_ = GetUpdateSpanStyle();
2593 switch (code) {
2594 case KeyCode::KEY_B:
2595 updateSpanStyle.updateFontWeight = updateSpanStyle_.updateFontWeight;
2596 spanStyle.SetFontWeight(updateSpanStyle_.updateFontWeight.value());
2597 break;
2598 case KeyCode::KEY_I:
2599 updateSpanStyle.updateItalicFontStyle = updateSpanStyle_.updateItalicFontStyle;
2600 spanStyle.SetFontStyle(updateSpanStyle_.updateItalicFontStyle.value());
2601 break;
2602 case KeyCode::KEY_U:
2603 updateSpanStyle.updateTextDecoration = updateSpanStyle_.updateTextDecoration;
2604 spanStyle.SetTextDecoration(updateSpanStyle_.updateTextDecoration.value());
2605 break;
2606 default:
2607 LOGW("Unsupported select operation for HandleSelectFontStyleWrapper");
2608 return;
2609 }
2610 }
2611 SetUpdateSpanStyle(updateSpanStyle);
2612 UpdateSpanStyle(start, end, spanStyle, imageStyle, false);
2613 }
2614
GetSelectSpansPositionInfo(int32_t & start,int32_t & end,SpanPositionInfo & startPositionSpanInfo,SpanPositionInfo & endPositionSpanInfo)2615 void RichEditorPattern::GetSelectSpansPositionInfo(
2616 int32_t& start, int32_t& end, SpanPositionInfo& startPositionSpanInfo, SpanPositionInfo& endPositionSpanInfo)
2617 {
2618 bool isText = false;
2619 auto host = GetContentHost();
2620 CHECK_NULL_VOID(host);
2621 std::find_if(spans_.begin(), spans_.end(), [&start, &end, &isText](const RefPtr<SpanItem>& spanItem) {
2622 if ((spanItem->rangeStart <= start) && (start < spanItem->position) && start < end) {
2623 if (spanItem->spanItemType == SpanItemType::NORMAL && spanItem->unicode == 0) {
2624 isText = true;
2625 return true;
2626 }
2627 start += spanItem->content.length();
2628 }
2629 return false;
2630 });
2631 CHECK_EQUAL_VOID(isText, false);
2632 std::find_if(spans_.rbegin(), spans_.rend(), [&end](const RefPtr<SpanItem>& spanItem) {
2633 if ((spanItem->rangeStart < end) && (end <= spanItem->position)) {
2634 if (spanItem->spanItemType == SpanItemType::NORMAL && spanItem->unicode == 0) {
2635 return true;
2636 }
2637 end = spanItem->rangeStart;
2638 }
2639 return false;
2640 });
2641 startPositionSpanInfo = GetSpanPositionInfo(start);
2642 startPositionSpanInfo.spanIndex_ =
2643 std::clamp(startPositionSpanInfo.spanIndex_, 0, static_cast<int32_t>(host->GetChildren().size()) - 1);
2644 if (end == GetTextContentLength()) {
2645 endPositionSpanInfo.spanIndex_ = spans_.size() - 1;
2646 auto spanIter = spans_.begin();
2647 endPositionSpanInfo.spanIndex_ =
2648 std::clamp(endPositionSpanInfo.spanIndex_, 0, static_cast<int32_t>(host->GetChildren().size()) - 1);
2649 std::advance(spanIter, endPositionSpanInfo.spanIndex_);
2650 auto contentLen = (*spanIter)->content.length();
2651 endPositionSpanInfo.spanStart_ = (*spanIter)->position - contentLen;
2652 endPositionSpanInfo.spanEnd_ = (*spanIter)->position;
2653 endPositionSpanInfo.spanOffset_ = contentLen;
2654 } else {
2655 endPositionSpanInfo = GetSpanPositionInfo(end);
2656 }
2657 if (endPositionSpanInfo.spanIndex_ == -1) {
2658 endPositionSpanInfo = startPositionSpanInfo;
2659 }
2660 }
2661
GetSpanNodeIter(int32_t index)2662 std::list<RefPtr<UINode>>::const_iterator RichEditorPattern::GetSpanNodeIter(int32_t index)
2663 {
2664 auto host = GetContentHost();
2665 CHECK_NULL_RETURN(host, {});
2666 auto spanNodeIter = host->GetChildren().begin();
2667 std::advance(spanNodeIter, index);
2668 return spanNodeIter;
2669 }
2670
GetSpanItemByIndex(int32_t index)2671 RefPtr<SpanItem> RichEditorPattern::GetSpanItemByIndex(int32_t index)
2672 {
2673 auto spanIter = spans_.begin();
2674 std::advance(spanIter, index);
2675 CHECK_NULL_RETURN(spanIter != spans_.end(), nullptr);
2676 return *spanIter;
2677 }
2678
GetSelectSpanSplit(SpanPositionInfo & startPositionSpanInfo,SpanPositionInfo & endPositionSpanInfo)2679 std::list<SpanPosition> RichEditorPattern::GetSelectSpanSplit(
2680 SpanPositionInfo& startPositionSpanInfo, SpanPositionInfo& endPositionSpanInfo)
2681 {
2682 std::list<SpanPosition> resultObjects;
2683 int32_t spanIndex = 0;
2684 auto itStart = GetSpanNodeIter(startPositionSpanInfo.spanIndex_);
2685 auto itEnd = GetSpanNodeIter(endPositionSpanInfo.spanIndex_);
2686 auto itEndNext = GetSpanNodeIter(endPositionSpanInfo.spanIndex_ + 1);
2687 for (auto itSelect = itStart; itSelect != itEndNext; itSelect++) {
2688 SpanPosition resultObject;
2689 auto spanNode = DynamicCast<SpanNode>(*itSelect);
2690 if (!spanNode || spanNode->GetTag() != V2::SPAN_ETS_TAG) {
2691 continue;
2692 }
2693 auto spanItem = spanNode->GetSpanItem();
2694 if (itSelect == itStart) {
2695 if (startPositionSpanInfo.spanOffset_ == 0) {
2696 resultObject.spanRange[RichEditorSpanRange::RANGESTART] = startPositionSpanInfo.spanStart_;
2697 } else {
2698 resultObject.spanRange[RichEditorSpanRange::RANGESTART] =
2699 startPositionSpanInfo.spanStart_ + startPositionSpanInfo.spanOffset_;
2700 }
2701 resultObject.spanRange[RichEditorSpanRange::RANGEEND] = startPositionSpanInfo.spanEnd_;
2702 resultObject.spanIndex = spanIndex;
2703 spanIndex++;
2704 resultObjects.emplace_back(resultObject);
2705 continue;
2706 }
2707 if (itSelect == itEnd) {
2708 resultObject.spanRange[RichEditorSpanRange::RANGESTART] = endPositionSpanInfo.spanStart_;
2709 if (endPositionSpanInfo.spanOffset_ == static_cast<int32_t>(spanItem->content.size())) {
2710 resultObject.spanRange[RichEditorSpanRange::RANGEEND] = endPositionSpanInfo.spanEnd_;
2711 } else {
2712 resultObject.spanRange[RichEditorSpanRange::RANGEEND] =
2713 endPositionSpanInfo.spanStart_ + endPositionSpanInfo.spanOffset_;
2714 }
2715 resultObject.spanIndex = spanIndex;
2716 spanIndex++;
2717 resultObjects.emplace_back(resultObject);
2718 continue;
2719 }
2720 resultObject.spanRange[RichEditorSpanRange::RANGESTART] =
2721 spanItem->position - spanItem->content.length();
2722 resultObject.spanRange[RichEditorSpanRange::RANGEEND] = spanItem->position;
2723 resultObject.spanIndex = spanIndex;
2724 spanIndex++;
2725 resultObjects.emplace_back(resultObject);
2726 }
2727 return resultObjects;
2728 }
2729
GetSelectSpanInfo(int32_t start,int32_t end)2730 std::list<SpanPosition> RichEditorPattern::GetSelectSpanInfo(int32_t start, int32_t end)
2731 {
2732 SpanPositionInfo startPositionSpanInfo(-1, -1, -1, -1);
2733 SpanPositionInfo endPositionSpanInfo(-1, -1, -1, -1);
2734 std::list<SpanPosition> resultObjects;
2735 int32_t spanIndex = 0;
2736 GetSelectSpansPositionInfo(start, end, startPositionSpanInfo, endPositionSpanInfo);
2737 CHECK_EQUAL_RETURN(startPositionSpanInfo.spanStart_, -1, resultObjects);
2738 if (startPositionSpanInfo.spanIndex_ == endPositionSpanInfo.spanIndex_) {
2739 SpanPosition resultObject;
2740 resultObject.spanRange[RichEditorSpanRange::RANGESTART] = start;
2741 resultObject.spanRange[RichEditorSpanRange::RANGEEND] = end;
2742 resultObject.spanIndex = spanIndex;
2743 resultObjects.emplace_back(resultObject);
2744 } else {
2745 resultObjects = GetSelectSpanSplit(startPositionSpanInfo, endPositionSpanInfo);
2746 }
2747 return resultObjects;
2748 }
2749
GetSpansInfoByRange(int32_t start,int32_t end)2750 SelectionInfo RichEditorPattern::GetSpansInfoByRange(int32_t start, int32_t end)
2751 {
2752 auto selectionInfo = GetSpansInfo(start, end , GetSpansMethod::GETSPANS);
2753 auto& resultObjects = selectionInfo.GetSelectionRef().resultObjects;
2754 for (auto& resObj : resultObjects) {
2755 CHECK_NULL_CONTINUE(resObj.type == SelectSpanType::TYPESPAN);
2756 auto uiNode = GetChildByIndex(resObj.spanPosition.spanIndex);
2757 auto spanNode = DynamicCast<SpanNode>(uiNode);
2758 CHECK_NULL_CONTINUE(spanNode);
2759 auto fontFamily = spanNode->GetFontFamily();
2760 IF_TRUE(!fontFamily, resObj.textStyle.fontFamily.clear());
2761 }
2762 return selectionInfo;
2763 }
2764
UpdateSelectSpanStyle(int32_t start,int32_t end,KeyCode code)2765 void RichEditorPattern::UpdateSelectSpanStyle(int32_t start, int32_t end, KeyCode code)
2766 {
2767 std::list<SpanPosition> resultObjects;
2768 resultObjects = GetSelectSpanInfo(start, end);
2769 bool isFirstText = true;
2770 UndoRedoRecord styledRecord;
2771 undoManager_->UpdateRecordBeforeChange(start, end - start, styledRecord, true);
2772 for (auto& spanStyleIter : resultObjects) {
2773 SetSelectSpanStyle(spanStyleIter.spanRange[RichEditorSpanRange::RANGESTART],
2774 spanStyleIter.spanRange[RichEditorSpanRange::RANGEEND], code, isFirstText);
2775 isFirstText = false;
2776 }
2777 undoManager_->RecordOperationAfterChange(start, end - start, styledRecord);
2778 }
2779
CheckStyledStringRangeValid(int32_t start,int32_t length)2780 bool RichEditorPattern::CheckStyledStringRangeValid(int32_t start, int32_t length)
2781 {
2782 if (!styledString_ || !styledString_->CheckRange(start, length)) {
2783 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "range:[%{public}d-%{public}d] is invalid or styledString is null",
2784 start, start + length);
2785 return false;
2786 }
2787 return true;
2788 }
2789
UpdateSelectStyledStringStyle(int32_t start,int32_t end,KeyCode code)2790 void RichEditorPattern::UpdateSelectStyledStringStyle(int32_t start, int32_t end, KeyCode code)
2791 {
2792 auto length = end - start;
2793 CHECK_NULL_VOID(CheckStyledStringRangeValid(start, length));
2794 UndoRedoRecord record;
2795 undoManager_->UpdateRecordBeforeChange(start, length, record, true);
2796 Font updateFont;
2797 bool isFirstSpanStylePresent;
2798 switch (code) {
2799 case KeyCode::KEY_B: {
2800 auto firstFontSpan = DynamicCast<FontSpan>(styledString_->GetSpan(start, 1, SpanType::Font));
2801 isFirstSpanStylePresent = firstFontSpan && firstFontSpan->GetFont().fontWeight == FontWeight::BOLD;
2802 updateFont.fontWeight = isFirstSpanStylePresent ? FontWeight::NORMAL : FontWeight::BOLD;
2803 UpdateStyledStringFontStyle(start, end, updateFont);
2804 record.AddUpdateSpanType(SpanType::Font);
2805 break;
2806 }
2807 case KeyCode::KEY_I: {
2808 auto firstFontSpan = DynamicCast<FontSpan>(styledString_->GetSpan(start, 1, SpanType::Font));
2809 isFirstSpanStylePresent = firstFontSpan && firstFontSpan->GetFont().fontStyle == OHOS::Ace::FontStyle::ITALIC;
2810 updateFont.fontStyle = isFirstSpanStylePresent ? OHOS::Ace::FontStyle::NORMAL : OHOS::Ace::FontStyle::ITALIC;
2811 UpdateStyledStringFontStyle(start, end, updateFont);
2812 record.AddUpdateSpanType(SpanType::Font);
2813 break;
2814 }
2815 case KeyCode::KEY_U: {
2816 auto firstDecorationSpan = DynamicCast<DecorationSpan>(styledString_->GetSpan(start, 1, SpanType::Decoration));
2817 isFirstSpanStylePresent =
2818 firstDecorationSpan &&
2819 V2::HasTextDecoration(firstDecorationSpan->GetTextDecorationTypes(), TextDecoration::UNDERLINE);
2820 auto updateDecorationType = isFirstSpanStylePresent ? TextDecoration::NONE : TextDecoration::UNDERLINE;
2821 UpdateStyledStringDecorationType(start, end, updateDecorationType);
2822 record.AddUpdateSpanType(SpanType::Decoration);
2823 break;
2824 }
2825 default:
2826 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "Unsupported key code for UpdateSelectStyledStringStyle");
2827 return;
2828 }
2829 undoManager_->RecordOperationAfterChange(start, length, record);
2830 }
2831
2832 template<typename T>
UpdateSpansStyleInRange(int32_t start,int32_t end,const RefPtr<SpanBase> & baseSpan,std::function<RefPtr<T> (const RefPtr<T> &)> && updateSpanFunc)2833 void RichEditorPattern::UpdateSpansStyleInRange(int32_t start, int32_t end, const RefPtr<SpanBase>& baseSpan,
2834 std::function<RefPtr<T>(const RefPtr<T>&)>&& updateSpanFunc)
2835 {
2836 auto length = end - start;
2837 CHECK_NULL_VOID(CheckStyledStringRangeValid(start, length));
2838 CHECK_NULL_VOID(baseSpan);
2839 auto spanType = baseSpan->GetSpanType();
2840 std::vector<RefPtr<SpanBase>> updateSpans;
2841 updateSpans.push_back(baseSpan);
2842 auto originalSpans = styledString_->GetSpans(start, length, spanType);
2843 for (auto& originalSpan : originalSpans) {
2844 auto originalTypedSpan = DynamicCast<T>(originalSpan);
2845 CHECK_NULL_CONTINUE(originalTypedSpan)
2846 updateSpans.push_back(updateSpanFunc(originalTypedSpan));
2847 }
2848 paragraphCache_.Clear();
2849 styledString_->BindWithSpans(updateSpans);
2850 styledString_->NotifySpanWatcher();
2851 }
2852
UpdateStyledStringFontStyle(int32_t start,int32_t end,const Font & font)2853 void RichEditorPattern::UpdateStyledStringFontStyle(int32_t start, int32_t end, const Font& font)
2854 {
2855 auto fontSpan = AceType::MakeRefPtr<FontSpan>(font, start, end);
2856 auto updateFontSpanFunc = [&font](const RefPtr<FontSpan>& oriFontSpan) -> RefPtr<FontSpan> {
2857 CHECK_NULL_RETURN(oriFontSpan, nullptr);
2858 auto fontStyle = oriFontSpan->GetFont();
2859 if (font.fontStyle.has_value()) {
2860 fontStyle.fontStyle = font.fontStyle.value();
2861 }
2862 if (font.fontWeight.has_value()) {
2863 fontStyle.fontWeight = font.fontWeight.value();
2864 }
2865 return AceType::MakeRefPtr<FontSpan>(fontStyle, oriFontSpan->GetStartIndex(), oriFontSpan->GetEndIndex());
2866 };
2867 UpdateSpansStyleInRange<FontSpan>(start, end, fontSpan, updateFontSpanFunc);
2868 }
2869
UpdateStyledStringDecorationType(int32_t start,int32_t end,const TextDecoration & type)2870 void RichEditorPattern::UpdateStyledStringDecorationType(int32_t start, int32_t end, const TextDecoration& type)
2871 {
2872 std::optional<Color> colorOption;
2873 std::optional<TextDecorationStyle> styleOption;
2874 std::optional<TextDecorationOptions> options;
2875 std::optional<float> lineThicknessScale;
2876 auto decorationSpan = AceType::MakeRefPtr<DecorationSpan>(
2877 std::vector<TextDecoration>({ type }), colorOption, styleOption, lineThicknessScale, options, start, end);
2878 auto updateDecorationSpanFunc = [&type](const RefPtr<DecorationSpan>& oriDecorationSpan) -> RefPtr<DecorationSpan> {
2879 CHECK_NULL_RETURN(oriDecorationSpan, nullptr);
2880 if (type == TextDecoration::NONE) {
2881 oriDecorationSpan->RemoveTextDecorationType(TextDecoration::UNDERLINE);
2882 } else {
2883 auto lastTypes = oriDecorationSpan->GetTextDecorationTypes();
2884 oriDecorationSpan->SetTextDecorationTypes(std::vector<TextDecoration> { type });
2885 for (auto lastType : lastTypes) {
2886 oriDecorationSpan->AddTextDecorationType(lastType);
2887 }
2888 }
2889 return oriDecorationSpan;
2890 };
2891 UpdateSpansStyleInRange<DecorationSpan>(start, end, decorationSpan, updateDecorationSpanFunc);
2892 }
2893
CloseSystemMenu()2894 void RichEditorPattern::CloseSystemMenu()
2895 {
2896 if (!SelectOverlayIsOn()) {
2897 return;
2898 }
2899 auto selectOverlayInfo = selectOverlay_->GetSelectOverlayInfo();
2900 if (selectOverlayInfo && !selectOverlayInfo->menuInfo.menuBuilder) {
2901 CloseSelectOverlay();
2902 }
2903 }
2904
SetAccessibilityAction()2905 void RichEditorPattern::SetAccessibilityAction()
2906 {
2907 auto host = GetHost();
2908 CHECK_NULL_VOID(host);
2909 auto property = host->GetAccessibilityProperty<AccessibilityProperty>();
2910 CHECK_NULL_VOID(property);
2911 property->SetActionSetSelection([weakPtr = WeakClaim(this)](int32_t start, int32_t end, bool isForward) {
2912 TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
2913 "Accessibility SetSelection, range=[%{public}d,%{public}d], isForward=%{public}d", start, end, isForward);
2914 const auto& pattern = weakPtr.Upgrade();
2915 CHECK_NULL_VOID(pattern);
2916 pattern->SetSelection(start, end, std::nullopt, isForward);
2917 });
2918
2919 property->SetActionSetIndex([weakPtr = WeakClaim(this)](int32_t index) {
2920 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Accessibility SetCaretOffset, index=%{public}d", index);
2921 const auto& pattern = weakPtr.Upgrade();
2922 CHECK_NULL_VOID(pattern);
2923 pattern->SetCaretOffset(index);
2924 });
2925
2926 property->SetActionGetIndex([weakPtr = WeakClaim(this)]() -> int32_t {
2927 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Accessibility GetCaretPosition");
2928 const auto& pattern = weakPtr.Upgrade();
2929 CHECK_NULL_RETURN(pattern, -1);
2930 return pattern->GetCaretPosition();
2931 });
2932 SetAccessibilityEditAction();
2933 }
2934
SetAccessibilityEditAction()2935 void RichEditorPattern::SetAccessibilityEditAction()
2936 {
2937 auto host = GetHost();
2938 CHECK_NULL_VOID(host);
2939 auto property = host->GetAccessibilityProperty<AccessibilityProperty>();
2940 CHECK_NULL_VOID(property);
2941 property->SetActionSetText([weakPtr = WeakClaim(this)](const std::string& value) {
2942 std::u16string u16Value = UtfUtils::Str8ToStr16(value);
2943 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "EditAction setText, length: %{public}d",
2944 static_cast<int32_t>(u16Value.length()));
2945 const auto& pattern = weakPtr.Upgrade();
2946 CHECK_NULL_VOID(pattern);
2947 pattern->InsertValueByOperationType(u16Value, OperationType::ACCESSIBILITY);
2948 });
2949
2950 property->SetActionCopy([weakPtr = WeakClaim(this)]() {
2951 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "EditAction copy");
2952 const auto& pattern = weakPtr.Upgrade();
2953 CHECK_NULL_VOID(pattern);
2954 pattern->HandleOnCopy();
2955 pattern->CloseSelectionMenu();
2956 });
2957
2958 property->SetActionCut([weakPtr = WeakClaim(this)]() {
2959 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "EditAction cut");
2960 const auto& pattern = weakPtr.Upgrade();
2961 CHECK_NULL_VOID(pattern);
2962 pattern->suppressAccessibilityEvent_ = false;
2963 pattern->HandleOnCut();
2964 });
2965
2966 property->SetActionPaste([weakPtr = WeakClaim(this)]() {
2967 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "EditAction paste");
2968 const auto& pattern = weakPtr.Upgrade();
2969 CHECK_NULL_VOID(pattern);
2970 pattern->suppressAccessibilityEvent_ = false;
2971 pattern->HandleOnPaste();
2972 pattern->CloseSelectionMenu();
2973 });
2974
2975 property->SetActionClearSelection([weakPtr = WeakClaim(this)]() {
2976 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "EditAction clearSelection");
2977 const auto& pattern = weakPtr.Upgrade();
2978 CHECK_NULL_VOID(pattern);
2979 pattern->CloseSelectionMenu();
2980 pattern->ResetSelection();
2981 pattern->StartTwinkling();
2982 });
2983 }
2984
IsAccessibilityClick()2985 bool RichEditorPattern::IsAccessibilityClick()
2986 {
2987 auto host = GetHost();
2988 CHECK_NULL_RETURN(host, false);
2989 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
2990 CHECK_NULL_RETURN(accessibilityProperty, false);
2991 return accessibilityProperty->GetAccessibilityFocusState();
2992 }
2993
GetParagraphInfo(int32_t start,int32_t end)2994 std::vector<ParagraphInfo> RichEditorPattern::GetParagraphInfo(int32_t start, int32_t end)
2995 {
2996 std::vector<ParagraphInfo> res;
2997 auto spanNodes = GetParagraphNodes(start, end);
2998 CHECK_NULL_RETURN(!spanNodes.empty(), {});
2999
3000 auto&& firstSpan = spanNodes.front()->GetSpanItem();
3001 auto paraStart = firstSpan->position - static_cast<int32_t>(firstSpan->content.length());
3002
3003 for (auto it = spanNodes.begin(); it != spanNodes.end(); ++it) {
3004 if (it == std::prev(spanNodes.end()) || (*it)->GetSpanItem()->content.back() == u'\n') {
3005 ParagraphInfo info;
3006 auto lm = (*it)->GetLeadingMarginValue({});
3007 std::optional<double> spacingOpt;
3008 if (auto spacing = (*it)->GetParagraphSpacing(); spacing.has_value()) {
3009 spacingOpt = spacing.value().ConvertToFp();
3010 }
3011 std::optional<int32_t> textVerticalAlignOpt;
3012 if (auto textVerticalAlign = (*it)->GetTextVerticalAlign(); textVerticalAlign.has_value()) {
3013 textVerticalAlignOpt = static_cast<int32_t>(textVerticalAlign.value());
3014 }
3015 res.emplace_back(ParagraphInfo {
3016 .leadingMarginPixmap = lm.pixmap,
3017 .leadingMarginSize = { lm.size.Width().ToString(),
3018 lm.size.Height().ToString() },
3019 .textAlign = static_cast<int32_t>((*it)->GetTextAlignValue(TextAlign::START)),
3020 .wordBreak = static_cast<int32_t>((*it)->GetWordBreakValue(WordBreak::BREAK_WORD)),
3021 .lineBreakStrategy = static_cast<int32_t>((*it)->GetLineBreakStrategyValue(LineBreakStrategy::GREEDY)),
3022 .paragraphSpacing = spacingOpt,
3023 .textVerticalAlign = textVerticalAlignOpt,
3024 .range = { paraStart, (*it)->GetSpanItem()->position },
3025 });
3026 paraStart = (*it)->GetSpanItem()->position;
3027 }
3028 }
3029
3030 return res;
3031 }
3032
GetParagraphLength(const std::list<RefPtr<UINode>> & spans) const3033 int32_t RichEditorPattern::GetParagraphLength(const std::list<RefPtr<UINode>>& spans) const
3034 {
3035 if (spans.empty()) {
3036 return 0;
3037 }
3038 int32_t imageSpanCnt = 0;
3039 for (auto it = spans.rbegin(); it != spans.rend(); ++it) {
3040 auto spanNode = DynamicCast<SpanNode>(*it);
3041 if (spanNode) {
3042 return spanNode->GetSpanItem()->position + imageSpanCnt;
3043 }
3044 ++imageSpanCnt;
3045 }
3046 return imageSpanCnt;
3047 }
3048
GetParagraphNodes(int32_t start,int32_t end) const3049 std::vector<RefPtr<SpanNode>> RichEditorPattern::GetParagraphNodes(int32_t start, int32_t end) const
3050 {
3051 CHECK_NULL_RETURN(start != end, {});
3052 auto host = GetContentHost();
3053 CHECK_NULL_RETURN(host, {});
3054 CHECK_NULL_RETURN(!host->GetChildren().empty(), {});
3055
3056 const auto& spans = host->GetChildren();
3057 int32_t length = GetParagraphLength(spans);
3058 std::vector<RefPtr<SpanNode>> res;
3059
3060 if (start >= length) {
3061 return res;
3062 }
3063
3064 auto headIt = spans.begin();
3065 auto flagNode = headIt;
3066 bool isEnd = false;
3067 int32_t spanEnd = -1;
3068 while (flagNode != spans.end()) {
3069 auto spanNode = DynamicCast<SpanNode>(*flagNode);
3070 if (spanNode) {
3071 auto&& info = spanNode->GetSpanItem();
3072 spanEnd = info->position;
3073 isEnd = info->content.back() == u'\n';
3074 } else {
3075 ++spanEnd;
3076 isEnd = false;
3077 }
3078 flagNode++;
3079 if (spanEnd > start) {
3080 break;
3081 }
3082 if (isEnd) {
3083 headIt = flagNode;
3084 }
3085 }
3086 while (headIt != flagNode) {
3087 auto spanNode = DynamicCast<SpanNode>(*headIt);
3088 if (spanNode) {
3089 res.emplace_back(spanNode);
3090 }
3091 headIt++;
3092 }
3093 while (flagNode != spans.end() && (spanEnd < end || !isEnd)) {
3094 auto spanNode = DynamicCast<SpanNode>(*flagNode);
3095 if (spanNode) {
3096 res.emplace_back(spanNode);
3097 auto&& info = spanNode->GetSpanItem();
3098 spanEnd = info->position;
3099 isEnd = info->content.back() == u'\n';
3100 } else {
3101 ++spanEnd;
3102 isEnd = false;
3103 }
3104 flagNode++;
3105 }
3106
3107 return res;
3108 }
3109
3110 // Calculates the range of span nodes(returns (-1, -1) if empty or invalid)
CalcSpansRange(const std::vector<RefPtr<SpanNode>> & spanNodes) const3111 std::pair<int32_t, int32_t> RichEditorPattern::CalcSpansRange(const std::vector<RefPtr<SpanNode>>& spanNodes) const
3112 {
3113 CHECK_NULL_RETURN(!spanNodes.empty(), std::make_pair(-1, -1));
3114 auto& firstSpanNode = spanNodes.front();
3115 auto& lastSpanNode = spanNodes.back();
3116 CHECK_NULL_RETURN(firstSpanNode && lastSpanNode, std::make_pair(-1, -1));
3117 auto& firstSpan = firstSpanNode->GetSpanItem();
3118 auto& lastSpan = lastSpanNode->GetSpanItem();
3119 CHECK_NULL_RETURN(firstSpan && lastSpan, std::make_pair(-1, -1));
3120 return std::make_pair(firstSpan->rangeStart, lastSpan->position);
3121 }
3122
UpdateParagraphStyle(int32_t start,int32_t end,const struct UpdateParagraphStyle & style)3123 void RichEditorPattern::UpdateParagraphStyle(int32_t start, int32_t end, const struct UpdateParagraphStyle& style)
3124 {
3125 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "updateParagraphStyle, range=[%{public}d,%{public}d]", start, end);
3126 UndoRedoRecord styledRecord;
3127 auto spanNodes = GetParagraphNodes(start, end);
3128 auto spanRange = CalcSpansRange(spanNodes);
3129 auto [changeStart, changeEnd] = (spanRange == std::make_pair(-1,-1)) ? std::make_pair(start, end) : spanRange;
3130 undoManager_->UpdateRecordBeforeChange(changeStart, changeEnd - changeStart, styledRecord, true);
3131 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "spanNode cnt=%{public}d, style=%{public}s",
3132 static_cast<int32_t>(spanNodes.size()), style.ToString().c_str());
3133 for (const auto& spanNode : spanNodes) {
3134 UpdateParagraphStyle(spanNode, style);
3135 }
3136 undoManager_->RecordOperationAfterChange(changeStart, changeEnd - changeStart, styledRecord);
3137 }
3138
UpdateParagraphStyle(RefPtr<SpanNode> spanNode,const struct UpdateParagraphStyle & style)3139 void RichEditorPattern::UpdateParagraphStyle(RefPtr<SpanNode> spanNode, const struct UpdateParagraphStyle& style)
3140 {
3141 CHECK_NULL_VOID(spanNode);
3142 spanNode->UpdateTextAlign(style.textAlign.value_or(TextAlign::START));
3143 spanNode->UpdateWordBreak(style.wordBreak.value_or(WordBreak::BREAK_WORD));
3144 spanNode->UpdateLineBreakStrategy(style.lineBreakStrategy.value_or(LineBreakStrategy::GREEDY));
3145 spanNode->UpdateTextVerticalAlign(style.textVerticalAlign.value_or(TextVerticalAlign::BASELINE));
3146 auto paragraphSpacing = spanNode->GetParagraphSpacing();
3147 if (style.paragraphSpacing.has_value()) {
3148 spanNode->UpdateParagraphSpacing(style.paragraphSpacing.value());
3149 } else if (!NearEqual(paragraphSpacing->Value(), 0.0f) || paragraphSpacing->Unit() != DimensionUnit::PX) {
3150 spanNode->ResetParagraphSpacing();
3151 }
3152 auto leadingMarginValue = spanNode->GetLeadingMarginValue({});
3153 if (style.leadingMargin.has_value() && !leadingMarginValue.CheckLeadingMargin(style.leadingMargin.value())) {
3154 spanNode->GetSpanItem()->leadingMargin = *style.leadingMargin;
3155 spanNode->UpdateLeadingMargin(*style.leadingMargin);
3156 }
3157 IF_PRESENT(GetHost(), MarkDirtyNode(PROPERTY_UPDATE_MEASURE));
3158 }
3159
ScheduleCaretTwinkling()3160 void RichEditorPattern::ScheduleCaretTwinkling()
3161 {
3162 ContainerScope scope(richEditorInstanceId_);
3163 auto host = GetHost();
3164 CHECK_NULL_VOID(host);
3165 auto context = host->GetContext();
3166 CHECK_NULL_VOID(context);
3167
3168 if (!context->GetTaskExecutor()) {
3169 return;
3170 }
3171
3172 if (isCursorAlwaysDisplayed_) {
3173 return;
3174 }
3175
3176 auto weak = WeakClaim(this);
3177 caretTwinklingTask_.Reset([weak, instanceId = richEditorInstanceId_] {
3178 ContainerScope scope(instanceId);
3179 auto client = weak.Upgrade();
3180 CHECK_NULL_VOID(client);
3181 client->OnCaretTwinkling();
3182 });
3183 auto taskExecutor = context->GetTaskExecutor();
3184 CHECK_NULL_VOID(taskExecutor);
3185 taskExecutor->PostDelayedTask(caretTwinklingTask_, TaskExecutor::TaskType::UI, twinklingInterval_,
3186 "ArkUIRichEditorScheduleCaretTwinkling");
3187 }
3188
StartTwinkling()3189 void RichEditorPattern::StartTwinkling()
3190 {
3191 caretTwinklingTask_.Cancel();
3192 caretVisible_ = true;
3193 auto tmpHost = GetHost();
3194 CHECK_NULL_VOID(tmpHost);
3195 tmpHost->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3196 ScheduleCaretTwinkling();
3197 // Fire on selecion change when caret invisible -> visible
3198 if (!caretTwinkling_) {
3199 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "StartTwinkling");
3200 caretTwinkling_ = true;
3201 FireOnSelectionChange(caretPosition_, caretPosition_);
3202 }
3203 }
3204
ShowCaretWithoutTwinkling()3205 void RichEditorPattern::ShowCaretWithoutTwinkling()
3206 {
3207 isCursorAlwaysDisplayed_ = true;
3208 StartTwinkling();
3209 }
3210
OnCaretTwinkling()3211 void RichEditorPattern::OnCaretTwinkling()
3212 {
3213 caretTwinklingTask_.Cancel();
3214 caretVisible_ = !caretVisible_;
3215 GetHost()->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3216 ScheduleCaretTwinkling();
3217 }
3218
StopTwinkling()3219 void RichEditorPattern::StopTwinkling()
3220 {
3221 if (caretTwinkling_) {
3222 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "StopTwinkling");
3223 }
3224 caretTwinkling_ = false;
3225 isCursorAlwaysDisplayed_ = false;
3226 caretTwinklingTask_.Cancel();
3227 if (caretVisible_) {
3228 caretVisible_ = false;
3229 GetHost()->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3230 }
3231 }
3232
HandleClickEvent(GestureEvent & info)3233 void RichEditorPattern::HandleClickEvent(GestureEvent& info)
3234 {
3235 CreateMultipleClickRecognizer();
3236 ResetAISelected(AIResetSelectionReason::CLICK);
3237 if (selectOverlay_->GetIsHandleMoving() || isMouseSelect_) {
3238 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "click rejected, isHandleMoving=%{public}d, isMouseSelect=%{public}d",
3239 selectOverlay_->GetIsHandleMoving(), isMouseSelect_);
3240 return;
3241 }
3242 auto focusHub = GetFocusHub();
3243 CHECK_NULL_VOID(focusHub);
3244 if (!focusHub->IsFocusable()) {
3245 return;
3246 }
3247
3248 if (!HasFocus() && !focusHub->IsFocusOnTouch().value_or(true)) {
3249 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleClickEvent fail when IsFocusOnTouch false");
3250 CloseSelectOverlay();
3251 StopTwinkling();
3252 return;
3253 }
3254
3255 selectionMenuOffsetClick_ = OffsetF(
3256 static_cast<float>(info.GetGlobalLocation().GetX()), static_cast<float>(info.GetGlobalLocation().GetY()));
3257 if (dataDetectorAdapter_->hasClickedAISpan_) {
3258 dataDetectorAdapter_->hasClickedAISpan_ = false;
3259 }
3260 multipleClickRecognizer_->Start(info);
3261 if (multipleClickRecognizer_->IsTripleClick()) {
3262 HandleTripleClickEvent(info);
3263 } else if (multipleClickRecognizer_->IsDoubleClick()) {
3264 HandleDoubleClickEvent(info);
3265 } else {
3266 HandleSingleClickEvent(info);
3267 NotifyCaretChange();
3268 }
3269 }
3270
HandleClickSelection(const OHOS::Ace::GestureEvent & info)3271 bool RichEditorPattern::HandleClickSelection(const OHOS::Ace::GestureEvent& info)
3272 {
3273 CHECK_NULL_RETURN(!selectOverlay_->GetIsHandleMoving(), true);
3274 if (SelectOverlayIsOn()) {
3275 selectOverlay_->SwitchToOverlayMode();
3276 selectOverlay_->ToggleMenu();
3277 } else {
3278 CalculateHandleOffsetAndShowOverlay();
3279 selectOverlay_->ProcessOverlay({.animation = true, .requestCode = REQUEST_RECREATE});
3280 }
3281 return true;
3282 }
3283
IsClickEventOnlyForMenuToggle(const OHOS::Ace::GestureEvent & info)3284 bool RichEditorPattern::IsClickEventOnlyForMenuToggle(const OHOS::Ace::GestureEvent& info)
3285 {
3286 CHECK_NULL_RETURN(info.GetSourceDevice() != SourceType::MOUSE, false);
3287 // In preview state or single handle showing, clicking handle has toggled the menu display
3288 bool hasHandledMenuToggleByClick =
3289 selectOverlay_->IsClickAtHandle(info) && (!isEditing_ || selectOverlay_->IsSingleHandleShow());
3290 CHECK_NULL_RETURN(!hasHandledMenuToggleByClick, true);
3291 if (showSelect_ && BetweenSelection(info.GetGlobalLocation())) {
3292 return HandleClickSelection(info);
3293 }
3294 return false;
3295 }
3296
HandleSingleClickEvent(OHOS::Ace::GestureEvent & info)3297 void RichEditorPattern::HandleSingleClickEvent(OHOS::Ace::GestureEvent& info)
3298 {
3299 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "handleSingleClick");
3300 hasClicked_ = true;
3301 lastClickTimeStamp_ = info.GetTimeStamp();
3302 CHECK_NULL_VOID(!IsClickEventOnlyForMenuToggle(info));
3303 CHECK_NULL_VOID(!HandleUrlSpanClickEvent(info));
3304
3305 bool isMouseClick = info.GetSourceDevice() == SourceType::MOUSE;
3306 auto localOffset = info.GetLocalLocation();
3307 IF_TRUE(isMouseClick, AdjustMouseLocalOffset(localOffset));
3308
3309 Offset textOffset = ConvertTouchOffsetToTextOffset(localOffset);
3310 IF_TRUE(!isMousePressed_, HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY())));
3311
3312 if (dataDetectorAdapter_->hasClickedAISpan_ || dataDetectorAdapter_->pressedByLeftMouse_) {
3313 IF_TRUE(SelectOverlayIsOn(), selectOverlay_->HideMenu());
3314 return;
3315 }
3316
3317 HandleUserClickEvent(info);
3318 CHECK_NULL_VOID(!info.IsPreventDefault());
3319 bool isMouseClickWithShift = shiftFlag_ && isMouseClick && !IsPreviewTextInputting();
3320 if (textSelector_.IsValid() && !isMouseSelect_ && !isMouseClickWithShift) {
3321 CloseSelectOverlay();
3322 ResetSelection();
3323 }
3324 moveCaretState_.Reset();
3325 caretUpdateType_ = CaretUpdateType::PRESSED;
3326 CHECK_NULL_VOID(overlayMod_);
3327 RectF lastCaretRect = GetCaretRect();
3328 int32_t lastCaretPosition = caretPosition_;
3329 bool isCaretTwinkling = caretTwinkling_;
3330 auto position = paragraphs_.GetIndex(textOffset);
3331 AdjustCursorPosition(position);
3332 if (auto focusHub = GetFocusHub(); focusHub) {
3333 IF_TRUE(!isMouseClick || (blockPress_ && !isMouseClickWithShift), SetCaretPosition(position));
3334 IF_TRUE(isMouseClickWithShift, HandleShiftSelect(position));
3335 if (focusHub->IsCurrentFocus()) {
3336 HandleOnEditChanged(true);
3337 }
3338 RICH_EDITOR_SCOPE(requestFocusBySingleClick_);
3339 if (focusHub->RequestFocusImmediately()) {
3340 IF_TRUE(!shiftFlag_ || textSelector_.SelectNothing(), StartTwinkling());
3341 RequestKeyboard(false, true, true, info.GetSourceDevice());
3342 }
3343 }
3344 UseHostToUpdateTextFieldManager();
3345 CalcCaretInfoByClick(localOffset);
3346 CHECK_NULL_VOID(!isMouseClick);
3347 if (IsShowSingleHandleByClick(info, lastCaretPosition, lastCaretRect, isCaretTwinkling)) {
3348 CreateAndShowSingleHandle();
3349 }
3350 }
3351
GetTextOffset(const Offset & localLocation,const RectF & contentRect)3352 PointF RichEditorPattern::GetTextOffset(const Offset &localLocation, const RectF &contentRect)
3353 {
3354 PointF textOffset = {static_cast<float>(localLocation.GetX()) - GetTextRect().GetX(),
3355 static_cast<float>(localLocation.GetY()) - GetTextRect().GetY()};
3356 return textOffset;
3357 }
3358
GetSelectedRects(int32_t start,int32_t end)3359 std::vector<RectF> RichEditorPattern::GetSelectedRects(int32_t start, int32_t end)
3360 {
3361 return paragraphs_.GetRects(start, end);
3362 }
3363
ConvertTouchOffsetToTextOffset(const Offset & touchOffset)3364 Offset RichEditorPattern::ConvertTouchOffsetToTextOffset(const Offset& touchOffset)
3365 {
3366 richTextRect_.SetTop(richTextRect_.GetY() - std::min(baselineOffset_, 0.0f));
3367 richTextRect_.SetHeight(richTextRect_.Height() - std::max(baselineOffset_, 0.0f));
3368 return touchOffset - Offset(richTextRect_.GetX(), richTextRect_.GetY());
3369 }
3370
IsShowSingleHandleByClick(const OHOS::Ace::GestureEvent & info,int32_t lastCaretPosition,const RectF & lastCaretRect,bool isCaretTwinkling)3371 bool RichEditorPattern::IsShowSingleHandleByClick(
3372 const OHOS::Ace::GestureEvent& info, int32_t lastCaretPosition, const RectF& lastCaretRect, bool isCaretTwinkling)
3373 {
3374 if (firstClickAfterWindowFocus_) {
3375 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "do not show single handle by first click after window focus");
3376 firstClickAfterWindowFocus_ = false;
3377 return false;
3378 }
3379 auto isAccessibilityClick = IsAccessibilityClick();
3380 if (!isCaretTwinkling || isAccessibilityClick) {
3381 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "isCaretTwinkling=%{public}d,isAccessibilityClick=%{public}d"
3382 ,isCaretTwinkling, isAccessibilityClick);
3383 return false;
3384 }
3385 auto offset = info.GetLocalLocation();
3386 Offset textOffset = ConvertTouchOffsetToTextOffset(offset);
3387 auto position = (GetTextContentLength() == 0) ? 0 : paragraphs_.GetIndex(textOffset);
3388 if (position != lastCaretPosition) {
3389 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "clickCaretPosition=%{public}d but lastCaretPosition=%{public}d",
3390 position, lastCaretPosition);
3391 return false;
3392 }
3393 auto paragraphEndPos = GetParagraphEndPosition(lastCaretPosition);
3394 if (lastCaretPosition == paragraphEndPos || IsTouchAtLineEnd(lastCaretPosition, textOffset)) {
3395 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "repeat click lineEnd or paragraphEndPos=%{public}d", paragraphEndPos);
3396 return true;
3397 }
3398 return RepeatClickCaret(offset, lastCaretRect);
3399 }
3400
RepeatClickCaret(const Offset & offset,const RectF & lastCaretRect)3401 bool RichEditorPattern::RepeatClickCaret(const Offset& offset, const RectF& lastCaretRect)
3402 {
3403 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "caretTwinkling=%{public}d offset=%{public}s lastCaretRect=%{public}s",
3404 caretTwinkling_, offset.ToString().c_str(), lastCaretRect.ToString().c_str());
3405 CHECK_NULL_RETURN(caretTwinkling_, false);
3406 auto lastCaretHeight = lastCaretRect.Height();
3407 auto handleHotZone = selectOverlay_->GetHandleHotZoneRadius();
3408 auto caretHotZoneRect =
3409 RectF(lastCaretRect.GetX() - handleHotZone, lastCaretRect.GetY(), handleHotZone * 2, lastCaretHeight);
3410 return caretHotZoneRect.IsInRegion(PointF(offset.GetX(), offset.GetY()));
3411 }
3412
CreateAndShowSingleHandle()3413 void RichEditorPattern::CreateAndShowSingleHandle()
3414 {
3415 if (IsPreviewTextInputting()) {
3416 return;
3417 }
3418 textResponseType_ = TextResponseType::LONG_PRESS;
3419 selectOverlay_->SetIsSingleHandle(true);
3420 textSelector_.Update(caretPosition_);
3421 CalculateHandleOffsetAndShowOverlay();
3422 UpdateSelectionType(GetSpansInfo(caretPosition_, caretPosition_, GetSpansMethod::ONSELECT));
3423 selectOverlay_->ProcessOverlay({ .animation = true });
3424 }
3425
MoveCaretAndStartFocus(const Offset & textOffset)3426 void RichEditorPattern::MoveCaretAndStartFocus(const Offset& textOffset)
3427 {
3428 auto position = paragraphs_.GetIndex(textOffset);
3429 AdjustCursorPosition(position);
3430
3431 auto focusHub = GetFocusHub();
3432 if (focusHub) {
3433 SetCaretPosition(position);
3434 if (focusHub->RequestFocusImmediately()) {
3435 IF_TRUE(!shiftFlag_ || textSelector_.SelectNothing(), StartTwinkling());
3436 if (overlayMod_) {
3437 RequestKeyboard(false, true, true);
3438 }
3439 HandleOnEditChanged(true);
3440 }
3441 }
3442 UseHostToUpdateTextFieldManager();
3443 }
3444
HandleDoubleClickEvent(OHOS::Ace::GestureEvent & info)3445 void RichEditorPattern::HandleDoubleClickEvent(OHOS::Ace::GestureEvent& info)
3446 {
3447 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleDoubleClickEvent");
3448 caretUpdateType_ = CaretUpdateType::DOUBLE_CLICK;
3449 HandleDoubleClickOrLongPress(info);
3450 caretUpdateType_ = CaretUpdateType::NONE;
3451 }
3452
HandleUserGestureEvent(GestureEvent & info,std::function<bool (RefPtr<SpanItem> item,GestureEvent & info)> && gestureFunc)3453 bool RichEditorPattern::HandleUserGestureEvent(
3454 GestureEvent& info, std::function<bool(RefPtr<SpanItem> item, GestureEvent& info)>&& gestureFunc)
3455 {
3456 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleUserGestureEvent");
3457 RectF textContentRect = contentRect_;
3458 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
3459 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
3460 if (!textContentRect.IsInRegion(PointF(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY())) ||
3461 spans_.empty()) {
3462 return false;
3463 }
3464 PointF textOffset = { info.GetLocalLocation().GetX() - GetTextRect().GetX(),
3465 info.GetLocalLocation().GetY() - GetTextRect().GetY() };
3466 int32_t start = 0;
3467 bool isParagraphHead = true;
3468 Offset paragraphOffset(0, 0);
3469 for (const auto& item : spans_) {
3470 if (!item) {
3471 continue;
3472 }
3473 std::vector<RectF> selectedRects = paragraphs_.GetRects(start, item->position);
3474 start = item->position;
3475 if (isParagraphHead && !selectedRects.empty()) {
3476 if (item->leadingMargin.has_value()) {
3477 auto addWidth = item->leadingMargin.value().size.Width();
3478 selectedRects[0].SetLeft(selectedRects[0].GetX() - addWidth.ConvertToPx());
3479 selectedRects[0].SetWidth(selectedRects[0].GetSize().Width() + addWidth.ConvertToPx());
3480 }
3481 paragraphOffset.SetX(selectedRects[0].GetOffset().GetX());
3482 paragraphOffset.SetY(selectedRects[0].GetOffset().GetY());
3483 isParagraphHead = false;
3484 }
3485 if (!isParagraphHead && item->content.back() == '\n') {
3486 isParagraphHead = true;
3487 }
3488 for (auto&& rect : selectedRects) {
3489 if (!rect.IsInRegion(textOffset)) {
3490 continue;
3491 }
3492 info = info.SetScreenLocation(
3493 Offset(textOffset.GetX() - paragraphOffset.GetX(), textOffset.GetY() - paragraphOffset.GetY()));
3494 info = info.SetGlobalDisplayLocation(
3495 Offset(textOffset.GetX() - paragraphOffset.GetX(), textOffset.GetY() - paragraphOffset.GetY()));
3496 return gestureFunc(item, info);
3497 }
3498 }
3499 return false;
3500 }
3501
ClickAISpan(const PointF & textOffset,const AISpan & aiSpan)3502 bool RichEditorPattern::ClickAISpan(const PointF& textOffset, const AISpan& aiSpan)
3503 {
3504 auto calculateHandleFunc = [weak = WeakClaim(this)]() {
3505 auto pattern = weak.Upgrade();
3506 CHECK_NULL_VOID(pattern);
3507 pattern->CalculateHandleOffsetAndShowOverlay();
3508 };
3509 auto showSelectOverlayFunc = GetAISelectTextFunc();
3510
3511 std::vector<RectF> aiRects = paragraphs_.GetRects(aiSpan.start, aiSpan.end);
3512 for (auto&& rect : aiRects) {
3513 if (rect.IsInRegion(textOffset)) {
3514 dataDetectorAdapter_->clickedAISpan_ = aiSpan;
3515 if (leftMousePress_) {
3516 dataDetectorAdapter_->pressedByLeftMouse_ = true;
3517 return true;
3518 }
3519 dataDetectorAdapter_->hasClickedAISpan_ = true;
3520 ShowAIEntityMenu(aiSpan, calculateHandleFunc, showSelectOverlayFunc);
3521 return true;
3522 }
3523 }
3524 return false;
3525 }
3526
CreateAIEntityMenu()3527 RefPtr<FrameNode> RichEditorPattern::CreateAIEntityMenu()
3528 {
3529 CHECK_NULL_RETURN(dataDetectorAdapter_ && IsAiSelected(), nullptr);
3530 auto aiSpan = dataDetectorAdapter_->aiSpanMap_.find(textSelector_.aiStart.value());
3531 if (aiSpan == dataDetectorAdapter_->aiSpanMap_.end()) {
3532 return nullptr;
3533 }
3534 auto host = GetHost();
3535 CHECK_NULL_RETURN(host, nullptr);
3536
3537 auto calculateHandleFunc = [weak = WeakClaim(this)]() {
3538 auto pattern = weak.Upgrade();
3539 CHECK_NULL_VOID(pattern);
3540 pattern->showSelect_ = true;
3541 pattern->CalculateHandleOffsetAndShowOverlay();
3542 };
3543 auto showSelectOverlayFunc = GetAISelectTextFunc();
3544
3545 SetOnClickMenu(aiSpan->second, calculateHandleFunc, showSelectOverlayFunc);
3546 auto [isShowCopy, isShowSelectText] = GetCopyAndSelectable();
3547 return dataDetectorAdapter_->CreateAIEntityMenu(aiSpan->second, host, { isShowCopy, isShowSelectText });
3548 }
3549
GetAISelectTextFunc()3550 std::function<void(const RectF& firstHandle, const RectF& secondHandle)> RichEditorPattern::GetAISelectTextFunc() {
3551 return [weak = WeakClaim(this)](const RectF& firstHandle, const RectF& secondHandle) {
3552 auto pattern = weak.Upgrade();
3553 CHECK_NULL_VOID(pattern);
3554 pattern->SetCaretPosition(pattern->textSelector_.destinationOffset);
3555 auto focusHub = pattern->GetFocusHub();
3556 CHECK_NULL_VOID(focusHub);
3557 focusHub->RequestFocusImmediately();
3558 IF_TRUE(!pattern->isEditing_, pattern->CloseKeyboard(true));
3559 pattern->ShowSelectOverlay(firstHandle, secondHandle);
3560 };
3561 }
3562
AdjustAIEntityRect(RectF & aiRect)3563 void RichEditorPattern::AdjustAIEntityRect(RectF& aiRect)
3564 {
3565 auto offset = GetPaintRectGlobalOffset(); // component offset relative to window
3566 aiRect -= offset; // aiRect offset relative to component
3567 aiRect = aiRect.IntersectRectT(contentRect_) + offset;
3568 }
3569
GetStartAndEnd(int32_t start,const RefPtr<SpanItem> & item)3570 std::pair<int32_t, int32_t> RichEditorPattern::GetStartAndEnd(int32_t start, const RefPtr<SpanItem>& item)
3571 {
3572 return isSpanStringMode_
3573 ? TextPattern::GetStartAndEnd(start, item)
3574 : std::make_pair(item->rangeStart, item->position);
3575 }
3576
HandleUrlSpanClickEvent(const GestureEvent & info)3577 bool RichEditorPattern::HandleUrlSpanClickEvent(const GestureEvent& info)
3578 {
3579 RectF textContentRect = contentRect_;
3580 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
3581 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
3582
3583 CheckClickedOnSpanOrText(textContentRect, info.GetLocalLocation());
3584 auto clickedSpanPosition = GetClickedSpanPosition();
3585 if (LessNotEqual(clickedSpanPosition, 0)) {
3586 return false;
3587 }
3588 auto iter = spans_.begin();
3589 std::advance(iter, clickedSpanPosition);
3590 RefPtr<SpanItem> span;
3591 if (iter == spans_.end()) {
3592 span = spans_.back();
3593 } else {
3594 span = *iter;
3595 }
3596 if (span && span->urlOnRelease) {
3597 span->urlOnRelease();
3598 return true;
3599 }
3600 return false;
3601 }
3602
HandleUserClickEvent(GestureEvent & info)3603 bool RichEditorPattern::HandleUserClickEvent(GestureEvent& info)
3604 {
3605 auto clickFunc = [](RefPtr<SpanItem> item, GestureEvent& info) -> bool {
3606 if (item && item->onClick) {
3607 item->onClick(info);
3608 return true;
3609 }
3610 return false;
3611 };
3612 return HandleUserGestureEvent(info, std::move(clickFunc));
3613 }
3614
CalcCaretInfoByClick(const Offset & touchOffset)3615 void RichEditorPattern::CalcCaretInfoByClick(const Offset& touchOffset)
3616 {
3617 auto textRect = GetTextRect();
3618 textRect.SetTop(textRect.GetY() - std::min(baselineOffset_, 0.0f));
3619 textRect.SetHeight(textRect.Height() - std::max(baselineOffset_, 0.0f));
3620 Offset textOffset = { touchOffset.GetX() - textRect.GetX(), touchOffset.GetY() - textRect.GetY() };
3621 auto [lastClickOffset, caretHeight] = CalcAndRecordLastClickCaretInfo(textOffset);
3622 CHECK_NULL_VOID(overlayMod_);
3623 DynamicCast<RichEditorOverlayModifier>(overlayMod_)->SetCaretOffsetAndHeight(lastClickOffset, caretHeight);
3624 MoveCaretToContentRect();
3625 }
3626
CalcAndRecordLastClickCaretInfo(const Offset & textOffset)3627 std::pair<OffsetF, float> RichEditorPattern::CalcAndRecordLastClickCaretInfo(const Offset& textOffset)
3628 {
3629 // get the caret position
3630 auto positionWithAffinity = paragraphs_.GetGlyphPositionAtCoordinate(textOffset);
3631 auto position = static_cast<int32_t>(positionWithAffinity.position_);
3632 // get the caret offset when click
3633 float caretHeight = 0.0f;
3634 auto lastClickOffset = paragraphs_.ComputeCursorInfoByClick(position, caretHeight,
3635 OffsetF(static_cast<float>(textOffset.GetX()), static_cast<float>(textOffset.GetY())));
3636
3637 lastClickOffset += richTextRect_.GetOffset();
3638 if (isShowPlaceholder_) {
3639 auto caretOffset = CalculateEmptyValueCaretOffset();
3640 lastClickOffset = caretOffset;
3641 }
3642 SetLastClickOffset(lastClickOffset);
3643 caretAffinityPolicy_ = (positionWithAffinity.affinity_ == TextAffinity::UPSTREAM)
3644 ? CaretAffinityPolicy::UPSTREAM_FIRST
3645 : CaretAffinityPolicy::DOWNSTREAM_FIRST;
3646 return std::make_pair(lastClickOffset, caretHeight);
3647 }
3648
InitClickEvent(const RefPtr<GestureEventHub> & gestureHub)3649 void RichEditorPattern::InitClickEvent(const RefPtr<GestureEventHub>& gestureHub)
3650 {
3651 CHECK_NULL_VOID(!clickEventInitialized_);
3652 CreateMultipleClickRecognizer();
3653 auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
3654 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "click callback, sourceType=%{public}d", info.GetSourceDevice());
3655 auto pattern = weak.Upgrade();
3656 CHECK_NULL_VOID(pattern);
3657 pattern->sourceType_ = info.GetSourceDevice();
3658 pattern->HandleClickEvent(info);
3659 };
3660 auto clickListener = MakeRefPtr<ClickEvent>(std::move(clickCallback));
3661 gestureHub->AddClickAfterEvent(clickListener);
3662 clickEventInitialized_ = true;
3663 }
3664
InitFocusEvent(const RefPtr<FocusHub> & focusHub)3665 void RichEditorPattern::InitFocusEvent(const RefPtr<FocusHub>& focusHub)
3666 {
3667 CHECK_NULL_VOID(!focusEventInitialized_);
3668 auto focusTask = [weak = WeakClaim(this)](FocusReason reason) {
3669 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "rich editor in focus");
3670 auto pattern = weak.Upgrade();
3671 CHECK_NULL_VOID(pattern);
3672 pattern->HandleFocusEvent(reason);
3673 };
3674 focusHub->SetOnFocusInternal(focusTask);
3675 auto blurTask = [weak = WeakClaim(this)]() {
3676 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "rich editor in blur");
3677 auto pattern = weak.Upgrade();
3678 CHECK_NULL_VOID(pattern);
3679 pattern->HandleBlurEvent();
3680 };
3681 focusHub->SetOnBlurInternal(blurTask);
3682 focusEventInitialized_ = true;
3683 auto keyTask = [weak = WeakClaim(this)](const KeyEvent& keyEvent) -> bool {
3684 auto pattern = weak.Upgrade();
3685 CHECK_NULL_RETURN(pattern, false);
3686 return pattern->OnKeyEvent(keyEvent);
3687 };
3688 focusHub->SetOnKeyEventInternal(std::move(keyTask));
3689 }
3690
GetBlurReason()3691 BlurReason RichEditorPattern::GetBlurReason()
3692 {
3693 auto host = GetHost();
3694 CHECK_NULL_RETURN(host, BlurReason::FOCUS_SWITCH);
3695 auto curFocusHub = host->GetFocusHub();
3696 CHECK_NULL_RETURN(curFocusHub, BlurReason::FOCUS_SWITCH);
3697 return curFocusHub->GetBlurReason();
3698 }
3699
HandleBlurEvent()3700 void RichEditorPattern::HandleBlurEvent()
3701 {
3702 auto reason = GetBlurReason();
3703 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleBlurEvent/%{public}d, blur reason=%{public}d", frameId_, reason);
3704 auto host = GetHost();
3705 ClearOnFocusTextField(RawPtr(host));
3706 host.Reset();
3707 CreateMultipleClickRecognizer();
3708 IF_PRESENT(multipleClickRecognizer_, Stop());
3709 CHECK_NULL_VOID(showSelect_ || !IsSelected());
3710 isLongPress_ = false;
3711 ResetTouchSelectState();
3712 shiftFlag_ = false;
3713 moveCaretState_.Reset();
3714 floatingCaretState_.Reset();
3715 firstClickResetTask_.Cancel();
3716 firstClickAfterWindowFocus_ = false;
3717 StopTwinkling();
3718 // The pattern handles blurevent, Need to close the softkeyboard first.
3719 if ((customKeyboardBuilder_ && isCustomKeyboardAttached_) || reason == BlurReason::FRAME_DESTROY) {
3720 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "RichEditor Blur, Close Keyboard.");
3721 CloseSelectOverlay();
3722 ResetSelection();
3723 CloseKeyboard(false);
3724 }
3725 #ifdef ANDROID_PLATFORM
3726 if (HasConnection()) {
3727 connection_->Close(GetInstanceId());
3728 connection_ = nullptr;
3729 }
3730 #endif
3731 if (magnifierController_) {
3732 magnifierController_->RemoveMagnifierFrameNode();
3733 }
3734 if (IsSelected() && reason == BlurReason::FOCUS_SWITCH) {
3735 CloseSelectOverlay();
3736 ResetSelection();
3737 } else if (IsSelected()) {
3738 #ifdef ANDROID_PLATFORM
3739 CloseSelectOverlay();
3740 ResetSelection();
3741 #else
3742 selectOverlay_->HideMenu(true);
3743 #endif
3744 } else {
3745 CloseSelectOverlay();
3746 }
3747 if (reason != BlurReason::WINDOW_BLUR) {
3748 lastSelectionRange_.reset();
3749 }
3750 HandleOnEditChanged(false);
3751 ReportComponentChangeEvent();
3752 }
3753
HandleFocusEvent(FocusReason focusReason)3754 void RichEditorPattern::HandleFocusEvent(FocusReason focusReason)
3755 {
3756 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleFocusEvent frameId:%{public}d reason:%{public}d", frameId_, focusReason);
3757 IF_TRUE(focusReason == FocusReason::WINDOW_FOCUS, ScheduleFirstClickResetAfterWindowFocus());
3758 blockKbInFloatingWindow_= false;
3759 UseHostToUpdateTextFieldManager();
3760 if (previewLongPress_ || isOnlyRequestFocus_) {
3761 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleFocusEvent, previewLongPress=%{public}d,"
3762 "OnlyRequestFocus=%{public}d", previewLongPress_, isOnlyRequestFocus_);
3763 isOnlyRequestFocus_ = false;
3764 return;
3765 }
3766 SetIsEnableSubWindowMenu();
3767 if (textSelector_.SelectNothing()) {
3768 StartTwinkling();
3769 }
3770 auto host = GetHost();
3771 if (host) {
3772 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
3773 }
3774
3775 bool clickAIMenu = dataDetectorAdapter_->hasClickedMenuOption_;
3776 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "longPress=%{public}d, clickAIMenu=%{public}d", isLongPress_, clickAIMenu);
3777 bool bindKeyboard = !isLongPress_ && !clickAIMenu;
3778 CHECK_NULL_VOID(bindKeyboard);
3779
3780 auto windowMode = GetWindowMode();
3781 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "requestKeyboard=%{public}d, windowMode=%{public}d, rightButton=%{public}d",
3782 needToRequestKeyboardOnFocus_, windowMode, usingMouseRightButton_);
3783
3784 bool needShowSoftKeyboard = needToRequestKeyboardOnFocus_;
3785 needShowSoftKeyboard &= !usingMouseRightButton_; // do not show kb when mouseRightClick
3786
3787 if (windowMode == WindowMode::WINDOW_MODE_FLOATING && focusReason == FocusReason::WINDOW_FOCUS) {
3788 blockKbInFloatingWindow_ = needShowSoftKeyboard;
3789 needShowSoftKeyboard = false;
3790 }
3791
3792 IF_TRUE(!requestFocusBySingleClick_, RequestKeyboard(false, true, needShowSoftKeyboard));
3793 HandleOnEditChanged(true);
3794 }
3795
OnFocusNodeChange(FocusReason focusReason)3796 void RichEditorPattern::OnFocusNodeChange(FocusReason focusReason)
3797 {
3798 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "OnFocusNodeChange/%{public}d, reason=%{public}d, blockKbInFloating=%{public}d",
3799 frameId_, focusReason, blockKbInFloatingWindow_);
3800 CHECK_NULL_VOID(blockKbInFloatingWindow_);
3801 blockKbInFloatingWindow_= false;
3802 CHECK_NULL_VOID(GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING);
3803 CHECK_NULL_VOID(focusReason != FocusReason::WINDOW_FOCUS);
3804 CHECK_NULL_VOID(HasFocus() && isEditing_);
3805 bool clickAIMenu = dataDetectorAdapter_->hasClickedMenuOption_;
3806 bool bindKeyboard = !isLongPress_ && !clickAIMenu;
3807 CHECK_NULL_VOID(bindKeyboard);
3808 CHECK_NULL_VOID(needToRequestKeyboardOnFocus_ && !usingMouseRightButton_);
3809
3810 RequestKeyboard(false, true, true);
3811 }
3812
GetWindowMode()3813 WindowMode RichEditorPattern::GetWindowMode()
3814 {
3815 auto pipelineContext = GetContext();
3816 CHECK_NULL_RETURN(pipelineContext, WindowMode::WINDOW_MODE_UNDEFINED);
3817 auto windowManager = pipelineContext->GetWindowManager();
3818 CHECK_NULL_RETURN(windowManager, WindowMode::WINDOW_MODE_UNDEFINED);
3819 return windowManager->GetWindowMode();
3820 }
3821
GetIsMidScene()3822 bool RichEditorPattern::GetIsMidScene()
3823 {
3824 auto context = GetContext();
3825 CHECK_NULL_RETURN(context, false);
3826 auto windowManager = context->GetWindowManager();
3827 CHECK_NULL_RETURN(windowManager, false);
3828 bool isMidScene = false;
3829 int32_t ret = windowManager->GetIsMidScene(isMidScene);
3830 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "GetIsMidScene ret=%{public}d", ret);
3831 return isMidScene;
3832 }
3833
UseHostToUpdateTextFieldManager()3834 void RichEditorPattern::UseHostToUpdateTextFieldManager()
3835 {
3836 auto host = GetHost();
3837 CHECK_NULL_VOID(host);
3838 auto context = host->GetContext();
3839 CHECK_NULL_VOID(context);
3840 auto globalOffset = host->GetPaintRectOffsetNG(false, true) - context->GetRootRect().GetOffset();
3841 UpdateTextFieldManager(Offset(globalOffset.GetX(), globalOffset.GetY()), frameRect_.Height());
3842 }
3843
CloseKeyboard(bool forceClose)3844 bool RichEditorPattern::CloseKeyboard(bool forceClose)
3845 {
3846 if (customKeyboardBuilder_ && isCustomKeyboardAttached_) {
3847 return CloseCustomKeyboard();
3848 }
3849 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "Request close soft keyboard.");
3850 #if defined(ENABLE_STANDARD_INPUT)
3851 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
3852 if (!imeAttached_ && !forceClose) {
3853 return false;
3854 }
3855 #endif
3856 auto inputMethod = MiscServices::InputMethodController::GetInstance();
3857 CHECK_NULL_RETURN(inputMethod, false);
3858 inputMethod->HideTextInput();
3859 inputMethod->Close();
3860 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
3861 imeAttached_ = false;
3862 #endif
3863 #else
3864 if (HasConnection()) {
3865 connection_->Close(GetInstanceId());
3866 connection_ = nullptr;
3867 }
3868 #endif
3869 return true;
3870 }
3871
HandleDraggableFlag(bool isTouchSelectArea)3872 void RichEditorPattern::HandleDraggableFlag(bool isTouchSelectArea)
3873 {
3874 if (copyOption_ != CopyOptions::None && (isTouchSelectArea || IsAiSelected())) {
3875 bool isContentDraggalbe = JudgeContentDraggable();
3876 if (isContentDraggalbe) {
3877 dragBoxes_ = GetTextBoxes();
3878 }
3879 SetIsTextDraggable(isContentDraggalbe || IsAiSelected());
3880 } else {
3881 SetIsTextDraggable(false);
3882 }
3883 }
3884
SetIsTextDraggable(bool isTextDraggable)3885 void RichEditorPattern::SetIsTextDraggable(bool isTextDraggable)
3886 {
3887 auto gestureHub = GetGestureEventHub();
3888 IF_PRESENT(gestureHub, SetIsTextDraggable(isTextDraggable));
3889 }
3890
JudgeContentDraggable()3891 bool RichEditorPattern::JudgeContentDraggable()
3892 {
3893 if (!IsSelected() || copyOption_ == CopyOptions::None) {
3894 return false ;
3895 }
3896 auto selectInfo = GetSpansInfo(textSelector_.GetTextStart(), textSelector_.GetTextEnd(), GetSpansMethod::ONSELECT);
3897 auto selResult = selectInfo.GetSelection().resultObjects;
3898 auto iter = std::find_if(selResult.begin(), selResult.end(), [](ResultObject& obj) { return obj.isDraggable; });
3899 return iter != selResult.end();
3900 }
3901
CalculateCaretOffsetAndHeight()3902 std::pair<OffsetF, float> RichEditorPattern::CalculateCaretOffsetAndHeight()
3903 {
3904 OffsetF caretOffset;
3905 float caretHeight = 0.0f;
3906 auto caretPosition = caretPosition_;
3907 float caretHeightUp = 0.0f;
3908 auto caretBoundaryRect = GetCaretBoundaryRect();
3909 OffsetF caretOffsetUp = CalcCursorOffsetByPosition(caretPosition, caretHeightUp, false, false);
3910 if (isShowPlaceholder_) {
3911 auto textAlign = GetTextAlignByDirection();
3912 IF_TRUE(textAlign == TextAlign::END, caretOffsetUp.SetX(caretBoundaryRect.Right()));
3913 return { caretOffsetUp, caretHeightUp };
3914 }
3915 float caretHeightDown = 0.0f;
3916 OffsetF caretOffsetDown = CalcCursorOffsetByPosition(caretPosition, caretHeightDown, true, false);
3917 bool isCaretPosInLineEnd = !NearEqual(caretOffsetDown.GetX(), caretOffsetUp.GetX(), 0.5f);
3918 bool isShowCaretDown = isCaretPosInLineEnd;
3919 if ((caretAffinityPolicy_ != CaretAffinityPolicy::DEFAULT) && isCaretPosInLineEnd) {
3920 // show caret by click
3921 isShowCaretDown = (caretAffinityPolicy_ == CaretAffinityPolicy::DOWNSTREAM_FIRST);
3922 }
3923 caretOffset = isShowCaretDown ? caretOffsetDown : caretOffsetUp;
3924 caretHeight = isShowCaretDown ? caretHeightDown : caretHeightUp;
3925 // Handle caret offset at the right boundary of the content rect
3926 if (GreatOrEqual(caretOffset.GetX(), caretBoundaryRect.Right())) {
3927 auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
3928 CHECK_NULL_RETURN(overlayModifier, std::make_pair(caretOffset, caretHeight));
3929 auto caretWidth = overlayModifier->GetCaretWidth();
3930 caretOffset.SetX(caretOffset.GetX() - caretWidth);
3931 }
3932 return std::make_pair(caretOffset, caretHeight);
3933 }
3934
CalculateEmptyValueCaretOffset()3935 OffsetF RichEditorPattern::CalculateEmptyValueCaretOffset()
3936 {
3937 OffsetF offset;
3938 offset.SetX(contentRect_.GetX());
3939 offset.SetY(contentRect_.GetY());
3940 auto textAlign = GetTextAlignByDirection();
3941 switch (textAlign) {
3942 case TextAlign::START:
3943 offset.SetX(contentRect_.GetX());
3944 break;
3945 case TextAlign::CENTER:
3946 offset.SetX(contentRect_.GetX() + contentRect_.Width() / 2.0f);
3947 break;
3948 case TextAlign::END: {
3949 auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
3950 auto caretWidth = overlayModifier ? overlayModifier->GetCaretWidth() : 0.0f;
3951 offset.SetX(contentRect_.Right() - caretWidth);
3952 break;
3953 }
3954 default:
3955 break;
3956 }
3957 return offset;
3958 }
3959
UpdateModifierCaretOffsetAndHeight()3960 void RichEditorPattern::UpdateModifierCaretOffsetAndHeight()
3961 {
3962 CHECK_NULL_VOID(overlayMod_);
3963 auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
3964 CHECK_NULL_VOID(overlayModifier);
3965 auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
3966 overlayModifier->SetCaretOffsetAndHeight(caretOffset, caretHeight);
3967 }
3968
NotifyCaretChange()3969 void RichEditorPattern::NotifyCaretChange()
3970 {
3971 CHECK_NULL_VOID(!IsSelected());
3972 TriggerAvoidOnCaretChange();
3973 }
3974
GetTextAlignByDirection()3975 TextAlign RichEditorPattern::GetTextAlignByDirection()
3976 {
3977 auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
3978 CHECK_NULL_RETURN(layoutProperty, TextAlign::START);
3979 auto textAlign = layoutProperty->GetTextAlignValue(TextAlign::START);
3980 auto direction = layoutProperty->GetNonAutoLayoutDirection();
3981 if (direction == TextDirection::RTL) {
3982 if (textAlign == TextAlign::START) {
3983 textAlign = TextAlign::END;
3984 } else {
3985 textAlign = TextAlign::START;
3986 }
3987 }
3988 return textAlign;
3989 }
3990
HandleLongPress(GestureEvent & info)3991 void RichEditorPattern::HandleLongPress(GestureEvent& info)
3992 {
3993 if (touchedFingerCount_ == 0) {
3994 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "no finger touched, skip long press event");
3995 return;
3996 }
3997 CHECK_NULL_VOID(!selectOverlay_->GetIsHandleMoving());
3998 auto focusHub = GetFocusHub();
3999 CHECK_NULL_VOID(focusHub);
4000 if (!focusHub->IsFocusable()) {
4001 return;
4002 }
4003 if (info.GetFingerList().size() > 1) {
4004 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "More than one finger detected, ignoring this long press event");
4005 return;
4006 }
4007 if (sourceType_ == SourceType::MOUSE && hasUrlSpan_) {
4008 HandleUrlSpanShowShadow(info.GetLocalLocation(), info.GetGlobalLocation(), GetUrlPressColor());
4009 }
4010
4011 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleLongPress");
4012 moveCaretState_.Reset();
4013 caretUpdateType_ = CaretUpdateType::LONG_PRESSED;
4014 selectionMenuOffsetClick_ = OffsetF(
4015 static_cast<float>(info.GetGlobalLocation().GetX()), static_cast<float>(info.GetGlobalLocation().GetY()));
4016 HandleDoubleClickOrLongPress(info);
4017 caretUpdateType_ = CaretUpdateType::NONE;
4018 }
4019
HandleUrlSpanShowShadow(const Offset & localLocation,const Offset & globalOffset,const Color & color)4020 bool RichEditorPattern::HandleUrlSpanShowShadow(const Offset& localLocation, const Offset& globalOffset, const Color& color)
4021 {
4022 RectF textContentRect = contentRect_;
4023 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
4024 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
4025
4026 auto localLocationOffset = localLocation;
4027 if (selectOverlay_->HasRenderTransform()) {
4028 localLocationOffset = ConvertGlobalToLocalOffset(globalOffset);
4029 }
4030
4031 PointF textOffset = {static_cast<float>(localLocationOffset.GetX()) - GetTextRect().GetX(),
4032 static_cast<float>(localLocationOffset.GetY()) - GetTextRect().GetY()};
4033 return ShowShadow(textOffset, color);
4034 }
4035
HandleDoubleClickOrLongPress(GestureEvent & info)4036 void RichEditorPattern::HandleDoubleClickOrLongPress(GestureEvent& info)
4037 {
4038 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "caretUpdateType=%{public}d", caretUpdateType_);
4039 if (IsPreviewTextInputting()) {
4040 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "do not handle DoubleClickOrLongPress in previewTextInputting");
4041 return;
4042 }
4043 if (IsDragging()) {
4044 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "do not handle DoubleClickOrLongPress during drag");
4045 return;
4046 }
4047 auto host = GetHost();
4048 CHECK_NULL_VOID(host);
4049 textResponseType_ = TextResponseType::LONG_PRESS;
4050 if (caretUpdateType_ == CaretUpdateType::LONG_PRESSED) {
4051 HandleUserLongPressEvent(info);
4052 } else if (caretUpdateType_ == CaretUpdateType::DOUBLE_CLICK) {
4053 HandleUserDoubleClickEvent(info);
4054 }
4055 bool isDoubleClick = caretUpdateType_== CaretUpdateType::DOUBLE_CLICK;
4056 if (isDoubleClick && info.GetSourceTool() == SourceTool::FINGER && IsSelected()) {
4057 showSelect_ = true;
4058 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
4059 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle);
4060 }
4061 bool isLongPressSelectArea = BetweenSelection(info.GetGlobalLocation()) && !isDoubleClick;
4062 HandleDraggableFlag(isLongPressSelectArea);
4063 bool isLongPressByMouse = isMousePressed_ && caretUpdateType_== CaretUpdateType::LONG_PRESSED;
4064 if (isLongPressSelectArea && !isLongPressByMouse) {
4065 StartVibratorByLongPress();
4066 }
4067 bool isMouseClickWithShift = shiftFlag_ && info.GetSourceDevice() == SourceType::MOUSE;
4068 bool isInterceptEvent = (isLongPressSelectArea && !IsAiSelected()) || isLongPressByMouse || isMouseClickWithShift;
4069 if (isInterceptEvent) {
4070 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "intercept longPressReason:[%{public}d, %{public}d] shiftSelect:%{public}d",
4071 isLongPressSelectArea, isLongPressByMouse, isMouseClickWithShift);
4072 return;
4073 }
4074 HandleDoubleClickOrLongPress(info, host);
4075 if (IsSelected()) {
4076 TriggerAvoidOnCaretChangeNextFrame();
4077 } else {
4078 ForceTriggerAvoidOnCaretChange(true);
4079 }
4080 }
4081
ConvertGlobalToLocalOffset(const Offset & globalOffset)4082 Offset RichEditorPattern::ConvertGlobalToLocalOffset(const Offset& globalOffset)
4083 {
4084 auto localPoint = OffsetF(globalOffset.GetX(), globalOffset.GetY());
4085 selectOverlay_->RevertLocalPointWithTransform(localPoint);
4086 return Offset(localPoint.GetX(), localPoint.GetY());
4087 }
4088
HandleSelect(GestureEvent & info,int32_t selectStart,int32_t selectEnd)4089 void RichEditorPattern::HandleSelect(GestureEvent& info, int32_t selectStart, int32_t selectEnd)
4090 {
4091 initSelector_ = { selectStart, selectEnd };
4092 if (IsSelected()) {
4093 showSelect_ = true;
4094 }
4095 FireOnSelect(selectStart, selectEnd);
4096 SetCaretPositionWithAffinity({ selectEnd, TextAffinity::UPSTREAM });
4097 MoveCaretToContentRect();
4098 CalculateHandleOffsetAndShowOverlay();
4099 if (IsShowSelectMenuUsingMouse()) {
4100 CloseSelectOverlay();
4101 }
4102 selectionMenuOffset_ = info.GetGlobalLocation();
4103 }
4104
HandleLongPressOnAiSelection()4105 bool RichEditorPattern::HandleLongPressOnAiSelection()
4106 {
4107 IF_TRUE(!CheckAIPreviewMenuEnable(), ResetAISelected(AIResetSelectionReason::LONG_PRESS));
4108 if (!IsAiSelected()) {
4109 ResetAISelected(AIResetSelectionReason::LONG_PRESS);
4110 return false;
4111 }
4112 ResetSelection();
4113 CloseSelectOverlay();
4114 ShowAIEntityPreviewMenuTimer();
4115 return true;
4116 }
4117
HandleDoubleClickOrLongPress(GestureEvent & info,RefPtr<FrameNode> host)4118 void RichEditorPattern::HandleDoubleClickOrLongPress(GestureEvent& info, RefPtr<FrameNode> host)
4119 {
4120 auto focusHub = host->GetOrCreateFocusHub();
4121 CHECK_NULL_VOID(focusHub);
4122 isLongPress_ = true;
4123 auto localOffset = info.GetLocalLocation();
4124 if (selectOverlay_->HasRenderTransform()) {
4125 localOffset = ConvertGlobalToLocalOffset(info.GetGlobalLocation());
4126 }
4127 auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
4128 Offset textOffset = { localOffset.GetX() - textPaintOffset.GetX(), localOffset.GetY() - textPaintOffset.GetY() };
4129 if (caretUpdateType_ == CaretUpdateType::LONG_PRESSED) {
4130 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "LONG_PRESSED and isEditing=%{public}d", isEditing_);
4131 if (textSelector_.IsValid()) {
4132 CloseSelectOverlay();
4133 ResetSelection();
4134 }
4135 IF_TRUE(!IsAiSelected(), StartVibratorByLongPress());
4136 editingLongPress_ = isEditing_;
4137 previewLongPress_ = !isEditing_;
4138 }
4139 CHECK_NULL_VOID(!HandleLongPressOnAiSelection());
4140 focusHub->RequestFocusImmediately();
4141 InitSelection(textOffset);
4142 auto selectEnd = textSelector_.GetTextEnd();
4143 auto selectStart = textSelector_.GetTextStart();
4144 HandleSelect(info, selectStart, selectEnd);
4145 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
4146 if (overlayMod_ && caretUpdateType_ == CaretUpdateType::DOUBLE_CLICK) {
4147 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "double click. shall enter edit state.set 1");
4148 HandleOnEditChanged(true);
4149 RequestKeyboard(false, true, true);
4150 }
4151 bool isDoubleClickByMouse =
4152 info.GetSourceDevice() == SourceType::MOUSE && caretUpdateType_ == CaretUpdateType::DOUBLE_CLICK;
4153 bool isShowSelectOverlay = !isDoubleClickByMouse && caretUpdateType_ != CaretUpdateType::LONG_PRESSED;
4154 if (isShowSelectOverlay) {
4155 selectOverlay_->SwitchToOverlayMode();
4156 selectOverlay_->ProcessOverlay({ .menuIsShow = !selectOverlay_->GetIsHandleMoving(), .animation = true });
4157 StopTwinkling();
4158 } else if (selectStart == selectEnd && isDoubleClickByMouse) {
4159 StartTwinkling();
4160 } else {
4161 StopTwinkling();
4162 }
4163 }
4164
StartVibratorByLongPress()4165 void RichEditorPattern::StartVibratorByLongPress()
4166 {
4167 CHECK_NULL_VOID(isEnableHapticFeedback_);
4168 VibratorUtils::StartVibraFeedback("longPress.light");
4169 }
4170
HandleUserLongPressEvent(GestureEvent & info)4171 bool RichEditorPattern::HandleUserLongPressEvent(GestureEvent& info)
4172 {
4173 auto longPressFunc = [](RefPtr<SpanItem> item, GestureEvent& info) -> bool {
4174 if (item && item->onLongPress) {
4175 item->onLongPress(info);
4176 return true;
4177 }
4178 return false;
4179 };
4180 return HandleUserGestureEvent(info, std::move(longPressFunc));
4181 }
4182
HandleUserDoubleClickEvent(GestureEvent & info)4183 bool RichEditorPattern::HandleUserDoubleClickEvent(GestureEvent& info)
4184 {
4185 auto doubleClickFunc = [](RefPtr<SpanItem> item, GestureEvent& info) -> bool {
4186 if (item && item->onDoubleClick) {
4187 item->onDoubleClick(info);
4188 return true;
4189 }
4190 return false;
4191 };
4192 return HandleUserGestureEvent(info, std::move(doubleClickFunc));
4193 }
4194
HandleMenuCallbackOnSelectAll(bool isShowMenu)4195 void RichEditorPattern::HandleMenuCallbackOnSelectAll(bool isShowMenu)
4196 {
4197 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleMenuCallbackOnSelectAll");
4198 if (selectOverlay_->IsUsingMouse()) {
4199 CloseSelectOverlay();
4200 }
4201 auto textSize = GetTextContentLength();
4202 textSelector_.Update(0, textSize);
4203 CalculateHandleOffsetAndShowOverlay();
4204 IF_TRUE(IsSelected(), StopTwinkling());
4205 auto selectOverlayInfo = selectOverlay_->GetSelectOverlayInfo();
4206 if (selectOverlayInfo && selectOverlay_->IsUsingMouse()) {
4207 textResponseType_ = static_cast<TextResponseType>(selectOverlayInfo->menuInfo.responseType.value_or(0));
4208 } else {
4209 textResponseType_ = TextResponseType::LONG_PRESS;
4210 }
4211 selectMenuInfo_.showCopyAll = false;
4212 auto host = GetHost();
4213 CHECK_NULL_VOID(host);
4214 FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
4215 showSelect_ = true;
4216 if (!selectOverlay_->IsUsingMouse()) {
4217 selectOverlay_->ProcessOverlay({ .menuIsShow = isShowMenu, .animation = true });
4218 }
4219 SetCaretPosition(textSize);
4220 MoveCaretToContentRect();
4221 TriggerAvoidOnCaretChangeNextFrame();
4222 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
4223 }
4224
InitLongPressEvent(const RefPtr<GestureEventHub> & gestureHub)4225 void RichEditorPattern::InitLongPressEvent(const RefPtr<GestureEventHub>& gestureHub)
4226 {
4227 CHECK_NULL_VOID(!longPressEvent_);
4228 auto longPressCallback = [weak = WeakClaim(this)](GestureEvent& info) {
4229 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "long press callback, sourceType=%{public}d", info.GetSourceDevice());
4230 auto pattern = weak.Upgrade();
4231 CHECK_NULL_VOID(pattern);
4232 pattern->sourceType_ = info.GetSourceDevice();
4233 pattern->HandleLongPress(info);
4234 };
4235 longPressEvent_ = MakeRefPtr<LongPressEvent>(std::move(longPressCallback));
4236 gestureHub->SetLongPressEvent(longPressEvent_);
4237
4238 auto onTextSelectorChange = [weak = WeakClaim(this), &selector = textSelector_]() {
4239 auto pattern = weak.Upgrade();
4240 CHECK_NULL_VOID(pattern);
4241 if (!selector.SelectNothing()) {
4242 pattern->StopTwinkling();
4243 }
4244 IF_PRESENT(pattern->oneStepDragController_,
4245 SetEnableEventResponse(selector, pattern->imageNodes, pattern->builderNodes));
4246 pattern->FireOnSelectionChange(selector);
4247 auto frameNode = pattern->GetHost();
4248 CHECK_NULL_VOID(frameNode);
4249 frameNode->OnAccessibilityEvent(AccessibilityEventType::TEXT_SELECTION_UPDATE);
4250 };
4251 textSelector_.SetOnAccessibility(std::move(onTextSelectorChange));
4252 }
4253
UpdateSelector(int32_t start,int32_t end)4254 void RichEditorPattern::UpdateSelector(int32_t start, int32_t end)
4255 {
4256 AdjustSelector(start, end);
4257 textSelector_.Update(start, end);
4258 }
4259
AdjustSelector(int32_t & start,int32_t & end,SelectorAdjustPolicy policy)4260 void RichEditorPattern::AdjustSelector(int32_t& start, int32_t& end, SelectorAdjustPolicy policy)
4261 {
4262 AdjustSelector(start, HandleType::FIRST, policy);
4263 AdjustSelector(end, HandleType::SECOND, policy);
4264 }
4265
AdjustSelector(int32_t & index,HandleType handleType,SelectorAdjustPolicy policy)4266 void RichEditorPattern::AdjustSelector(int32_t& index, HandleType handleType, SelectorAdjustPolicy policy)
4267 {
4268 bool isAdjust = AdjustSelectorForSymbol(index, handleType, policy);
4269 CHECK_NULL_VOID(!isAdjust);
4270 AdjustSelectorForEmoji(index, handleType, policy);
4271 }
4272
AdjustSelectorForSymbol(int32_t & index,HandleType handleType,SelectorAdjustPolicy policy)4273 bool RichEditorPattern::AdjustSelectorForSymbol(int32_t& index, HandleType handleType, SelectorAdjustPolicy policy)
4274 {
4275 auto it = GetSpanIter(index);
4276 CHECK_NULL_RETURN((it != spans_.end()), false);
4277 auto spanItem = *it;
4278 CHECK_NULL_RETURN(spanItem, false);
4279
4280 auto spanStart = spanItem->rangeStart;
4281 auto spanEnd = spanItem->position;
4282 if (spanItem->unicode != 0 && spanItem->Contains(index)) {
4283 auto it = SELECTOR_ADJUST_DIR_MAP.find({ handleType, policy });
4284 index = (it->second == MoveDirection::BACKWARD) ? spanStart : spanEnd;
4285 return true;
4286 }
4287 return false;
4288 }
4289
GetEmojiRelation(int index)4290 EmojiRelation RichEditorPattern::GetEmojiRelation(int index)
4291 {
4292 auto it = GetSpanIter(index);
4293 CHECK_NULL_RETURN((it != spans_.end()), EmojiRelation::NO_EMOJI);
4294 auto spanItem = *it;
4295 CHECK_NULL_RETURN(spanItem, EmojiRelation::NO_EMOJI);
4296 int32_t emojiStartIndex;
4297 int32_t emojiEndIndex;
4298 return TextEmojiProcessor::GetIndexRelationToEmoji(index - spanItem->rangeStart, spanItem->content,
4299 emojiStartIndex, emojiEndIndex);
4300 }
4301
AdjustSelectorForEmoji(int & index,HandleType handleType,SelectorAdjustPolicy policy)4302 bool RichEditorPattern::AdjustSelectorForEmoji(int& index, HandleType handleType, SelectorAdjustPolicy policy)
4303 {
4304 auto it = GetSpanIter(index);
4305 CHECK_NULL_RETURN((it != spans_.end()), false);
4306 auto spanItem = *it;
4307 CHECK_NULL_RETURN(spanItem, false);
4308
4309 int32_t emojiStartIndex;
4310 int32_t emojiEndIndex;
4311 int32_t spanStart = spanItem->rangeStart;
4312 EmojiRelation relation = TextEmojiProcessor::GetIndexRelationToEmoji(index - spanStart, spanItem->content,
4313 emojiStartIndex, emojiEndIndex);
4314 if (relation != EmojiRelation::IN_EMOJI && relation != EmojiRelation::MIDDLE_EMOJI) {
4315 // no need adjusting when index is not warpped in emojis
4316 return false;
4317 }
4318 int32_t start = 0;
4319 int32_t end = 0;
4320 bool isBoundaryGet = paragraphs_.GetWordBoundary(index, start, end); // boundary from engine
4321 if (isBoundaryGet) {
4322 if (handleType == HandleType::FIRST) {
4323 index = start;
4324 } else {
4325 if (index > start) {
4326 // index to emoji, move index to end of emoji, double check "in emoji state"
4327 index = end;
4328 }
4329 }
4330 } else {
4331 if (relation == EmojiRelation::IN_EMOJI) {
4332 int32_t indexInSpan = (handleType == HandleType::FIRST) ? emojiStartIndex : emojiEndIndex;
4333 index = spanItem->rangeStart + indexInSpan;
4334 }
4335 }
4336 TAG_LOGD(AceLogTag::ACE_RICH_TEXT,
4337 "index=%{public}d, handleType=%{public}d, emojiRange=[%{public}d,%{public}d] isBoundaryGet=%{public}d "\
4338 "boundary=[%{public}d, %{public}d]",
4339 index, handleType, emojiStartIndex, emojiEndIndex, isBoundaryGet, start, end);
4340 return true;
4341 }
4342
GetSpanIter(int32_t index)4343 std::list<RefPtr<SpanItem>>::iterator RichEditorPattern::GetSpanIter(int32_t index)
4344 {
4345 return std::find_if(spans_.begin(), spans_.end(), [index](const RefPtr<SpanItem>& spanItem) {
4346 return spanItem->rangeStart <= index && index < spanItem->position;
4347 });
4348 }
4349
GetSpanType(int32_t index)4350 SpanItemType RichEditorPattern::GetSpanType(int32_t index)
4351 {
4352 auto it = GetSpanIter(index);
4353 CHECK_NULL_RETURN((it != spans_.end()), SpanItemType::NORMAL);
4354 auto& spanItem = *it;
4355 CHECK_NULL_RETURN(spanItem, SpanItemType::NORMAL);
4356 return spanItem->spanItemType;
4357 }
4358
GetGlyphPositionAtCoordinate(int32_t x,int32_t y)4359 PositionWithAffinity RichEditorPattern::GetGlyphPositionAtCoordinate(int32_t x, int32_t y)
4360 {
4361 Offset offset(x, y);
4362 return paragraphs_.GetGlyphPositionAtCoordinate(ConvertTouchOffsetToTextOffset(offset));
4363 }
4364
InitDragDropEvent()4365 void RichEditorPattern::InitDragDropEvent()
4366 {
4367 auto host = GetHost();
4368 CHECK_NULL_VOID(host);
4369 auto gestureHub = host->GetOrCreateGestureEventHub();
4370 CHECK_NULL_VOID(gestureHub);
4371 gestureHub->InitDragDropEvent();
4372 gestureHub->SetThumbnailCallback(GetThumbnailCallback());
4373 auto eventHub = host->GetOrCreateEventHub<EventHub>();
4374 CHECK_NULL_VOID(eventHub);
4375 auto onDragMove = [weakPtr = WeakClaim(this)](
4376 const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& extraParams) {
4377 auto pattern = weakPtr.Upgrade();
4378 CHECK_NULL_VOID(pattern);
4379 pattern->showSelect_ = false;
4380 pattern->OnDragMove(event);
4381 };
4382 eventHub->SetOnDragMove(std::move(onDragMove));
4383 OnDragStartAndEnd();
4384 onDragDropAndLeave();
4385 }
4386
OnDragStartAndEnd()4387 void RichEditorPattern::OnDragStartAndEnd()
4388 {
4389 auto host = GetHost();
4390 CHECK_NULL_VOID(host);
4391 auto eventHub = host->GetOrCreateEventHub<EventHub>();
4392 CHECK_NULL_VOID(eventHub);
4393 auto onDragStart = [weakPtr = WeakClaim(this)](const RefPtr<OHOS::Ace::DragEvent>& event,
4394 const std::string& extraParams) -> NG::DragDropInfo {
4395 NG::DragDropInfo itemInfo;
4396 auto pattern = weakPtr.Upgrade();
4397 CHECK_NULL_RETURN(pattern, itemInfo);
4398 return pattern->HandleDragStart(event, extraParams);
4399 };
4400 eventHub->SetDefaultOnDragStart(std::move(onDragStart));
4401 auto onDragEnd = [weakPtr = WeakClaim(this), scopeId = Container::CurrentId()](
4402 const RefPtr<OHOS::Ace::DragEvent>& event) {
4403 ContainerScope scope(scopeId);
4404 auto pattern = weakPtr.Upgrade();
4405 CHECK_NULL_VOID(pattern);
4406 pattern->isDragSponsor_ = false;
4407 pattern->dragRange_ = { 0, 0 };
4408 pattern->showSelect_ = true;
4409 pattern->isMousePressed_ = false;
4410 pattern->StopAutoScroll();
4411 pattern->ClearRedoOperationRecords();
4412 pattern->OnDragEnd(event);
4413 };
4414 eventHub->SetOnDragEnd(std::move(onDragEnd));
4415 }
4416
HandleDragStart(const RefPtr<Ace::DragEvent> & event,const std::string & extraParams)4417 NG::DragDropInfo RichEditorPattern::HandleDragStart(const RefPtr<Ace::DragEvent>& event, const std::string& extraParams)
4418 {
4419 if (!isDragSponsor_) {
4420 isDragSponsor_ = true;
4421 dragRange_ = IsAiSelected()
4422 ? std::make_pair(textSelector_.aiStart.value(), textSelector_.aiEnd.value())
4423 : std::make_pair(textSelector_.GetTextStart(),textSelector_.GetTextEnd());
4424 }
4425 sourceTool_ = event ? event->GetSourceTool() : SourceTool::UNKNOWN;
4426 timestamp_ = std::chrono::system_clock::now().time_since_epoch().count();
4427 auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
4428 CHECK_NULL_RETURN(eventHub, {});
4429 eventHub->SetTimestamp(timestamp_);
4430 showSelect_ = false;
4431 HandleUrlSpanForegroundClear();
4432 auto dropInfo = OnDragStart(event, extraParams);
4433 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleDragStart dragStatus=%{public}d", status_);
4434 return dropInfo;
4435 }
4436
onDragDropAndLeave()4437 void RichEditorPattern::onDragDropAndLeave()
4438 {
4439 auto host = GetHost();
4440 CHECK_NULL_VOID(host);
4441 auto eventHub = host->GetOrCreateEventHub<EventHub>();
4442 CHECK_NULL_VOID(eventHub);
4443 auto onDragDrop = [weakPtr = WeakClaim(this), scopeId = Container::CurrentId()](
4444 const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& value) {
4445 ContainerScope scope(scopeId);
4446 auto pattern = weakPtr.Upgrade();
4447 CHECK_NULL_VOID(pattern);
4448 auto host = pattern->GetHost();
4449 CHECK_NULL_VOID(host);
4450 auto eventHub = host->GetOrCreateEventHub<EventHub>();
4451 pattern->sourceTool_ = event ? event->GetSourceTool() : SourceTool::UNKNOWN;
4452 CHECK_NULL_VOID(eventHub && eventHub->IsEnabled());
4453 bool isCopy = false;
4454 if (pattern->status_ == Status::DRAGGING) {
4455 CHECK_NULL_VOID(event);
4456 auto gesturePressedCodes = event->GetPressedKeyCodes();
4457 if ((gesturePressedCodes.size() == 1) && ((gesturePressedCodes[0] == KeyCode::KEY_CTRL_LEFT) ||
4458 (gesturePressedCodes[0] == KeyCode::KEY_CTRL_RIGHT))) {
4459 isCopy = true;
4460 }
4461 }
4462 pattern->status_ = Status::ON_DROP;
4463 pattern->HandleOnDragDrop(event, isCopy);
4464 pattern->status_ = Status::NONE;
4465 };
4466 eventHub->SetOnDrop(std::move(onDragDrop));
4467 auto onDragDragLeave = [weakPtr = WeakClaim(this), scopeId = Container::CurrentId()](
4468 const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& value) {
4469 ContainerScope scope(scopeId);
4470 auto pattern = weakPtr.Upgrade();
4471 IF_PRESENT(pattern, StopAutoScroll());
4472 };
4473 eventHub->SetOnDragLeave(onDragDragLeave);
4474 }
4475
ClearDragDropEvent()4476 void RichEditorPattern::ClearDragDropEvent()
4477 {
4478 auto host = GetHost();
4479 CHECK_NULL_VOID(host);
4480 SetIsTextDraggable(false);
4481 auto eventHub = host->GetOrCreateEventHub<EventHub>();
4482 CHECK_NULL_VOID(eventHub);
4483 eventHub->SetOnDragStart(nullptr);
4484 eventHub->SetOnDragEnter(nullptr);
4485 eventHub->SetOnDragMove(nullptr);
4486 eventHub->SetOnDragLeave(nullptr);
4487 eventHub->SetOnDragEnd(nullptr);
4488 eventHub->SetOnDrop(nullptr);
4489 }
4490
OnDragMove(const RefPtr<OHOS::Ace::DragEvent> & event)4491 void RichEditorPattern::OnDragMove(const RefPtr<OHOS::Ace::DragEvent>& event)
4492 {
4493 auto theme = GetTheme<RichEditorTheme>();
4494 CHECK_NULL_VOID(theme);
4495 auto touchX = event->GetX();
4496 auto touchY = event->GetY();
4497 auto textRect = GetTextRect();
4498 textRect.SetTop(textRect.GetY() - std::min(baselineOffset_, 0.0f));
4499 Offset textOffset = { touchX - textRect.GetX() - GetParentGlobalOffset().GetX(),
4500 touchY - textRect.GetY() - GetParentGlobalOffset().GetY() - theme->GetInsertCursorOffset().ConvertToPx() };
4501 auto position = isShowPlaceholder_? 0 : paragraphs_.GetIndex(textOffset);
4502 ResetSelection();
4503 CloseSelectOverlay();
4504 SetCaretPosition(position);
4505 CalcAndRecordLastClickCaretInfo(textOffset);
4506 auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
4507 CHECK_NULL_VOID(overlayMod_);
4508 auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
4509 overlayModifier->SetCaretOffsetAndHeight(caretOffset, caretHeight);
4510
4511 auto host = GetHost();
4512 CHECK_NULL_VOID(host);
4513 if (host->GetDragPreviewOption().enableEdgeAutoScroll) {
4514 AutoScrollParam param = { .autoScrollEvent = AutoScrollEvent::DRAG, .showScrollbar = true };
4515 auto localOffset = OffsetF(touchX, touchY) - parentGlobalOffset_;
4516 AutoScrollByEdgeDetection(param, localOffset, EdgeDetectionStrategy::IN_BOUNDARY);
4517 } else if (isAutoScrollRunning_) {
4518 StopAutoScroll();
4519 }
4520 }
4521
OnDragEnd(const RefPtr<Ace::DragEvent> & event)4522 void RichEditorPattern::OnDragEnd(const RefPtr<Ace::DragEvent>& event)
4523 {
4524 ResetDragRecordSize(-1);
4525 if (status_ == Status::DRAGGING) {
4526 status_ = Status::NONE;
4527 }
4528 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "OnDragEnd dragStatus=%{public}d", status_);
4529 ResetDragSpanItems();
4530 CHECK_NULL_VOID(!recoverDragResultObjects_.empty());
4531 UpdateSpanItemDragStatus(recoverDragResultObjects_, false);
4532 recoverDragResultObjects_.clear();
4533 auto focusHub = GetFocusHub();
4534 if (event && focusHub && event->GetResult() != DragRet::DRAG_SUCCESS && focusHub->IsFocusable()) {
4535 afterDragSelect_ = true;
4536 HandleSelectionChange(recoverStart_, recoverEnd_);
4537 showSelect_ = true;
4538 CalculateHandleOffsetAndShowOverlay();
4539 ResetSelection();
4540 }
4541 auto host = GetHost();
4542 IF_PRESENT(host, MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF));
4543 }
4544
ToStyledString(int32_t start,int32_t end)4545 RefPtr<SpanString> RichEditorPattern::ToStyledString(int32_t start, int32_t end)
4546 {
4547 auto length = GetTextContentLength();
4548 int32_t realStart = (start == -1) ? 0 : std::clamp(start, 0, length);
4549 int32_t realEnd = (end == -1) ? length : std::clamp(end, 0, length);
4550 if (realStart > realEnd) {
4551 std::swap(realStart, realEnd);
4552 }
4553 RefPtr<SpanString> spanString = MakeRefPtr<SpanString>(u"");
4554 if (aiWriteAdapter_->GetAIWrite()) {
4555 SetSubSpansWithAIWrite(spanString, realStart, realEnd);
4556 } else {
4557 SetSubSpans(spanString, realStart, realEnd, spans_);
4558 }
4559 SetSubMap(spanString);
4560 return spanString;
4561 }
4562
FromStyledString(const RefPtr<SpanString> & spanString)4563 SelectionInfo RichEditorPattern::FromStyledString(const RefPtr<SpanString>& spanString)
4564 {
4565 std::list<ResultObject> resultObjects;
4566 int32_t start = 0;
4567 int32_t end = 0;
4568 if (spanString && !spanString->GetSpanItems().empty()) {
4569 auto spans = spanString->GetSpanItems();
4570 int32_t index = 0;
4571 std::for_each(spans.begin(), spans.end(),
4572 [&index, &resultObjects, weak = WeakClaim(this)](RefPtr<SpanItem>& item) {
4573 CHECK_NULL_VOID(item);
4574 auto pattern = weak.Upgrade();
4575 CHECK_NULL_VOID(pattern);
4576 auto obj = item->GetSpanResultObject(item->interval.first, item->interval.second);
4577 if (AceType::InstanceOf<ImageSpanItem>(item)) {
4578 obj.imageStyle = pattern->GetImageStyleBySpanItem(item);
4579 } else if (!AceType::InstanceOf<CustomSpanItem>(item)) {
4580 obj.textStyle = pattern->GetTextStyleBySpanItem(item);
4581 }
4582 obj.spanPosition.spanIndex = index;
4583 ++index;
4584 if (obj.isInit) {
4585 resultObjects.emplace_back(obj);
4586 }
4587 });
4588 if (spans.back()) {
4589 end = spans.back()->interval.second;
4590 }
4591 if (spans.front()) {
4592 start = spans.front()->interval.first;
4593 }
4594 }
4595 SelectionInfo selection;
4596 selection.SetSelectionEnd(end);
4597 selection.SetSelectionStart(start);
4598 selection.SetResultObjectList(resultObjects);
4599 return selection;
4600 }
4601
GetTextStyleBySpanItem(const RefPtr<SpanItem> & spanItem)4602 TextStyleResult RichEditorPattern::GetTextStyleBySpanItem(const RefPtr<SpanItem>& spanItem)
4603 {
4604 TextStyleResult textStyle;
4605 CHECK_NULL_RETURN(spanItem, textStyle);
4606 auto theme = GetTheme<RichEditorTheme>();
4607 TextStyle style = theme ? theme->GetTextStyle() : TextStyle();
4608 if (spanItem->fontStyle) {
4609 textStyle.fontColor = spanItem->fontStyle->GetTextColor().value_or(style.GetTextColor()).ColorToString();
4610 textStyle.fontSize =
4611 spanItem->fontStyle->GetFontSize().value_or(Dimension(DEFAULT_TEXT_SIZE, DimensionUnit::FP)).ConvertToFp();
4612 textStyle.fontStyle =
4613 static_cast<int32_t>(spanItem->fontStyle->GetItalicFontStyle().value_or(OHOS::Ace::FontStyle::NORMAL));
4614 textStyle.fontWeight = static_cast<int32_t>(spanItem->fontStyle->GetFontWeight().value_or(FontWeight::NORMAL));
4615 std::string fontFamilyValue;
4616 const std::vector<std::string> defaultFontFamily = { "HarmonyOS Sans" };
4617 auto fontFamily = spanItem->fontStyle->GetFontFamily().value_or(defaultFontFamily);
4618 for (const auto& str : fontFamily) {
4619 fontFamilyValue += str;
4620 fontFamilyValue += ",";
4621 }
4622 fontFamilyValue = fontFamilyValue.substr(0, fontFamilyValue.size() ? fontFamilyValue.size() - 1 : 0);
4623 textStyle.fontFamily = !fontFamilyValue.empty() ? fontFamilyValue : defaultFontFamily.front();
4624 textStyle.decorationType =
4625 static_cast<int32_t>(spanItem->fontStyle->GetTextDecorationFirst());
4626 textStyle.decorationColor =
4627 spanItem->fontStyle->GetTextDecorationColor().value_or(style.GetTextDecorationColor()).ColorToString();
4628 textStyle.decorationStyle =
4629 static_cast<int32_t>(spanItem->fontStyle->GetTextDecorationStyle().value_or(TextDecorationStyle::SOLID));
4630 textStyle.lineThicknessScale = static_cast<float>(spanItem->fontStyle->GetLineThicknessScale().value_or(1.0f));
4631 textStyle.fontFeature = spanItem->fontStyle->GetFontFeature().value_or(ParseFontFeatureSettings("\"pnum\" 1"));
4632 textStyle.letterSpacing = spanItem->fontStyle->GetLetterSpacing().value_or(Dimension()).ConvertToFp();
4633 }
4634 CopyTextLineStyleToTextStyleResult(spanItem, textStyle);
4635 textStyle.textBackgroundStyle = spanItem->backgroundStyle;
4636 return textStyle;
4637 }
4638
CopyTextLineStyleToTextStyleResult(const RefPtr<SpanItem> & spanItem,TextStyleResult & textStyle)4639 void RichEditorPattern::CopyTextLineStyleToTextStyleResult(const RefPtr<SpanItem>& spanItem,
4640 TextStyleResult& textStyle)
4641 {
4642 CHECK_NULL_VOID(spanItem->textLineStyle);
4643 textStyle.lineHeight = spanItem->textLineStyle->GetLineHeight().value_or(Dimension()).ConvertToFp();
4644 textStyle.halfLeading = spanItem->textLineStyle->GetHalfLeading().value_or(false);
4645 textStyle.lineSpacing = spanItem->textLineStyle->GetLineSpacing().value_or(Dimension()).ConvertToFp();
4646 textStyle.textAlign = static_cast<int32_t>(spanItem->textLineStyle->GetTextAlign().value_or(TextAlign::START));
4647 auto lm = spanItem->textLineStyle->GetLeadingMargin();
4648 if (lm.has_value()) {
4649 textStyle.leadingMarginSize[RichEditorLeadingRange::LEADING_START] = lm.value().size.Width().ToString();
4650 textStyle.leadingMarginSize[RichEditorLeadingRange::LEADING_END] = lm.value().size.Height().ToString();
4651 }
4652 textStyle.wordBreak =
4653 static_cast<int32_t>(spanItem->textLineStyle->GetWordBreak().value_or(WordBreak::BREAK_WORD));
4654 textStyle.lineBreakStrategy =
4655 static_cast<int32_t>(spanItem->textLineStyle->GetLineBreakStrategy().value_or(LineBreakStrategy::GREEDY));
4656 textStyle.paragraphSpacing = spanItem->textLineStyle->GetParagraphSpacing();
4657 auto verticalAlign = spanItem->textLineStyle->GetTextVerticalAlign();
4658 IF_TRUE(verticalAlign.has_value(), textStyle.textVerticalAlign = static_cast<int32_t>(verticalAlign.value()));
4659 }
4660
GetImageStyleBySpanItem(const RefPtr<SpanItem> & spanItem)4661 ImageStyleResult RichEditorPattern::GetImageStyleBySpanItem(const RefPtr<SpanItem>& spanItem)
4662 {
4663 ImageStyleResult imageStyle;
4664 auto imageSpanItem = DynamicCast<ImageSpanItem>(spanItem);
4665 CHECK_NULL_RETURN(imageSpanItem, imageStyle);
4666 auto imageAttributeOp = imageSpanItem->options.imageAttribute;
4667 CHECK_NULL_RETURN(imageAttributeOp.has_value(), imageStyle);
4668 auto imageSizeOp = imageAttributeOp->size;
4669 if (imageSizeOp.has_value() && imageSizeOp->width.has_value() && imageSizeOp->height.has_value()) {
4670 imageStyle.size[RichEditorImageSize::SIZEWIDTH] = imageSizeOp->width->ConvertToPx();
4671 imageStyle.size[RichEditorImageSize::SIZEHEIGHT] = imageSizeOp->height->ConvertToPx();
4672 }
4673 if (imageAttributeOp->verticalAlign.has_value()) {
4674 imageStyle.verticalAlign = static_cast<int32_t>(imageAttributeOp->verticalAlign.value());
4675 }
4676 if (imageAttributeOp->objectFit.has_value()) {
4677 imageStyle.objectFit = static_cast<int32_t>(imageAttributeOp->objectFit.value());
4678 }
4679 if (imageAttributeOp->marginProp.has_value()) {
4680 imageStyle.margin = imageAttributeOp->marginProp->ToString();
4681 }
4682 if (imageAttributeOp->borderRadius.has_value()) {
4683 imageStyle.borderRadius = imageAttributeOp->borderRadius->ToString();
4684 }
4685 return imageStyle;
4686 }
4687
SetSubSpansWithAIWrite(RefPtr<SpanString> & spanString,int32_t start,int32_t end)4688 void RichEditorPattern::SetSubSpansWithAIWrite(RefPtr<SpanString>& spanString, int32_t start, int32_t end)
4689 {
4690 placeholderSpansMap_.clear();
4691 CHECK_NULL_VOID(spanString);
4692 std::list<RefPtr<SpanItem>> subSpans;
4693 std::u16string text;
4694 size_t index = 0;
4695 size_t placeholderGains = 0;
4696 for (const auto& spanItem : spans_) {
4697 if (!spanItem) {
4698 continue;
4699 }
4700 auto oldEnd = spanItem->position;
4701 auto oldStart = spanItem->rangeStart;
4702 if (oldEnd <= start || oldStart >= end) {
4703 continue;
4704 }
4705 RefPtr<SpanItem> newSpanItem = MakeRefPtr<SpanItem>();
4706 auto spanStart = oldStart <= start ? 0 : oldStart - start;
4707 auto spanEnd = oldEnd < end ? oldEnd - start : end - start;
4708 spanStart += static_cast<int32_t>(placeholderGains);
4709 if (spanItem->spanItemType == SpanItemType::NORMAL) {
4710 newSpanItem = spanItem->GetSameStyleSpanItem();
4711 newSpanItem->urlAddress = spanItem->urlAddress;
4712 newSpanItem->content = spanItem->content
4713 .substr(std::max(start - oldStart, 0), std::min(end, oldEnd) - std::max(start, oldStart));
4714 } else {
4715 InitPlaceholderSpansMap(newSpanItem, spanItem, index, placeholderGains);
4716 spanEnd += static_cast<int32_t>(placeholderGains);
4717 }
4718 newSpanItem->interval = {spanStart, spanEnd};
4719 newSpanItem->position = spanEnd;
4720 newSpanItem->rangeStart = spanStart;
4721 newSpanItem->textLineStyle->ResetLeadingMargin();
4722 text.append(newSpanItem->content);
4723 subSpans.emplace_back(newSpanItem);
4724 }
4725 spanString->SetString(text);
4726 spanString->SetSpanItems(std::move(subSpans));
4727 }
4728
InitPlaceholderSpansMap(RefPtr<SpanItem> & newSpanItem,const RefPtr<SpanItem> & spanItem,size_t & index,size_t & placeholderGains)4729 void RichEditorPattern::InitPlaceholderSpansMap(
4730 RefPtr<SpanItem>& newSpanItem, const RefPtr<SpanItem>& spanItem, size_t& index, size_t& placeholderGains)
4731 {
4732 newSpanItem->content = UtfUtils::Str8ToStr16("![id" + std::to_string(index++) + "]");
4733 switch (spanItem->spanItemType) {
4734 case SpanItemType::SYMBOL: {
4735 placeholderSpansMap_[newSpanItem->content] = spanItem;
4736 placeholderGains += PLACEHOLDER_LENGTH - SYMBOL_CONTENT_LENGTH;
4737 break;
4738 }
4739 case SpanItemType::CustomSpan: {
4740 if (!isSpanStringMode_) {
4741 placeholderSpansMap_[newSpanItem->content] = spanItem;
4742 } else {
4743 auto customSpanItem = DynamicCast<CustomSpanItem>(spanItem);
4744 placeholderSpansMap_[newSpanItem->content] = customSpanItem;
4745 }
4746 placeholderGains += PLACEHOLDER_LENGTH - CUSTOM_CONTENT_LENGTH;
4747 break;
4748 }
4749 case SpanItemType::IMAGE: {
4750 placeholderSpansMap_[newSpanItem->content] = spanItem;
4751 placeholderGains += PLACEHOLDER_LENGTH - CUSTOM_CONTENT_LENGTH;
4752 break;
4753 }
4754 default:
4755 break;
4756 }
4757 }
4758
SetSubSpans(RefPtr<SpanString> & spanString,int32_t start,int32_t end,const std::list<RefPtr<SpanItem>> & spans)4759 void RichEditorPattern::SetSubSpans(RefPtr<SpanString>& spanString, int32_t start, int32_t end,
4760 const std::list<RefPtr<SpanItem>>& spans)
4761 {
4762 CHECK_NULL_VOID(spanString);
4763 std::list<RefPtr<SpanItem>> subSpans;
4764 std::u16string text;
4765 for (const auto& spanItem : spans) {
4766 if (!spanItem || spanItem->spanItemType == SpanItemType::CustomSpan ||
4767 spanItem->spanItemType == SpanItemType::SYMBOL) {
4768 continue;
4769 }
4770 auto spanEndPos = spanItem->position;
4771 auto spanStartPos = spanItem->rangeStart;
4772 if (spanEndPos > start && spanStartPos < end) {
4773 int32_t oldStart = spanStartPos;
4774 int32_t oldEnd = spanEndPos;
4775 auto spanStart = oldStart <= start ? 0 : oldStart - start;
4776 auto spanEnd = oldEnd < end ? oldEnd - start : end - start;
4777 auto newSpanItem = GetSameSpanItem(spanItem);
4778 CHECK_NULL_CONTINUE(newSpanItem);
4779 newSpanItem->spanItemType = spanItem->spanItemType;
4780 newSpanItem->interval = {spanStart, spanEnd};
4781 newSpanItem->position = spanEnd;
4782 newSpanItem->rangeStart = spanStart;
4783 newSpanItem->content = spanItem->content
4784 .substr(std::max(start - oldStart, 0), std::min(end, oldEnd) - std::max(start, oldStart));
4785 text.append(newSpanItem->content);
4786 subSpans.emplace_back(newSpanItem);
4787 }
4788 }
4789 spanString->SetString(text);
4790 spanString->SetSpanItems(std::move(subSpans));
4791 }
4792
GetSameSpanItem(const RefPtr<SpanItem> & spanItem)4793 RefPtr<SpanItem> RichEditorPattern::GetSameSpanItem(const RefPtr<SpanItem>& spanItem)
4794 {
4795 CHECK_NULL_RETURN(spanItem, nullptr);
4796 if (spanItem->spanItemType == SpanItemType::IMAGE) {
4797 auto imageSpanItem = DynamicCast<ImageSpanItem>(spanItem);
4798 CHECK_NULL_RETURN(imageSpanItem, nullptr);
4799 auto newSpanItem = MakeRefPtr<ImageSpanItem>();
4800 auto options = imageSpanItem->options;
4801 if (!options.imagePixelMap) {
4802 auto imageNode = GetImageSpanNodeBySpanItem(imageSpanItem);
4803 CHECK_NULL_RETURN(imageNode, nullptr);
4804 auto pattern = imageNode->GetPattern<ImagePattern>();
4805 CHECK_NULL_RETURN(pattern, nullptr);
4806 auto image = pattern->GetCanvasImage();
4807 CHECK_NULL_RETURN(image, nullptr);
4808 auto pixelMap = image->GetPixelMap();
4809 if (!pixelMap) {
4810 pixelMap = imageNode->GetDragPixelMap();
4811 }
4812 options.imagePixelMap = pixelMap;
4813 }
4814 newSpanItem->SetImageSpanOptions(options);
4815 return newSpanItem;
4816 } else if (spanItem->spanItemType == SpanItemType::NORMAL) {
4817 auto newSpanItem = spanItem->GetSameStyleSpanItem();
4818 newSpanItem->urlAddress = spanItem->urlAddress;
4819 return newSpanItem;
4820 }
4821 return nullptr;
4822 }
4823
GetImageSpanNodeBySpanItem(const RefPtr<ImageSpanItem> & spanItem)4824 RefPtr<ImageSpanNode> RichEditorPattern::GetImageSpanNodeBySpanItem(const RefPtr<ImageSpanItem>& spanItem)
4825 {
4826 auto host = GetContentHost();
4827 CHECK_NULL_RETURN(host, nullptr);
4828 auto uiNodes = host->GetChildren();
4829 auto it = std::find_if(uiNodes.begin(), uiNodes.end(), [spanItem](const RefPtr<UINode>& uiNode) {
4830 auto imageSpanNode = DynamicCast<ImageSpanNode>(uiNode);
4831 CHECK_NULL_RETURN(imageSpanNode, false);
4832 return imageSpanNode->GetSpanItem() == spanItem;
4833 });
4834 CHECK_NULL_RETURN(it != uiNodes.end(), nullptr);
4835 return DynamicCast<ImageSpanNode>(*it);
4836 }
4837
SetSubMap(RefPtr<SpanString> & spanString)4838 void RichEditorPattern::SetSubMap(RefPtr<SpanString>& spanString)
4839 {
4840 CHECK_NULL_VOID(spanString);
4841 auto subSpans = spanString->GetSpanItems();
4842 std::unordered_map<SpanType, std::list<RefPtr<SpanBase>>> subMap;
4843 for (auto& spanItem : subSpans) {
4844 if (!spanItem) {
4845 continue;
4846 }
4847 auto start = spanItem->rangeStart;
4848 auto end = spanItem->position;
4849 std::list<RefPtr<SpanBase>> spanBases;
4850 if (spanItem->spanItemType == SpanItemType::IMAGE) {
4851 spanBases = { spanString->ToImageSpan(spanItem, start, end) };
4852 } else if (spanItem->spanItemType == SpanItemType::NORMAL) {
4853 spanBases = { spanString->ToFontSpan(spanItem, start, end),
4854 spanString->ToDecorationSpan(spanItem, start, end),
4855 spanString->ToBaselineOffsetSpan(spanItem, start, end),
4856 spanString->ToLetterSpacingSpan(spanItem, start, end),
4857 spanString->ToGestureSpan(spanItem, start, end),
4858 spanString->ToParagraphStyleSpan(spanItem, start, end),
4859 spanString->ToLineHeightSpan(spanItem, start, end),
4860 spanString->ToBackgroundColorSpan(spanItem, start, end),
4861 spanString->ToUrlSpan(spanItem, start, end) };
4862 }
4863 for (auto& spanBase : spanBases) {
4864 if (!spanBase) {
4865 continue;
4866 }
4867 auto it = subMap.find(spanBase->GetSpanType());
4868 if (it == subMap.end()) {
4869 subMap.insert({ spanBase->GetSpanType(), { spanBase } });
4870 } else {
4871 it->second.emplace_back(std::move(spanBase));
4872 }
4873 }
4874 }
4875 spanString->SetSpanMap(std::move(subMap));
4876 }
4877
AddSpanByPasteData(const RefPtr<SpanString> & spanString,TextChangeReason reason)4878 void RichEditorPattern::AddSpanByPasteData(const RefPtr<SpanString>& spanString, TextChangeReason reason)
4879 {
4880 CHECK_NULL_VOID(spanString);
4881 if (spanString->GetSpansMap().empty()) {
4882 CompleteStyledString(const_cast<RefPtr<SpanString>&>(spanString));
4883 }
4884 if (isSpanStringMode_) {
4885 InsertStyledStringByPaste(spanString);
4886 } else {
4887 AddSpansByPaste(spanString->GetSpanItems(), reason);
4888 }
4889
4890 if (aiWriteAdapter_->GetAIWrite()) {
4891 return;
4892 }
4893 StartTwinkling();
4894 auto host = GetHost();
4895 CHECK_NULL_VOID(host);
4896 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
4897 host->MarkModifyDone();
4898 }
4899
CompleteStyledString(RefPtr<SpanString> & spanString)4900 void RichEditorPattern::CompleteStyledString(RefPtr<SpanString>& spanString)
4901 {
4902 CHECK_NULL_VOID(spanString);
4903 std::u16string text;
4904 auto spans = spanString->GetSpanItems();
4905 std::for_each(spans.begin(), spans.end(), [&text](RefPtr<SpanItem>& item) {
4906 CHECK_NULL_VOID(item);
4907 text.append(item->content);
4908 item->position = item->interval.second;
4909 item->rangeStart = item->interval.first;
4910 });
4911 spanString->SetString(std::move(text));
4912 SetSubMap(spanString);
4913 }
4914
InsertStyledStringByPaste(const RefPtr<SpanString> & spanString)4915 void RichEditorPattern::InsertStyledStringByPaste(const RefPtr<SpanString>& spanString)
4916 {
4917 InsertStyledString(spanString, caretPosition_, true);
4918 }
4919
InsertStyledString(const RefPtr<SpanString> & spanString,int32_t insertIndex,bool updateCaret)4920 void RichEditorPattern::InsertStyledString(const RefPtr<SpanString>& spanString, int32_t insertIndex, bool updateCaret)
4921 {
4922 CHECK_NULL_VOID(spanString && styledString_);
4923 int32_t changeStart = insertIndex;
4924 int32_t changeLength = 0;
4925 if (textSelector_.IsValid()) {
4926 changeStart = textSelector_.GetTextStart();
4927 changeLength = textSelector_.GetTextEnd() - textSelector_.GetTextStart();
4928 }
4929 UndoRedoRecord record;
4930 undoManager_->ApplyOperationToRecord(changeStart, changeLength, spanString, record);
4931 CHECK_NULL_VOID(BeforeStyledStringChange(record));
4932 undoManager_->RecordOperation(record);
4933 auto subSpanString = spanString;
4934 int32_t startLength = maxLength_.value_or(INT_MAX) + changeLength - GetTextContentLength();
4935 if (spanString->GetLength() >= startLength) {
4936 auto range = TextEmojiProcessor::CalSubU16stringRange(
4937 startLength, spanString->GetLength() - startLength, spanString->GetU16string(), true, true);
4938 auto subLength = range.endIndex - range.startIndex;
4939 subSpanString = subSpanString->GetSubSpanString(0, spanString->GetLength() - subLength);
4940 }
4941
4942 if (changeLength > 0 && subSpanString->GetLength() > 0) {
4943 DeleteForwardInStyledString(changeLength, false);
4944 }
4945 ResetSelection();
4946 styledString_->InsertSpanString(changeStart, subSpanString);
4947 IF_TRUE(updateCaret, SetCaretPosition(insertIndex + subSpanString->GetLength()));
4948 AfterStyledStringChange(changeStart, changeLength, subSpanString->GetU16string());
4949 }
4950
HandleOnDragInsertStyledString(const RefPtr<SpanString> & spanString,bool isCopy)4951 void RichEditorPattern::HandleOnDragInsertStyledString(const RefPtr<SpanString>& spanString, bool isCopy)
4952 {
4953 CHECK_NULL_VOID(spanString);
4954 int currentCaretPosition = caretPosition_;
4955 auto strLength = spanString->GetLength();
4956 insertValueLength_ = strLength;
4957 UndoRedoRecord record;
4958 undoManager_->ApplyOperationToRecord(currentCaretPosition, 0, spanString, record);
4959 if (isDragSponsor_ && !isCopy) {
4960 bool isInsertForward = currentCaretPosition < dragRange_.first;
4961 bool isInsertBackward = currentCaretPosition > dragRange_.second;
4962 CHECK_NULL_VOID(isInsertForward || isInsertBackward);
4963 CHECK_NULL_VOID(BeforeStyledStringChange(record));
4964 undoManager_->RecordOperation(record);
4965 styledString_->InsertSpanString(currentCaretPosition, spanString);
4966 AfterStyledStringChange(record);
4967 if (isInsertForward) {
4968 // Record current state before updating caret position by insertion
4969 undoManager_->RecordSelectionBefore();
4970 SetCaretPosition(currentCaretPosition + strLength);
4971 dragRange_.first += strLength;
4972 dragRange_.second += strLength;
4973 }
4974 DeleteValueInStyledString(dragRange_.first, strLength, true, false);
4975 undoManager_->ClearSelectionBefore();
4976 } else {
4977 CHECK_NULL_VOID(BeforeStyledStringChange(record));
4978 undoManager_->RecordOperation(record);
4979 styledString_->InsertSpanString(currentCaretPosition, spanString);
4980 SetCaretPosition(currentCaretPosition + strLength);
4981 AfterStyledStringChange(record);
4982 }
4983 StartTwinkling();
4984 auto host = GetContentHost();
4985 CHECK_NULL_VOID(host);
4986 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
4987 }
4988
AddSpansByPaste(const std::list<RefPtr<NG::SpanItem>> & spans,TextChangeReason reason)4989 void RichEditorPattern::AddSpansByPaste(const std::list<RefPtr<NG::SpanItem>>& spans, TextChangeReason reason)
4990 {
4991 if (GetTextContentLength() >= maxLength_.value_or(INT_MAX)) {
4992 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "AddSpansByPaste: Reach the maxLength. maxLength=%{public}d", maxLength_.value_or(INT_MAX));
4993 return;
4994 }
4995 if (textSelector_.IsValid()) {
4996 auto deleteStart = textSelector_.GetTextStart();
4997 auto deleteLength = textSelector_.GetTextEnd() - deleteStart;
4998 UndoRedoRecord styledRecord;
4999 undoManager_->UpdateRecordBeforeChange(deleteStart, deleteLength, styledRecord);
5000 SetCaretPosition(deleteStart);
5001 DeleteForward(deleteStart, deleteLength);
5002 ResetSelection();
5003 undoManager_->RecordOperationAfterChange(deleteStart, 0, styledRecord);
5004 }
5005 for (const auto& spanItem : spans) {
5006 if (!spanItem) {
5007 continue;
5008 }
5009 auto imageSpanItem = DynamicCast<ImageSpanItem>(spanItem);
5010 if (imageSpanItem) {
5011 auto options = imageSpanItem->options;
5012 options.offset = caretPosition_;
5013 AddImageSpan(options, reason, true, caretPosition_, true);
5014 } else {
5015 auto options = GetTextSpanOptions(spanItem);
5016 AddTextSpan(options, reason, true, caretPosition_);
5017 }
5018 }
5019 }
5020
CalculateTruncationLength(const std::u16string & insertValue,int32_t start)5021 int32_t RichEditorPattern::CalculateTruncationLength(const std::u16string& insertValue, int32_t start)
5022 {
5023 if (!textSelector_.SelectNothing()) {
5024 start += textSelector_.GetTextEnd() - textSelector_.GetTextStart();
5025 }
5026 auto truncationLength = static_cast<int32_t>(insertValue.length()) - start;
5027 auto range = TextEmojiProcessor::CalSubU16stringRange(start, truncationLength, insertValue, true, true);
5028 auto allowInsertLength = static_cast<int32_t>(insertValue.length()) - range.endIndex + range.startIndex;
5029 return allowInsertLength;
5030 }
5031
GetParagraphStyle(const RefPtr<SpanItem> & spanItem)5032 struct UpdateParagraphStyle RichEditorPattern::GetParagraphStyle(const RefPtr<SpanItem>& spanItem)
5033 {
5034 struct UpdateParagraphStyle paraStyle;
5035 paraStyle.textAlign = spanItem->textLineStyle->GetTextAlign();
5036 paraStyle.leadingMargin = spanItem->textLineStyle->GetLeadingMargin();
5037 paraStyle.wordBreak = spanItem->textLineStyle->GetWordBreak();
5038 paraStyle.lineBreakStrategy = spanItem->textLineStyle->GetLineBreakStrategy();
5039 paraStyle.paragraphSpacing = spanItem->textLineStyle->GetParagraphSpacing();
5040 paraStyle.textVerticalAlign = spanItem->textLineStyle->GetTextVerticalAlign();
5041 return paraStyle;
5042 }
5043
GetTextSpanOptions(const RefPtr<SpanItem> & spanItem)5044 TextSpanOptions RichEditorPattern::GetTextSpanOptions(const RefPtr<SpanItem>& spanItem)
5045 {
5046 CHECK_NULL_RETURN(spanItem, {});
5047 TextStyle textStyle = GetDefaultTextStyle();
5048 UseSelfStyle(spanItem->fontStyle, spanItem->textLineStyle, textStyle);
5049 textStyle.SetTextBackgroundStyle(spanItem->backgroundStyle);
5050 TextSpanOptions options;
5051 options.value = spanItem->content;
5052 options.offset = caretPosition_;
5053 UserGestureOptions gestureOption;
5054 gestureOption.onClick = spanItem->onClick;
5055 gestureOption.onLongPress = spanItem->onLongPress;
5056 options.urlAddress = spanItem->urlAddress;
5057 options.userGestureOption = gestureOption;
5058 options.style = textStyle;
5059 options.paraStyle = GetParagraphStyle(spanItem);
5060 return options;
5061 }
5062
ResetDragSpanItems()5063 void RichEditorPattern::ResetDragSpanItems()
5064 {
5065 auto host = GetContentHost();
5066 CHECK_NULL_VOID(host);
5067 std::unordered_set<int32_t> nodeIds;
5068 std::for_each(dragSpanItems_.begin(), dragSpanItems_.end(), [&nodeIds](RefPtr<SpanItem>& item) {
5069 CHECK_NULL_VOID(item);
5070 item->EndDrag();
5071 auto imageSpanItem = DynamicCast<ImageSpanItem>(item);
5072 if (imageSpanItem) {
5073 nodeIds.emplace(imageSpanItem->nodeId_);
5074 return;
5075 }
5076 auto placeholderSpanItem = DynamicCast<PlaceholderSpanItem>(item);
5077 if (placeholderSpanItem) {
5078 nodeIds.emplace(placeholderSpanItem->placeholderSpanNodeId);
5079 }
5080 });
5081 const auto& childrens = host->GetChildren();
5082 for (const auto& child : childrens) {
5083 auto findResult = nodeIds.find(child->GetId());
5084 CHECK_NULL_CONTINUE(findResult != nodeIds.end());
5085 auto node = DynamicCast<FrameNode>(child);
5086 CHECK_NULL_CONTINUE(node);
5087 auto renderContext = node->GetRenderContext();
5088 CHECK_NULL_CONTINUE(renderContext);
5089 renderContext->UpdateOpacity(1);
5090 node->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
5091 }
5092 dragSpanItems_.clear();
5093 }
5094
SelectOverlayIsOn()5095 bool RichEditorPattern::SelectOverlayIsOn()
5096 {
5097 return selectOverlay_->SelectOverlayIsOn();
5098 }
5099
UpdateEditingValue(const std::shared_ptr<TextEditingValue> & value,bool needFireChangeEvent)5100 void RichEditorPattern::UpdateEditingValue(const std::shared_ptr<TextEditingValue>& value, bool needFireChangeEvent)
5101 {
5102 #ifdef ENABLE_STANDARD_INPUT
5103 InsertValue(UtfUtils::Str8ToStr16(value->text), true);
5104 #else
5105 if (value->isDelete) {
5106 HandleOnDelete(true);
5107 } else {
5108 #ifdef CROSS_PLATFORM
5109 #ifdef IOS_PLATFORM
5110 compose_ = value->compose;
5111 unmarkText_ = value->unmarkText;
5112 #endif
5113 #ifdef ANDROID_PLATFORM
5114 if (value->appendText.empty()) {
5115 return;
5116 }
5117 #endif
5118 InsertValue(UtfUtils::Str8ToStr16(value->appendText), true);
5119 #else
5120 InsertValue(UtfUtils::Str8ToStr16(value->appendText));
5121 #endif
5122 }
5123 #endif
5124 }
5125
HandleAISpanHoverEvent(const MouseInfo & info)5126 void RichEditorPattern::HandleAISpanHoverEvent(const MouseInfo& info)
5127 {
5128 if (info.GetAction() != MouseAction::MOVE || !NeedShowAIDetect()) {
5129 return;
5130 }
5131 auto scrollBar = GetScrollBar();
5132 if (scrollBar && (scrollBar->IsHover() || scrollBar->IsPressed())) {
5133 return;
5134 }
5135 if (dataDetectorAdapter_->aiSpanRects_.empty()) {
5136 for (const auto& kv : dataDetectorAdapter_->aiSpanMap_) {
5137 auto& aiSpan = kv.second;
5138 const auto& aiRects = paragraphs_.GetRects(aiSpan.start, aiSpan.end);
5139 dataDetectorAdapter_->aiSpanRects_.insert(
5140 dataDetectorAdapter_->aiSpanRects_.end(), aiRects.begin(), aiRects.end());
5141 }
5142 }
5143
5144 auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
5145 PointF textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
5146 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
5147 auto host = GetHost();
5148 CHECK_NULL_VOID(host);
5149 auto pipeline = GetContext();
5150 CHECK_NULL_VOID(pipeline);
5151 auto nodeId = host->GetId();
5152 for (auto&& rect : dataDetectorAdapter_->aiSpanRects_) {
5153 if (!rect.IsInRegion(textOffset)) {
5154 continue;
5155 }
5156 if (currentMouseStyle_ != MouseFormat::HAND_POINTING) {
5157 pipeline->ChangeMouseStyle(nodeId, MouseFormat::HAND_POINTING);
5158 currentMouseStyle_ = MouseFormat::HAND_POINTING;
5159 }
5160 return;
5161 }
5162 if (currentMouseStyle_ != MouseFormat::TEXT_CURSOR) {
5163 pipeline->ChangeMouseStyle(nodeId, MouseFormat::TEXT_CURSOR);
5164 currentMouseStyle_ = MouseFormat::TEXT_CURSOR;
5165 }
5166 }
5167
InitMouseEvent()5168 void RichEditorPattern::InitMouseEvent()
5169 {
5170 CHECK_NULL_VOID(!mouseEventInitialized_);
5171 auto host = GetHost();
5172 CHECK_NULL_VOID(host);
5173 auto eventHub = host->GetOrCreateEventHub<EventHub>();
5174 CHECK_NULL_VOID(eventHub);
5175 auto inputHub = eventHub->GetOrCreateInputEventHub();
5176 CHECK_NULL_VOID(inputHub);
5177
5178 auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
5179 auto pattern = weak.Upgrade();
5180 CHECK_NULL_VOID(pattern);
5181 pattern->HandleMouseEvent(info);
5182 };
5183 auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
5184 inputHub->AddOnMouseEvent(mouseEvent);
5185 auto hoverTask = [weak = WeakClaim(this)](bool isHover, HoverInfo& hoverInfo) {
5186 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "on hover event isHover=%{public}d", isHover);
5187 auto pattern = weak.Upgrade();
5188 if (pattern) {
5189 pattern->OnHover(isHover, hoverInfo);
5190 }
5191 };
5192 auto hoverEvent = MakeRefPtr<InputEvent>(std::move(hoverTask));
5193 inputHub->AddOnHoverEvent(hoverEvent);
5194 mouseEventInitialized_ = true;
5195 }
5196
OnHover(bool isHover,HoverInfo & hoverInfo)5197 void RichEditorPattern::OnHover(bool isHover, HoverInfo& hoverInfo)
5198 {
5199 auto sourceTool = hoverInfo.GetSourceTool();
5200 auto mouseAction = hoverInfo.GetMouseAction();
5201 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "isHover=%{public}d, sourceTool=%{public}d, mouseAction=%{public}d",
5202 isHover, sourceTool, mouseAction);
5203 if (!isHover && lastHoverSpanItem_) {
5204 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "spanItem hover false");
5205 lastHoverSpanItem_->onHover_(false, lastHoverInfo_);
5206 lastHoverSpanItem_.Reset();
5207 }
5208 auto scrollBar = GetScrollBar();
5209 if (isHover && (!scrollBar || !scrollBar->IsPressed())) {
5210 ChangeMouseStyle(MouseFormat::TEXT_CURSOR);
5211 } else {
5212 auto host = GetHost();
5213 auto pipeline = GetContext();
5214 bool hideMouseByTouch = sourceTool == SourceTool::FINGER && mouseAction == MouseAction::WINDOW_LEAVE;
5215 bool keepStyle = host && pipeline && hideMouseByTouch;
5216 if (keepStyle) {
5217 pipeline->FreeMouseStyleHoldNode(host->GetId());
5218 } else {
5219 ChangeMouseStyle(MouseFormat::DEFAULT, true);
5220 }
5221 HandleUrlSpanForegroundClear();
5222 }
5223 }
5224
ChangeMouseStyle(MouseFormat format,bool freeMouseHoldNode)5225 void RichEditorPattern::ChangeMouseStyle(MouseFormat format, bool freeMouseHoldNode)
5226 {
5227 auto host = GetHost();
5228 CHECK_NULL_VOID(host);
5229 auto pipeline = GetContext();
5230 CHECK_NULL_VOID(pipeline);
5231 auto nodeId = host->GetId();
5232 // Do not change mouse style to text-cursor if the right-button custom menu is showing
5233 bool shouldPreventChange = (format == MouseFormat::TEXT_CURSOR && selectOverlay_->IsRightButtonCustomMenuShow());
5234 CHECK_NULL_VOID(!shouldPreventChange);
5235 pipeline->SetMouseStyleHoldNode(nodeId);
5236 pipeline->ChangeMouseStyle(nodeId, format);
5237 currentMouseStyle_ = format;
5238 IF_TRUE(freeMouseHoldNode, pipeline->FreeMouseStyleHoldNode(nodeId));
5239 }
5240
RequestKeyboard(bool isFocusViewChanged,bool needStartTwinkling,bool needShowSoftKeyboard,SourceType sourceType)5241 bool RichEditorPattern::RequestKeyboard(bool isFocusViewChanged, bool needStartTwinkling, bool needShowSoftKeyboard,
5242 SourceType sourceType)
5243 {
5244 auto host = GetHost();
5245 CHECK_NULL_RETURN(host, false);
5246 auto context = host->GetContext();
5247 CHECK_NULL_RETURN(context, false);
5248 CHECK_NULL_RETURN(needShowSoftKeyboard, false);
5249 if (needShowSoftKeyboard && customKeyboardBuilder_) {
5250 return RequestCustomKeyboard();
5251 }
5252 #if defined(ENABLE_STANDARD_INPUT)
5253 if (!EnableStandardInput(needShowSoftKeyboard, sourceType)) {
5254 return false;
5255 }
5256 #else
5257 if (!UnableStandardInput(isFocusViewChanged)) {
5258 return false;
5259 }
5260 #endif
5261 return true;
5262 }
5263
5264 #if defined(ENABLE_STANDARD_INPUT)
5265 #ifdef WINDOW_SCENE_SUPPORTED
GetSCBSystemWindowId()5266 uint32_t RichEditorPattern::GetSCBSystemWindowId()
5267 {
5268 RefPtr<FrameNode> frameNode = GetHost();
5269 CHECK_NULL_RETURN(frameNode, {});
5270 auto focusSystemWindowId = WindowSceneHelper::GetFocusSystemWindowId(frameNode);
5271 return focusSystemWindowId;
5272 }
5273 #endif
5274
EnableStandardInput(bool needShowSoftKeyboard,SourceType sourceType)5275 bool RichEditorPattern::EnableStandardInput(bool needShowSoftKeyboard, SourceType sourceType)
5276 {
5277 auto host = GetHost();
5278 CHECK_NULL_RETURN(host, false);
5279 auto context = host->GetContext();
5280 CHECK_NULL_RETURN(context, false);
5281 if (richEditTextChangeListener_ == nullptr) {
5282 richEditTextChangeListener_ = new OnTextChangedListenerImpl(WeakClaim(this));
5283 }
5284 auto inputMethod = MiscServices::InputMethodController::GetInstance();
5285 CHECK_NULL_RETURN(inputMethod, false);
5286 auto miscTextConfig = GetMiscTextConfig();
5287 CHECK_NULL_RETURN(miscTextConfig.has_value(), false);
5288 MiscServices::TextConfig textconfig = miscTextConfig.value();
5289 TAG_LOGD(
5290 AceLogTag::ACE_RICH_TEXT, "RequestKeyboard set calling window id is : %{public}u"
5291 " RequestKeyboard set placeholder length is : %{public}zu", miscTextConfig->windowId,
5292 CountUtf16Chars(textconfig.inputAttribute.placeholder));
5293 #ifdef WINDOW_SCENE_SUPPORTED
5294 auto systemWindowId = GetSCBSystemWindowId();
5295 if (systemWindowId) {
5296 miscTextConfig->windowId = systemWindowId;
5297 }
5298 #endif
5299 auto textFieldManager = DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
5300 if (host && textFieldManager) {
5301 textFieldManager->SetImeAttached(true);
5302 textFieldManager->SetLastRequestKeyboardId(host->GetId());
5303 }
5304 OHOS::MiscServices::AttachOptions attachOptions;
5305 attachOptions.isShowKeyboard = needShowSoftKeyboard;
5306 attachOptions.requestKeyboardReason =
5307 static_cast<OHOS::MiscServices::RequestKeyboardReason>(static_cast<int32_t>(sourceType));
5308 auto ret = inputMethod->Attach(richEditTextChangeListener_, attachOptions, textconfig);
5309 if (ret == MiscServices::ErrorCode::NO_ERROR) {
5310 textFieldManager->SetIsImeAttached(true);
5311 textFieldManager->SetAttachInputId(host->GetId());
5312 }
5313 UpdateCaretInfoToController();
5314 if (context) {
5315 inputMethod->SetCallingWindow(context->GetWindowId());
5316 }
5317 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
5318 imeAttached_ = true;
5319 #endif
5320 return true;
5321 }
5322
GetMiscTextConfig()5323 std::optional<MiscServices::TextConfig> RichEditorPattern::GetMiscTextConfig()
5324 {
5325 auto tmpHost = GetHost();
5326 CHECK_NULL_RETURN(tmpHost, {});
5327 auto pipeline = tmpHost->GetContextRefPtr();
5328 auto renderContext = tmpHost->GetRenderContext();
5329 CHECK_NULL_RETURN(pipeline && renderContext, {});
5330
5331 float caretHeight = 0.0f;
5332 OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight);
5333 caretHeight = CalcCursorHeight(caretHeight);
5334
5335 // richeditor relative to root node offset(without transform)
5336 auto parentGlobalOffset = renderContext->GetPaintRectWithoutTransform().GetOffset() -
5337 pipeline->GetRootRect().GetOffset();
5338 // caret top (without transform)
5339 auto caretTop = caretOffset.GetY() + parentGlobalOffset.GetY();
5340 double positionY = parentGlobalOffset.GetY();
5341 double height = caretTop + caretHeight + KEYBOARD_AVOID_OFFSET.ConvertToPx() - positionY;
5342 std::u16string placeholder = TruncateText(UtfUtils::Str8ToStr16(GetPlaceHolder()), MAX_PLACEHOLDER_SIZE);
5343 std::u16string abilityName = TruncateText(UtfUtils::Str8ToStr16(AceApplicationInfo::GetInstance()
5344 .GetAbilityName()), MAX_ABILITY_NAME_SIZE);
5345
5346 if (auto manager = pipeline->GetSafeAreaManager(); manager) {
5347 auto keyboardOffset = manager->GetKeyboardOffset();
5348 positionY -= keyboardOffset;
5349 }
5350 OffsetF caretLeftTopPoint(caretOffset.GetX() + parentGlobalOffset.GetX(), caretTop);
5351 OffsetF caretRightBottomPoint(caretLeftTopPoint.GetX() + GetCaretWidth(), caretLeftTopPoint.GetY() + caretHeight);
5352 HandlePointWithTransform(caretLeftTopPoint);
5353 HandlePointWithTransform(caretRightBottomPoint);
5354 // window rect relative to screen
5355 auto windowRect = pipeline->GetCurrentWindowRect();
5356
5357 ContainerScope scope(GetInstanceId());
5358 auto container = AceType::DynamicCast<Platform::AceContainer>(Container::Current());
5359
5360 MiscServices::CursorInfo cursorInfo { .left = caretLeftTopPoint.GetX() + windowRect.Left(),
5361 .top = caretLeftTopPoint.GetY() + windowRect.Top(),
5362 .width = std::abs(caretLeftTopPoint.GetX() - caretRightBottomPoint.GetX()),
5363 .height = std::abs(caretLeftTopPoint.GetY() - caretRightBottomPoint.GetY()) };
5364 MiscServices::InputAttribute inputAttribute = { .inputPattern = (int32_t)TextInputType::UNSPECIFIED,
5365 .enterKeyType = (int32_t)GetTextInputActionValue(GetDefaultTextInputAction()),
5366 .isTextPreviewSupported = isTextPreviewSupported_ && (!isSpanStringMode_ || isAPI18Plus),
5367 .immersiveMode = static_cast<int32_t>(keyboardAppearance_),
5368 .placeholder = placeholder,
5369 .abilityName = abilityName };
5370 auto start = textSelector_.IsValid() ? textSelector_.GetStart() : caretPosition_;
5371 auto end = textSelector_.IsValid() ? textSelector_.GetEnd() : caretPosition_;
5372 MiscServices::TextConfig textConfig = { .inputAttribute = inputAttribute,
5373 .cursorInfo = cursorInfo,
5374 .range = { .start = start, .end = end },
5375 .windowId = pipeline->GetFocusWindowId(),
5376 .positionY = positionY + windowRect.Top(),
5377 .height = height,
5378 .abilityToken = container ? container->GetToken() : nullptr
5379 };
5380 return textConfig;
5381 }
5382
CalcCursorHeight(float & caretHeight)5383 float RichEditorPattern::CalcCursorHeight(float& caretHeight)
5384 {
5385 if (NearZero(caretHeight)) {
5386 auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
5387 caretHeight = overlayModifier ? overlayModifier->GetCaretHeight() : DEFAULT_CARET_HEIGHT;
5388 }
5389 if (NearZero(caretHeight)) {
5390 auto [caretAdjustOffset, caretAdjustHeight] = CalculateCaretOffsetAndHeight();
5391 caretHeight = caretAdjustHeight;
5392 }
5393 return caretHeight;
5394 }
5395
5396 #else
UnableStandardInput(bool isFocusViewChanged)5397 bool RichEditorPattern::UnableStandardInput(bool isFocusViewChanged)
5398 {
5399 auto host = GetHost();
5400 CHECK_NULL_RETURN(host, false);
5401 auto context = host->GetContext();
5402 CHECK_NULL_RETURN(context, false);
5403 #ifndef CROSS_PLATFORM
5404 if (HasConnection()) {
5405 connection_->Show(isFocusViewChanged, GetInstanceId());
5406 return true;
5407 }
5408 TextInputConfiguration config;
5409 config.type = TextInputType::UNSPECIFIED;
5410 config.action = TextInputAction::DONE;
5411 config.obscureText = false;
5412 #else
5413 TextInputConfiguration config;
5414 if (UnableStandardInputCrossPlatform(config, isFocusViewChanged)) {
5415 return true;
5416 }
5417 #endif
5418 connection_ =
5419 TextInputProxy::GetInstance().Attach(WeakClaim(this), config, context->GetTaskExecutor(), GetInstanceId());
5420 if (!HasConnection()) {
5421 return false;
5422 }
5423 TextEditingValue value;
5424 if (spans_.empty()) {
5425 value.text = UtfUtils::Str16ToStr8(textForDisplay_);
5426 } else {
5427 for (auto it = spans_.begin(); it != spans_.end(); it++) {
5428 if ((*it)->placeholderIndex < 0) {
5429 value.text.append(UtfUtils::Str16ToStr8((*it)->content));
5430 } else {
5431 value.text.append(" ");
5432 }
5433 }
5434 }
5435 value.selection.Update(caretPosition_, caretPosition_);
5436 connection_->SetEditingState(value, GetInstanceId());
5437 connection_->Show(isFocusViewChanged, GetInstanceId());
5438 return true;
5439 }
5440 #endif
5441 #ifdef CROSS_PLATFORM
UnableStandardInputCrossPlatform(TextInputConfiguration & config,bool isFocusViewChanged)5442 bool RichEditorPattern::UnableStandardInputCrossPlatform(TextInputConfiguration& config, bool isFocusViewChanged)
5443 {
5444 #ifdef ANDROID_PLATFORM
5445 if (HasConnection()) {
5446 auto isCurrentClient = Platform::TextInputClientHandler::GetInstance().ConnectionIsCurrent(GetInstanceId(),
5447 AceType::RawPtr(connection_));
5448 if (!isCurrentClient) {
5449 connection_ = nullptr;
5450 }
5451 }
5452 #endif
5453 if (HasConnection()) {
5454 #ifdef IOS_PLATFORM
5455 Platform::TextInputClientHandler::GetInstance().SetCurrentConnection(connection_);
5456 #endif
5457 connection_->Show(isFocusViewChanged, GetInstanceId());
5458 return true;
5459 }
5460 config.type = TextInputType::UNSPECIFIED;
5461 config.action = GetTextInputActionValue(GetDefaultTextInputAction());
5462 config.maxLength = maxLength_.value_or(INT_MAX);
5463 config.obscureText = false;
5464 return false;
5465 }
5466 #endif
5467
OnColorConfigurationUpdate()5468 void RichEditorPattern::OnColorConfigurationUpdate()
5469 {
5470 auto colorMode = GetColorMode();
5471 floatingCaretState_.UpdateOriginCaretColor(GetDisplayColorMode());
5472 if (colorMode == ColorMode::COLOR_MODE_UNDEFINED) {
5473 OnCommonColorChange();
5474 }
5475 }
5476
OnThemeScopeUpdate(int32_t themeScopeId)5477 bool RichEditorPattern::OnThemeScopeUpdate(int32_t themeScopeId)
5478 {
5479 IF_PRESENT(magnifierController_, SetColorModeChange(true));
5480 floatingCaretState_.UpdateOriginCaretColor(GetDisplayColorMode());
5481 OnCommonColorChange();
5482 return false;
5483 }
5484
OnCommonColorChange()5485 void RichEditorPattern::OnCommonColorChange()
5486 {
5487 auto host = GetHost();
5488 auto theme = GetTheme<RichEditorTheme>();
5489 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
5490 CHECK_NULL_VOID(host && theme && textLayoutProperty);
5491
5492 auto displayColorMode = GetDisplayColorMode();
5493 COLOR_MODE_LOCK(displayColorMode);
5494
5495 const auto& themeTextStyle = theme->GetTextStyle();
5496 auto themeTextColor = themeTextStyle.GetTextColor();
5497 auto themeTextDecColor = themeTextStyle.GetTextDecorationColor();
5498 textLayoutProperty->UpdateTextColor(themeTextColor);
5499 textLayoutProperty->UpdateTextDecorationColor(themeTextDecColor);
5500 auto themeUrlSpanColor = GetUrlSpanColor();
5501 textLayoutProperty->UpdateUrlDefualtColor(themeUrlSpanColor);
5502 textLayoutProperty->UpdateUrlHoverColor(GetUrlHoverColor());
5503 textLayoutProperty->UpdateUrlPressedColor(GetUrlPressColor());
5504
5505 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "theme, ColorMode=%{public}d, TextColor=%{public}s, DecorationColor=%{public}s",
5506 displayColorMode, themeTextColor.ToString().c_str(), themeTextDecColor.ToString().c_str());
5507
5508 const auto& spans = host->GetChildren();
5509 for (const auto& uiNode : spans) {
5510 auto placeholderSpan = DynamicCast<PlaceholderSpanNode>(uiNode);
5511 if (placeholderSpan) {
5512 auto spanItem = placeholderSpan->GetSpanItem();
5513 CHECK_NULL_CONTINUE(spanItem);
5514 IF_PRESENT(spanItem, UpdateColorByResourceId());
5515 }
5516 auto spanNode = DynamicCast<SpanNode>(uiNode);
5517 CHECK_NULL_CONTINUE(spanNode);
5518 auto spanItem = spanNode->GetSpanItem();
5519 CHECK_NULL_CONTINUE(spanItem);
5520 auto& textColor = spanItem->urlOnRelease ? themeUrlSpanColor : themeTextColor;
5521 IF_TRUE(spanItem->useThemeFontColor, spanNode->UpdateTextColorWithoutCheck(textColor));
5522 IF_TRUE(spanItem->useThemeDecorationColor, spanNode->UpdateTextDecorationColorWithoutCheck(themeTextDecColor));
5523 spanNode->UpdateColorByResourceId();
5524 }
5525 paragraphCache_.Clear();
5526 IF_PRESENT(typingTextStyle_, UpdateColorByResourceId());
5527 IF_PRESENT(typingStyle_, UpdateColorByResourceId());
5528 IF_PRESENT(selectedBackgroundColor_, UpdateColorByResourceId());
5529
5530 IF_PRESENT(magnifierController_, SetColorModeChange(true));
5531 auto scrollBar = GetScrollBar();
5532 auto scrollbarTheme = GetTheme<ScrollBarTheme>();
5533 CHECK_NULL_VOID(scrollBar && scrollbarTheme);
5534 scrollBar->SetForegroundColor(scrollbarTheme->GetForegroundColor());
5535 scrollBar->SetBackgroundColor(scrollbarTheme->GetBackgroundColor());
5536 }
5537
UpdateCaretInfoToController()5538 void RichEditorPattern::UpdateCaretInfoToController()
5539 {
5540 CHECK_NULL_VOID(HasFocus());
5541 std::u16string text = u"";
5542 for (auto iter = spans_.begin(); iter != spans_.end(); iter++) {
5543 text += (*iter)->content;
5544 }
5545 auto start = textSelector_.IsValid() ? textSelector_.GetTextStart() : caretPosition_;
5546 auto end = textSelector_.IsValid() ? textSelector_.GetTextEnd() : caretPosition_;
5547 #if defined(ENABLE_STANDARD_INPUT)
5548 auto miscTextConfig = GetMiscTextConfig();
5549 CHECK_NULL_VOID(miscTextConfig.has_value());
5550 MiscServices::CursorInfo cursorInfo = miscTextConfig.value().cursorInfo;
5551 MiscServices::InputMethodController::GetInstance()->OnCursorUpdate(cursorInfo);
5552 MiscServices::InputMethodController::GetInstance()->OnSelectionChange(
5553 text, start, end);
5554 TAG_LOGD(AceLogTag::ACE_RICH_TEXT,
5555 "CursorInfo: pos=[%{public}.2f,%{public}.2f], size=[%{public}.2f,%{public}.2f], caret=%{public}d;"
5556 "OnSelectionChange: textLen=%{public}zu, range=[%{public}d,%{public}d]",
5557 cursorInfo.left, cursorInfo.top, cursorInfo.width, cursorInfo.height, caretPosition_,
5558 text.length(), start, end);
5559 #else
5560 if (HasConnection()) {
5561 TextEditingValue editingValue;
5562 editingValue.text = UtfUtils::Str16ToStr8(text);
5563 editingValue.hint = "";
5564 editingValue.selection.Update(start, end);
5565 connection_->SetEditingState(editingValue, GetInstanceId());
5566 }
5567 #endif
5568 }
5569
HasConnection() const5570 bool RichEditorPattern::HasConnection() const
5571 {
5572 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
5573 return imeAttached_;
5574 #else
5575 return connection_ != nullptr;
5576 #endif
5577 }
5578
SetCustomKeyboardOption(bool supportAvoidance)5579 void RichEditorPattern::SetCustomKeyboardOption(bool supportAvoidance)
5580 {
5581 keyboardAvoidance_ = supportAvoidance;
5582 }
5583
RequestCustomKeyboard()5584 bool RichEditorPattern::RequestCustomKeyboard()
5585 {
5586 #if defined(ENABLE_STANDARD_INPUT)
5587 auto inputMethod = MiscServices::InputMethodController::GetInstance();
5588 if (inputMethod) {
5589 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "RequestCKeyboard,close softkeyboard.");
5590 inputMethod->RequestHideInput();
5591 inputMethod->Close();
5592 }
5593 #else
5594 if (HasConnection()) {
5595 connection_->Close(GetInstanceId());
5596 connection_ = nullptr;
5597 }
5598 #endif
5599
5600 if (isCustomKeyboardAttached_) {
5601 return true;
5602 }
5603 CHECK_NULL_RETURN(customKeyboardBuilder_, false);
5604 auto frameNode = GetHost();
5605 CHECK_NULL_RETURN(frameNode, false);
5606 auto pipeline = frameNode->GetContext();
5607 CHECK_NULL_RETURN(pipeline, false);
5608 auto overlayManager = pipeline->GetOverlayManager();
5609 CHECK_NULL_RETURN(overlayManager, false);
5610 overlayManager->SetCustomKeyboardOption(keyboardAvoidance_);
5611 auto textFieldManager = DynamicCast<TextFieldManagerNG>(pipeline->GetTextFieldManager());
5612 CHECK_NULL_RETURN(textFieldManager, false);
5613 if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_FOURTEEN)) {
5614 textFieldManager->SetUsingCustomKeyboardAvoid(keyboardAvoidance_);
5615 }
5616 overlayManager->BindKeyboard(customKeyboardBuilder_, frameNode->GetId());
5617 isCustomKeyboardAttached_ = true;
5618 contentChange_ = false;
5619 keyboardOverlay_ = overlayManager;
5620 auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
5621 keyboardOverlay_->AvoidCustomKeyboard(frameNode->GetId(), caretHeight);
5622 return true;
5623 }
5624
CloseCustomKeyboard()5625 bool RichEditorPattern::CloseCustomKeyboard()
5626 {
5627 auto frameNode = GetHost();
5628 CHECK_NULL_RETURN(frameNode, false);
5629 CHECK_NULL_RETURN(keyboardOverlay_, false);
5630 keyboardOverlay_->CloseKeyboard(frameNode->GetId());
5631 isCustomKeyboardAttached_ = false;
5632 contentChange_ = false;
5633 auto pipeline = frameNode->GetContext();
5634 CHECK_NULL_RETURN(pipeline, false);
5635 auto textFieldManager = DynamicCast<TextFieldManagerNG>(pipeline->GetTextFieldManager());
5636 CHECK_NULL_RETURN(textFieldManager, false);
5637 if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_FOURTEEN)) {
5638 textFieldManager->SetUsingCustomKeyboardAvoid(keyboardAvoidance_);
5639 }
5640 return true;
5641 }
5642
CheckPreviewTextValidate(const std::u16string & previewTextValue,const PreviewRange range)5643 int32_t RichEditorPattern::CheckPreviewTextValidate(const std::u16string& previewTextValue, const PreviewRange range)
5644 {
5645 if (IsDragging()) {
5646 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "SetPreviewText is not allowed while dragging");
5647 return ERROR_BAD_PARAMETERS;
5648 }
5649 return NO_ERRORS;
5650 }
5651
CheckPreviewTextValidate(const std::string & previewTextValue,const PreviewRange range)5652 int32_t RichEditorPattern::CheckPreviewTextValidate(const std::string& previewTextValue, const PreviewRange range)
5653 {
5654 return CheckPreviewTextValidate(UtfUtils::Str8DebugToStr16(previewTextValue), range);
5655 }
5656
SetPreviewText(const std::u16string & previewTextValue,const PreviewRange range)5657 int32_t RichEditorPattern::SetPreviewText(const std::u16string& previewTextValue, const PreviewRange range)
5658 {
5659 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "SetPreviewText, range=[%{public}d,%{public}d], isSSMode=%{public}d",
5660 range.start, range.end, isSpanStringMode_);
5661 CHECK_NULL_RETURN(!isSpanStringMode_ || isAPI18Plus, ERROR_BAD_PARAMETERS);
5662 SEC_TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "previewText=%{public}s", UtfUtils::Str16ToStr8(previewTextValue).c_str());
5663 auto host = GetHost();
5664 CHECK_NULL_RETURN(host, ERROR_BAD_PARAMETERS);
5665 CHECK_NULL_RETURN(!IsDragging(), ERROR_BAD_PARAMETERS);
5666
5667 if (!IsPreviewTextInputting()) {
5668 if (!InitPreviewText(previewTextValue, range)) {
5669 return ERROR_BAD_PARAMETERS;
5670 }
5671 } else {
5672 if (!UpdatePreviewText(previewTextValue, range)) {
5673 return ERROR_BAD_PARAMETERS;
5674 }
5675 }
5676 previewTextRecord_.replacedRange.Set(previewTextRecord_.startOffset, previewTextRecord_.endOffset);
5677 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
5678 return NO_ERRORS;
5679 }
5680
SetPreviewTextForDelete(int32_t oriLength,bool isBackward,bool isByIME)5681 bool RichEditorPattern::SetPreviewTextForDelete(int32_t oriLength, bool isBackward, bool isByIME)
5682 {
5683 if (!IsPreviewTextInputting() || !isByIME) {
5684 return true;
5685 }
5686 auto previewContent = previewTextRecord_.previewContent;
5687 auto startOffset = previewTextRecord_.startOffset;
5688 auto endOffset = previewTextRecord_.endOffset;
5689 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "SetPreviewTextForDelete offset=[%{public}d, %{public}d]",
5690 startOffset, endOffset);
5691 if (caretPosition_ < startOffset || caretPosition_ > endOffset) {
5692 TAG_LOGW(AceLogTag::ACE_RICH_TEXT,
5693 "preview abnormal, offset=[%{public}d, %{public}d], caretPosition_=%{public}d",
5694 startOffset, endOffset, caretPosition_);
5695 return true;
5696 }
5697
5698 std::u16string previewTextValue = u"";
5699 int32_t deleteLength = 0;
5700 PreviewRange range;
5701 if (isBackward) {
5702 deleteLength = std::clamp(oriLength, 0, caretPosition_ - startOffset);
5703 range.Set(startOffset, caretPosition_);
5704 previewTextValue = previewContent.substr(0, std::max(0, caretPosition_ - deleteLength - startOffset));
5705 } else {
5706 deleteLength = std::clamp(oriLength, 0, endOffset - caretPosition_);
5707 range.Set(startOffset, std::min(caretPosition_ + deleteLength, endOffset));
5708 previewTextValue = previewContent.substr(0, std::max(0, caretPosition_ - startOffset));
5709 }
5710 if (deleteLength == 0) {
5711 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "SetPreviewTextForDelete not deleted");
5712 return false;
5713 }
5714 SetPreviewText(previewTextValue, range);
5715 if (previewTextRecord_.previewContent.empty()) {
5716 FinishTextPreview();
5717 }
5718 return false;
5719 }
5720
InitPreviewText(const std::u16string & previewTextValue,const PreviewRange & range)5721 bool RichEditorPattern::InitPreviewText(const std::u16string& previewTextValue, const PreviewRange& range)
5722 {
5723 if (range.start != -1 || range.end != -1) {
5724 return ReplaceText(previewTextValue, range);
5725 }
5726 // interrupt touch selecting when initialize preview text
5727 ResetTouchSelectState();
5728 IF_PRESENT(magnifierController_, RemoveMagnifierFrameNode());
5729 auto& record = previewTextRecord_;
5730 record.needReplacePreviewText = true;
5731 record.previewTextHasStarted = true;
5732 record.replacedRange = range;
5733 record.startOffset = textSelector_.SelectNothing() ? caretPosition_ : textSelector_.GetTextStart();
5734 record.newPreviewContent = previewTextValue;
5735 auto length = static_cast<int32_t>(previewTextValue.length());
5736 record.endOffset = record.startOffset + length;
5737 auto spanCountBefore = spans_.size();
5738 ProcessInsertValue(previewTextValue, OperationType::IME, false);
5739 if (!previewTextRecord_.previewTextHasStarted) {
5740 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "previewText ends abnormally");
5741 return false;
5742 }
5743 record.isSpanSplit = spans_.size() - spanCountBefore > 1;
5744 record.previewContent = record.newPreviewContent;
5745 record.newPreviewContent.clear();
5746 record.needReplacePreviewText = false;
5747 return true;
5748 }
5749
ReplaceText(const std::u16string & previewTextValue,const PreviewRange & range)5750 bool RichEditorPattern::ReplaceText(const std::u16string& previewTextValue, const PreviewRange& range)
5751 {
5752 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "ReplaceText");
5753 if (range.start < 0 || range.end < range.start || range.end > GetTextContentLength()) {
5754 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "bad PreviewRange");
5755 return false;
5756 }
5757 previewTextRecord_.replacedRange = range;
5758 previewTextRecord_.needReplaceText = true;
5759 previewTextRecord_.startOffset = range.start;
5760 previewTextRecord_.endOffset = range.end;
5761 ProcessInsertValue(previewTextValue, OperationType::IME, false);
5762 previewTextRecord_.needReplaceText = false;
5763 return true;
5764 }
5765
5766 // Used for text replacement, without notifying developer caret change
DeleteByRange(OperationRecord * const record,int32_t start,int32_t end)5767 void RichEditorPattern::DeleteByRange(OperationRecord* const record, int32_t start, int32_t end)
5768 {
5769 auto length = end - start;
5770 CHECK_NULL_VOID(length > 0);
5771 lastCaretPosition_ = caretPosition_;
5772 caretPosition_ = std::clamp(start, 0, GetTextContentLength());
5773 if (isSpanStringMode_) {
5774 DeleteValueInStyledString(start, length, true, false);
5775 return;
5776 }
5777 std::u16string deleteText = DeleteForwardOperation(length, false);
5778 if (record && deleteText.length() != 0) {
5779 record->deleteText = deleteText;
5780 }
5781 }
5782
NotUpdateCaretInPreview(int32_t caret,const PreviewTextRecord & record)5783 bool RichEditorPattern::NotUpdateCaretInPreview(int32_t caret, const PreviewTextRecord& record)
5784 {
5785 CHECK_NULL_RETURN(record.IsValid(), false);
5786 bool caretInSecondPos = caret == record.startOffset + 1;
5787 bool caretNotInEndPos = caret != record.endOffset;
5788
5789 auto& curContent = record.previewContent;
5790 auto& newContent = record.newPreviewContent;
5791 auto enFilter = [](char c) { return isalpha(c) || c == '\''; };
5792 bool curAllEn = std::all_of(curContent.begin(), curContent.end(), enFilter);
5793 bool newAllEn = std::all_of(newContent.begin(), newContent.end(), enFilter);
5794
5795 return caretInSecondPos && caretNotInEndPos && curAllEn && newAllEn;
5796 }
5797
UpdatePreviewText(const std::u16string & previewTextValue,const PreviewRange & range)5798 bool RichEditorPattern::UpdatePreviewText(const std::u16string& previewTextValue, const PreviewRange& range)
5799 {
5800 auto& record = previewTextRecord_;
5801 if (range.start == -1 && range.end == -1 && !record.previewContent.empty()) {
5802 record.replacedRange.Set(record.startOffset, record.endOffset);
5803 record.newPreviewContent = previewTextValue;
5804 record.needReplacePreviewText = true;
5805 record.needUpdateCaret = !NotUpdateCaretInPreview(caretPosition_, record);
5806 ProcessInsertValue(previewTextValue, OperationType::IME, false);
5807 record.previewContent = record.newPreviewContent;
5808 record.newPreviewContent.clear();
5809 record.endOffset = record.startOffset + static_cast<int32_t>(previewTextValue.length());
5810 record.needReplacePreviewText = false;
5811 record.needUpdateCaret = true;
5812 } else {
5813 if (range.start < record.startOffset || range.end > record.endOffset || range.end < range.start) {
5814 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "bad PreviewRange");
5815 return false;
5816 }
5817 if (previewTextValue.empty() && range.start == range.end) {
5818 SetCaretPosition(range.end);
5819 return false;
5820 }
5821 auto replaceIndex = range.start - record.startOffset;
5822 auto replaceLength = range.end - range.start;
5823 auto oldContent = record.previewContent;
5824 auto oldPreviewLength = static_cast<int32_t>(oldContent.length());
5825 if (replaceIndex < 0 || replaceIndex + replaceLength > oldPreviewLength) {
5826 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "bad replaced range ");
5827 return false;
5828 }
5829 auto newContent = oldContent.replace(replaceIndex, replaceLength, previewTextValue);
5830 record.replacedRange = range;
5831 record.newPreviewContent = newContent;
5832 record.needReplacePreviewText = true;
5833 ProcessInsertValue(previewTextValue, OperationType::IME, false);
5834 record.previewContent = record.newPreviewContent;
5835 record.newPreviewContent.clear();
5836 record.endOffset = record.startOffset + static_cast<int32_t>(newContent.length());
5837 record.needReplacePreviewText = false;
5838 }
5839 return true;
5840 }
5841
GetPreviewTextInfo() const5842 const PreviewTextInfo RichEditorPattern::GetPreviewTextInfo() const
5843 {
5844 PreviewTextInfo info;
5845 if (!previewTextRecord_.previewContent.empty()) {
5846 info.value = previewTextRecord_.previewContent;
5847 info.offset = previewTextRecord_.startOffset;
5848 }
5849 return info;
5850 }
5851
MergeAdjacentSpans(int32_t caretPosition)5852 void RichEditorPattern::MergeAdjacentSpans(int32_t caretPosition)
5853 {
5854 auto host = GetContentHost();
5855 CHECK_NULL_VOID(host);
5856 auto uiNodes = host->GetChildren();
5857 auto it = std::find_if(uiNodes.begin(), uiNodes.end(), [caretPosition](const RefPtr<UINode>& uiNode) {
5858 auto spanNode = DynamicCast<SpanNode>(uiNode);
5859 CHECK_NULL_RETURN(spanNode, false);
5860 return spanNode->GetSpanItem()->position == caretPosition;
5861 });
5862 CHECK_NULL_VOID(it != uiNodes.end());
5863 auto beforeSpanNode = DynamicCast<SpanNode>(*it);
5864 ++it;
5865 CHECK_NULL_VOID(it != uiNodes.end());
5866 auto afterSpanNode = DynamicCast<SpanNode>(*it);
5867 CHECK_NULL_VOID(beforeSpanNode && afterSpanNode);
5868 CHECK_NULL_VOID(beforeSpanNode->GetTag() == V2::SPAN_ETS_TAG && afterSpanNode->GetTag() == V2::SPAN_ETS_TAG);
5869 auto beforeSpanItem = beforeSpanNode->GetSpanItem();
5870 auto afterSpanItem = afterSpanNode->GetSpanItem();
5871 CHECK_NULL_VOID(beforeSpanItem && afterSpanItem);
5872 beforeSpanNode->UpdateContent(beforeSpanItem->content + afterSpanItem->content);
5873 afterSpanItem->content.clear();
5874 RemoveEmptySpans();
5875 }
5876
FinishTextPreview()5877 void RichEditorPattern::FinishTextPreview()
5878 {
5879 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "FinishTextPreview byImf");
5880 if (previewTextRecord_.previewContent.empty()) {
5881 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "previewContent is empty");
5882 RemoveEmptySpans();
5883 IF_TRUE(previewTextRecord_.isSpanSplit, MergeAdjacentSpans(caretPosition_));
5884 previewTextRecord_.Reset();
5885 return;
5886 }
5887 auto previewContent = previewTextRecord_.previewContent;
5888 FinishTextPreviewInner();
5889 ProcessInsertValue(previewContent, OperationType::FINISH_PREVIEW, true);
5890 }
5891
FinishTextPreviewInner(bool deletePreviewText)5892 void RichEditorPattern::FinishTextPreviewInner(bool deletePreviewText)
5893 {
5894 CHECK_NULL_VOID(previewTextRecord_.previewTextHasStarted);
5895 TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
5896 "FinishTextPreviewInner, deleteText=%{public}d, previewContent is empty=%{public}d", deletePreviewText,
5897 previewTextRecord_.previewContent.empty());
5898 previewTextRecord_.previewTextExiting = true;
5899 IF_TRUE(deletePreviewText && !previewTextRecord_.previewContent.empty(),
5900 DeleteByRange(nullptr, previewTextRecord_.startOffset, previewTextRecord_.endOffset));
5901 previewTextRecord_.Reset();
5902 }
5903
NotifyExitTextPreview(bool deletePreviewText)5904 void RichEditorPattern::NotifyExitTextPreview(bool deletePreviewText)
5905 {
5906 CHECK_NULL_VOID(previewTextRecord_.previewTextHasStarted);
5907 CHECK_NULL_VOID(HasFocus());
5908 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "NotifyExitTextPreview");
5909 auto startOffset = previewTextRecord_.startOffset;
5910 auto length = previewTextRecord_.endOffset - previewTextRecord_.startOffset;
5911 undoManager_->RecordSelectionBefore(TextRange{ startOffset, startOffset });
5912 FinishTextPreviewInner(deletePreviewText);
5913 if (!deletePreviewText) {
5914 UndoRedoRecord styledRecord;
5915 undoManager_->UpdateRecordBeforeChange(startOffset, 0, styledRecord);
5916 undoManager_->UpdateRecordAfterChange(startOffset, length, styledRecord);
5917 undoManager_->RecordInsertOperation(styledRecord);
5918 }
5919 NotifyImfFinishTextPreview();
5920 }
5921
NotifyImfFinishTextPreview()5922 void RichEditorPattern::NotifyImfFinishTextPreview()
5923 {
5924 #if defined(ENABLE_STANDARD_INPUT)
5925 MiscServices::InputMethodController::GetInstance()->OnSelectionChange(u"", caretPosition_, caretPosition_);
5926 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "notify imf that richEditor exit textPreview");
5927 #endif
5928 }
5929
GetPreviewTextRects()5930 std::vector<RectF> RichEditorPattern::GetPreviewTextRects()
5931 {
5932 auto rects = paragraphs_.GetRects(previewTextRecord_.startOffset, previewTextRecord_.endOffset,
5933 RectHeightPolicy::COVER_TEXT);
5934 auto offset = GetTextRect().GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
5935 for (RectF& rect : rects) {
5936 rect += offset;
5937 }
5938 return rects;
5939 }
5940
GetPreviewTextStyle() const5941 PreviewTextStyle RichEditorPattern::GetPreviewTextStyle() const
5942 {
5943 auto previewTextStyle = PreviewTextStyle::NORMAL;
5944 auto property = GetLayoutProperty<RichEditorLayoutProperty>();
5945 if (property && property->HasPreviewTextStyle()) {
5946 auto style = property->GetPreviewTextStyle();
5947 CHECK_NULL_RETURN(style.has_value(), previewTextStyle);
5948 if (style.value() == PREVIEW_STYLE_NORMAL) {
5949 previewTextStyle = PreviewTextStyle::NORMAL;
5950 } else if (style.value() == PREVIEW_STYLE_UNDERLINE) {
5951 previewTextStyle = PreviewTextStyle::UNDERLINE;
5952 } else {
5953 TAG_LOGW(
5954 AceLogTag::ACE_RICH_TEXT, "invalid previewTextStyle of RichEditorLayoutProperty");
5955 }
5956 }
5957 return previewTextStyle;
5958 }
5959
GetPreviewTextDecorationColor() const5960 const Color& RichEditorPattern::GetPreviewTextDecorationColor() const
5961 {
5962 auto pipeline = PipelineBase::GetCurrentContextSafelyWithCheck();
5963 CHECK_NULL_RETURN(pipeline, Color::TRANSPARENT);
5964 auto theme = pipeline->GetTheme<RichEditorTheme>();
5965 CHECK_NULL_RETURN(theme, Color::TRANSPARENT);
5966 if (GetPreviewTextStyle() == PreviewTextStyle::UNDERLINE) {
5967 return theme->GetPreviewUnderLineColor();
5968 }
5969 return Color::TRANSPARENT;
5970 }
5971
GetPreviewTextUnderlineWidth() const5972 float RichEditorPattern::GetPreviewTextUnderlineWidth() const
5973 {
5974 auto pipeline = PipelineBase::GetCurrentContextSafelyWithCheck();
5975 CHECK_NULL_RETURN(pipeline, 0.0f);
5976 auto theme = pipeline->GetTheme<RichEditorTheme>();
5977 CHECK_NULL_RETURN(theme, 0.0f);
5978 return theme->GetPreviewUnderlineWidth().ConvertToPx();
5979 }
5980
IsIMEOperation(OperationType operationType)5981 bool RichEditorPattern::IsIMEOperation(OperationType operationType)
5982 {
5983 return operationType == OperationType::IME
5984 || operationType == OperationType::FINISH_PREVIEW
5985 || operationType == OperationType::STYLUS;
5986 }
5987
InsertValue(const std::string & insertValue,bool isIME)5988 void RichEditorPattern::InsertValue(const std::string& insertValue, bool isIME)
5989 {
5990 InsertValue(UtfUtils::Str8ToStr16(insertValue), isIME);
5991 }
5992
InsertValue(const std::u16string & insertValue,bool isIME)5993 void RichEditorPattern::InsertValue(const std::u16string& insertValue, bool isIME)
5994 {
5995 InsertValueByOperationType(insertValue, isIME ? OperationType::IME : OperationType::DEFAULT);
5996 }
5997
InsertValueByOperationType(const std::u16string & insertValue,OperationType operationType)5998 void RichEditorPattern::InsertValueByOperationType(const std::u16string& insertValue, OperationType operationType)
5999 {
6000 ProcessInsertValue(insertValue, operationType, true);
6001 }
6002
ProcessTextTruncationOperation(std::u16string & text,bool shouldCommitInput)6003 bool RichEditorPattern::ProcessTextTruncationOperation(std::u16string& text, bool shouldCommitInput)
6004 {
6005 #if defined(IOS_PLATFORM)
6006 if (compose_.IsValid()) {
6007 return true;
6008 }
6009 if (GetTextContentLength() - text.length() < maxLength_.value_or(INT_MAX) && text.length() == 1 && !unmarkText_) {
6010 return true;
6011 }
6012 #endif
6013 bool needTruncationInsertValue = shouldCommitInput || !previewTextRecord_.needReplacePreviewText;
6014 int32_t selectLength =
6015 textSelector_.SelectNothing() ? 0 : textSelector_.GetTextEnd() - textSelector_.GetTextStart();
6016 int32_t previewContentLength = previewTextRecord_.previewContent.empty()
6017 ? previewTextRecord_.replacedRange.end - previewTextRecord_.replacedRange.start
6018 : static_cast<int32_t>(previewTextRecord_.previewContent.length());
6019
6020 if (!needTruncationInsertValue || GetTextContentLength() - previewContentLength < maxLength_.value_or(INT_MAX)) {
6021 if (needTruncationInsertValue && text.length() != 1) {
6022 auto maxLength = maxLength_.value_or(INT_MAX) - GetTextContentLength() + previewContentLength;
6023 auto allowInsertLength = CalculateTruncationLength(text, maxLength);
6024 if (allowInsertLength == 0) {
6025 TAG_LOGD(AceLogTag::ACE_RICH_TEXT,
6026 "ProcessTextTruncation: No space to insert text. maxLength=%{public}d",
6027 maxLength_.value_or(INT_MAX));
6028 return false;
6029 }
6030 text = text.substr(0, allowInsertLength);
6031 return true;
6032 }
6033 return true;
6034 }
6035
6036 if (previewTextRecord_.needReplaceText) {
6037 text = text.substr(0, maxLength_.value_or(INT_MAX) + selectLength);
6038 return true;
6039 }
6040 if (!textSelector_.SelectNothing()) {
6041 auto allowInsertLength = CalculateTruncationLength(text, 0);
6042 if (allowInsertLength == 0) {
6043 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "ProcessTextTruncation: No space to insert text. maxLength=%{public}d",
6044 maxLength_.value_or(INT_MAX));
6045 return false;
6046 }
6047 text = text.substr(0, allowInsertLength);
6048 return true;
6049 }
6050 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "ProcessTextTruncation: maxLength=%{public}d", maxLength_.value_or(INT_MAX));
6051 IF_TRUE(IsPreviewTextInputting(), FinishTextPreviewInner());
6052 return false;
6053 }
6054
ProcessInsertValueMore(const std::u16string & text,OperationRecord record,OperationType operationType,RichEditorChangeValue changeValue,PreviewTextRecord preRecord,bool shouldCommitInput)6055 void RichEditorPattern::ProcessInsertValueMore(const std::u16string& text, OperationRecord record,
6056 OperationType operationType, RichEditorChangeValue changeValue, PreviewTextRecord preRecord, bool shouldCommitInput)
6057 {
6058 if (preRecord.needReplacePreviewText && !previewTextRecord_.needReplacePreviewText) {
6059 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "previewText finished when ProcessInsertValue");
6060 NotifyImfFinishTextPreview();
6061 return;
6062 }
6063 ClearRedoOperationRecords();
6064 #if defined(IOS_PLATFORM)
6065 if (compose_.IsValid() && (record.addText.value_or(u"").length() > 0 || unmarkText_)) {
6066 DeleteByRange(&record, compose_.GetStart(), compose_.GetEnd());
6067 }
6068 #endif
6069 InsertValueOperation(text, &record, operationType, shouldCommitInput);
6070 record.afterCaretPosition = caretPosition_;
6071 if (isDragSponsor_) {
6072 record.deleteCaretPosition = dragRange_.first;
6073 }
6074 IF_TRUE(shouldCommitInput, AddInsertOperationRecord(record));
6075 AfterContentChange(changeValue);
6076 }
6077
6078 // operationType: when type is IME, it controls whether to perform ime callbacks
6079 // shouldCommitInput: true means real input; false means preview input
ProcessInsertValue(const std::u16string & insertValue,OperationType operationType,bool shouldCommitInput)6080 void RichEditorPattern::ProcessInsertValue(const std::u16string& insertValue, OperationType operationType,
6081 bool shouldCommitInput)
6082 {
6083 CONTENT_MODIFY_LOCK(this);
6084 auto text = insertValue;
6085 if (!ProcessTextTruncationOperation(text, shouldCommitInput)) {
6086 return;
6087 }
6088 bool isIME = IsIMEOperation(operationType);
6089 TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
6090 "insertLen=%{public}zu, isIME=%{public}d, shouldCommitInput=%{public}d, isSpanString=%{public}d",
6091 insertValue.length(), isIME, shouldCommitInput, isSpanStringMode_);
6092 SEC_TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "insertValue=%{public}s",
6093 StringUtils::RestoreEscape(UtfUtils::Str16ToStr8(insertValue)).c_str());
6094
6095 if (isIME && shouldCommitInput && (!isEditing_ || IsDragging()) && operationType != OperationType::FINISH_PREVIEW) {
6096 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "NOT allow input, isEditing=%{public}d, isDragging=%{public}d",
6097 isEditing_, IsDragging());
6098 return;
6099 }
6100 if (isSpanStringMode_) {
6101 InsertValueInStyledString(text, shouldCommitInput);
6102 return;
6103 }
6104 OperationRecord record;
6105 record.beforeCaretPosition = textSelector_.IsValid() ? textSelector_.GetTextStart() : caretPosition_ + moveLength_;
6106 record.addText = text;
6107
6108 RichEditorChangeValue changeValue(OPERATION_REASON_MAP.find(operationType)->second);
6109 PreviewTextRecord preRecord = previewTextRecord_;
6110 bool allowContentChange = BeforeChangeText(changeValue, record, RecordType::INSERT);
6111 if (shouldCommitInput && previewTextRecord_.IsValid()) {
6112 FinishTextPreviewInner();
6113 record.beforeCaretPosition = caretPosition_;
6114 }
6115 bool allowImeInput = isIME ? BeforeIMEInsertValue(text) : true;
6116 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "allowContentChange=%{public}d, allowImeInput=%{public}d, needReplacePreviewText=%{public}d",
6117 allowContentChange, allowImeInput, previewTextRecord_.needReplacePreviewText);
6118 bool allowPreviewText = previewTextRecord_.needReplacePreviewText;
6119 bool isAllowInsert = (allowContentChange && allowImeInput) || allowPreviewText;
6120 if (!isAllowInsert) {
6121 previewInputRecord_.Reset();
6122 undoManager_->ClearPreviewInputRecord();
6123 #if defined(IOS_PLATFORM)
6124 if (compose_.IsValid() && (record.addText.value_or(u"").length() > 0 || unmarkText_)) {
6125 DeleteByRange(&record, compose_.GetStart(), compose_.GetEnd());
6126 }
6127 #endif
6128 return;
6129 }
6130 ProcessInsertValueMore(text, record, operationType, changeValue, preRecord, shouldCommitInput);
6131 }
6132
DeleteSelectionOrPreviewText(OperationRecord * const record,UndoRedoRecord & styledRecord,bool shouldCommitInput)6133 void RichEditorPattern::DeleteSelectionOrPreviewText(
6134 OperationRecord* const record, UndoRedoRecord& styledRecord, bool shouldCommitInput)
6135 {
6136 bool isSelector = textSelector_.IsValid();
6137 auto rangeStart = isSelector ? TextRange{ textSelector_.GetTextStart(), textSelector_.GetTextEnd() }
6138 : TextRange{ caretPosition_, caretPosition_};
6139 if (shouldCommitInput) {
6140 undoManager_->UpdateRecordBeforeChange(rangeStart.start, rangeStart.GetLength(), styledRecord);
6141 } else if (isSelector) {
6142 undoManager_->RecordPreviewInputtingStart(rangeStart.start, rangeStart.GetLength());
6143 }
6144 if (isSelector) {
6145 DeleteByRange(record, textSelector_.GetTextStart(), textSelector_.GetTextEnd());
6146 if (!shouldCommitInput && record) {
6147 previewInputRecord_.deleteText = record->deleteText;
6148 previewInputRecord_.beforeCaretPosition = rangeStart.start;
6149 }
6150 ResetSelection();
6151 } else if (previewTextRecord_.needReplacePreviewText || previewTextRecord_.needReplaceText) {
6152 DeleteByRange(record, previewTextRecord_.replacedRange.start, previewTextRecord_.replacedRange.end);
6153 }
6154 }
6155
InsertValueOperation(const std::u16string & insertValue,OperationRecord * const record,OperationType operationType,bool shouldCommitInput)6156 void RichEditorPattern::InsertValueOperation(const std::u16string& insertValue, OperationRecord* const record,
6157 OperationType operationType, bool shouldCommitInput)
6158 {
6159 UndoRedoRecord styledRecord;
6160 DeleteSelectionOrPreviewText(record, styledRecord, shouldCommitInput);
6161 bool isSingleHandleMoving = selectOverlay_->IsSingleHandleMoving();
6162 CloseSelectOverlay();
6163 TextInsertValueInfo info;
6164 CalcInsertValueObj(info);
6165 IF_TRUE((!caretVisible_ || isSingleHandleMoving) && HasFocus(), StartTwinkling());
6166 auto host = GetContentHost();
6167 CHECK_NULL_VOID(host);
6168 bool isIME = IsIMEOperation(operationType);
6169 RefPtr<SpanNode> spanNode = DynamicCast<SpanNode>(host->GetChildAtIndex(info.GetSpanIndex()));
6170 RefPtr<SpanNode> spanNodeBefore = DynamicCast<SpanNode>(host->GetChildAtIndex(info.GetSpanIndex() - 1));
6171 RefPtr<SpanNode> targetSpanNode = spanNode;
6172 bool needCreateNewSpan = host->GetChildren().empty();
6173 if (info.GetOffsetInSpan() == 0) {
6174 bool spanNodeBeforeCanInsert = spanNodeBefore && spanNodeBefore->GetTag() == V2::SPAN_ETS_TAG;
6175 bool spanNodeCanInsert = spanNode && spanNode->GetTag() == V2::SPAN_ETS_TAG;
6176 bool insertToBeforeNode = spanNodeBeforeCanInsert && !spanNodeCanInsert;
6177 insertToBeforeNode |= spanNodeBeforeCanInsert && spanNodeCanInsert && !IsLineSeparatorInLast(spanNodeBefore);
6178 if (insertToBeforeNode) {
6179 auto spanItem = spanNodeBefore->GetSpanItem();
6180 info.SetSpanIndex(info.GetSpanIndex() - 1);
6181 info.SetOffsetInSpan(spanItem->position - spanItem->rangeStart);
6182 targetSpanNode = spanNodeBefore;
6183 }
6184 needCreateNewSpan |= !spanNodeBeforeCanInsert && !spanNodeCanInsert;
6185 }
6186 auto insertLength = static_cast<int32_t>(insertValue.length());
6187 bool isCreate = true;
6188 if (needCreateNewSpan) {
6189 CreateTextSpanNode(targetSpanNode, info, insertValue);
6190 } else if (typingStyle_.has_value() && !HasSameTypingStyle(targetSpanNode)) {
6191 InsertDiffStyleValueInSpan(targetSpanNode, info, insertValue);
6192 } else {
6193 InsertValueToSpanNode(targetSpanNode, insertValue, info);
6194 isCreate = false;
6195 }
6196 if (shouldCommitInput) {
6197 undoManager_->UpdateRecordAfterChange(styledRecord.rangeBefore.start, insertLength, styledRecord);
6198 undoManager_->RecordInsertOperation(styledRecord);
6199 }
6200 AfterInsertValue(targetSpanNode, insertLength, isCreate, isIME);
6201 }
6202
CreateTextStyleByTypingStyle()6203 TextStyle RichEditorPattern::CreateTextStyleByTypingStyle()
6204 {
6205 auto theme = GetTheme<RichEditorTheme>();
6206 auto ret = theme ? theme->GetTextStyle() : TextStyle();
6207 CHECK_NULL_RETURN(typingStyle_.has_value() && typingTextStyle_.has_value(), ret);
6208 const auto& updateSpanStyle = typingStyle_.value();
6209 const auto& textStyle = typingTextStyle_.value();
6210 IF_TRUE(updateSpanStyle.updateFontFeature, ret.SetFontFeatures(textStyle.GetFontFeatures()));
6211 IF_TRUE(updateSpanStyle.updateTextColor, ret.SetTextColor(textStyle.GetTextColor()));
6212 IF_TRUE(updateSpanStyle.updateLineHeight, ret.SetLineHeight(textStyle.GetLineHeight()));
6213 IF_TRUE(updateSpanStyle.updateLetterSpacing, ret.SetLetterSpacing(textStyle.GetLetterSpacing()));
6214 IF_TRUE(updateSpanStyle.updateFontSize, ret.SetFontSize(textStyle.GetFontSize()));
6215 IF_TRUE(updateSpanStyle.updateItalicFontStyle, ret.SetFontStyle(textStyle.GetFontStyle()));
6216 IF_TRUE(updateSpanStyle.updateFontWeight, ret.SetFontWeight(textStyle.GetFontWeight()));
6217 IF_TRUE(updateSpanStyle.updateFontFamily, ret.SetFontFamilies(textStyle.GetFontFamilies()));
6218 IF_TRUE(updateSpanStyle.updateTextShadows, ret.SetTextShadows(textStyle.GetTextShadows()));
6219 IF_TRUE(updateSpanStyle.updateHalfLeading, ret.SetHalfLeading(textStyle.GetHalfLeading()));
6220 IF_TRUE(updateSpanStyle.updateTextDecoration, ret.SetTextDecoration(textStyle.GetTextDecoration()));
6221 IF_TRUE(updateSpanStyle.updateTextDecorationColor, ret.SetTextDecorationColor(textStyle.GetTextDecorationColor()));
6222 IF_TRUE(updateSpanStyle.updateTextDecorationStyle, ret.SetTextDecorationStyle(textStyle.GetTextDecorationStyle()));
6223 IF_TRUE(updateSpanStyle.updateLineThicknessScale, ret.SetLineThicknessScale(textStyle.GetLineThicknessScale()));
6224 IF_TRUE(updateSpanStyle.updateTextBackgroundStyle, ret.SetTextBackgroundStyle(textStyle.GetTextBackgroundStyle()));
6225 return ret;
6226 }
6227
InsertDiffStyleValueInSpan(RefPtr<SpanNode> & spanNode,const TextInsertValueInfo & info,const std::u16string & insertValue)6228 void RichEditorPattern::InsertDiffStyleValueInSpan(
6229 RefPtr<SpanNode>& spanNode, const TextInsertValueInfo& info, const std::u16string& insertValue)
6230 {
6231 auto host = GetContentHost();
6232 CHECK_NULL_VOID(host);
6233 TextSpanOptions options;
6234 options.value = insertValue;
6235 options.offset = caretPosition_;
6236 options.style = CreateTextStyleByTypingStyle();
6237 options.useThemeFontColor = typingStyle_->useThemeFontColor;
6238 options.useThemeDecorationColor = typingStyle_->useThemeDecorationColor;
6239 options.optionSource = OptionSource::IME_INSERT;
6240 bool useTypingParaStyle = styleManager_->UseTypingParaStyle(spans_, caretPosition_);
6241 IF_TRUE(useTypingParaStyle, options.paraStyle = styleManager_->GetTypingParagraphStyle());
6242 auto newSpanIndex = AddTextSpanOperation(options, false, -1, true, false);
6243 auto newSpanNode = DynamicCast<SpanNode>(host->GetChildAtIndex(newSpanIndex));
6244 if (!useTypingParaStyle || !options.paraStyle.has_value()) {
6245 CopyTextSpanLineStyle(spanNode, newSpanNode, true);
6246 }
6247 spanNode = newSpanNode;
6248 }
6249
IsLineSeparatorInLast(RefPtr<SpanNode> & spanNode)6250 bool RichEditorPattern::IsLineSeparatorInLast(RefPtr<SpanNode>& spanNode)
6251 {
6252 std::u16string content = spanNode->GetSpanItem()->content;
6253 return !content.empty() && content.back() == u'\n';
6254 }
6255
InsertValueToSpanNode(RefPtr<SpanNode> & spanNode,const std::u16string & insertValue,const TextInsertValueInfo & info)6256 void RichEditorPattern::InsertValueToSpanNode(
6257 RefPtr<SpanNode>& spanNode, const std::u16string& insertValue, const TextInsertValueInfo& info)
6258 {
6259 auto spanItem = spanNode->GetSpanItem();
6260 CHECK_NULL_VOID(spanItem);
6261 auto textTemp = spanItem->content;
6262 auto textTempSize = static_cast<int32_t>(textTemp.size());
6263 if (textTempSize < info.GetOffsetInSpan() || info.GetOffsetInSpan() < 0) {
6264 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "InsertValueToSpanNode error, spanSize=%{public}d, offsetInSpan=%{public}d",
6265 textTempSize, info.GetOffsetInSpan());
6266 RichEditorInfo errorInfo { RichEditorErrorType::INSERT_VALUE, textTempSize,
6267 static_cast<int32_t>(insertValue.length()), info.GetOffsetInSpan() };
6268 RichEditorErrorReport(errorInfo);
6269 return;
6270 }
6271 bool needTypingParaStyle = styleManager_->NeedTypingParagraphStyle(spans_, caretPosition_);
6272 textTemp.insert(info.GetOffsetInSpan(), insertValue);
6273 spanNode->UpdateContent(textTemp);
6274 UpdateSpanPosition();
6275 SpanNodeFission(spanNode);
6276 if (needTypingParaStyle) {
6277 auto spanIter = spans_.begin();
6278 auto spanIndex = info.GetSpanIndex();
6279 std::advance(spanIter, spanIndex + 1);
6280 while (spanIter != spans_.end()) {
6281 styleManager_->UpdateStyleByTypingParagraphStyle(*(spanIter));
6282 ++spanIter;
6283 }
6284 }
6285 }
6286
CreateTextSpanNode(RefPtr<SpanNode> & spanNode,const TextInsertValueInfo & info,const std::u16string & insertValue)6287 void RichEditorPattern::CreateTextSpanNode(
6288 RefPtr<SpanNode>& spanNode, const TextInsertValueInfo& info, const std::u16string& insertValue)
6289 {
6290 auto host = GetContentHost();
6291 CHECK_NULL_VOID(host);
6292 spanNode = SpanNode::GetOrCreateSpanNode(ElementRegister::GetInstance()->MakeUniqueId());
6293 spanNode->MountToParent(host, info.GetSpanIndex());
6294 auto spanItem = spanNode->GetSpanItem();
6295 if (typingStyle_.has_value() && typingTextStyle_.has_value()) {
6296 spanItem->useThemeFontColor = typingStyle_->useThemeFontColor;
6297 spanItem->useThemeDecorationColor = typingStyle_->useThemeDecorationColor;
6298 UpdateTextStyle(spanNode, typingStyle_.value(), typingTextStyle_.value());
6299 auto spanItem = spanNode->GetSpanItem();
6300 spanItem->SetTextStyle(typingTextStyle_);
6301 } else {
6302 spanNode->UpdateFontSize(Dimension(DEFAULT_TEXT_SIZE, DimensionUnit::FP));
6303 SetDefaultColor(spanNode);
6304 }
6305 bool needTypingParaStyle = styleManager_->UseTypingParaStyle(spans_, caretPosition_);
6306 bool useTypingParaStyle = needTypingParaStyle && styleManager_->UpdateStyleByTypingParagraphStyle(spanNode);
6307 AddSpanItem(spanItem, info.GetSpanIndex());
6308 spanNode->UpdateContent(insertValue);
6309 UpdateSpanPosition();
6310 SpanNodeFission(spanNode, useTypingParaStyle);
6311 }
6312
SetDefaultColor(RefPtr<SpanNode> & spanNode)6313 void RichEditorPattern::SetDefaultColor(RefPtr<SpanNode>& spanNode)
6314 {
6315 auto richEditorTheme = GetTheme<RichEditorTheme>();
6316 CHECK_NULL_VOID(richEditorTheme);
6317 Color textColor = richEditorTheme->GetTextStyle().GetTextColor();
6318 spanNode->UpdateTextColorWithoutCheck(textColor);
6319 spanNode->UpdateTextDecorationColorWithoutCheck(textColor);
6320 if (auto& spanItem = spanNode->GetSpanItem(); spanItem && spanItem->urlOnRelease) {
6321 spanNode->UpdateTextColor(GetUrlSpanColor());
6322 }
6323 }
6324
BeforeIMEInsertValue(const std::u16string & insertValue)6325 bool RichEditorPattern::BeforeIMEInsertValue(const std::u16string& insertValue)
6326 {
6327 auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
6328 CHECK_NULL_RETURN(eventHub, true);
6329 RichEditorInsertValue insertValueInfo;
6330 insertValueInfo.SetInsertOffset(caretPosition_);
6331 if (!previewTextRecord_.newPreviewContent.empty()) {
6332 insertValueInfo.SetPreviewText(previewTextRecord_.newPreviewContent);
6333 } else {
6334 insertValueInfo.SetInsertValue(insertValue);
6335 }
6336 return eventHub->FireAboutToIMEInput(insertValueInfo);
6337 }
6338
AfterInsertValue(const RefPtr<SpanNode> & spanNode,int32_t insertValueLength,bool isCreate,bool isIME)6339 void RichEditorPattern::AfterInsertValue(
6340 const RefPtr<SpanNode>& spanNode, int32_t insertValueLength, bool isCreate, bool isIME)
6341 {
6342 isTextChange_ = true;
6343 moveDirection_ = MoveDirection::FORWARD;
6344 moveLength_ += insertValueLength;
6345 IF_TRUE(!previewTextRecord_.needUpdateCaret, moveLength_ = 0);
6346 UpdateSpanPosition();
6347 if (isIME || aiWriteAdapter_->GetAIWrite()) {
6348 AfterIMEInsertValue(spanNode, insertValueLength, isCreate);
6349 return;
6350 }
6351 MoveCaretAfterTextChange();
6352 }
6353
AfterIMEInsertValue(const RefPtr<SpanNode> & spanNode,int32_t insertValueLength,bool isCreate)6354 bool RichEditorPattern::AfterIMEInsertValue(const RefPtr<SpanNode>& spanNode, int32_t insertValueLength, bool isCreate)
6355 {
6356 ACE_SCOPED_TRACE("RichEditorAfterIMEInsertValue");
6357 auto host = GetContentHost();
6358 CHECK_NULL_RETURN(host, false);
6359 auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
6360 CHECK_NULL_RETURN(eventHub, false);
6361
6362 RichEditorAbstractSpanResult retInfo;
6363 retInfo.SetSpanIndex(host->GetChildIndex(spanNode));
6364 retInfo.SetEraseLength(insertValueLength);
6365 auto spanItem = spanNode->GetSpanItem();
6366 if (!previewTextRecord_.newPreviewContent.empty()) {
6367 retInfo.SetPreviewText(previewTextRecord_.newPreviewContent);
6368 } else {
6369 retInfo.SetValue(spanItem->content);
6370 }
6371 auto contentLength = static_cast<int32_t>(spanItem->content.length());
6372 retInfo.SetSpanRangeStart(spanItem->position - contentLength);
6373 retInfo.SetSpanRangeEnd(spanItem->position);
6374 retInfo.SetOffsetInSpan(caretPosition_ - retInfo.GetSpanRangeStart());
6375 retInfo.SetFontColor(spanNode->GetTextColorValue(Color::BLACK).ColorToString());
6376 retInfo.SetFontSize(spanNode->GetFontSizeValue(Dimension(DEFAULT_TEXT_SIZE, DimensionUnit::FP)).ConvertToVp());
6377 retInfo.SetFontStyle(spanNode->GetItalicFontStyleValue(OHOS::Ace::FontStyle::NORMAL));
6378 retInfo.SetFontWeight(static_cast<int32_t>(spanNode->GetFontWeightValue(FontWeight::NORMAL)));
6379 retInfo.SetTextStyle(GetTextStyleObject(spanNode));
6380 retInfo.SetUrlAddress(spanItem->GetUrlAddress());
6381 std::string fontFamilyValue;
6382 auto fontFamily = spanNode->GetFontFamilyValue({ "HarmonyOS Sans" });
6383 for (const auto& str : fontFamily) {
6384 fontFamilyValue += str;
6385 }
6386 retInfo.SetFontFamily(fontFamilyValue);
6387 retInfo.SetTextDecoration(spanNode->GetTextDecorationFirst());
6388 retInfo.SetTextDecorationStyle(spanNode->GetTextDecorationStyleValue(TextDecorationStyle::SOLID));
6389 retInfo.SetLineThicknessScale(spanNode->GetLineThicknessScaleValue(1.0f));
6390 retInfo.SetFontFeature(spanNode->GetFontFeatureValue(ParseFontFeatureSettings("\"pnum\" 1")));
6391 retInfo.SetColor(spanNode->GetTextDecorationColorValue(Color::BLACK).ColorToString());
6392 TextRange onDidIMEInputRange{ caretPosition_, caretPosition_ + insertValueLength };
6393 MoveCaretAfterTextChange();
6394 eventHub->FireOnIMEInputComplete(retInfo);
6395 OnReportRichEditorEvent("onIMEInputComplete");
6396 eventHub->FireOnDidIMEInput(onDidIMEInputRange);
6397 return true;
6398 }
6399
DoDeleteActions(int32_t currentPosition,int32_t length,RichEditorDeleteValue & info,bool isIME)6400 bool RichEditorPattern::DoDeleteActions(int32_t currentPosition, int32_t length, RichEditorDeleteValue& info,
6401 bool isIME)
6402 {
6403 auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
6404 CHECK_NULL_RETURN(eventHub, false);
6405 auto allowDelete = eventHub->FireAboutToDelete(info);
6406 info.ResetRichEditorDeleteSpans();
6407 CalcDeleteValueObj(currentPosition, length, info);
6408 bool doDelete = allowDelete || IsPreviewTextInputting();
6409 bool needRecord = isIME && !IsPreviewTextInputting();
6410 if (doDelete) {
6411 bool isSingleHandleMoving = selectOverlay_->IsSingleHandleMoving();
6412 UndoRedoRecord styledRecord;
6413 IF_TRUE(needRecord, undoManager_->UpdateRecordBeforeChange(currentPosition, length, styledRecord));
6414 CloseSelectOverlay();
6415 ResetSelection();
6416 DeleteByDeleteValueInfo(info);
6417 IF_TRUE((!caretVisible_ || isSingleHandleMoving) && HasFocus(), StartTwinkling());
6418 undoManager_->RecordOperationAfterChange(currentPosition, 0, styledRecord);
6419 eventHub->FireOnDeleteComplete();
6420 OnReportRichEditorEvent("OnDeleteComplete");
6421 }
6422 return doDelete;
6423 }
6424
IsEmojiOnCaretPosition(int32_t & emojiLength,bool isBackward,int32_t length)6425 std::pair<bool, bool> RichEditorPattern::IsEmojiOnCaretPosition(int32_t& emojiLength, bool isBackward, int32_t length)
6426 {
6427 bool isEmojiOnCaretBackward = false;
6428 bool isEmojiOnCaretForward = false;
6429 std::u16string u16;
6430 GetContentBySpans(u16);
6431 auto caretPos = std::clamp(caretPosition_, 0, static_cast<int32_t>(u16.length()));
6432 emojiLength = TextEmojiProcessor::Delete(caretPos, length, u16, isBackward);
6433 if (emojiLength > 0) {
6434 if (isBackward) {
6435 isEmojiOnCaretBackward = true;
6436 } else {
6437 isEmojiOnCaretForward = true;
6438 }
6439 }
6440 return std::make_pair(isEmojiOnCaretBackward, isEmojiOnCaretForward);
6441 }
6442
HandleOnDelete(bool backward)6443 void RichEditorPattern::HandleOnDelete(bool backward)
6444 {
6445 if (backward) {
6446 #if defined(PREVIEW)
6447 DeleteForward(1, TextChangeReason::INPUT);
6448 #else
6449 DeleteBackward(1, TextChangeReason::INPUT);
6450 #endif
6451 } else {
6452 #if defined(PREVIEW)
6453 DeleteBackward(1, TextChangeReason::INPUT);
6454 #else
6455 DeleteForward(1, TextChangeReason::INPUT);
6456 #endif
6457 }
6458 }
6459
CalculateDeleteLength(int32_t length,bool isBackward)6460 int32_t RichEditorPattern::CalculateDeleteLength(int32_t length, bool isBackward)
6461 {
6462 // handle selector
6463 if (!textSelector_.SelectNothing()) {
6464 lastCaretPosition_ = caretPosition_;
6465 caretPosition_ = isBackward ? textSelector_.GetTextEnd() : textSelector_.GetTextStart();
6466 return textSelector_.GetTextEnd() - textSelector_.GetTextStart();
6467 }
6468
6469 // handle symbol, assume caret is not within symbol
6470 auto iter = std::find_if(spans_.begin(), spans_.end(), [index = caretPosition_, isBackward]
6471 (const RefPtr<SpanItem>& spanItem) {
6472 return isBackward
6473 ? (spanItem->rangeStart < index && index <= spanItem->position)
6474 : (spanItem->rangeStart <= index && index < spanItem->position);
6475 });
6476 CHECK_NULL_RETURN(iter == spans_.end() || !(*iter) || (*iter)->unicode == 0, SYMBOL_SPAN_LENGTH);
6477
6478 // handle emoji
6479 int32_t emojiLength = 0;
6480 auto [isEmojiOnCaretBackward, isEmojiOnCaretForward] = IsEmojiOnCaretPosition(emojiLength, isBackward, length);
6481 if ((isBackward && isEmojiOnCaretBackward) || (!isBackward && isEmojiOnCaretForward)) {
6482 return emojiLength;
6483 }
6484
6485 return length;
6486 }
6487
6488 // only called by IME
DeleteBackward(int32_t length)6489 void RichEditorPattern::DeleteBackward(int32_t length)
6490 {
6491 DeleteBackward(length, TextChangeReason::INPUT, true);
6492 }
6493
DeleteBackward(int32_t oriLength,TextChangeReason reason,bool isByIME)6494 void RichEditorPattern::DeleteBackward(int32_t oriLength, TextChangeReason reason, bool isByIME)
6495 {
6496 int32_t length = isAPI14Plus ? std::clamp(oriLength, 0, caretPosition_) : oriLength;
6497 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "oriLength=%{public}d, length=%{public}d, isDragging=%{public}d",
6498 oriLength, length, IsDragging());
6499 CHECK_NULL_VOID(!IsDragging());
6500 CHECK_NULL_VOID(SetPreviewTextForDelete(oriLength, true, isByIME));
6501 if (isSpanStringMode_) {
6502 DeleteBackwardInStyledString(length);
6503 return;
6504 }
6505 OperationRecord record;
6506 record.beforeCaretPosition = caretPosition_;
6507 RichEditorChangeValue changeValue(reason);
6508 CHECK_NULL_VOID(BeforeChangeText(changeValue, record, RecordType::DEL_BACKWARD, length));
6509 std::u16string deleteText = DeleteBackwardOperation(length);
6510 if (deleteText.length() != 0) {
6511 ClearRedoOperationRecords();
6512 record.deleteText = deleteText;
6513 record.afterCaretPosition = caretPosition_;
6514 AddOperationRecord(record);
6515 AfterContentChange(changeValue);
6516 }
6517 }
6518
DeleteBackwardOperation(int32_t length)6519 std::u16string RichEditorPattern::DeleteBackwardOperation(int32_t length)
6520 {
6521 length = CalculateDeleteLength(length, true);
6522 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "delete length=%{public}d", length);
6523 std::u16string textContent;
6524 GetContentBySpans(textContent);
6525
6526 if (static_cast<int32_t>(textContent.length()) != GetTextContentLength()) {
6527 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "textContent length mismatch, %{public}d vs. %{public}d",
6528 static_cast<int32_t>(textContent.length()), GetTextContentLength());
6529 }
6530 auto start = std::clamp(caretPosition_ - length, 0, static_cast<int32_t>(textContent.length()));
6531 std::u16string deleteText =
6532 textContent.substr(static_cast<uint32_t>(start), static_cast<uint32_t>(caretPosition_ - start));
6533 RichEditorDeleteValue info;
6534 info.SetRichEditorDeleteDirection(RichEditorDeleteDirection::BACKWARD);
6535 if (caretPosition_ == 0) {
6536 info.SetLength(0);
6537 DoDeleteActions(0, 0, info);
6538 return deleteText;
6539 }
6540 info.SetOffset(caretPosition_ - length);
6541 info.SetLength(length);
6542 int32_t currentPosition = std::clamp((caretPosition_ - length), 0, static_cast<int32_t>(GetTextContentLength()));
6543 if (!spans_.empty()) {
6544 CalcDeleteValueObj(currentPosition, length, info);
6545 bool doDelete = DoDeleteActions(currentPosition, length, info);
6546 if (!doDelete) {
6547 return u"";
6548 }
6549 }
6550 auto host = GetContentHost();
6551 if (host && host->GetChildren().empty()) {
6552 textForDisplay_.clear();
6553 }
6554 RequestKeyboardToEdit();
6555 return deleteText;
6556 }
6557
6558 // only called by IME
DeleteForward(int32_t length)6559 void RichEditorPattern::DeleteForward(int32_t length)
6560 {
6561 DeleteForward(length, TextChangeReason::INPUT, true);
6562 }
6563
DeleteForward(int32_t oriLength,TextChangeReason reason,bool isByIME)6564 void RichEditorPattern::DeleteForward(int32_t oriLength, TextChangeReason reason, bool isByIME)
6565 {
6566 int32_t length = isAPI14Plus ? std::clamp(oriLength, 0, GetTextContentLength() - caretPosition_) : oriLength;
6567 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "oriLength=%{public}d, length=%{public}d, isDragging=%{public}d",
6568 oriLength, length, IsDragging());
6569 CHECK_NULL_VOID(!IsDragging());
6570 CHECK_NULL_VOID(SetPreviewTextForDelete(oriLength, false, isByIME));
6571 if (isSpanStringMode_) {
6572 DeleteForwardInStyledString(length);
6573 return;
6574 }
6575 if (IsPreviewTextInputting()) {
6576 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "do not handle DeleteForward in previewTextInputting");
6577 return;
6578 }
6579 OperationRecord record;
6580 record.beforeCaretPosition = caretPosition_;
6581 RichEditorChangeValue changeValue(reason);
6582 CHECK_NULL_VOID(BeforeChangeText(changeValue, record, RecordType::DEL_FORWARD, length));
6583 std::u16string deleteText = DeleteForwardOperation(length);
6584 if (deleteText.length() != 0) {
6585 ClearRedoOperationRecords();
6586 record.deleteText = deleteText;
6587 record.afterCaretPosition = caretPosition_;
6588 AddOperationRecord(record);
6589 AfterContentChange(changeValue);
6590 }
6591 }
6592
DeleteForwardOperation(int32_t length,bool isIME)6593 std::u16string RichEditorPattern::DeleteForwardOperation(int32_t length, bool isIME)
6594 {
6595 length = CalculateDeleteLength(length, false);
6596 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "delete length=%{public}d", length);
6597 std::u16string textContent;
6598 GetContentBySpans(textContent);
6599 if (static_cast<int32_t>(textContent.length()) != GetTextContentLength()) {
6600 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "textContent length mismatch, %{public}d vs. %{public}d",
6601 static_cast<int32_t>(textContent.length()), GetTextContentLength());
6602 }
6603 auto end = std::clamp(caretPosition_ + length, 0, static_cast<int32_t>(textContent.length()));
6604 std::u16string deleteText = textContent.substr(
6605 static_cast<uint32_t>(std::clamp(caretPosition_, 0, static_cast<int32_t>(textContent.length()))),
6606 static_cast<uint32_t>(end - caretPosition_));
6607 RichEditorDeleteValue info;
6608 info.SetOffset(caretPosition_);
6609 info.SetRichEditorDeleteDirection(RichEditorDeleteDirection::FORWARD);
6610 int32_t currentPosition = caretPosition_;
6611 if (currentPosition == GetTextContentLength()) {
6612 info.SetLength(0);
6613 DoDeleteActions(currentPosition, 0, info, isIME);
6614 return deleteText;
6615 }
6616 info.SetLength(length);
6617 if (!spans_.empty()) {
6618 CalcDeleteValueObj(currentPosition, length, info);
6619 bool doDelete = DoDeleteActions(currentPosition, length, info, isIME);
6620 if (!doDelete) {
6621 return u"";
6622 }
6623 }
6624 return deleteText;
6625 }
6626
DeleteContent(int32_t length)6627 void RichEditorPattern::DeleteContent(int32_t length)
6628 {
6629 length = CalculateDeleteLength(length, true);
6630 std::u16string textContent;
6631 GetContentBySpans(textContent);
6632
6633 auto start = std::clamp(GetTextContentLength() - length, 0, static_cast<int32_t>(textContent.length()));
6634 std::u16string deleteText =
6635 textContent.substr(static_cast<uint32_t>(start), static_cast<uint32_t>(GetTextContentLength() - start));
6636 RichEditorDeleteValue info;
6637 info.SetRichEditorDeleteDirection(RichEditorDeleteDirection::BACKWARD);
6638 if (GetTextContentLength() == 0) {
6639 info.SetLength(0);
6640 DoDeleteActions(0, 0, info);
6641 return;
6642 }
6643 info.SetOffset(GetTextContentLength() - length);
6644 info.SetLength(length);
6645 int32_t currentPosition = std::clamp((GetTextContentLength() - length), 0, static_cast<int32_t>(GetTextContentLength()));
6646 if (!spans_.empty()) {
6647 CalcDeleteValueObj(currentPosition, length, info);
6648 bool doDelete = DoDeleteActions(currentPosition, length, info);
6649 if (!doDelete) {
6650 return;
6651 }
6652 }
6653 auto host = GetContentHost();
6654 if (host && host->GetChildren().empty()) {
6655 textForDisplay_.clear();
6656 }
6657 RequestKeyboardToEdit();
6658 }
6659
DeleteToMaxLength(std::optional<int32_t> length)6660 void RichEditorPattern::DeleteToMaxLength(std::optional<int32_t> length)
6661 {
6662 int maxLength = length.value_or(INT_MAX);
6663 if (maxLength >= GetTextContentLength() || maxLength < 0) {
6664 return;
6665 }
6666 int32_t textContentLength = GetTextContentLength();
6667 if (isSpanStringMode_) {
6668 DeleteValueInStyledString(maxLength, GetTextContentLength() - maxLength);
6669 } else {
6670 while (textContentLength > maxLength) {
6671 textContentLength -= CalculateDeleteLength(CUSTOM_CONTENT_LENGTH, true);
6672 DeleteContent(CUSTOM_CONTENT_LENGTH);
6673 }
6674 }
6675 }
6676
OnBackPressed()6677 bool RichEditorPattern::OnBackPressed()
6678 {
6679 auto tmpHost = GetHost();
6680 CHECK_NULL_RETURN(tmpHost, false);
6681 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "RichEditor %{public}d receives back press event, isStopBackPress=%{public}d",
6682 tmpHost->GetId(), isStopBackPress_);
6683 if (SelectOverlayIsOn()) {
6684 CloseSelectOverlay();
6685 textSelector_.Update(textSelector_.destinationOffset);
6686 StartTwinkling();
6687 return isStopBackPress_;
6688 }
6689 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
6690 if (!imeShown_ && !isCustomKeyboardAttached_) {
6691 #else
6692 if (!isCustomKeyboardAttached_) {
6693 #endif
6694 return false;
6695 }
6696 tmpHost->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6697 ResetSelection();
6698 CloseKeyboard(false);
6699 FocusHub::LostFocusToViewRoot();
6700 #if defined(ANDROID_PLATFORM)
6701 return false;
6702 #else
6703 return isStopBackPress_;
6704 #endif
6705 }
6706
6707 void RichEditorPattern::SetInputMethodStatus(bool keyboardShown)
6708 {
6709 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
6710 imeShown_ = keyboardShown;
6711 #endif
6712 }
6713
6714 bool RichEditorPattern::BeforeStatusCursorMove(bool isLeft)
6715 {
6716 CHECK_NULL_RETURN(textSelector_.IsValid(), true);
6717 CHECK_NULL_RETURN(!selectOverlay_->IsSingleHandleShow(), true);
6718 SetCaretPosition(isLeft ? textSelector_.GetTextStart() : textSelector_.GetTextEnd());
6719 MoveCaretToContentRect();
6720 StartTwinkling();
6721 CloseSelectOverlay();
6722 ResetSelection();
6723 return false;
6724 }
6725
6726 bool RichEditorPattern::CursorMoveLeft()
6727 {
6728 CHECK_NULL_RETURN(BeforeStatusCursorMove(true), false);
6729 CloseSelectOverlay();
6730 ResetSelection();
6731 int32_t emojiLength = 0;
6732 int32_t caretPosition = caretPosition_;
6733 constexpr int32_t DELETE_COUNT = 1;
6734 auto [isEmojiOnCaretBackward, isEmojiOnCaretForward] = IsEmojiOnCaretPosition(emojiLength, true, DELETE_COUNT);
6735 if (isEmojiOnCaretBackward) {
6736 caretPosition = std::clamp((caretPosition_ - emojiLength), 0, static_cast<int32_t>(GetTextContentLength()));
6737 } else {
6738 caretPosition = std::clamp((caretPosition_ - 1), 0, static_cast<int32_t>(GetTextContentLength()));
6739 }
6740 AdjustSelectorForSymbol(caretPosition, HandleType::SECOND, SelectorAdjustPolicy::EXCLUDE);
6741 if (caretPosition_ == caretPosition) {
6742 return false;
6743 }
6744 SetCaretPosition(caretPosition);
6745 MoveCaretToContentRect();
6746 StartTwinkling();
6747 auto host = GetHost();
6748 CHECK_NULL_RETURN(host, false);
6749 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6750 return true;
6751 }
6752
6753 bool RichEditorPattern::CursorMoveRight()
6754 {
6755 CHECK_NULL_RETURN(BeforeStatusCursorMove(false), false);
6756 CloseSelectOverlay();
6757 ResetSelection();
6758 int32_t emojiLength = 0;
6759 int32_t caretPosition = caretPosition_;
6760 constexpr int32_t DELETE_COUNT = 1;
6761 auto [isEmojiOnCaretBackward, isEmojiOnCaretForward] = IsEmojiOnCaretPosition(emojiLength, false, DELETE_COUNT);
6762 if (isEmojiOnCaretForward) {
6763 caretPosition = std::clamp((caretPosition_ + emojiLength), 0, static_cast<int32_t>(GetTextContentLength()));
6764 } else {
6765 caretPosition = std::clamp((caretPosition_ + 1), 0, static_cast<int32_t>(GetTextContentLength()));
6766 }
6767 AdjustSelectorForSymbol(caretPosition, HandleType::SECOND, SelectorAdjustPolicy::INCLUDE);
6768 if (caretPosition_ == caretPosition) {
6769 return false;
6770 }
6771 SetCaretPosition(caretPosition);
6772 MoveCaretToContentRect();
6773 StartTwinkling();
6774 auto host = GetHost();
6775 CHECK_NULL_RETURN(host, false);
6776 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6777 return true;
6778 }
6779
6780 bool RichEditorPattern::CursorMoveUp()
6781 {
6782 CloseSelectOverlay();
6783 ResetSelection();
6784 float caretHeight = 0.0f;
6785 float leadingMarginOffset = 0.0f;
6786 CaretOffsetInfo caretInfo;
6787 if (static_cast<int32_t>(GetTextContentLength()) > 1) {
6788 caretInfo = GetCaretOffsetInfoByPosition();
6789 int32_t caretPosition = CalcMoveUpPos(leadingMarginOffset);
6790 CHECK_NULL_RETURN(overlayMod_, false);
6791 auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
6792 auto currentCaretOffsetOverlay = overlayMod->GetCaretOffset();
6793 auto caretOffsetWidth = overlayMod->GetCaretWidth();
6794 auto rectLineInfo = CalcLineInfoByPosition();
6795 caretPosition = std::clamp(caretPosition, 0, static_cast<int32_t>(GetTextContentLength()));
6796 if (caretPosition_ == caretPosition) {
6797 caretPosition = 0;
6798 }
6799 // at line middle or line end
6800 bool cursorNotAtLineStart =
6801 NearEqual(currentCaretOffsetOverlay.GetX(), caretInfo.caretOffsetUp.GetX(), caretOffsetWidth);
6802 bool isEnter = NearZero(currentCaretOffsetOverlay.GetX() - richTextRect_.GetX(), rectLineInfo.GetX());
6803 SetCaretPosition(caretPosition);
6804 MoveCaretToContentRect();
6805 if (cursorNotAtLineStart && !isEnter) {
6806 OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight, false, false);
6807 SetLastClickOffset(caretOffset);
6808 caretAffinityPolicy_ = CaretAffinityPolicy::UPSTREAM_FIRST;
6809 }
6810 }
6811 StartTwinkling();
6812 auto host = GetHost();
6813 CHECK_NULL_RETURN(host, false);
6814 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6815 return true;
6816 }
6817
6818 bool RichEditorPattern::CursorMoveDown()
6819 {
6820 CloseSelectOverlay();
6821 ResetSelection();
6822 if (static_cast<int32_t>(GetTextContentLength()) > 1) {
6823 float caretHeight = 0.0f;
6824 float leadingMarginOffset = 0.0f;
6825 float caretHeightEnd = 0.0f;
6826 CaretOffsetInfo caretInfo;
6827 int32_t caretPositionEnd;
6828 caretInfo = GetCaretOffsetInfoByPosition();
6829 caretPositionEnd = CalcMoveDownPos(leadingMarginOffset);
6830 CHECK_NULL_RETURN(overlayMod_, false);
6831 auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
6832 auto caretOffsetOverlay = overlayMod->GetCaretOffset();
6833 auto caretOffsetWidth = overlayMod->GetCaretWidth();
6834 bool cursorNotAtLineStart =
6835 NearEqual(caretOffsetOverlay.GetX(), caretInfo.caretOffsetUp.GetX(), caretOffsetWidth);
6836 bool isEnter = NearZero(caretInfo.caretOffsetUp.GetX() - richTextRect_.GetX(), leadingMarginOffset);
6837 caretPositionEnd = std::clamp(caretPositionEnd, 0, static_cast<int32_t>(GetTextContentLength()));
6838 auto currentLineInfo = CalcLineInfoByPosition();
6839 if (caretPositionEnd <= caretPosition_) {
6840 OffsetF caretOffsetEnd = CalcCursorOffsetByPosition(GetTextContentLength(), caretHeightEnd);
6841 if (NearEqual(caretOffsetEnd.GetY() - GetTextRect().GetY(), currentLineInfo.GetY(), 0.5f)) {
6842 caretPositionEnd = GetTextContentLength();
6843 } else {
6844 caretPositionEnd += 1;
6845 }
6846 }
6847 SetCaretPosition(caretPositionEnd);
6848 if (cursorNotAtLineStart && caretPosition_ != 0 && !isEnter) {
6849 OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight, false, false);
6850 SetLastClickOffset(caretOffset);
6851 caretAffinityPolicy_ = CaretAffinityPolicy::UPSTREAM_FIRST;
6852 }
6853 MoveCaretToContentRect();
6854 }
6855 StartTwinkling();
6856 auto host = GetHost();
6857 CHECK_NULL_RETURN(host, false);
6858 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6859 return true;
6860 }
6861
6862 void RichEditorPattern::CursorMoveToNextWord(CaretMoveIntent direction)
6863 {
6864 CHECK_NULL_VOID(direction == CaretMoveIntent::LeftWord || direction == CaretMoveIntent::RightWord);
6865 bool isDirectionLeft = direction == CaretMoveIntent::LeftWord;
6866 auto index = caretPosition_;
6867 if (!textSelector_.SelectNothing()) {
6868 index = isDirectionLeft ? textSelector_.GetTextStart() + 1 : textSelector_.GetTextEnd() - 1;
6869 }
6870 auto newPos = isDirectionLeft ? GetLeftWordIndex(index) : GetRightWordIndex(index);
6871 CloseSelectOverlay();
6872 ResetSelection();
6873 SetCaretPosition(newPos);
6874 MoveCaretToContentRect();
6875 IF_TRUE(isEditing_, StartTwinkling());
6876 auto host = GetHost();
6877 CHECK_NULL_VOID(host);
6878 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6879 }
6880
6881 int32_t RichEditorPattern::GetLeftWordIndex(int32_t index)
6882 {
6883 AdjustIndexSkipSpace(index, MoveDirection::BACKWARD);
6884 int32_t newPos = std::max(0, index - 1);
6885 AdjustSelectorForSymbol(newPos, HandleType::FIRST, SelectorAdjustPolicy::INCLUDE);
6886 AdjustWordSelection(newPos, index);
6887 AdjustSelector(newPos, HandleType::FIRST);
6888 return newPos;
6889 }
6890
6891 int32_t RichEditorPattern::GetRightWordIndex(int32_t index)
6892 {
6893 int32_t newPos = std::min(index + 1, GetTextContentLength());
6894 AdjustWordSelection(index, newPos);
6895 AdjustSelector(newPos, HandleType::SECOND);
6896 AdjustIndexSkipSpace(newPos, MoveDirection::FORWARD);
6897 return newPos;
6898 }
6899
6900 bool RichEditorPattern::CursorMoveToParagraphBegin()
6901 {
6902 CloseSelectOverlay();
6903 ResetSelection();
6904 auto newPos = GetParagraphBeginPosition(caretPosition_);
6905 if (newPos == caretPosition_ && caretPosition_ > 0) {
6906 newPos = GetParagraphBeginPosition(caretPosition_ - 1);
6907 }
6908 if (newPos == caretPosition_) {
6909 return false;
6910 }
6911 SetCaretPosition(newPos);
6912 MoveCaretToContentRect();
6913 StartTwinkling();
6914 auto host = GetHost();
6915 CHECK_NULL_RETURN(host, false);
6916 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6917 return true;
6918 }
6919
6920 bool RichEditorPattern::CursorMoveToParagraphEnd()
6921 {
6922 CloseSelectOverlay();
6923 ResetSelection();
6924 auto newPos = GetParagraphEndPosition(caretPosition_);
6925 if (newPos == caretPosition_ && caretPosition_ < static_cast<int32_t>(GetTextContentLength())) {
6926 newPos = GetParagraphEndPosition(caretPosition_ + 1);
6927 }
6928 if (newPos == caretPosition_) {
6929 return false;
6930 }
6931 SetCaretPosition(newPos);
6932 MoveCaretToContentRect();
6933 StartTwinkling();
6934 auto host = GetHost();
6935 CHECK_NULL_RETURN(host, false);
6936 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6937 return true;
6938 }
6939
6940 bool RichEditorPattern::CursorMoveHome()
6941 {
6942 CloseSelectOverlay();
6943 ResetSelection();
6944 if (0 == caretPosition_) {
6945 return false;
6946 }
6947 SetCaretPosition(0);
6948 MoveCaretToContentRect();
6949 StartTwinkling();
6950 auto host = GetHost();
6951 CHECK_NULL_RETURN(host, false);
6952 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6953 return true;
6954 }
6955
6956 bool RichEditorPattern::CursorMoveEnd()
6957 {
6958 int32_t currentPositionIndex = 0;
6959 if (textSelector_.SelectNothing()) {
6960 currentPositionIndex = caretPosition_;
6961 } else {
6962 currentPositionIndex = textSelector_.GetTextEnd();
6963 }
6964 CloseSelectOverlay();
6965 ResetSelection();
6966 auto newPos = GetTextContentLength();
6967 if (newPos == currentPositionIndex) {
6968 StartTwinkling();
6969 return false;
6970 }
6971 SetCaretPosition(newPos);
6972 MoveCaretToContentRect();
6973 StartTwinkling();
6974 auto host = GetHost();
6975 CHECK_NULL_RETURN(host, false);
6976 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6977 return true;
6978 }
6979
6980 int32_t RichEditorPattern::GetLeftWordPosition(int32_t caretPosition)
6981 {
6982 int32_t offset = 0;
6983 bool jumpSpace = true;
6984 for (auto iter = spans_.rbegin(); iter != spans_.rend(); iter++) {
6985 auto span = *iter;
6986 auto content = span->content;
6987 if (caretPosition <= span->position - static_cast<int32_t>(content.length())) {
6988 continue;
6989 }
6990 int32_t position = span->position;
6991 for (auto iterContent = content.rbegin(); iterContent != content.rend(); iterContent++) {
6992 if (position-- > caretPosition) {
6993 continue;
6994 }
6995 if (*iterContent != u' ' || span->placeholderIndex >= 0) {
6996 jumpSpace = false;
6997 }
6998 if (position + 1 == caretPosition) {
6999 if (!(StringUtils::IsLetterOrNumberForWchar(*iterContent) ||
7000 (*iterContent == u' ' && span->placeholderIndex < 0))) {
7001 return std::clamp(caretPosition - 1, 0, static_cast<int32_t>(GetTextContentLength()));
7002 }
7003 }
7004 if (!jumpSpace) {
7005 if (!StringUtils::IsLetterOrNumberForWchar(*iterContent)) {
7006 return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
7007 }
7008 } else {
7009 if (*iterContent == u' ' && span->placeholderIndex >= 0) {
7010 return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
7011 }
7012 }
7013 offset++;
7014 }
7015 }
7016 return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
7017 }
7018
7019 int32_t RichEditorPattern::GetRightWordPosition(int32_t caretPosition)
7020 {
7021 int32_t offset = 0;
7022 bool jumpSpace = false;
7023 for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
7024 auto span = *iter;
7025 auto content = span->content;
7026 if (caretPosition > span->position) {
7027 continue;
7028 }
7029 int32_t position = span->position - static_cast<int32_t>(content.length());
7030 for (auto iterContent = content.cbegin(); iterContent != content.cend(); iterContent++) {
7031 if (position++ < caretPosition) {
7032 continue;
7033 }
7034 if (*iterContent == u' ' && span->placeholderIndex < 0) {
7035 jumpSpace = true;
7036 offset++;
7037 continue;
7038 }
7039 if (position - 1 == caretPosition) {
7040 if (!StringUtils::IsLetterOrNumberForWchar(*iterContent)) {
7041 return std::clamp(caretPosition + 1, 0, static_cast<int32_t>(GetTextContentLength()));
7042 }
7043 }
7044 if (jumpSpace) {
7045 if (*iterContent != u' ' || span->placeholderIndex >= 0) {
7046 return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
7047 }
7048 } else {
7049 if (!(StringUtils::IsLetterOrNumberForWchar(*iterContent) ||
7050 (*iterContent == u' ' && span->placeholderIndex < 0))) {
7051 return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
7052 }
7053 }
7054 offset++;
7055 }
7056 }
7057 return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
7058 }
7059
7060 int32_t RichEditorPattern::GetParagraphBeginPosition(int32_t caretPosition)
7061 {
7062 int32_t offset = 0;
7063 for (auto iter = spans_.rbegin(); iter != spans_.rend(); iter++) {
7064 auto span = *iter;
7065 auto content = span->content;
7066 if (caretPosition <= span->position - static_cast<int32_t>(content.length())) {
7067 continue;
7068 }
7069 int32_t position = span->position;
7070 for (auto iterContent = content.rbegin(); iterContent != content.rend(); iterContent++) {
7071 if (position-- > caretPosition) {
7072 continue;
7073 }
7074 if (*iterContent == u'\n') {
7075 return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
7076 }
7077 offset++;
7078 }
7079 }
7080 return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
7081 }
7082
7083 int32_t RichEditorPattern::GetParagraphEndPosition(int32_t caretPosition)
7084 {
7085 int32_t offset = 0;
7086 for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
7087 auto span = *iter;
7088 auto content = span->content;
7089 if (caretPosition > span->position) {
7090 continue;
7091 }
7092 int32_t position = span->position - static_cast<int32_t>(content.length());
7093 for (auto iterContent = content.cbegin(); iterContent != content.cend(); iterContent++) {
7094 if (position++ < caretPosition) {
7095 continue;
7096 }
7097 if (*iterContent == u'\n') {
7098 return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
7099 }
7100 offset++;
7101 }
7102 }
7103 return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
7104 }
7105
7106 void RichEditorPattern::HandleOnSelectAll()
7107 {
7108 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnSelectAll IsPreviewTextInputting:%{public}d", IsPreviewTextInputting());
7109 CHECK_NULL_VOID(!IsPreviewTextInputting());
7110 selectOverlay_->CloseOverlay(true, CloseReason::CLOSE_REASON_SELECT_ALL);
7111 auto host = GetHost();
7112 CHECK_NULL_VOID(host);
7113 textResponseType_.reset();
7114 int32_t newPos = static_cast<int32_t>(GetTextContentLength());
7115 textSelector_.Update(0, newPos);
7116 FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
7117 SetCaretPosition(newPos);
7118 MoveCaretToContentRect();
7119 IF_TRUE(IsSelected(), StopTwinkling());
7120 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
7121 }
7122
7123 int32_t RichEditorPattern::CaretPositionSelectEmoji(CaretMoveIntent direction)
7124 {
7125 int32_t newPos = caretPosition_;
7126 int32_t emojiLength = 0;
7127 constexpr int32_t DELETE_COUNT = 1;
7128 if (direction == CaretMoveIntent::Left) {
7129 auto [isEmojiOnCaretBackward, isEmojiOnCaretForward] = IsEmojiOnCaretPosition(emojiLength, true, DELETE_COUNT);
7130 if (isEmojiOnCaretBackward) {
7131 newPos = caretPosition_ - emojiLength;
7132 } else {
7133 newPos = caretPosition_ - 1;
7134 }
7135 AdjustSelectorForSymbol(newPos, HandleType::FIRST, SelectorAdjustPolicy::INCLUDE);
7136 return newPos;
7137 }
7138 auto [isEmojiOnCaretBackward, isEmojiOnCaretForward] = IsEmojiOnCaretPosition(emojiLength, false, DELETE_COUNT);
7139 if (direction == CaretMoveIntent::Right) {
7140 if (isEmojiOnCaretForward) {
7141 newPos = caretPosition_ + emojiLength;
7142 } else {
7143 newPos = caretPosition_ + 1;
7144 }
7145 AdjustSelectorForSymbol(newPos, HandleType::SECOND, SelectorAdjustPolicy::INCLUDE);
7146 }
7147 return newPos;
7148 }
7149
7150 void RichEditorPattern::HandleSelect(CaretMoveIntent direction)
7151 {
7152 if (IsPreviewTextInputting()) {
7153 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleSelect blocked during preview text input");
7154 return;
7155 }
7156 ResetTouchAndMoveCaretState(false);
7157 CloseSelectOverlay();
7158 auto host = GetHost();
7159 CHECK_NULL_VOID(host);
7160 int32_t newPos, fixedPos = caretPosition_;
7161 if (IsSelected()) {
7162 fixedPos = (caretPosition_ == textSelector_.GetTextStart() ? textSelector_.GetTextEnd()
7163 : textSelector_.GetTextStart());
7164 }
7165 newPos = HandleSelectWrapper(direction, fixedPos);
7166 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleSelect [%{public}d-%{public}d] direction=%{public}d",
7167 fixedPos, newPos, direction);
7168 if (newPos == -1) {
7169 return;
7170 }
7171 newPos = std::clamp(newPos, 0, static_cast<int32_t>(GetTextContentLength()));
7172 if (newPos == caretPosition_) {
7173 return;
7174 }
7175 UpdateSelector(fixedPos, newPos);
7176 FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
7177 SetCaretPosition(newPos);
7178 MoveCaretToContentRect();
7179 if (textSelector_.SelectNothing()) {
7180 StartTwinkling();
7181 } else {
7182 StopTwinkling();
7183 }
7184 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
7185 }
7186
7187 void RichEditorPattern::ClearOperationRecords()
7188 {
7189 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "ClearOperationRecords");
7190 undoManager_->ClearUndoRedoRecords();
7191 previewInputRecord_.Reset();
7192 ClearRedoOperationRecords();
7193 if (operationRecords_.empty()) {
7194 return;
7195 }
7196 operationRecords_.clear();
7197 }
7198
7199 void RichEditorPattern::ClearRedoOperationRecords()
7200 {
7201 if (redoOperationRecords_.empty()) {
7202 return;
7203 }
7204 redoOperationRecords_.clear();
7205 }
7206
7207 void RichEditorPattern::AddInsertOperationRecord(OperationRecord& record)
7208 {
7209 if (previewInputRecord_.deleteText) {
7210 record.deleteText = previewInputRecord_.deleteText;
7211 }
7212 if (previewInputRecord_.beforeCaretPosition != -1) {
7213 record.beforeCaretPosition = previewInputRecord_.beforeCaretPosition;
7214 }
7215 previewInputRecord_.Reset();
7216 AddOperationRecord(record);
7217 }
7218
7219 void RichEditorPattern::AddOperationRecord(const OperationRecord& record)
7220 {
7221 if (operationRecords_.size() >= RECORD_MAX_LENGTH) {
7222 // case of max length is 0
7223 if (operationRecords_.empty()) {
7224 return;
7225 }
7226 operationRecords_.erase(operationRecords_.begin());
7227 }
7228 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AddOperationRecord record:%{public}s", record.ToString().c_str());
7229 operationRecords_.emplace_back(record);
7230 }
7231
7232 void RichEditorPattern::UpdateShiftFlag(const KeyEvent& keyEvent)
7233 {
7234 bool hasKeyShift = keyEvent.HasKey(KeyCode::KEY_SHIFT_LEFT) || keyEvent.HasKey(KeyCode::KEY_SHIFT_RIGHT);
7235 auto action = keyEvent.action;
7236 bool isShiftPressed = hasKeyShift && (action == KeyAction::DOWN || action == KeyAction::UP);
7237 if (isShiftPressed != shiftFlag_) {
7238 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "UpdateShiftFlag:%{public}d by action:%{public}d", isShiftPressed, action);
7239 shiftFlag_ = isShiftPressed;
7240 }
7241 }
7242
7243 bool RichEditorPattern::HandleOnEscape()
7244 {
7245 CloseSelectOverlay();
7246 return false;
7247 }
7248
7249 void RichEditorPattern::HandleOnUndoAction()
7250 {
7251 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnUndoAction, IsSupportStyledUndo:%{public}d", IsSupportStyledUndo());
7252 if (IsSupportStyledUndo()) {
7253 undoManager_->UndoByRecords();
7254 return;
7255 }
7256 if (operationRecords_.empty()) {
7257 return;
7258 }
7259 auto value = operationRecords_.back();
7260 RichEditorChangeValue changeValue(TextChangeReason::UNDO);
7261 CHECK_NULL_VOID(BeforeChangeText(changeValue, value, RecordType::UNDO));
7262 operationRecords_.pop_back();
7263 if (redoOperationRecords_.size() >= RECORD_MAX_LENGTH && !(redoOperationRecords_.empty())) {
7264 redoOperationRecords_.erase(redoOperationRecords_.begin());
7265 }
7266 redoOperationRecords_.push_back(value);
7267 CloseSelectOverlay();
7268 ResetSelection();
7269 if (value.addText.has_value() && value.deleteCaretPosition != -1) {
7270 UndoDrag(value);
7271 AfterContentChange(changeValue);
7272 return;
7273 }
7274 if (value.addText.has_value() && value.deleteText.has_value()) {
7275 SetCaretPosition(value.afterCaretPosition);
7276 DeleteBackwardOperation(TextEmojiProcessor::GetCharacterNum(value.addText.value_or(u"")));
7277 InsertValueOperation(value.deleteText.value_or(u""));
7278 AfterContentChange(changeValue);
7279 return;
7280 }
7281 if (value.addText.has_value()) {
7282 SetCaretPosition(value.afterCaretPosition);
7283 DeleteBackwardOperation(TextEmojiProcessor::GetCharacterNum(value.addText.value_or(u"")));
7284 }
7285 if (value.deleteText.has_value()) {
7286 SetCaretPosition(value.afterCaretPosition);
7287 InsertValueOperation(value.deleteText.value_or(u""));
7288 }
7289 AfterContentChange(changeValue);
7290 }
7291
7292 void RichEditorPattern::HandleOnRedoAction()
7293 {
7294 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnRedoAction, IsSupportStyledUndo:%{public}d", IsSupportStyledUndo());
7295 if (IsSupportStyledUndo()) {
7296 undoManager_->RedoByRecords();
7297 return;
7298 }
7299 if (redoOperationRecords_.empty()) {
7300 return;
7301 }
7302 auto value = redoOperationRecords_.back();
7303 RichEditorChangeValue changeValue(TextChangeReason::REDO);
7304 CHECK_NULL_VOID(BeforeChangeText(changeValue, value, RecordType::REDO));
7305 redoOperationRecords_.pop_back();
7306 if (value.addText.has_value() && value.deleteCaretPosition != -1) {
7307 RedoDrag(value);
7308 AfterContentChange(changeValue);
7309 return;
7310 }
7311 if (value.addText.has_value() && value.deleteText.has_value()) {
7312 SetCaretPosition(value.beforeCaretPosition);
7313 DeleteForwardOperation(value.deleteText.value_or(u"").length());
7314 InsertValueOperation(value.addText.value_or(u""));
7315 operationRecords_.push_back(value);
7316 AfterContentChange(changeValue);
7317 return;
7318 }
7319 if (value.deleteText.has_value()) {
7320 SetCaretPosition(value.beforeCaretPosition);
7321 if (value.beforeCaretPosition != value.afterCaretPosition) {
7322 DeleteBackwardOperation(value.deleteText.value_or(u"").length());
7323 } else {
7324 DeleteForwardOperation(value.deleteText.value_or(u"").length());
7325 }
7326 }
7327 if (value.addText.has_value()) {
7328 SetCaretPosition(value.beforeCaretPosition);
7329 InsertValueOperation(value.addText.value_or(u""));
7330 }
7331 operationRecords_.push_back(value);
7332 AfterContentChange(changeValue);
7333 }
7334
7335 void RichEditorPattern::CalcInsertValueObj(TextInsertValueInfo& info)
7336 {
7337 if (spans_.empty()) {
7338 info.SetSpanIndex(0);
7339 info.SetOffsetInSpan(0);
7340 return;
7341 }
7342 auto it = std::find_if(
7343 spans_.begin(), spans_.end(), [caretPosition = caretPosition_ + moveLength_](const RefPtr<SpanItem>& spanItem) {
7344 if (spanItem->content.empty()) {
7345 return spanItem->position == caretPosition;
7346 }
7347 return spanItem->rangeStart <= caretPosition && caretPosition < spanItem->position;
7348 });
7349 if (it != spans_.end() && (*it)->unicode != 0 && (*it)->position - caretPosition_ + moveLength_ == 1) {
7350 it++;
7351 moveLength_++;
7352 }
7353 info.SetSpanIndex(std::distance(spans_.begin(), it));
7354 if (it == spans_.end()) {
7355 info.SetOffsetInSpan(0);
7356 return;
7357 }
7358 info.SetOffsetInSpan(
7359 caretPosition_ + moveLength_ - ((*it)->position - (*it)->content.length()));
7360 }
7361
7362 void RichEditorPattern::CalcDeleteValueObj(int32_t currentPosition, int32_t length, RichEditorDeleteValue& info)
7363 {
7364 auto it =
7365 std::find_if(spans_.begin(), spans_.end(), [caretPosition = currentPosition](const RefPtr<SpanItem>& spanItem) {
7366 return (spanItem->position - static_cast<int32_t>(spanItem->content.length()) <= caretPosition) &&
7367 (caretPosition < spanItem->position);
7368 });
7369 while (it != spans_.end() && length > 0) {
7370 if ((*it)->placeholderIndex >= 0 || (*it)->unicode != 0) {
7371 RichEditorAbstractSpanResult spanResult;
7372 spanResult.SetSpanIndex(std::distance(spans_.begin(), it));
7373 int32_t eraseLength = 0;
7374 if ((*it)->unicode != 0) {
7375 eraseLength = DeleteValueSetSymbolSpan(*it, spanResult);
7376 } else if (AceType::InstanceOf<ImageSpanItem>(*it)) {
7377 eraseLength = DeleteValueSetImageSpan(*it, spanResult);
7378 } else {
7379 eraseLength = DeleteValueSetBuilderSpan(*it, spanResult);
7380 }
7381 currentPosition += eraseLength;
7382 length -= eraseLength;
7383 info.SetRichEditorDeleteSpans(spanResult);
7384 } else {
7385 RichEditorAbstractSpanResult spanResult;
7386 spanResult.SetSpanIndex(std::distance(spans_.begin(), it));
7387 auto eraseLength = DeleteValueSetTextSpan(*it, currentPosition, length, spanResult);
7388 length -= eraseLength;
7389 currentPosition += eraseLength;
7390 info.SetRichEditorDeleteSpans(spanResult);
7391 }
7392 std::advance(it, 1);
7393 }
7394 }
7395
7396 RefPtr<SpanNode> RichEditorPattern::GetSpanNodeBySpanItem(const RefPtr<SpanItem> spanItem)
7397 {
7398 RefPtr<SpanNode> spanNode;
7399 auto iter = std::find(spans_.begin(), spans_.end(), spanItem);
7400 if (iter == spans_.end()) {
7401 return spanNode;
7402 }
7403 auto spanIndex = std::distance(spans_.begin(), iter);
7404 auto host = GetContentHost();
7405 CHECK_NULL_RETURN(host, spanNode);
7406 auto it = host->GetChildren().begin();
7407 std::advance(it, spanIndex);
7408 spanNode = AceType::DynamicCast<SpanNode>(*it);
7409 return spanNode;
7410 }
7411
7412 int32_t RichEditorPattern::DeleteValueSetSymbolSpan(
7413 const RefPtr<SpanItem>& spanItem, RichEditorAbstractSpanResult& spanResult)
7414 {
7415 spanResult.SetSpanType(SpanResultType::SYMBOL);
7416 spanResult.SetSpanRangeEnd(spanItem->position);
7417 spanResult.SetSpanRangeStart(spanItem->position - SYMBOL_SPAN_LENGTH);
7418 spanResult.SetEraseLength(SYMBOL_SPAN_LENGTH);
7419 spanResult.SetValueString(std::to_string(spanItem->unicode));
7420 spanResult.SetValueResource(spanItem->GetResourceObject());
7421 auto spanNode = GetSpanNodeBySpanItem(spanItem);
7422 if (spanNode) {
7423 spanResult.SetSymbolSpanStyle(GetSymbolSpanStyleObject(spanNode));
7424 }
7425 return SYMBOL_SPAN_LENGTH;
7426 }
7427
7428 int32_t RichEditorPattern::DeleteValueSetImageSpan(
7429 const RefPtr<SpanItem>& spanItem, RichEditorAbstractSpanResult& spanResult)
7430 {
7431 spanResult.SetSpanType(SpanResultType::IMAGE);
7432 spanResult.SetSpanRangeEnd(spanItem->position);
7433 spanResult.SetSpanRangeStart(spanItem->position - 1);
7434 spanResult.SetEraseLength(1);
7435 auto host = GetContentHost();
7436 CHECK_NULL_RETURN(host, IMAGE_SPAN_LENGTH);
7437 auto uiNode = host->GetChildAtIndex(spanResult.GetSpanIndex());
7438 CHECK_NULL_RETURN(uiNode, IMAGE_SPAN_LENGTH);
7439 auto imageNode = AceType::DynamicCast<FrameNode>(uiNode);
7440 CHECK_NULL_RETURN(imageNode, IMAGE_SPAN_LENGTH);
7441 auto imageRenderCtx = imageNode->GetRenderContext();
7442 if (imageRenderCtx->GetBorderRadius()) {
7443 BorderRadiusProperty brp;
7444 auto jsonObject = JsonUtil::Create(true);
7445 auto jsonBorder = JsonUtil::Create(true);
7446 InspectorFilter filter;
7447 imageRenderCtx->GetBorderRadiusValue(brp).ToJsonValue(jsonObject, jsonBorder, filter);
7448 spanResult.SetBorderRadius(jsonObject->GetValue("borderRadius")->IsObject()
7449 ? jsonObject->GetValue("borderRadius")->ToString()
7450 : jsonObject->GetString("borderRadius"));
7451 }
7452 auto geometryNode = imageNode->GetGeometryNode();
7453 CHECK_NULL_RETURN(geometryNode, IMAGE_SPAN_LENGTH);
7454 auto imageLayoutProperty = DynamicCast<ImageLayoutProperty>(imageNode->GetLayoutProperty());
7455 CHECK_NULL_RETURN(imageLayoutProperty, IMAGE_SPAN_LENGTH);
7456 spanResult.SetSizeWidth(geometryNode->GetMarginFrameSize().Width());
7457 spanResult.SetSizeHeight(geometryNode->GetMarginFrameSize().Height());
7458 if (imageLayoutProperty->GetMarginProperty()) {
7459 spanResult.SetMargin(imageLayoutProperty->GetMarginProperty()->ToString());
7460 }
7461 if (!imageLayoutProperty->GetImageSourceInfo()->GetPixmap()) {
7462 spanResult.SetValueResourceStr(imageLayoutProperty->GetImageSourceInfo()->GetSrc());
7463 } else {
7464 spanResult.SetValuePixelMap(imageLayoutProperty->GetImageSourceInfo()->GetPixmap());
7465 }
7466 if (imageLayoutProperty->HasImageFit()) {
7467 spanResult.SetImageFit(imageLayoutProperty->GetImageFitValue());
7468 }
7469 if (imageLayoutProperty->HasVerticalAlign()) {
7470 spanResult.SetVerticalAlign(imageLayoutProperty->GetVerticalAlignValue());
7471 }
7472 return IMAGE_SPAN_LENGTH;
7473 }
7474
7475 int32_t RichEditorPattern::DeleteValueSetBuilderSpan(
7476 const RefPtr<SpanItem>& spanItem, RichEditorAbstractSpanResult& spanResult)
7477 {
7478 spanResult.SetSpanType(SpanResultType::IMAGE);
7479 spanResult.SetSpanRangeEnd(spanItem->position);
7480 spanResult.SetSpanRangeStart(spanItem->position - 1);
7481 spanResult.SetEraseLength(1);
7482 auto host = GetContentHost();
7483 CHECK_NULL_RETURN(host, 1);
7484 auto uiNode = host->GetChildAtIndex(spanResult.GetSpanIndex());
7485 CHECK_NULL_RETURN(uiNode, 1);
7486 auto builderNode = AceType::DynamicCast<FrameNode>(uiNode);
7487 CHECK_NULL_RETURN(builderNode, 1);
7488 auto geometryNode = builderNode->GetGeometryNode();
7489 CHECK_NULL_RETURN(geometryNode, 1);
7490 spanResult.SetSizeWidth(geometryNode->GetMarginFrameSize().Width());
7491 spanResult.SetSizeHeight(geometryNode->GetMarginFrameSize().Height());
7492 return 1;
7493 }
7494
7495 int32_t RichEditorPattern::DeleteValueSetTextSpan(
7496 const RefPtr<SpanItem>& spanItem, int32_t currentPosition, int32_t length, RichEditorAbstractSpanResult& spanResult)
7497 {
7498 spanResult.SetSpanType(SpanResultType::TEXT);
7499 auto contentStartPosition
7500 = spanItem->position - static_cast<int32_t>(spanItem->content.length());
7501 spanResult.SetSpanRangeStart(contentStartPosition);
7502 int32_t eraseLength = 0;
7503 if (spanItem->position - currentPosition >= length) {
7504 eraseLength = length;
7505 } else {
7506 eraseLength = spanItem->position - currentPosition;
7507 }
7508 spanResult.SetSpanRangeEnd(spanItem->position);
7509 if (!previewTextRecord_.previewContent.empty()) {
7510 spanResult.SetPreviewText(previewTextRecord_.previewContent);
7511 } else {
7512 spanResult.SetValue(spanItem->content);
7513 }
7514 spanResult.SetOffsetInSpan(currentPosition - contentStartPosition);
7515 spanResult.SetEraseLength(eraseLength);
7516 spanResult.SetUrlAddress(spanItem->GetUrlAddress());
7517 if (!spanItem->GetTextStyle().has_value()) {
7518 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "SpanItem text style is empty.");
7519 return eraseLength;
7520 }
7521 spanResult.SetFontColor(spanItem->GetTextStyle()->GetTextColor().ColorToString());
7522 spanResult.SetFontSize(spanItem->GetTextStyle()->GetFontSize().ConvertToFp());
7523 spanResult.SetFontStyle(spanItem->GetTextStyle()->GetFontStyle());
7524 spanResult.SetFontWeight((int32_t)(spanItem->GetTextStyle()->GetFontWeight()));
7525 if (!spanItem->GetTextStyle()->GetFontFamilies().empty()) {
7526 spanResult.SetFontFamily(spanItem->GetTextStyle()->GetFontFamilies().at(0));
7527 }
7528 spanResult.SetColor(spanItem->GetTextStyle()->GetTextDecorationColor().ColorToString());
7529 spanResult.SetTextDecoration(spanItem->GetTextStyle()->GetTextDecorationFirst());
7530 spanResult.SetTextDecorationStyle(spanItem->GetTextStyle()->GetTextDecorationStyle());
7531 spanResult.SetLineThicknessScale(spanItem->GetTextStyle()->GetLineThicknessScale());
7532 spanResult.SetFontFeature(spanItem->GetTextStyle()->GetFontFeatures());
7533 auto host = GetContentHost();
7534 CHECK_NULL_RETURN(host, eraseLength);
7535 auto uiNode = host->GetChildAtIndex(spanResult.GetSpanIndex());
7536 CHECK_NULL_RETURN(uiNode, eraseLength);
7537 auto spanNode = DynamicCast<SpanNode>(uiNode);
7538 CHECK_NULL_RETURN(spanNode, eraseLength);
7539 spanResult.SetTextStyle(GetTextStyleObject(spanNode));
7540 return eraseLength;
7541 }
7542
7543 void RichEditorPattern::DeleteByDeleteValueInfo(const RichEditorDeleteValue& info)
7544 {
7545 auto deleteSpans = info.GetRichEditorDeleteSpans();
7546 if (deleteSpans.empty()) {
7547 return;
7548 }
7549 auto host = GetHost();
7550 CHECK_NULL_VOID(host);
7551 ProcessDeleteNodes(deleteSpans);
7552 UpdateSpanPosition();
7553 SetCaretPosition(info.GetOffset(), false);
7554 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
7555 OnModifyDone();
7556 }
7557
7558 int32_t RichEditorPattern::ProcessDeleteNodes(std::list<RichEditorAbstractSpanResult>& deleteSpans)
7559 {
7560 auto eraseLength = 0;
7561 auto host = GetContentHost();
7562 CHECK_NULL_RETURN(host, eraseLength);
7563 std::set<int32_t, std::greater<int32_t>> deleteNodes;
7564 for (const auto& it : deleteSpans) {
7565 eraseLength += it.GetEraseLength();
7566 switch (it.GetType()) {
7567 case SpanResultType::TEXT: {
7568 auto ui_node = host->GetChildAtIndex(it.GetSpanIndex());
7569 CHECK_NULL_RETURN(ui_node, eraseLength);
7570 auto spanNode = DynamicCast<SpanNode>(ui_node);
7571 CHECK_NULL_RETURN(spanNode, eraseLength);
7572 auto spanItem = spanNode->GetSpanItem();
7573 CHECK_NULL_RETURN(spanItem, eraseLength);
7574 auto textTemp = spanItem->content;
7575 auto textTempSize = static_cast<int32_t>(textTemp.size());
7576 if (textTempSize < it.OffsetInSpan()) {
7577 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "ProcessDeleteNodes failed, "
7578 "contentLen=%{public}zu, spanItemSize=%{public}d, offsetInSpan=%{public}d",
7579 textTemp.length(), textTempSize, it.OffsetInSpan());
7580 RichEditorInfo errorInfo { RichEditorErrorType::DELETE_NODE, textTempSize,
7581 it.GetEraseLength(), it.OffsetInSpan() };
7582 RichEditorErrorReport(errorInfo);
7583 continue;
7584 }
7585 textTemp.erase(it.OffsetInSpan(), it.GetEraseLength());
7586 if (textTemp.size() == 0) {
7587 deleteNodes.emplace(it.GetSpanIndex());
7588 }
7589 spanNode->UpdateContent(textTemp);
7590 spanItem->position -= it.GetEraseLength();
7591 break;
7592 }
7593 case SpanResultType::IMAGE:
7594 deleteNodes.emplace(it.GetSpanIndex());
7595 placeholderCount_--;
7596 break;
7597 case SpanResultType::SYMBOL:
7598 deleteNodes.emplace(it.GetSpanIndex());
7599 break;
7600 default:
7601 break;
7602 }
7603 }
7604 RemoveEmptySpan(deleteNodes);
7605 return eraseLength;
7606 }
7607
7608 void RichEditorPattern::RemoveEmptySpan(std::set<int32_t, std::greater<int32_t>>& deleteSpanIndexs)
7609 {
7610 auto host = GetContentHost();
7611 CHECK_NULL_VOID(host);
7612 for (auto index : deleteSpanIndexs) {
7613 host->RemoveChildAtIndex(index);
7614 auto it = spans_.begin();
7615 std::advance(it, index);
7616 if (it != spans_.end()) {
7617 spans_.erase(it);
7618 }
7619 }
7620 }
7621
7622 RefPtr<GestureEventHub> RichEditorPattern::GetGestureEventHub() {
7623 auto host = GetHost();
7624 CHECK_NULL_RETURN(host, nullptr);
7625 return host->GetOrCreateGestureEventHub();
7626 }
7627
7628 bool RichEditorPattern::OnKeyEvent(const KeyEvent& keyEvent)
7629 {
7630 return TextInputClient::HandleKeyEvent(keyEvent);
7631 }
7632
7633 void RichEditorPattern::HandleSetSelection(int32_t start, int32_t end, bool showHandle)
7634 {
7635 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleSetSelection, range=[%{public}d,%{public}d], showHandle=%{public}d",
7636 start, end, showHandle);
7637 SelectionOptions options;
7638 options.handlePolicy = showHandle ? HandlePolicy::SHOW : HandlePolicy::HIDE;
7639 SetSelection(start, end, options);
7640 }
7641
7642 void RichEditorPattern::HandleExtendAction(int32_t action)
7643 {
7644 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleExtendAction %{public}d", action);
7645 switch (action) {
7646 case ACTION_SELECT_ALL:
7647 HandleMenuCallbackOnSelectAll(false);
7648 break;
7649 case ACTION_CUT:
7650 HandleOnCut();
7651 break;
7652 case ACTION_COPY:
7653 HandleOnCopy();
7654 break;
7655 case ACTION_PASTE:
7656 HandleOnPaste();
7657 break;
7658 default:
7659 break;
7660 }
7661 }
7662
7663 void RichEditorPattern::CursorMove(CaretMoveIntent direction)
7664 {
7665 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "direction=%{public}d", direction);
7666 switch (direction) {
7667 case CaretMoveIntent::Left:
7668 CursorMoveLeft();
7669 break;
7670 case CaretMoveIntent::Right:
7671 CursorMoveRight();
7672 break;
7673 case CaretMoveIntent::Up:
7674 CursorMoveUp();
7675 break;
7676 case CaretMoveIntent::Down:
7677 CursorMoveDown();
7678 break;
7679 case CaretMoveIntent::LeftWord:
7680 CursorMoveToNextWord(direction);
7681 break;
7682 case CaretMoveIntent::RightWord:
7683 CursorMoveToNextWord(direction);
7684 break;
7685 case CaretMoveIntent::ParagraghBegin:
7686 CursorMoveToParagraphBegin();
7687 break;
7688 case CaretMoveIntent::ParagraghEnd:
7689 CursorMoveToParagraphEnd();
7690 break;
7691 case CaretMoveIntent::Home:
7692 CursorMoveHome();
7693 break;
7694 case CaretMoveIntent::End:
7695 CursorMoveEnd();
7696 break;
7697 case CaretMoveIntent::LineBegin:
7698 CursorMoveLineBegin();
7699 break;
7700 case CaretMoveIntent::LineEnd:
7701 CursorMoveLineEnd();
7702 break;
7703 default:
7704 LOGW("Unsupported cursor move operation for rich editor");
7705 }
7706 }
7707
7708 void RichEditorPattern::MoveCaretAfterTextChange()
7709 {
7710 CHECK_NULL_VOID(isTextChange_ && moveLength_ != 0);
7711 isTextChange_ = false;
7712 switch (moveDirection_) {
7713 case MoveDirection::BACKWARD:
7714 SetCaretPosition(std::clamp((caretPosition_ - moveLength_), 0, GetTextContentLength()), false);
7715 break;
7716 case MoveDirection::FORWARD:
7717 SetCaretPosition(std::clamp((caretPosition_ + moveLength_), 0, GetTextContentLength()), false);
7718 break;
7719 default:
7720 break;
7721 }
7722 moveLength_ = 0;
7723 }
7724
7725 void RichEditorPattern::InitTouchEvent()
7726 {
7727 CHECK_NULL_VOID(!touchListener_);
7728 auto gesture = GetGestureEventHub();
7729 CHECK_NULL_VOID(gesture);
7730 auto touchTask = [weak = WeakClaim(this)](TouchEventInfo& info) {
7731 auto pattern = weak.Upgrade();
7732 CHECK_NULL_VOID(pattern);
7733 pattern->sourceType_ = info.GetSourceDevice();
7734 pattern->HandleTouchEvent(info);
7735 };
7736 touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
7737 gesture->AddTouchEvent(touchListener_);
7738 }
7739
7740 void RichEditorPattern::InitPanEvent()
7741 {
7742 CHECK_NULL_VOID(!panEvent_);
7743 auto gestureHub = GetGestureEventHub();
7744 CHECK_NULL_VOID(gestureHub);
7745 auto actionStartTask = [](const GestureEvent& info) {};
7746 auto actionUpdateTask = [](const GestureEvent& info) {};
7747 auto actionEndTask = [](const GestureEvent& info) {};
7748 GestureEventNoParameter actionCancelTask;
7749 panEvent_ = MakeRefPtr<PanEvent>(std::move(actionStartTask), std::move(actionUpdateTask),
7750 std::move(actionEndTask), std::move(actionCancelTask));
7751 PanDirection panDirection = { .type = PanDirection::ALL };
7752 PanDistanceMap distanceMap = { { SourceTool::UNKNOWN, DEFAULT_PAN_DISTANCE.ConvertToPx() },
7753 { SourceTool::PEN, DEFAULT_PEN_PAN_DISTANCE.ConvertToPx() } };
7754 gestureHub->AddPanEvent(panEvent_, panDirection, 1, distanceMap);
7755 gestureHub->SetPanEventType(GestureTypeName::PAN_GESTURE);
7756 gestureHub->SetOnGestureJudgeNativeBegin([weak = WeakClaim(this)](const RefPtr<NG::GestureInfo>& gestureInfo,
7757 const std::shared_ptr<BaseGestureEvent>& info) -> GestureJudgeResult {
7758 auto pattern = weak.Upgrade();
7759 CHECK_NULL_RETURN(pattern, GestureJudgeResult::CONTINUE);
7760 auto gestureType = gestureInfo->GetType();
7761 auto inputEventType = gestureInfo->GetInputEventType();
7762 bool isDraggingCaret = (gestureType == GestureTypeName::PAN_GESTURE)
7763 && (inputEventType == InputEventType::TOUCH_SCREEN) && pattern->moveCaretState_.isMoveCaret;
7764 bool isMouseSelecting = (gestureType == GestureTypeName::PAN_GESTURE)
7765 && (inputEventType == InputEventType::MOUSE_BUTTON) && !pattern->blockPress_;
7766 if (isDraggingCaret || isMouseSelecting) {
7767 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "prevent pan gesture draggingCaret=%{public}d mouseSelecting=%{public}d",
7768 isDraggingCaret, isMouseSelecting);
7769 return GestureJudgeResult::CONTINUE;
7770 }
7771 CHECK_NULL_RETURN(gestureInfo->GetType() != GestureTypeName::PAN_GESTURE, GestureJudgeResult::REJECT);
7772 return GestureJudgeResult::CONTINUE;
7773 });
7774 }
7775
7776 void RichEditorPattern::HandleTouchEvent(TouchEventInfo& info)
7777 {
7778 CHECK_NULL_VOID(!selectOverlay_->IsTouchAtHandle(info));
7779 CHECK_NULL_VOID(!info.GetTouches().empty());
7780 HandleUserTouchEvent(info);
7781 auto acceptedTouchInfo = GetAcceptedTouchLocationInfo(info);
7782 CHECK_NULL_VOID(acceptedTouchInfo.has_value());
7783 auto touchInfo = acceptedTouchInfo.value();
7784 auto touchType = touchInfo.GetTouchType();
7785 if (touchType == TouchType::DOWN) {
7786 HandleTouchDown(touchInfo);
7787 if (hasUrlSpan_) {
7788 HandleUrlSpanShowShadow(touchInfo.GetLocalLocation(), touchInfo.GetGlobalLocation(), GetUrlPressColor());
7789 }
7790 } else if (touchType == TouchType::UP) {
7791 IF_TRUE(status_ == Status::FLOATING, status_ = Status::NONE);
7792 HandleTouchUp();
7793 if (hasUrlSpan_) {
7794 HandleUrlSpanForegroundClear();
7795 }
7796 } else if (touchType == TouchType::MOVE) {
7797 HandleTouchMove(touchInfo);
7798 } else if (touchType == TouchType::CANCEL) {
7799 IF_PRESENT(magnifierController_, RemoveMagnifierFrameNode());
7800 HandleTouchCancelAfterLongPress();
7801 ResetTouchAndMoveCaretState();
7802 }
7803 }
7804
7805 std::optional<TouchLocationInfo> RichEditorPattern::GetAcceptedTouchLocationInfo(const TouchEventInfo& info)
7806 {
7807 const auto& touchInfos = info.GetChangedTouches();
7808 CHECK_NULL_RETURN(!touchInfos.empty(), std::nullopt);
7809 CHECK_NULL_RETURN(isTouchSelecting_ || moveCaretState_.touchFingerId.has_value(), touchInfos.front());
7810 const int32_t touchFingerId = isTouchSelecting_ ? selectingFingerId_ : moveCaretState_.touchFingerId.value();
7811 for (const auto& touchInfo : touchInfos) {
7812 if (touchInfo.GetFingerId() == touchFingerId) {
7813 return touchInfo;
7814 }
7815 }
7816 return std::nullopt;
7817 }
7818
7819 void RichEditorPattern::HandleUrlSpanForegroundClear()
7820 {
7821 CHECK_NULL_VOID(overlayMod_);
7822 overlayMod_->ClearSelectedForegroundColorAndRects();
7823 MarkDirtySelf();
7824 }
7825
7826 void RichEditorPattern::HandleTouchDown(const TouchLocationInfo& info)
7827 {
7828 auto sourceTool = info.GetSourceTool();
7829 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Touch down longPressState=[%{public}d, %{public}d], source=%{public}d,"
7830 "fingers=%{public}d", previewLongPress_, editingLongPress_, sourceTool, ++touchedFingerCount_);
7831 globalOffsetOnMoveStart_ = GetPaintRectGlobalOffset();
7832 moveCaretState_.Reset();
7833 ResetTouchSelectState();
7834 CHECK_NULL_VOID(HasFocus() && sourceTool == SourceTool::FINGER);
7835 auto touchDownOffset = info.GetLocalLocation();
7836 moveCaretState_.touchDownOffset = touchDownOffset;
7837 RectF lastCaretRect = GetCaretRect();
7838 if (RepeatClickCaret(touchDownOffset, lastCaretRect)) {
7839 moveCaretState_.isTouchCaret = true;
7840 }
7841 }
7842
7843 void RichEditorPattern::HandleTouchUp()
7844 {
7845 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleTouchUp, fingers=%{public}d", --touchedFingerCount_);
7846 HandleTouchUpAfterLongPress();
7847 ResetTouchAndMoveCaretState();
7848 ResetTouchSelectState();
7849 if (magnifierController_) {
7850 magnifierController_->RemoveMagnifierFrameNode();
7851 }
7852 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
7853 if (isLongPress_) {
7854 isLongPress_ = false;
7855 }
7856 #endif
7857 }
7858
7859 void RichEditorPattern::StartFloatingCaretLand()
7860 {
7861 AnimationUtils::StopAnimation(magnifierAnimation_);
7862 CHECK_NULL_VOID(floatingCaretState_.isFloatingCaretVisible);
7863 auto caretOffset = CalculateCaretOffsetAndHeight().first;
7864 auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
7865 IF_PRESENT(overlayMod, StartFloatingCaretLand(caretOffset));
7866 }
7867
7868 void RichEditorPattern::ResetTouchAndMoveCaretState(bool needAnimation)
7869 {
7870 if (moveCaretState_.isMoveCaret) {
7871 isCursorAlwaysDisplayed_ = false;
7872 IF_TRUE(isEditing_, StartTwinkling());
7873 }
7874 StopAutoScroll();
7875 CheckScrollable();
7876 UpdateScrollBarOffset();
7877 moveCaretState_.Reset();
7878 needAnimation ? StartFloatingCaretLand() : ResetFloatingCaretState();
7879 }
7880
7881 void RichEditorPattern::ResetTouchSelectState()
7882 {
7883 selectingFingerId_ = -1;
7884 isTouchSelecting_ = false;
7885 previewLongPress_ = false;
7886 editingLongPress_ = false;
7887 }
7888
7889 void RichEditorPattern::ScheduleFirstClickResetAfterWindowFocus()
7890 {
7891 auto pipeline = GetContext();
7892 CHECK_NULL_VOID(pipeline);
7893 auto taskExecutor = pipeline->GetTaskExecutor();
7894 CHECK_NULL_VOID(taskExecutor);
7895 firstClickResetTask_.Cancel();
7896 firstClickResetTask_.Reset([weak = WeakClaim(this)]() {
7897 auto pattern = weak.Upgrade();
7898 CHECK_NULL_VOID(pattern);
7899 pattern->firstClickAfterWindowFocus_ = false;
7900 });
7901 firstClickAfterWindowFocus_ = true;
7902 taskExecutor->PostDelayedTask(firstClickResetTask_, TaskExecutor::TaskType::UI,
7903 RICH_EDITOR_TWINKLING_INTERVAL_MS, "ResetFirstClickAfterWindowFocus");
7904 }
7905
7906 void RichEditorPattern::HandleTouchUpAfterLongPress()
7907 {
7908 CHECK_NULL_VOID((editingLongPress_ || previewLongPress_) && !IsAiSelected());
7909 auto selectStart = std::min(textSelector_.GetTextStart(), GetTextContentLength());
7910 auto selectEnd = std::min(textSelector_.GetTextEnd(), GetTextContentLength());
7911 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "after long press textSelector=[%{public}d, %{public}d] isEditing=%{public}d",
7912 selectStart, selectEnd, isEditing_);
7913 textSelector_.Update(selectStart, selectEnd);
7914 FireOnSelect(selectStart, selectEnd);
7915 SetCaretPositionWithAffinity({ selectEnd, TextAffinity::UPSTREAM });
7916 CalculateHandleOffsetAndShowOverlay();
7917 selectOverlay_->ProcessOverlay({ .animation = true });
7918 FireOnSelectionChange(selectStart, selectEnd);
7919 IF_TRUE(IsSingleHandle(), ForceTriggerAvoidOnCaretChange());
7920 }
7921
7922 void RichEditorPattern::HandleTouchCancelAfterLongPress()
7923 {
7924 CHECK_NULL_VOID(editingLongPress_ || previewLongPress_);
7925 auto selectStart = std::min(textSelector_.GetTextStart(), GetTextContentLength());
7926 auto selectEnd = std::min(textSelector_.GetTextEnd(), GetTextContentLength());
7927 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "touch canceled, textSelector=[%{public}d, %{public}d] isEditing=%{public}d",
7928 selectStart, selectEnd, isEditing_);
7929 textSelector_.Update(selectStart, selectEnd);
7930 SetCaretPositionWithAffinity({ selectEnd, TextAffinity::UPSTREAM });
7931 CalculateHandleOffsetAndShowOverlay();
7932 selectOverlay_->ProcessOverlay({ .menuIsShow = selectOverlay_->IsCurrentMenuVisibile(), .animation = true });
7933 FireOnSelectionChange(selectStart, selectEnd);
7934 }
7935
7936 void RichEditorPattern::HandleTouchMove(const TouchLocationInfo& info)
7937 {
7938 auto originalLocaloffset = info.GetLocalLocation();
7939 auto offset = AdjustLocalOffsetOnMoveEvent(originalLocaloffset);
7940 if ((previewLongPress_ || editingLongPress_) && !IsAiSelected()) {
7941 if (!isTouchSelecting_ && !IsAiSelected()) {
7942 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Touch selecting start id= %{public}d", info.GetFingerId());
7943 showSelect_ = true;
7944 isTouchSelecting_ = true;
7945 selectingFingerId_ = info.GetFingerId();
7946 }
7947 UpdateSelectionByTouchMove(offset);
7948 return;
7949 }
7950 CHECK_NULL_VOID(moveCaretState_.isTouchCaret && caretTwinkling_);
7951 if (!moveCaretState_.isMoveCaret) {
7952 auto moveDistance = (offset - moveCaretState_.touchDownOffset).GetDistance();
7953 IF_TRUE(GreatNotEqual(moveDistance, moveCaretState_.minDistance.ConvertToPx()),
7954 OnMoveCaretStart(info.GetFingerId()));
7955 }
7956 UpdateCaretByTouchMove(offset);
7957 }
7958
7959 void RichEditorPattern::OnMoveCaretStart(int32_t fingerId)
7960 {
7961 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Move caret start id=%{public}d", fingerId);
7962 moveCaretState_.isMoveCaret = true;
7963 moveCaretState_.touchFingerId = fingerId;
7964 scrollable_ = false;
7965 SetScrollEnabled(scrollable_);
7966 UpdateScrollBarOffset();
7967 ShowCaretWithoutTwinkling();
7968 }
7969
7970 void RichEditorPattern::UpdateCaretByTouchMove(const Offset& offset)
7971 {
7972 CHECK_NULL_VOID(moveCaretState_.isMoveCaret);
7973 auto host = GetHost();
7974 CHECK_NULL_VOID(host);
7975 if (SelectOverlayIsOn()) {
7976 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Close select overlay while dragging caret");
7977 selectOverlay_->CloseOverlay(false, CloseReason::CLOSE_REASON_NORMAL);
7978 }
7979 auto touchOffset = Offset(offset.GetX(), std::max(offset.GetY(), static_cast<double>(contentRect_.GetY())));
7980 Offset textOffset = ConvertTouchOffsetToTextOffset(touchOffset);
7981 auto positionWithAffinity = paragraphs_.GetGlyphPositionAtCoordinate(textOffset);
7982 SetCaretPositionWithAffinity(positionWithAffinity);
7983 SetCaretTouchMoveOffset(offset);
7984 CalcAndRecordLastClickCaretInfo(textOffset);
7985 auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
7986 auto floatingCaretCenter =
7987 Offset(floatingCaretState_.touchMoveOffset->GetX(), caretOffset.GetY() + caretHeight / 2);
7988 SetMagnifierOffsetWithAnimation(floatingCaretCenter);
7989 AutoScrollParam param = { .autoScrollEvent = AutoScrollEvent::CARET, .showScrollbar = true };
7990 AutoScrollByEdgeDetection(param, OffsetF(offset.GetX(), offset.GetY()), EdgeDetectionStrategy::OUT_BOUNDARY);
7991 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
7992 }
7993
7994 void RichEditorPattern::SetCaretTouchMoveOffset(const Offset& localOffset)
7995 {
7996 double moveDistance = 0.0;
7997 auto positionType = GetPositionTypeFromLine();
7998 auto caretBoundaryRect = GetCaretBoundaryRect();
7999 if (positionType == PositionType::DEFAULT) {
8000 floatingCaretState_.UpdateByTouchMove(localOffset, moveDistance, caretBoundaryRect);
8001 return;
8002 }
8003 auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
8004 bool isCaretAtEmptyParagraph =
8005 positionType == PositionType::PARAGRAPH_START && caretPosition_ == GetParagraphEndPosition(caretPosition_);
8006 if (isCaretAtEmptyParagraph) {
8007 moveDistance = std::abs(localOffset.GetX() - caretOffset.GetX());
8008 } else {
8009 moveDistance = (caretAffinityPolicy_ == CaretAffinityPolicy::DOWNSTREAM_FIRST || caretPosition_ == 0)
8010 ? caretOffset.GetX() - localOffset.GetX() : localOffset.GetX() - caretOffset.GetX();
8011 }
8012 floatingCaretState_.UpdateByTouchMove(localOffset, moveDistance, caretBoundaryRect);
8013 }
8014
8015 RectF RichEditorPattern::GetCaretBoundaryRect()
8016 {
8017 auto caretBoundaryRect = contentRect_;
8018 auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
8019 CHECK_NULL_RETURN(overlayModifier, caretBoundaryRect);
8020 auto caretWidth = overlayModifier->GetCaretWidth();
8021 caretBoundaryRect.SetWidth(caretBoundaryRect.Width() - caretWidth);
8022 return caretBoundaryRect;
8023 }
8024
8025 void RichEditorPattern::SetMagnifierLocalOffset(Offset offset)
8026 {
8027 CHECK_NULL_VOID(magnifierController_);
8028 auto localOffset = OffsetF{ offset.GetX(), offset.GetY() };
8029 auto localOffsetWithTrans = localOffset;
8030 selectOverlay_->GetLocalPointWithTransform(localOffsetWithTrans);
8031 magnifierController_->SetLocalOffset(localOffsetWithTrans, localOffset);
8032 }
8033
8034 void RichEditorPattern::SetMagnifierOffsetWithAnimation(Offset offset)
8035 {
8036 CHECK_NULL_VOID(magnifierController_);
8037 auto currentLocalOffset = magnifierController_->GetLocalOffset();
8038 auto currentOffset = magnifierController_->GetLocalOffsetWithoutTrans().value_or(currentLocalOffset);
8039 if (NearEqual(currentOffset.GetY(), offset.GetY(), 0.5f) || !magnifierController_->GetShowMagnifier()) {
8040 SetMagnifierLocalOffset(offset);
8041 return;
8042 }
8043 AnimationUtils::StopAnimation(magnifierAnimation_);
8044 AnimationOption option{ MAGNIFIER_ANIMATION_CURVE, MAGNIFIER_ANIMATION_DURATION };
8045 magnifierAnimation_ = AnimationUtils::StartAnimation(option, [weak = WeakClaim(this), offset]() {
8046 auto pattern = weak.Upgrade();
8047 CHECK_NULL_VOID(pattern);
8048 pattern->SetMagnifierLocalOffset(offset);
8049 });
8050 }
8051
8052 Offset RichEditorPattern::AdjustLocalOffsetOnMoveEvent(const Offset& originalOffset)
8053 {
8054 auto deltaOffset = GetPaintRectGlobalOffset() - globalOffsetOnMoveStart_;
8055 return { originalOffset.GetX() - deltaOffset.GetX(), originalOffset.GetY() - deltaOffset.GetY() };
8056 }
8057
8058 void RichEditorPattern::StartVibratorByIndexChange(int32_t currentIndex, int32_t preIndex)
8059 {
8060 CHECK_NULL_VOID(isEnableHapticFeedback_ && (currentIndex != preIndex));
8061 VibratorUtils::StartVibraFeedback("slide");
8062 }
8063
8064 bool RichEditorPattern::IsScrollBarPressed(const MouseInfo& info)
8065 {
8066 auto scrollBar = GetScrollBar();
8067 bool isScrollBarShow = scrollBar && scrollBar->NeedPaint();
8068 CHECK_NULL_RETURN(isScrollBarShow, false);
8069 Point point(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY());
8070 return scrollBar->InBarRectRegion(point);
8071 }
8072
8073 void RichEditorPattern::HandleMouseLeftButtonMove(const MouseInfo& info)
8074 {
8075 ACE_SCOPED_TRACE("RichEditorHandleMouseLeftButtonMove");
8076 CHECK_NULL_VOID(!IsPreviewTextInputting());
8077 if (blockPress_) {
8078 ACE_SCOPED_TRACE("RichEditorUpdateDragBoxes");
8079 dragBoxes_ = GetTextBoxes();
8080 return;
8081 }
8082 CHECK_NULL_VOID(leftMousePress_);
8083
8084 auto localOffset = info.GetLocalLocation();
8085 const auto& globalOffset = info.GetGlobalLocation();
8086 auto paintOffset = GetPaintRectGlobalOffset();
8087 if (!selectOverlay_->HasRenderTransform()) {
8088 localOffset = Offset(globalOffset.GetX() - paintOffset.GetX(), globalOffset.GetY() - paintOffset.GetY());
8089 }
8090 AdjustMouseLocalOffset(localOffset);
8091 Offset textOffset = ConvertTouchOffsetToTextOffset(localOffset);
8092 if (dataDetectorAdapter_->pressedByLeftMouse_) {
8093 dataDetectorAdapter_->pressedByLeftMouse_ = false;
8094 MoveCaretAndStartFocus(textOffset);
8095 }
8096
8097 auto focusHub = GetFocusHub();
8098 CHECK_NULL_VOID(focusHub);
8099 CHECK_NULL_VOID(focusHub->IsCurrentFocus());
8100
8101 mouseStatus_ = MouseStatus::MOVE;
8102 HandleMouseSelect(localOffset);
8103 auto host = GetHost();
8104 CHECK_NULL_VOID(host);
8105 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
8106 }
8107
8108 void RichEditorPattern::AdjustMouseLocalOffset(Offset& offset)
8109 {
8110 CHECK_NULL_VOID(GreatNotEqual(offset.GetY(), richTextRect_.Bottom()));
8111 offset.SetX(richTextRect_.Right());
8112 }
8113
8114 void RichEditorPattern::HandleMouseSelect(const Offset& localOffset)
8115 {
8116 if (!textSelector_.IsValid()) {
8117 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "prevent mouse selecting because selection has been reset");
8118 return;
8119 }
8120 Offset textOffset = ConvertTouchOffsetToTextOffset(localOffset);
8121 auto position = GetTextContentLength() == 0 ? 0 : paragraphs_.GetIndex(textOffset);
8122 UpdateSelector(textSelector_.baseOffset, position);
8123 if (!isFirstMouseSelect_) {
8124 AdjustCursorPosition(position);
8125 SetCaretPosition(position);
8126 AutoScrollParam param = {
8127 .autoScrollEvent = AutoScrollEvent::MOUSE, .showScrollbar = true, .eventOffset = localOffset
8128 };
8129 AutoScrollByEdgeDetection(param, OffsetF(localOffset.GetX(), localOffset.GetY()),
8130 EdgeDetectionStrategy::OUT_BOUNDARY);
8131 showSelect_ = true;
8132 } else {
8133 isFirstMouseSelect_ = false;
8134 }
8135 if (textSelector_.SelectNothing()) {
8136 StartTwinkling();
8137 } else {
8138 StopTwinkling();
8139 }
8140 isMouseSelect_ = true;
8141 }
8142
8143 void RichEditorPattern::HandleMouseLeftButtonPress(const MouseInfo& info)
8144 {
8145 isMousePressed_ = true;
8146 auto frameNodeRange = GetSpanRangeByLocalOffset(info.GetLocalLocation());
8147 bool frameNodeSelected = textSelector_.ContainsRange(frameNodeRange);
8148 bool pressFrameNode = !frameNodeSelected && InRangeRect(info.GetGlobalLocation(), frameNodeRange);
8149 if (IsPreviewTextInputting() || IsScrollBarPressed(info) || BetweenSelectedPosition(info.GetGlobalLocation()) ||
8150 pressFrameNode) {
8151 blockPress_ = true;
8152 return;
8153 }
8154 auto focusHub = GetFocusHub();
8155 CHECK_NULL_VOID(focusHub);
8156 if (!focusHub->IsFocusable()) {
8157 return;
8158 }
8159 auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
8160 auto localOffset = info.GetLocalLocation();
8161 AdjustMouseLocalOffset(localOffset);
8162 Offset textOffset = { localOffset.GetX() - textPaintOffset.GetX(), localOffset.GetY() - textPaintOffset.GetY() };
8163 int32_t position = (GetTextContentLength() == 0) ? 0 : paragraphs_.GetIndex(textOffset);
8164 if (shiftFlag_) {
8165 HandleShiftSelect(position);
8166 } else {
8167 IF_TRUE(!textSelector_.SelectNothing(), ResetSelection());
8168 textSelector_.Update(position);
8169 }
8170 leftMousePress_ = true;
8171 globalOffsetOnMoveStart_ = GetPaintRectGlobalOffset();
8172 mouseStatus_ = MouseStatus::PRESSED;
8173 blockPress_ = false;
8174 caretUpdateType_ = CaretUpdateType::PRESSED;
8175 dataDetectorAdapter_->pressedByLeftMouse_ = false;
8176 HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY()));
8177 if (dataDetectorAdapter_->pressedByLeftMouse_) {
8178 return;
8179 }
8180 UseHostToUpdateTextFieldManager();
8181 MoveCaretAndStartFocus(textOffset);
8182 CalcCaretInfoByClick(localOffset);
8183 }
8184
8185 void RichEditorPattern::HandleShiftSelect(int32_t position)
8186 {
8187 CHECK_NULL_VOID(shiftFlag_);
8188 int32_t start = textSelector_.SelectNothing() ? caretPosition_ : textSelector_.baseOffset;
8189 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleShiftSelect [%{public}d-%{public}d]", start, position);
8190 UpdateSelector(start, position);
8191 SetCaretPosition(position);
8192 FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
8193 auto host = GetHost();
8194 CHECK_NULL_VOID(host);
8195 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
8196 }
8197
8198 void RichEditorPattern::HandleMouseLeftButtonRelease(const MouseInfo& info)
8199 {
8200 blockPress_ = false;
8201 leftMousePress_ = false;
8202 CHECK_NULL_VOID(!IsPreviewTextInputting());
8203 auto oldMouseStatus = mouseStatus_;
8204 mouseStatus_ = MouseStatus::RELEASED;
8205 isMouseSelect_ = false;
8206 isFirstMouseSelect_ = true;
8207 if (!showSelect_) {
8208 showSelect_ = true;
8209 ResetSelection();
8210 }
8211 if (dataDetectorAdapter_->pressedByLeftMouse_ && oldMouseStatus != MouseStatus::MOVE && !IsDragging()) {
8212 dataDetectorAdapter_->ResponseBestMatchItem(dataDetectorAdapter_->clickedAISpan_);
8213 dataDetectorAdapter_->pressedByLeftMouse_ = false;
8214 isMousePressed_ = false;
8215 return;
8216 }
8217
8218 auto selectStart = std::min(textSelector_.baseOffset, textSelector_.destinationOffset);
8219 auto selectEnd = std::max(textSelector_.baseOffset, textSelector_.destinationOffset);
8220 if (selectStart != selectEnd && isMousePressed_ && oldMouseStatus == MouseStatus::MOVE) {
8221 FireOnSelect(selectStart, selectEnd);
8222 }
8223 StopAutoScroll();
8224 if (textSelector_.IsValid() && !textSelector_.StartEqualToDest() && IsSelectedBindSelectionMenu() &&
8225 oldMouseStatus == MouseStatus::MOVE) {
8226 auto offsetX = static_cast<float>(info.GetGlobalLocation().GetX());
8227 auto offsetY = static_cast<float>(info.GetGlobalLocation().GetY());
8228 selectionMenuOffsetByMouse_ = OffsetF(offsetX, offsetY);
8229 selectionMenuOffsetClick_ = OffsetF(offsetX, offsetY);
8230 ShowSelectOverlay(RectF(), RectF(), false, TextResponseType::SELECTED_BY_MOUSE);
8231 }
8232 isMousePressed_ = false;
8233 RequestKeyboardToEdit();
8234 }
8235
8236 void RichEditorPattern::HandleMouseLeftButton(const MouseInfo& info)
8237 {
8238 if (info.GetAction() == MouseAction::MOVE) {
8239 HandleMouseLeftButtonMove(info);
8240 } else if (info.GetAction() == MouseAction::PRESS) {
8241 HandleMouseLeftButtonPress(info);
8242 } else if (info.GetAction() == MouseAction::RELEASE) {
8243 HandleMouseLeftButtonRelease(info);
8244 }
8245 }
8246
8247 void RichEditorPattern::HandleMouseRightButton(const MouseInfo& info)
8248 {
8249 CHECK_NULL_VOID(!IsPreviewTextInputting());
8250 IF_TRUE(IsDragging(), isInterceptMouseRightRelease_ = true);
8251 auto focusHub = GetFocusHub();
8252 CHECK_NULL_VOID(focusHub && focusHub->IsFocusable());
8253 if (info.GetAction() == MouseAction::RELEASE && isInterceptMouseRightRelease_) {
8254 usingMouseRightButton_ = false;
8255 isMousePressed_ = false;
8256 isInterceptMouseRightRelease_ = false;
8257 return;
8258 }
8259 if (info.GetAction() == MouseAction::PRESS) {
8260 isMousePressed_ = true;
8261 usingMouseRightButton_ = true;
8262 CloseSelectOverlay();
8263 } else if (info.GetAction() == MouseAction::RELEASE) {
8264 auto offsetX = static_cast<float>(info.GetGlobalLocation().GetX());
8265 auto offsetY = static_cast<float>(info.GetGlobalLocation().GetY());
8266 selectionMenuOffsetByMouse_ = OffsetF(offsetX, offsetY);
8267 selectionMenuOffsetClick_ = OffsetF(offsetX, offsetY);
8268 selectOverlay_->SetIsSingleHandle(false);
8269 if (textSelector_.IsValid() && BetweenSelection(info.GetGlobalLocation())) {
8270 ShowSelectOverlay(RectF(), RectF(), IsSelectAll(), TextResponseType::RIGHT_CLICK);
8271 isMousePressed_ = false;
8272 usingMouseRightButton_ = false;
8273 return;
8274 }
8275 auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
8276 Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
8277 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
8278 HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY()));
8279 if (dataDetectorAdapter_->hasClickedAISpan_) {
8280 dataDetectorAdapter_->hasClickedAISpan_ = false;
8281 isMousePressed_ = false;
8282 usingMouseRightButton_ = false;
8283 return;
8284 }
8285 if (textSelector_.IsValid()) {
8286 CloseSelectOverlay();
8287 ResetSelection();
8288 }
8289 MouseRightFocus(info);
8290 ShowSelectOverlay(RectF(), RectF(), IsSelectAll(), TextResponseType::RIGHT_CLICK);
8291 isMousePressed_ = false;
8292 usingMouseRightButton_ = false;
8293 }
8294 }
8295
8296 std::pair<int32_t, int32_t> RichEditorPattern::GetSpanRangeByLocalOffset(Offset localOffset)
8297 {
8298 Offset textOffset = ConvertTouchOffsetToTextOffset(localOffset);
8299 auto [pos, affinity] = paragraphs_.GetGlyphPositionAtCoordinate(textOffset);
8300 auto spanFilter = [](SpanItemType itemType) {
8301 return itemType == SpanItemType::IMAGE
8302 || itemType == SpanItemType::PLACEHOLDER
8303 || itemType == SpanItemType::CustomSpan; };
8304 auto it = std::find_if(spans_.begin(), spans_.end(),
8305 [pos = static_cast<int32_t>(pos), affinity = affinity, spanFilter](const RefPtr<SpanItem>& spanItem) {
8306 CHECK_NULL_RETURN(spanFilter(spanItem->spanItemType), false);
8307 return pos == (affinity == TextAffinity::UPSTREAM ? spanItem->position : spanItem->rangeStart);
8308 });
8309 bool isMouseOnTarget = it != spans_.end();
8310 CHECK_NULL_RETURN(isMouseOnTarget, std::make_pair(-1, -1));
8311 return affinity == TextAffinity::UPSTREAM ? std::make_pair(pos - 1, pos) : std::make_pair(pos, pos + 1);
8312 }
8313
8314 void RichEditorPattern::MouseRightFocus(const MouseInfo& info)
8315 {
8316 auto host = GetHost();
8317 CHECK_NULL_VOID(host);
8318 auto focusHub = host->GetOrCreateFocusHub();
8319 CHECK_NULL_VOID(focusHub);
8320
8321 auto selectRange = GetSpanRangeByLocalOffset(info.GetLocalLocation());
8322 if (InRangeRect(info.GetGlobalLocation(), selectRange)) {
8323 selectedType_ = TextSpanType::IMAGE;
8324 textSelector_.Update(selectRange.first, selectRange.second);
8325 focusHub->RequestFocusImmediately();
8326 SetCaretPositionWithAffinity({ selectRange.second, TextAffinity::UPSTREAM });
8327 FireOnSelect(selectRange.first, selectRange.second);
8328 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
8329 return;
8330 }
8331 if (textSelector_.IsValid()) {
8332 ResetSelection();
8333 }
8334 Offset textOffset = ConvertTouchOffsetToTextOffset(info.GetLocalLocation());
8335 auto position = paragraphs_.GetIndex(textOffset);
8336 SetCaretPosition(position);
8337 focusHub->RequestFocusImmediately();
8338 CalcAndRecordLastClickCaretInfo(textOffset);
8339 auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
8340 selectedType_ = TextSpanType::TEXT;
8341 CHECK_NULL_VOID(overlayMod_);
8342 DynamicCast<RichEditorOverlayModifier>(overlayMod_)->SetCaretOffsetAndHeight(caretOffset, caretHeight);
8343 StartTwinkling();
8344 RequestKeyboardToEdit();
8345 }
8346
8347 void RichEditorPattern::FireOnSelect(int32_t selectStart, int32_t selectEnd)
8348 {
8349 auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
8350 CHECK_NULL_VOID(eventHub);
8351 auto textSelectInfo = GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT);
8352 if (!textSelectInfo.GetSelection().resultObjects.empty()) {
8353 eventHub->FireOnSelect(&textSelectInfo);
8354 }
8355 UpdateSelectionType(textSelectInfo);
8356 }
8357
8358 void RichEditorPattern::UpdateSelectionType(const SelectionInfo& textSelectInfo)
8359 {
8360 if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
8361 TextPattern::UpdateSelectionType(GetAdjustedSelectionInfo(textSelectInfo));
8362 } else {
8363 TextPattern::UpdateSelectionType(textSelectInfo);
8364 }
8365 }
8366
8367 SelectionInfo RichEditorPattern::GetAdjustedSelectionInfo(const SelectionInfo& textSelectInfo)
8368 {
8369 auto selection = textSelectInfo.GetSelection();
8370 auto resultObjects = selection.resultObjects;
8371 std::for_each(resultObjects.begin(), resultObjects.end(), [](ResultObject& object) {
8372 if (object.type == SelectSpanType::TYPEIMAGE && object.valueString == u" " && object.valuePixelMap == nullptr) {
8373 object.type = SelectSpanType::TYPEBUILDERSPAN;
8374 }
8375 });
8376 SelectionInfo adjustedInfo;
8377 adjustedInfo.SetSelectionStart(selection.selection[RichEditorSpanRange::RANGESTART]);
8378 adjustedInfo.SetSelectionEnd(selection.selection[RichEditorSpanRange::RANGEEND]);
8379 adjustedInfo.SetResultObjectList(resultObjects);
8380 return adjustedInfo;
8381 }
8382
8383 void RichEditorPattern::HandleMouseEvent(const MouseInfo& info)
8384 {
8385 HandleImageHoverEvent(info);
8386 if (selectOverlay_->IsHandleShow() && info.GetAction() == MouseAction::PRESS) {
8387 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Close selectOverlay when handle is showing");
8388 CloseSelectOverlay();
8389 }
8390 auto scrollBar = GetScrollBar();
8391 if (scrollBar && (scrollBar->IsHover() || scrollBar->IsPressed())) {
8392 ChangeMouseStyle(MouseFormat::DEFAULT);
8393 HandleUrlSpanForegroundClear();
8394 return;
8395 }
8396
8397 if (hasUrlSpan_ && !IsDragging()) {
8398 auto show = HandleUrlSpanShowShadow(info.GetLocalLocation(), info.GetGlobalLocation(), GetUrlHoverColor());
8399 if (show) {
8400 ChangeMouseStyle(MouseFormat::HAND_POINTING);
8401 } else {
8402 ChangeMouseStyle(MouseFormat::TEXT_CURSOR);
8403 }
8404 }
8405
8406 if (currentMouseStyle_ == MouseFormat::DEFAULT && !IsDragging()) {
8407 ChangeMouseStyle(MouseFormat::TEXT_CURSOR);
8408 }
8409
8410 caretUpdateType_ = CaretUpdateType::NONE;
8411 if (info.GetButton() == MouseButton::LEFT_BUTTON) {
8412 sourceType_ = info.GetSourceDevice();
8413 HandleMouseLeftButton(info);
8414 } else if (info.GetButton() == MouseButton::RIGHT_BUTTON) {
8415 sourceType_ = info.GetSourceDevice();
8416 HandleMouseRightButton(info);
8417 }
8418 }
8419
8420 Color RichEditorPattern::GetUrlHoverColor()
8421 {
8422 auto theme = GetTheme<RichEditorTheme>();
8423 CHECK_NULL_RETURN(theme, Color());
8424 return theme->GetUrlHoverColor();
8425 }
8426
8427 Color RichEditorPattern::GetUrlPressColor()
8428 {
8429 auto theme = GetTheme<RichEditorTheme>();
8430 CHECK_NULL_RETURN(theme, Color());
8431 return theme->GetUrlPressColor();
8432 }
8433
8434 Color RichEditorPattern::GetUrlSpanColor()
8435 {
8436 auto theme = GetTheme<RichEditorTheme>();
8437 CHECK_NULL_RETURN(theme, Color());
8438 return theme->GetUrlDefaultColor();
8439 }
8440
8441 void RichEditorPattern::TriggerAvoidOnCaretChange()
8442 {
8443 CHECK_NULL_VOID(HasFocus());
8444 auto pipeline = GetContext();
8445 CHECK_NULL_VOID(pipeline);
8446 auto textFieldManager = DynamicCast<TextFieldManagerNG>(pipeline->GetTextFieldManager());
8447 CHECK_NULL_VOID(textFieldManager);
8448 CHECK_NULL_VOID(pipeline->UsingCaretAvoidMode());
8449 auto safeAreaManager = pipeline->GetSafeAreaManager();
8450 if (!safeAreaManager || NearZero(safeAreaManager->GetKeyboardInset().Length(), 0)) {
8451 return;
8452 }
8453 auto lastCaretPos = GetLastCaretPos();
8454 auto caretPos = textFieldManager->GetFocusedNodeCaretRect().Top() + textFieldManager->GetHeight();
8455 if (lastCaretPos.has_value() && caretPos > lastCaretPos.value() && !isTriggerAvoidOnCaretAvoidMode_) {
8456 return;
8457 }
8458 SetLastCaretPos(caretPos);
8459 textFieldManager->SetHeight(GetCaretRect().Height());
8460 auto taskExecutor = pipeline->GetTaskExecutor();
8461 CHECK_NULL_VOID(taskExecutor);
8462 taskExecutor->PostTask([manager = WeakPtr<TextFieldManagerNG>(textFieldManager)] {
8463 auto textFieldManager = manager.Upgrade();
8464 CHECK_NULL_VOID(textFieldManager);
8465 textFieldManager->TriggerAvoidOnCaretChange();
8466 }, TaskExecutor::TaskType::UI, "ArkUIRichEditorTriggerAvoidOnCaretChange", PriorityType::VIP);
8467 }
8468
8469 void RichEditorPattern::OnWindowSizeChanged(int32_t width, int32_t height, WindowSizeChangeReason type)
8470 {
8471 selectOverlay_->UpdateMenuOnWindowSizeChanged(type);
8472 CHECK_NULL_VOID(type == WindowSizeChangeReason::ROTATION);
8473 auto host = GetHost();
8474 CHECK_NULL_VOID(host);
8475 auto context = host->GetContextRefPtr();
8476 CHECK_NULL_VOID(context);
8477 auto taskExecutor = context->GetTaskExecutor();
8478 CHECK_NULL_VOID(taskExecutor);
8479 auto textFieldManager = DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
8480 CHECK_NULL_VOID(textFieldManager);
8481 textFieldManager->ResetOptionalClickPosition();
8482 taskExecutor->PostTask(
8483 [weak = WeakClaim(this), manager = WeakPtr<TextFieldManagerNG>(textFieldManager)] {
8484 auto pattern = weak.Upgrade();
8485 CHECK_NULL_VOID(pattern);
8486 pattern->UpdateParentOffsetAndOverlay();
8487 pattern->UpdateModifierCaretOffsetAndHeight();
8488 pattern->UpdateTextFieldManager(Offset(pattern->parentGlobalOffset_.GetX(),
8489 pattern->parentGlobalOffset_.GetY()), pattern->frameRect_.Height());
8490 pattern->UpdateCaretInfoToController();
8491 if (pattern->HasFocus()) {
8492 auto textFieldManager = manager.Upgrade();
8493 CHECK_NULL_VOID(textFieldManager);
8494 auto container = Container::Current();
8495 CHECK_NULL_VOID(container);
8496 auto displayInfo = container->GetDisplayInfo();
8497 if (displayInfo) {
8498 auto dmRotation = static_cast<int32_t>(displayInfo->GetRotation());
8499 textFieldManager->SetFocusFieldOrientation(dmRotation);
8500 }
8501 }
8502 },
8503 TaskExecutor::TaskType::UI, "ArkUIRichEditorOnWindowSizeChangedRotation", PriorityType::VIP);
8504 }
8505
8506 void RichEditorPattern::CopySelectionMenuParams(SelectOverlayInfo& selectInfo, TextResponseType responseType)
8507 {
8508 auto selectType = selectedType_.value_or(TextSpanType::NONE);
8509 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "textSpanType=%{public}d, responseType=%{public}d", selectType, responseType);
8510 std::shared_ptr<SelectionMenuParams> menuParams = GetMenuParams(selectType, responseType);
8511 CHECK_NULL_VOID(menuParams);
8512
8513 // long pressing on the image needs to set the position of the pop-up menu following the long pressing position
8514 if (selectType == TextSpanType::IMAGE && !selectInfo.isUsingMouse) {
8515 selectInfo.menuInfo.menuOffset = OffsetF(selectionMenuOffset_.GetX(), selectionMenuOffset_.GetY());
8516 }
8517
8518 CopyBindSelectionMenuParams(selectInfo, menuParams);
8519 }
8520
8521 void RichEditorPattern::ProcessOverlay(const OverlayRequest& request)
8522 {
8523 // this selectOverlay_ and selectOverlay_ in TextPattern are two distinct objects.
8524 selectOverlay_->ProcessOverlay(request);
8525 }
8526
8527 void RichEditorPattern::ShowSelectOverlay(const RectF& firstHandle, const RectF& secondHandle, bool isCopyAll,
8528 TextResponseType responseType, bool handleReverse)
8529 {
8530 CHECK_NULL_VOID(!IsPreviewTextInputting());
8531 textResponseType_ = responseType;
8532 ProcessOverlay({ .animation = true });
8533 }
8534
8535 void RichEditorPattern::SetIsEnableSubWindowMenu()
8536 {
8537 selectOverlay_->SetIsHostNodeEnableSubWindowMenu(true);
8538 }
8539
8540 void RichEditorPattern::CheckEditorTypeChange()
8541 {
8542 CHECK_NULL_VOID(selectOverlayProxy_);
8543 CHECK_NULL_VOID(!selectOverlayProxy_->IsClosed());
8544 if (selectOverlayProxy_->GetSelectOverlayMangerInfo().menuInfo.editorType.value_or(static_cast<int32_t>(
8545 TextSpanType::NONE)) != static_cast<int32_t>(selectedType_.value_or(TextSpanType::NONE))) {
8546 CloseSelectOverlay();
8547 }
8548 }
8549
8550 void RichEditorPattern::AsyncHandleOnCopyStyledStringHtml(RefPtr<SpanString>& subSpanString)
8551 {
8552 auto pipeline = GetContext();
8553 CHECK_NULL_VOID(pipeline);
8554 auto taskExecutor = pipeline->GetTaskExecutor();
8555 CHECK_NULL_VOID(taskExecutor);
8556 auto multiTypeRecordImpl = AceType::MakeRefPtr<MultiTypeRecordImpl>();
8557 subSpanString->EncodeTlv(multiTypeRecordImpl->GetSpanStringBuffer());
8558 multiTypeRecordImpl->SetPlainText(subSpanString->GetString());
8559 std::list<RefPtr<SpanItem>> copySpans = CopySpansForClipboard(subSpanString->GetSpanItems());
8560 taskExecutor->PostTask(
8561 [copySpans, multiTypeRecordImpl, weak = WeakClaim(this), task = WeakClaim(RawPtr(taskExecutor))]() {
8562 auto richEditor = weak.Upgrade();
8563 CHECK_NULL_VOID(richEditor);
8564 RefPtr<PasteDataMix> pasteData = richEditor->clipboard_->CreatePasteDataMix();
8565 CHECK_NULL_VOID(multiTypeRecordImpl);
8566 std::string htmlStr = HtmlUtils::ToHtml(copySpans);
8567 multiTypeRecordImpl->SetHtmlText(htmlStr);
8568
8569 auto uiTaskExecutor = task.Upgrade();
8570 CHECK_NULL_VOID(uiTaskExecutor);
8571 uiTaskExecutor->PostTask([weak, pasteData, multiTypeRecordImpl]() {
8572 auto richEditor = weak.Upgrade();
8573 CHECK_NULL_VOID(richEditor);
8574 richEditor->clipboard_->AddMultiTypeRecord(pasteData, multiTypeRecordImpl);
8575 richEditor->clipboard_->SetData(pasteData, richEditor->copyOption_);
8576 }, TaskExecutor::TaskType::UI, "AsyncHandleOnCopyStyledStringSetClipboardData");
8577 }, TaskExecutor::TaskType::BACKGROUND, "AsyncHandleOnCopyStyledStringHtml");
8578 }
8579
8580 void RichEditorPattern::OnCopyOperationExt(RefPtr<PasteDataMix>& pasteData)
8581 {
8582 auto subSpanString =
8583 ToStyledString(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
8584 std::vector<uint8_t> tlvData;
8585 subSpanString->EncodeTlv(tlvData);
8586 auto text = subSpanString->GetString();
8587 clipboard_->AddSpanStringRecord(pasteData, tlvData);
8588 }
8589
8590 void RichEditorPattern::HandleOnCopyStyledString()
8591 {
8592 auto subSpanString = styledString_->GetSubSpanString(textSelector_.GetTextStart(),
8593 textSelector_.GetTextEnd() - textSelector_.GetTextStart());
8594 subSpanString->isFromStyledStringMode = true;
8595 #ifdef PREVIEW
8596 clipboard_->SetData(subSpanString->GetString(), CopyOptions::Distributed);
8597 #else
8598 AsyncHandleOnCopyStyledStringHtml(subSpanString);
8599 #endif
8600 }
8601
8602 std::list<RefPtr<SpanItem>> RichEditorPattern::CopySpansForClipboard(const std::list<RefPtr<SpanItem>>& spans)
8603 {
8604 std::list<RefPtr<SpanItem>> copySpans;
8605 for (const auto& spanItem : spans) {
8606 // only make normal/image spanItem, because copy or cut only for normal/image spanItem
8607 auto newSpanItem = GetSameSpanItem(spanItem);
8608 CHECK_NULL_CONTINUE(newSpanItem);
8609 newSpanItem->position = spanItem->position;
8610 newSpanItem->rangeStart = spanItem->rangeStart;
8611 newSpanItem->paragraphIndex = spanItem->paragraphIndex;
8612 newSpanItem->content = spanItem->content;
8613 newSpanItem->description = spanItem->description;
8614 copySpans.emplace_back(newSpanItem);
8615 }
8616 return copySpans;
8617 }
8618
8619 void RichEditorPattern::OnCopyOperation(bool isUsingExternalKeyboard)
8620 {
8621 if (isSpanStringMode_) {
8622 HandleOnCopyStyledString();
8623 return;
8624 }
8625 caretUpdateType_ = CaretUpdateType::NONE;
8626 auto pipeline = GetContext();
8627 CHECK_NULL_VOID(pipeline);
8628 auto taskExecutor = pipeline->GetTaskExecutor();
8629 CHECK_NULL_VOID(taskExecutor);
8630 auto selectStart = textSelector_.GetTextStart();
8631 auto selectEnd = textSelector_.GetTextEnd();
8632 auto textSelectInfo = GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT);
8633 auto copyResultObjects = textSelectInfo.GetSelection().resultObjects;
8634 CHECK_NULL_VOID(!copyResultObjects.empty());
8635 std::list<RefPtr<SpanItem>> copySpans = CopySpansForClipboard(spans_);
8636 ACE_SCOPED_TRACE("RichEditorOnCopyOperation");
8637 taskExecutor->PostTask(
8638 [weak = WeakClaim(this), task = WeakClaim(RawPtr(taskExecutor)), copyResultObjects, copySpans]() {
8639 auto richEditor = weak.Upgrade();
8640 CHECK_NULL_VOID(richEditor);
8641 ACE_SCOPED_TRACE("RichEditorAsyncHandleOnCopy");
8642 RefPtr<PasteDataMix> pasteData = richEditor->clipboard_->CreatePasteDataMix();
8643 for (auto resultObj = copyResultObjects.rbegin(); resultObj != copyResultObjects.rend(); ++resultObj) {
8644 richEditor->ProcessResultObject(pasteData, *resultObj, copySpans);
8645 }
8646 auto uiTaskExecutor = task.Upgrade();
8647 CHECK_NULL_VOID(uiTaskExecutor);
8648 uiTaskExecutor->PostTask([weak, pasteData]() {
8649 auto richEditor = weak.Upgrade();
8650 CHECK_NULL_VOID(richEditor);
8651 richEditor->clipboard_->SetData(pasteData, richEditor->copyOption_);
8652 }, TaskExecutor::TaskType::UI, "AsyncHandleOnCopyWithoutSpanStringSetClipboardData");
8653 }, TaskExecutor::TaskType::BACKGROUND, "AsyncHandleOnCopyWithoutSpanStringHtml");
8654 }
8655
8656 void RichEditorPattern::ProcessResultObject(RefPtr<PasteDataMix> pasteData, const ResultObject& result,
8657 const std::list<RefPtr<SpanItem>>& spans)
8658 {
8659 CHECK_NULL_VOID(pasteData);
8660 auto multiTypeRecordImpl = AceType::MakeRefPtr<MultiTypeRecordImpl>();
8661 if (result.type == SelectSpanType::TYPESPAN) {
8662 auto data = UtfUtils::Str16ToStr8(GetSelectedSpanText(result.valueString,
8663 result.offsetInSpan[RichEditorSpanRange::RANGESTART], result.offsetInSpan[RichEditorSpanRange::RANGEEND]));
8664 #ifdef PREVIEW
8665 clipboard_->SetData(data, CopyOptions::Distributed);
8666 #else
8667 multiTypeRecordImpl->SetPlainText(data);
8668 auto subSpanString = GetSpanStringByResultObject(result, spans);
8669 subSpanString->EncodeTlv(multiTypeRecordImpl->GetSpanStringBuffer());
8670 auto htmlStr = HtmlUtils::ToHtml(RawPtr(subSpanString));
8671 multiTypeRecordImpl->SetHtmlText(htmlStr);
8672 clipboard_->AddMultiTypeRecord(pasteData, multiTypeRecordImpl);
8673 #endif
8674 return;
8675 }
8676 if (result.type == SelectSpanType::TYPEIMAGE) {
8677 #ifdef PREVIEW
8678 if (result.valuePixelMap) {
8679 clipboard_->AddPixelMapRecord(pasteData, result.valuePixelMap);
8680 } else {
8681 clipboard_->AddImageRecord(pasteData, UtfUtils::Str16ToStr8(result.valueString));
8682 }
8683 #else
8684 if (result.valuePixelMap) {
8685 multiTypeRecordImpl->SetPixelMap(result.valuePixelMap);
8686 } else {
8687 multiTypeRecordImpl->SetUri(UtfUtils::Str16ToStr8(result.valueString));
8688 }
8689 auto subSpanString = GetSpanStringByResultObject(result, spans);
8690 subSpanString->EncodeTlv(multiTypeRecordImpl->GetSpanStringBuffer());
8691 auto htmlStr = HtmlUtils::ToHtml(RawPtr(subSpanString));
8692 multiTypeRecordImpl->SetHtmlText(htmlStr);
8693 clipboard_->AddMultiTypeRecord(pasteData, multiTypeRecordImpl);
8694 #endif
8695 }
8696 }
8697
8698 std::pair<int32_t, int32_t> RichEditorPattern::GetSpanRangeByResultObject(const ResultObject& result)
8699 {
8700 auto selectStart = result.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] +
8701 result.offsetInSpan[RichEditorSpanRange::RANGESTART];
8702 auto selectEnd = result.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] +
8703 result.offsetInSpan[RichEditorSpanRange::RANGEEND];
8704 return { selectStart, selectEnd };
8705 }
8706
8707 RefPtr<SpanString> RichEditorPattern::GetSpanStringByResultObject(const ResultObject& result,
8708 const std::list<RefPtr<SpanItem>>& spans)
8709 {
8710 auto [selectStart, selectEnd] = GetSpanRangeByResultObject(result);
8711 RefPtr<SpanString> spanString = MakeRefPtr<SpanString>(u"");
8712 SetSubSpans(spanString, selectStart, selectEnd, spans);
8713 SetSubMap(spanString);
8714 return spanString;
8715 }
8716
8717 void RichEditorPattern::HandleOnCopy(bool isUsingExternalKeyboard)
8718 {
8719 CHECK_NULL_VOID(clipboard_);
8720 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "isUsingExternalKeyboard=%{public}d, copyOption=%{public}d",
8721 isUsingExternalKeyboard, copyOption_);
8722 if (copyOption_ == CopyOptions::None) {
8723 return;
8724 }
8725 auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
8726 CHECK_NULL_VOID(eventHub);
8727 TextCommonEvent event;
8728 eventHub->FireOnCopy(event);
8729 IF_TRUE(!event.IsPreventDefault(), OnCopyOperation(isUsingExternalKeyboard));
8730 if (selectOverlay_->IsUsingMouse() || isUsingExternalKeyboard) {
8731 CloseSelectOverlay();
8732 } else {
8733 selectOverlay_->HideMenu();
8734 }
8735 }
8736
8737 void RichEditorPattern::ResetAfterPaste()
8738 {
8739 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "ResetAfterPaste");
8740 auto pasteStr = GetPasteStr();
8741 SetCaretSpanIndex(-1);
8742 StartTwinkling();
8743 RequestKeyboardToEdit();
8744 CloseSelectOverlay();
8745 InsertValueByPaste(pasteStr);
8746 ClearPasteStr();
8747 }
8748
8749 void RichEditorPattern::InsertValueByPaste(const std::u16string& pasteStr)
8750 {
8751 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "InsertValueByPaste");
8752 if (isSpanStringMode_) {
8753 InsertValueInStyledString(pasteStr, true, true);
8754 return;
8755 }
8756 InsertValueByOperationType(pasteStr, OperationType::PASTE);
8757 }
8758
8759 void RichEditorPattern::HandleOnPaste()
8760 {
8761 if (IsPreviewTextInputting()) {
8762 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Paste blocked during preview text input");
8763 suppressAccessibilityEvent_ = true;
8764 return;
8765 }
8766 auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
8767 CHECK_NULL_VOID(eventHub);
8768 TextCommonEvent event;
8769 eventHub->FireOnPaste(event);
8770 OnReportRichEditorEvent("onPasteComplete");
8771 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleOnPaste, preventDefault=%{public}d", event.IsPreventDefault());
8772 if (event.IsPreventDefault()) {
8773 CloseSelectOverlay();
8774 ResetSelection();
8775 StartTwinkling();
8776 RequestKeyboardToEdit();
8777 suppressAccessibilityEvent_ = true;
8778 return;
8779 }
8780 CHECK_NULL_VOID(clipboard_);
8781 #ifdef PREVIEW
8782 auto pasteCallback = [weak = WeakClaim(this)](const std::string& text) {
8783 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "pasteCallback callback in previewer");
8784 auto richEditor = weak.Upgrade();
8785 CHECK_NULL_VOID(richEditor);
8786 richEditor->PasteStr(text);
8787 };
8788 clipboard_->GetData(pasteCallback);
8789 #else
8790 clipboard_->GetSpanStringData(CreatePasteCallback());
8791 #endif
8792 }
8793
8794 std::function<void(std::vector<std::vector<uint8_t>>&, const std::string&, bool&)>
8795 RichEditorPattern::CreatePasteCallback()
8796 {
8797 auto isSpanStringMode = isSpanStringMode_;
8798 auto pasteCallback = [weak = WeakClaim(this), isSpanStringMode](std::vector<std::vector<uint8_t>>& arrs,
8799 const std::string& text, bool& isMulitiTypeRecord) {
8800 auto richEditor = weak.Upgrade();
8801 CHECK_NULL_VOID(richEditor);
8802 std::list<RefPtr<SpanString>> spanStrings;
8803 bool isFromStyledString = false;
8804 for (auto&& arr : arrs) {
8805 auto spanString = SpanString::DecodeTlv(arr);
8806 IF_TRUE(spanString->isFromStyledStringMode, isFromStyledString = true);
8807 spanStrings.push_back(spanString);
8808 }
8809 TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
8810 "pasteCallback callback, isMulitiTypeRecord : [%{public}d], isSpanStringMode : [%{public}d], "
8811 "isFromStyledString : [%{public}d]",
8812 isMulitiTypeRecord, isSpanStringMode, isFromStyledString);
8813 if (spanStrings.empty() || (!isSpanStringMode && !isFromStyledString)) {
8814 richEditor->PasteStr(text);
8815 return;
8816 }
8817 richEditor->CloseSelectOverlay();
8818 for (auto&& spanString : spanStrings) {
8819 richEditor->AddSpanByPasteData(spanString);
8820 richEditor->RequestKeyboardToEdit();
8821 }
8822 };
8823 return pasteCallback;
8824 }
8825
8826 void RichEditorPattern::PasteStr(const std::string& text)
8827 {
8828 if (text.empty()) {
8829 ResetSelection();
8830 StartTwinkling();
8831 CloseSelectOverlay();
8832 RequestKeyboardToEdit();
8833 return;
8834 }
8835 AddPasteStr(text);
8836 ResetAfterPaste();
8837 }
8838
8839 void RichEditorPattern::SetCaretSpanIndex(int32_t index)
8840 {
8841 caretSpanIndex_ = index;
8842 }
8843
8844 void RichEditorPattern::HandleOnCut()
8845 {
8846 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "copyOption=%{public}d, textSelector_.IsValid()=%{public}d",
8847 copyOption_, textSelector_.IsValid());
8848 if (copyOption_ == CopyOptions::None) {
8849 suppressAccessibilityEvent_ = true;
8850 return;
8851 }
8852 if (!textSelector_.IsValid()) {
8853 suppressAccessibilityEvent_ = true;
8854 return;
8855 }
8856 auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
8857 CHECK_NULL_VOID(eventHub);
8858 TextCommonEvent event;
8859 eventHub->FireOnCut(event);
8860 if (event.IsPreventDefault()) {
8861 CloseSelectOverlay();
8862 ResetSelection();
8863 StartTwinkling();
8864 RequestKeyboardToEdit();
8865 suppressAccessibilityEvent_ = true;
8866 return;
8867 }
8868
8869 caretUpdateType_ = CaretUpdateType::NONE;
8870 OnCopyOperation();
8871 DeleteBackward(1, TextChangeReason::CUT);
8872 }
8873
8874 void RichEditorPattern::HandleOnShare()
8875 {
8876 auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
8877 CHECK_NULL_VOID(eventHub);
8878 TextCommonEvent event;
8879 eventHub->FireOnShare(event);
8880 selectOverlay_->HideMenu(true);
8881 auto value = selectOverlay_->GetSelectedText();
8882 auto shareWord = std::regex_replace(value, REMOVE_SPACE_CHARS, "");
8883 CHECK_NULL_VOID(!shareWord.empty());
8884 auto pipeline = GetContext();
8885 CHECK_NULL_VOID(pipeline);
8886 auto containerId = pipeline->GetInstanceId();
8887 auto contentRect = selectOverlay_->GetSelectArea();
8888 TextShareAdapter::StartTextShareTask(containerId, contentRect, shareWord);
8889 }
8890
8891 void RichEditorPattern::HandleAIMenuOption(const std::string& labelInfo)
8892 {
8893 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleAIMenuOption labelInfo=%{public}s", labelInfo.c_str());
8894 TextPattern::HandleAIMenuOption(labelInfo);
8895 }
8896
8897 void RichEditorPattern::UpdateAIMenuOptions()
8898 {
8899 bool isLocalOrDistributed = copyOption_ == CopyOptions::Local || copyOption_ == CopyOptions::Distributed;
8900 if (!isLocalOrDistributed || !NeedShowAIDetect()) {
8901 isShowAIMenuOption_ = false;
8902 } else {
8903 auto aiItemOptions = GetAIItemOption();
8904 isShowAIMenuOption_ = TextPattern::PrepareAIMenuOptions(aiItemOptions);
8905 aiMenuOptions_ = aiItemOptions;
8906 }
8907 bool isAskCeliaEnabled = isLocalOrDistributed;
8908 if (IsEditing()) {
8909 isAskCeliaEnabled &= IsSelected();
8910 } else {
8911 isAskCeliaEnabled &= !IsShowAIMenuOption();
8912 }
8913 SetIsAskCeliaEnabled(isAskCeliaEnabled);
8914 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "UpdateAIMenuOptions isShowAIMenuOption=%{public}d "
8915 "isAskCeliaEnabled=%{public}d", isShowAIMenuOption_, isAskCeliaEnabled_);
8916 if (!IsSupportAskCelia()) {
8917 SetIsAskCeliaEnabled(false);
8918 }
8919 CHECK_NULL_VOID(dataDetectorAdapter_);
8920 if (isAskCeliaEnabled_ && !NeedShowAIDetect() &&
8921 dataDetectorAdapter_->textDetectResult_.menuOptionAndAction.empty()) {
8922 dataDetectorAdapter_->GetAIEntityMenu();
8923 }
8924 }
8925
8926 Offset RichEditorPattern::ConvertGlobalToTextOffset(const Offset& globalOffset)
8927 {
8928 auto host = GetHost();
8929 CHECK_NULL_RETURN(host, {});
8930 auto offset = host->GetPaintRectOffset(false, true);
8931 auto localOffset = globalOffset - Offset(offset.GetX(), offset.GetY());
8932 if (selectOverlay_->HasRenderTransform()) {
8933 localOffset = ConvertGlobalToLocalOffset(globalOffset);
8934 }
8935 return ConvertTouchOffsetToTextOffset(localOffset);
8936 }
8937
8938 bool RichEditorPattern::CheckAIPreviewMenuEnable()
8939 {
8940 return TextPattern::CheckAIPreviewMenuEnable() && copyOption_ != CopyOptions::None;
8941 }
8942
8943 void RichEditorPattern::InitAiSelection(const Offset& globalOffset, bool isBetweenSelection)
8944 {
8945 ResetAISelected(AIResetSelectionReason::INIT_SELECTION);
8946 CHECK_NULL_VOID(CheckAIPreviewMenuEnable());
8947 if (showSelect_ && isBetweenSelection) {
8948 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "no need for InitAiSelection");
8949 return;
8950 }
8951 int32_t extend = 0;
8952 auto host = GetHost();
8953 CHECK_NULL_VOID(host);
8954 Offset textOffset = ConvertGlobalToTextOffset(globalOffset);
8955 auto [pos, affinity] = paragraphs_.GetGlyphPositionAtCoordinate(textOffset);
8956 int32_t start = 0;
8957 int32_t end = 0;
8958 bool isAiSpan = false;
8959 auto aiSpanIter = dataDetectorAdapter_->aiSpanMap_.upper_bound(pos);
8960 if (aiSpanIter != dataDetectorAdapter_->aiSpanMap_.begin()) {
8961 --aiSpanIter;
8962 }
8963 start = aiSpanIter->second.start;
8964 end = aiSpanIter->second.end;
8965 auto position = static_cast<int32_t>(pos);
8966 if (position >= start && position < end && InRangeRect(globalOffset, { start, end })) {
8967 isAiSpan = true;
8968 }
8969 if (isAiSpan && start >= 0 && start < end) {
8970 textSelector_.aiStart = start;
8971 textSelector_.aiEnd = end;
8972 }
8973 TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
8974 "InitAiSelection[id:%{public}d][extend:%{public}d][start:%{public}d][end:%{public}d]", host->GetId(), extend,
8975 textSelector_.aiStart.value_or(-1), textSelector_.aiEnd.value_or(-1));
8976 }
8977
8978 std::function<void(Offset)> RichEditorPattern::GetThumbnailCallback()
8979 {
8980 return [wk = WeakClaim(this)](const Offset& point) {
8981 auto pattern = wk.Upgrade();
8982 CHECK_NULL_VOID(pattern);
8983 auto isBetweenSelection = pattern->BetweenSelectedPosition(point);
8984 pattern->InitAiSelection(point, isBetweenSelection);
8985 if (!isBetweenSelection && !pattern->IsAiSelected()) {
8986 return;
8987 }
8988 auto isContentDraggable = pattern->JudgeContentDraggable();
8989 if (!isContentDraggable && !pattern->IsAiSelected()) {
8990 TAG_LOGE(AceLogTag::ACE_RICH_TEXT, "GetThumbnailCallback call, draggable is false");
8991 pattern->SetIsTextDraggable(false);
8992 return;
8993 }
8994 if (pattern->dragBoxes_.empty()) {
8995 pattern->dragBoxes_ = pattern->GetTextBoxes();
8996 }
8997 pattern->CreateDragNode();
8998 };
8999 }
9000
9001 void RichEditorPattern::CreateDragNode()
9002 {
9003 auto host = GetHost();
9004 auto contentHost = GetContentHost();
9005 CHECK_NULL_VOID(host && contentHost);
9006 auto children = contentHost->GetChildren();
9007 std::list<RefPtr<FrameNode>> imageChildren;
9008 for (const auto& child : children) {
9009 auto node = DynamicCast<FrameNode>(child);
9010 CHECK_NULL_CONTINUE(node);
9011 if (auto& tag = node->GetTag(); tag == V2::IMAGE_ETS_TAG || tag == V2::PLACEHOLDER_SPAN_ETS_TAG) {
9012 imageChildren.emplace_back(node);
9013 }
9014 }
9015 TextDragInfo info;
9016 info.maxSelectedWidth = GetMaxSelectedWidth();
9017 info.handleColor = GetCaretColor();
9018 info.selectedBackgroundColor = GetSelectedBackgroundColor();
9019 auto selectOverlayInfo = selectOverlay_->GetSelectOverlayInfo();
9020 if (selectOverlayInfo.has_value()) {
9021 if (selectOverlayInfo->firstHandle.isShow) {
9022 info.firstHandle = selectOverlayInfo->firstHandle.paintRect;
9023 }
9024 if (selectOverlayInfo->secondHandle.isShow) {
9025 info.secondHandle = selectOverlayInfo->secondHandle.paintRect;
9026 }
9027 }
9028 if (textSelector_.GetTextEnd() - textSelector_.GetTextStart() == 1) {
9029 auto spanItem = GetSpanItemByPosition(textSelector_.GetTextStart());
9030 auto placeholderSpanItem = DynamicCast<PlaceholderSpanItem>(spanItem);
9031 if (placeholderSpanItem) {
9032 info.dragBackgroundColor = placeholderSpanItem->dragBackgroundColor_;
9033 info.isDragShadowNeeded = placeholderSpanItem->isDragShadowNeeded_;
9034 }
9035 }
9036 dragNode_ = RichEditorDragPattern::CreateDragNode(host, imageChildren, info);
9037 CHECK_NULL_VOID(dragNode_);
9038 InitDragShadow(host, dragNode_, info.isDragShadowNeeded, info.dragBackgroundColor.has_value());
9039 FrameNode::ProcessOffscreenNode(dragNode_);
9040 }
9041
9042 float RichEditorPattern::GetMaxSelectedWidth()
9043 {
9044 auto boxes = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
9045 CHECK_NULL_RETURN(!boxes.empty(), 0.0f);
9046 float startX = boxes.front().Left();
9047 float endX = boxes.front().Right();
9048 for (const auto& box : boxes) {
9049 startX = std::min(startX, box.Left());
9050 endX = std::max(endX, box.Right());
9051 }
9052 startX = std::min(0.0f, startX);
9053 return std::abs(startX - endX);
9054 }
9055
9056 void RichEditorPattern::InitDragShadow(const RefPtr<FrameNode>& host, const RefPtr<FrameNode>& dragNode,
9057 bool isDragShadowNeeded, bool hasDragBackgroundColor)
9058 {
9059 CHECK_NULL_VOID(host && dragNode);
9060 auto textDragPattern = dragNode->GetPattern<TextDragPattern>();
9061 CHECK_NULL_VOID(textDragPattern);
9062 auto option = host->GetDragPreviewOption();
9063 if (isDragShadowNeeded) {
9064 option.options.shadowPath = textDragPattern->GetBackgroundPath()->ConvertToSVGString();
9065 option.options.shadow = Shadow(RICH_DEFAULT_ELEVATION, {0.0, 0.0}, Color(RICH_DEFAULT_SHADOW_COLOR),
9066 ShadowStyle::OuterFloatingSM);
9067 option.options.isFilled = !hasDragBackgroundColor;
9068 } else {
9069 option.options.shadowPath.clear();
9070 option.options.shadow.reset();
9071 option.options.isFilled = true;
9072 }
9073 host->SetDragPreviewOptions(option);
9074 }
9075
9076 void RichEditorPattern::CreateHandles()
9077 {
9078 if (IsDragging()) {
9079 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "do not show handles when dragging");
9080 return;
9081 }
9082 auto host = GetHost();
9083 CHECK_NULL_VOID(host);
9084 CalculateHandleOffsetAndShowOverlay();
9085 bool isCurrentMenuVisibile = selectOverlay_->IsCurrentMenuVisibile();
9086 selectOverlay_->UpdateMenuOffset();
9087 if (!isCurrentMenuVisibile) {
9088 selectOverlay_->HideMenu();
9089 }
9090 }
9091
9092 void RichEditorPattern::ShowHandles(const bool isNeedShowHandles)
9093 {
9094 if (!IsSelected()) {
9095 showSelect_ = true;
9096 IF_TRUE(isEditing_, StartTwinkling());
9097 return;
9098 }
9099 ShowHandles();
9100 }
9101
9102 void RichEditorPattern::ShowHandles()
9103 {
9104 auto host = GetHost();
9105 CHECK_NULL_VOID(host);
9106 if (!selectOverlay_->IsBothHandlesShow() && !selectOverlay_->SelectOverlayIsCreating()) {
9107 showSelect_ = true;
9108 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
9109 CHECK_NULL_VOID(textSelector_.IsValid());
9110 CHECK_NULL_VOID(!isMouseSelect_);
9111 CalculateHandleOffsetAndShowOverlay();
9112 selectOverlay_->ProcessOverlay({.menuIsShow = false, .animation = false});
9113 }
9114 }
9115
9116 void RichEditorPattern::OnAreaChangedInner()
9117 {
9118 auto context = GetContext();
9119 CHECK_NULL_VOID(context);
9120 auto prevParentGlobalOffset = parentGlobalOffset_;
9121 UpdateParentOffsetAndOverlay();
9122 IF_TRUE(parentGlobalOffset_ != prevParentGlobalOffset,
9123 UpdateTextFieldManager(Offset(parentGlobalOffset_.GetX(), parentGlobalOffset_.GetY()), frameRect_.Height()));
9124 IF_TRUE(parentGlobalOffset_ != prevParentGlobalOffset, UpdateCaretInfoToController());
9125 }
9126
9127 void RichEditorPattern::UpdateParentOffsetAndOverlay()
9128 {
9129 auto parentGlobalOffset = GetPaintRectGlobalOffset(); // offset on screen(with transformation)
9130 CHECK_NULL_VOID(parentGlobalOffset != parentGlobalOffset_);
9131 parentGlobalOffset_ = parentGlobalOffset;
9132 selectOverlay_->UpdateSelectOverlayOnAreaChanged();
9133 CloseAIMenu();
9134 }
9135
9136 void RichEditorPattern::CloseAIMenu()
9137 {
9138 auto context = GetContext();
9139 CHECK_NULL_VOID(context);
9140 auto overlayManager = context->GetOverlayManager();
9141 IF_PRESENT(overlayManager, CloseAIEntityMenu(frameId_));
9142 }
9143
9144 void RichEditorPattern::CloseSelectionMenu()
9145 {
9146 // used by sdk
9147 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "CloseSelectionMenu");
9148 CloseSelectOverlay();
9149 }
9150
9151 void RichEditorPattern::OnDragNodeFloating()
9152 {
9153 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "OnDragNodeFloating");
9154 status_ = Status::FLOATING;
9155 }
9156
9157 void RichEditorPattern::CloseSelectOverlay()
9158 {
9159 // used by inner
9160 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "CloseSelectOverlay");
9161 selectOverlay_->CloseOverlay(true, CloseReason::CLOSE_REASON_NORMAL);
9162 }
9163
9164 void RichEditorPattern::CloseHandleAndSelect()
9165 {
9166 selectOverlay_->CloseOverlay(false, CloseReason::CLOSE_REASON_DRAG_FLOATING);
9167 showSelect_ = false;
9168 auto host = GetHost();
9169 IF_PRESENT(host, MarkDirtyNode(PROPERTY_UPDATE_RENDER));
9170 }
9171
9172 void RichEditorPattern::CalculateHandleOffsetAndShowOverlay(bool isUsingMouse)
9173 {
9174 auto globalOffset = GetGlobalOffset();
9175 if (!selectOverlay_->GetIsHandleMoving()) {
9176 textSelector_.ReverseTextSelector();
9177 }
9178 int32_t baseOffset = std::min(textSelector_.baseOffset, GetTextContentLength());
9179 int32_t destinationOffset = std::min(textSelector_.destinationOffset, GetTextContentLength());
9180 SizeF firstHandlePaintSize;
9181 SizeF secondHandlePaintSize;
9182 OffsetF firstHandleOffset;
9183 OffsetF secondHandleOffset;
9184 auto isSingleHandle = IsSingleHandle();
9185 selectOverlay_->SetIsSingleHandle(isSingleHandle);
9186 if (isSingleHandle) {
9187 auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
9188 // only show the second handle.
9189 secondHandlePaintSize = { SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), caretHeight };
9190 secondHandleOffset = caretOffset + globalOffset;
9191 } else {
9192 float startSelectHeight = 0.0f;
9193 float endSelectHeight = 0.0f;
9194 auto startOffset = CalcCursorOffsetByPosition(baseOffset, startSelectHeight, true, false);
9195 auto endOffset = CalcCursorOffsetByPosition(destinationOffset, endSelectHeight, false, false);
9196 firstHandlePaintSize = { SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), startSelectHeight };
9197 secondHandlePaintSize = { SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), endSelectHeight };
9198 firstHandleOffset = startOffset + globalOffset;
9199 secondHandleOffset = endOffset + globalOffset;
9200 firstHandleOffset.SetX(firstHandleOffset.GetX() - firstHandlePaintSize.Width() / 2.0f);
9201 secondHandleOffset.SetX(secondHandleOffset.GetX() - secondHandlePaintSize.Width() / 2.0f);
9202 }
9203 textSelector_.selectionBaseOffset = firstHandleOffset;
9204 textSelector_.selectionDestinationOffset = secondHandleOffset;
9205 textSelector_.firstHandle = RectF{ firstHandleOffset, firstHandlePaintSize };
9206 textSelector_.secondHandle = RectF{ secondHandleOffset, secondHandlePaintSize };
9207 }
9208
9209 void RichEditorPattern::CalculateDefaultHandleHeight(float& height)
9210 {
9211 #ifdef ENABLE_ROSEN_BACKEND
9212 MeasureContext content;
9213 content.textContent = "a";
9214 content.fontSize = TEXT_DEFAULT_FONT_SIZE;
9215 auto fontweight = StringUtils::FontWeightToString(FontWeight::NORMAL);
9216 content.fontWeight = fontweight;
9217 height = std::max(static_cast<float>(RosenRenderCustomPaint::MeasureTextSizeInner(content).Height()), 0.0f);
9218 #endif
9219 }
9220
9221 OffsetF RichEditorPattern::GetGlobalOffset() const
9222 {
9223 auto host = GetHost();
9224 CHECK_NULL_RETURN(host, OffsetF());
9225 auto pipeline = host->GetContext();
9226 CHECK_NULL_RETURN(pipeline, OffsetF());
9227 auto rootOffset = pipeline->GetRootRect().GetOffset();
9228 auto richEditorPaintOffset = host->GetPaintRectOffsetNG(false, true);
9229 if (selectOverlay_->HasRenderTransform()) {
9230 richEditorPaintOffset = selectOverlay_->GetPaintOffsetWithoutTransform();
9231 }
9232 return richEditorPaintOffset - rootOffset;
9233 }
9234
9235 bool RichEditorPattern::IsSingleHandle()
9236 {
9237 CHECK_NULL_RETURN(!selectOverlay_->GetIsHandleMoving(), selectOverlay_->IsSingleHandle());
9238 return GetTextContentLength() == 0 || !IsSelected();
9239 }
9240
9241 bool RichEditorPattern::IsHandlesShow()
9242 {
9243 return selectOverlay_->IsBothHandlesShow();
9244 }
9245
9246 void RichEditorPattern::ResetSelection()
9247 {
9248 bool selectNothing = textSelector_.SelectNothing();
9249 textSelector_.Update(-1, -1);
9250 if (!selectNothing) {
9251 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "ResetSelection");
9252 auto host = GetHost();
9253 CHECK_NULL_VOID(host);
9254 auto eventHub = host->GetOrCreateEventHub<RichEditorEventHub>();
9255 CHECK_NULL_VOID(eventHub);
9256 auto textSelectInfo = GetSpansInfo(-1, -1, GetSpansMethod::ONSELECT);
9257 eventHub->FireOnSelect(&textSelectInfo);
9258 UpdateSelectionType(textSelectInfo);
9259 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
9260 }
9261 }
9262
9263 bool RichEditorPattern::BetweenSelection(const Offset& globalOffset)
9264 {
9265 if (IsAiSelected()) {
9266 return InRangeRect(globalOffset, { textSelector_.aiStart.value(), textSelector_.aiEnd.value() });
9267 } else {
9268 return InRangeRect(globalOffset, { textSelector_.GetTextStart(), textSelector_.GetTextEnd() });
9269 }
9270 }
9271
9272 bool RichEditorPattern::InRangeRect(const Offset& globalOffset, const std::pair<int32_t, int32_t>& range)
9273 {
9274 auto host = GetHost();
9275 CHECK_NULL_RETURN(host, false);
9276 CHECK_NULL_RETURN(0 <= range.first && range.first < range.second, false);
9277 auto offset = host->GetPaintRectOffsetNG(false, true);
9278 auto localOffset = globalOffset - Offset(offset.GetX(), offset.GetY());
9279 if (selectOverlay_->HasRenderTransform()) {
9280 localOffset = ConvertGlobalToLocalOffset(globalOffset);
9281 }
9282 auto eventHub = host->GetOrCreateEventHub<EventHub>();
9283 if (GreatNotEqual(range.second, range.first)) {
9284 // Determine if the pan location is in the selected area
9285 auto rangeRects = paragraphs_.GetRects(range.first, range.second);
9286 auto panOffset = OffsetF(localOffset.GetX(), localOffset.GetY()) - GetTextRect().GetOffset() +
9287 OffsetF(0.0, std::min(baselineOffset_, 0.0f));
9288 for (const auto& rangeRect : rangeRects) {
9289 if (rangeRect.IsInRegion(PointF(panOffset.GetX(), panOffset.GetY()))) {
9290 return true;
9291 }
9292 }
9293 }
9294 return false;
9295 }
9296
9297 bool RichEditorPattern::BetweenSelectedPosition(const Offset& globalOffset)
9298 {
9299 return copyOption_ != CopyOptions::None && BetweenSelection(globalOffset);
9300 }
9301
9302 void RichEditorPattern::HandleSurfaceChanged(
9303 int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight, WindowSizeChangeReason type)
9304 {
9305 if (newWidth != prevWidth || newHeight != prevHeight) {
9306 TextPattern::HandleSurfaceChanged(newWidth, newHeight, prevWidth, prevHeight, type);
9307 UpdateOriginIsMenuShow(false);
9308 }
9309 UpdateCaretInfoToController();
9310 if (magnifierController_) {
9311 magnifierController_->RemoveMagnifierFrameNode();
9312 }
9313 }
9314
9315 void RichEditorPattern::HandleSurfacePositionChanged(int32_t posX, int32_t posY)
9316 {
9317 UpdateCaretInfoToController();
9318 }
9319
9320 void RichEditorPattern::DumpInfo()
9321 {
9322 auto& dumpLog = DumpLog::GetInstance();
9323 if (customKeyboardBuilder_) {
9324 dumpLog.AddDesc(std::string("CustomKeyboard, Attached: ").append(std::to_string(isCustomKeyboardAttached_)));
9325 }
9326 auto host = GetHost();
9327 CHECK_NULL_VOID(host);
9328 auto richEditorTheme = GetTheme<RichEditorTheme>();
9329 CHECK_NULL_VOID(richEditorTheme);
9330 dumpLog.AddDesc(std::string("caret offset: ").append(GetCaretRect().GetOffset().ToString()));
9331 dumpLog.AddDesc(std::string("caret height: ")
9332 .append(std::to_string(NearZero(GetCaretRect().Height())
9333 ? richEditorTheme->GetDefaultCaretHeight().ConvertToPx()
9334 : GetCaretRect().Height())));
9335 dumpLog.AddDesc(std::string("text rect: ").append(richTextRect_.ToString()));
9336 dumpLog.AddDesc(std::string("content rect: ").append(contentRect_.ToString()));
9337 auto richEditorPaintOffset = host->GetPaintRectOffsetNG(false, true);
9338 bool hasRenderTransform = selectOverlay_->HasRenderTransform();
9339 if (hasRenderTransform) {
9340 richEditorPaintOffset = selectOverlay_->GetPaintOffsetWithoutTransform();
9341 }
9342 dumpLog.AddDesc(std::string("hasRenderTransform: ").append(std::to_string(hasRenderTransform)));
9343 dumpLog.AddDesc(std::string("richEditorPaintOffset: ").append(richEditorPaintOffset.ToString()));
9344 auto selectOverlayInfo = selectOverlay_->GetSelectOverlayInfo();
9345 CHECK_NULL_VOID(selectOverlayInfo);
9346 dumpLog.AddDesc(std::string("selectOverlay info: ").append(selectOverlayInfo->ToString()));
9347 dumpLog.AddDesc(std::string("IsAIWrite: ").append(std::to_string(IsShowAIWrite())));
9348 dumpLog.AddDesc(std::string("keyboardAppearance: ")
9349 .append(std::to_string(static_cast<int32_t>(keyboardAppearance_))));
9350 }
9351
9352 void RichEditorPattern::RichEditorErrorReport(RichEditorInfo& info)
9353 {
9354 auto pipeline = GetContext();
9355 CHECK_NULL_VOID(pipeline);
9356 auto taskExecutor = pipeline->GetTaskExecutor();
9357 CHECK_NULL_VOID(taskExecutor);
9358 taskExecutor->PostTask(
9359 [info] {
9360 EventReport::ReportRichEditorInfo(info);
9361 },
9362 TaskExecutor::TaskType::BACKGROUND, "ArkUIRichEditorErrorReport");
9363 }
9364
9365 bool RichEditorPattern::HasFocus() const
9366 {
9367 auto focusHub = GetFocusHub();
9368 CHECK_NULL_RETURN(focusHub, false);
9369 return focusHub->IsCurrentFocus();
9370 }
9371
9372 void RichEditorPattern::UpdateTextFieldManager(const Offset& offset, float height)
9373 {
9374 CHECK_NULL_VOID(HasFocus());
9375 auto context = GetContext();
9376 CHECK_NULL_VOID(context);
9377 auto richEditorTheme = context->GetTheme<RichEditorTheme>();
9378 CHECK_NULL_VOID(richEditorTheme);
9379 auto textFieldManager = DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
9380 CHECK_NULL_VOID(textFieldManager);
9381 auto safeAreaManager = context->GetSafeAreaManager();
9382 CHECK_NULL_VOID(safeAreaManager);
9383 auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
9384 textFieldManager->SetClickPosition({ offset.GetX() + caretOffset.GetX(), offset.GetY() + caretOffset.GetY() });
9385 textFieldManager->SetHeight(NearZero(caretHeight)
9386 ? richEditorTheme->GetDefaultCaretHeight().ConvertToPx()
9387 : caretHeight);
9388 textFieldManager->SetClickPositionOffset(safeAreaManager->GetKeyboardOffset());
9389 textFieldManager->SetOnFocusTextField(WeakClaim(this));
9390 if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_FOURTEEN)) {
9391 textFieldManager->SetUsingCustomKeyboardAvoid(keyboardAvoidance_);
9392 }
9393 if (!isTextChange_) {
9394 return;
9395 }
9396 auto taskExecutor = context->GetTaskExecutor();
9397 CHECK_NULL_VOID(taskExecutor);
9398 taskExecutor->PostTask(
9399 [weak = WeakClaim(this)] {
9400 auto pattern = weak.Upgrade();
9401 CHECK_NULL_VOID(pattern);
9402 pattern->ScrollToSafeArea();
9403 },
9404 TaskExecutor::TaskType::UI, "ArkUIRichEditorScrollToSafeArea", PriorityType::VIP);
9405 }
9406
9407 bool RichEditorPattern::IsDisabled() const
9408 {
9409 auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
9410 CHECK_NULL_RETURN(eventHub, true);
9411 return !eventHub->IsEnabled();
9412 }
9413
9414 void RichEditorPattern::MouseDoubleClickParagraphEnd(int32_t& index)
9415 {
9416 bool isMouseDoubleClick = caretUpdateType_ == CaretUpdateType::DOUBLE_CLICK && sourceType_ == SourceType::MOUSE;
9417 CHECK_NULL_VOID(isMouseDoubleClick);
9418 auto paragraphEndPos = GetParagraphEndPosition(index);
9419 auto paragraphBeginPos = GetParagraphBeginPosition(index);
9420 bool isBeginEqualEnd = paragraphBeginPos == paragraphEndPos;
9421 CHECK_NULL_VOID(!isBeginEqualEnd);
9422 if (index == paragraphEndPos) {
9423 index -= 1;
9424 }
9425 }
9426
9427 void RichEditorPattern::AdjustSelectionExcludeSymbol(int32_t& start, int32_t& end)
9428 {
9429 AdjustSelectorForSymbol(start, HandleType::FIRST, SelectorAdjustPolicy::EXCLUDE);
9430 AdjustSelectorForSymbol(end, HandleType::SECOND, SelectorAdjustPolicy::EXCLUDE);
9431 }
9432
9433 void RichEditorPattern::InitSelection(const Offset& pos)
9434 {
9435 auto [currentPosition, selectType] = JudgeSelectType(pos);
9436 switch (selectType) {
9437 case SelectType::SELECT_NOTHING:
9438 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "select nothing currentPos=%{public}d", currentPosition);
9439 textSelector_.Update(currentPosition, currentPosition);
9440 return;
9441 case SelectType::SELECT_BACKWARD:
9442 currentPosition = std::max(0, currentPosition - 1);
9443 break;
9444 case SelectType::SELECT_FORWARD:
9445 break;
9446 default:
9447 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "exception select type");
9448 }
9449 int32_t nextPosition = std::min(currentPosition + 1, GetTextContentLength());
9450 AdjustSelectionExcludeSymbol(currentPosition, nextPosition);
9451 if (!IsCustomSpanInCaretPos(currentPosition, true)) {
9452 AdjustWordSelection(currentPosition, nextPosition);
9453 }
9454 AdjustSelector(currentPosition, nextPosition);
9455 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "init select [%{public}d--%{public}d]", currentPosition, nextPosition);
9456 textSelector_.Update(currentPosition, nextPosition);
9457 if (IsSelectEmpty(currentPosition, nextPosition)) {
9458 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "select rect is empty, select nothing");
9459 textSelector_.Update(currentPosition, currentPosition);
9460 }
9461 }
9462
9463 std::pair<int32_t, SelectType> RichEditorPattern::JudgeSelectType(const Offset& pos)
9464 {
9465 auto positionWithAffinity = paragraphs_.GetGlyphPositionAtCoordinate(pos);
9466 auto currentPosition = (GetTextContentLength() == 0) ? 0 : static_cast<int32_t>(positionWithAffinity.position_);
9467 auto selectType = SelectType::SELECT_NOTHING;
9468 CHECK_NULL_RETURN(GetTextContentLength() != 0, std::make_pair(currentPosition, selectType));
9469 bool isNeedSkipLineSeparator = !editingLongPress_ && IsSelectEmpty(currentPosition, currentPosition + 1);
9470 if (isNeedSkipLineSeparator && AdjustIndexSkipLineSeparator(currentPosition)) {
9471 return std::make_pair(currentPosition, SelectType::SELECT_BACKWARD);
9472 }
9473 auto height = paragraphs_.GetHeight();
9474 if (GreatNotEqual(pos.GetY(), height)) {
9475 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "touchPosY[%{public}f] > paragraphsHeight[%{public}f]", pos.GetY(), height);
9476 IF_TRUE(!editingLongPress_, selectType = SelectType::SELECT_BACKWARD);
9477 return std::make_pair(GetTextContentLength(), selectType);
9478 }
9479 TextAffinity currentAffinity = positionWithAffinity.affinity_;
9480 bool isTouchLineEnd = currentAffinity == TextAffinity::UPSTREAM && !IsTouchBeforeCaret(currentPosition, pos);
9481 if (editingLongPress_ && isTouchLineEnd) {
9482 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "touchLineEnd select nothing currentAffinity=%{public}d", currentAffinity);
9483 return std::make_pair(currentPosition, selectType);
9484 }
9485 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "currentPosition=%{public}d, currentAffinity=%{public}d",
9486 currentPosition, currentAffinity);
9487 selectType = (currentAffinity == TextAffinity::UPSTREAM) ? SelectType::SELECT_BACKWARD : SelectType::SELECT_FORWARD;
9488 return std::make_pair(currentPosition, selectType);
9489 }
9490
9491 bool RichEditorPattern::IsSelectEmpty(int32_t start, int32_t end)
9492 {
9493 auto selectedRects = paragraphs_.GetRects(start, end);
9494 return selectedRects.empty() || (selectedRects.size() == 1 && NearZero((selectedRects[0].Width())));
9495 }
9496
9497 bool RichEditorPattern::AdjustIndexSkipLineSeparator(int32_t& currentPosition)
9498 {
9499 std::u16string contentText;
9500 for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
9501 contentText.append((*iter)->content);
9502 CHECK_NULL_BREAK(currentPosition > (*iter)->position);
9503 }
9504 auto contentLength = static_cast<int32_t>(contentText.length());
9505 if (currentPosition > contentLength) {
9506 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "currentPosition=%{public}d but contentLength=%{public}d",
9507 currentPosition, contentLength);
9508 return false;
9509 }
9510 auto index = currentPosition - 1;
9511 while (index > 0) {
9512 CHECK_NULL_BREAK(contentText[index] == u'\n');
9513 index--;
9514 }
9515 if (index != currentPosition - 1) {
9516 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "skip lineSeparator %{public}d->%{public}d", currentPosition, index + 1);
9517 currentPosition = index + 1;
9518 return true;
9519 }
9520 return false;
9521 }
9522
9523 bool RichEditorPattern::AdjustIndexSkipSpace(int32_t& currentPosition, const MoveDirection direction)
9524 {
9525 bool isBackward = (direction == MoveDirection::BACKWARD);
9526 std::u16string contentText;
9527 GetContentBySpans(contentText);
9528 auto contentLength = static_cast<int32_t>(contentText.length());
9529 bool isPositionInvalid = (isBackward && currentPosition == 0) || (!isBackward && currentPosition == contentLength);
9530 if (isPositionInvalid) {
9531 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "AdjustIndexSkipSpace position=%{public}d but contentLength=%{public}d",
9532 currentPosition, contentLength);
9533 return false;
9534 }
9535 int32_t index = isBackward ? (currentPosition - 1) : currentPosition;
9536 while (isBackward ? index >= 0 : index < contentLength) {
9537 CHECK_NULL_BREAK(contentText[index] == u' ' && GetSpanType(index) == SpanItemType::NORMAL);
9538 isBackward ? index-- : index++;
9539 }
9540 int32_t adjustedIndex = isBackward ? (index + 1) : index;
9541 if (adjustedIndex != currentPosition) {
9542 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "skip space %{public}d->%{public}d", currentPosition, adjustedIndex);
9543 currentPosition = adjustedIndex;
9544 return true;
9545 }
9546 return false;
9547 }
9548
9549 bool RichEditorPattern::ResetOnInvalidSelection(int32_t start, int32_t end)
9550 {
9551 if (start < end) {
9552 return false;
9553 }
9554 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "SetSelection failed, the selected area is empty.");
9555 CloseSelectOverlay();
9556 ResetSelection();
9557 StartTwinkling();
9558 return true;
9559 }
9560
9561 bool RichEditorPattern::IsShowHandle()
9562 {
9563 auto richEditorTheme = GetTheme<RichEditorTheme>();
9564 CHECK_NULL_RETURN(richEditorTheme, false);
9565 return !richEditorTheme->IsRichEditorShowHandle();
9566 }
9567
9568 void RichEditorPattern::UpdateSelectionInfo(int32_t start, int32_t end)
9569 {
9570 UpdateSelectionType(GetSpansInfo(start, end, GetSpansMethod::ONSELECT));
9571 auto selectOverlayInfo = selectOverlay_->GetSelectOverlayInfo();
9572 textResponseType_ = selectOverlayInfo
9573 ? static_cast<TextResponseType>(selectOverlayInfo->menuInfo.responseType.value_or(0))
9574 : TextResponseType::LONG_PRESS;
9575 if (IsShowHandle() && !IsUsingMouse()) {
9576 ResetIsMousePressed();
9577 sourceType_ = SourceType::TOUCH;
9578 }
9579 }
9580
9581 void RichEditorPattern::SetSelection(int32_t start, int32_t end, const std::optional<SelectionOptions>& options,
9582 bool isForward)
9583 {
9584 bool hasFocus = HasFocus();
9585 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "setSelection, range=[%{public}d,%{public}d], hasFocus=%{public}d, "
9586 "isEditing=%{public}d", start, end, hasFocus, isEditing_);
9587 CHECK_NULL_VOID(hasFocus);
9588 if (IsPreviewTextInputting()) {
9589 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "SetSelection failed for previewText inputting");
9590 return;
9591 }
9592 if (start == -1 && end == -1) {
9593 start = 0;
9594 end = GetTextContentLength();
9595 } else {
9596 start = std::clamp(start, 0, GetTextContentLength());
9597 end = std::clamp(end, 0, GetTextContentLength());
9598 }
9599 CHECK_NULL_VOID(!ResetOnInvalidSelection(start, end));
9600 UpdateSelector(start, end);
9601
9602 if (textSelector_.IsValid() && !textSelector_.StartEqualToDest()) {
9603 StopTwinkling();
9604 if (start != textSelector_.GetTextStart() || end != textSelector_.GetTextEnd()) {
9605 FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
9606 }
9607 }
9608 SetCaretPosition(isForward ? textSelector_.GetTextStart() : textSelector_.GetTextEnd());
9609 MoveCaretToContentRect();
9610 CalculateHandleOffsetAndShowOverlay();
9611 UpdateSelectionInfo(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
9612 ProcessOverlayOnSetSelection(options);
9613 auto host = GetHost();
9614 CHECK_NULL_VOID(host);
9615 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
9616 }
9617
9618 void RichEditorPattern::ProcessOverlayOnSetSelection(const std::optional<SelectionOptions>& options)
9619 {
9620 if (options.has_value()) {
9621 auto handlePolicy = options.value().handlePolicy;
9622 IF_TRUE(handlePolicy == HandlePolicy::SHOW, selectOverlay_->ProcessOverlay({ .animation = true }));
9623 IF_TRUE(handlePolicy == HandlePolicy::HIDE, CloseSelectOverlay());
9624 CHECK_NULL_VOID(handlePolicy == HandlePolicy::DEFAULT);
9625 }
9626 if (!IsShowHandle()) {
9627 CloseSelectOverlay();
9628 } else if (!options.has_value() || options.value().menuPolicy == MenuPolicy::DEFAULT) {
9629 selectOverlay_->ProcessOverlay({ .menuIsShow = selectOverlay_->IsCurrentMenuVisibile(),
9630 .animation = true, .requestCode = REQUEST_RECREATE });
9631 IF_PRESENT(magnifierController_, RemoveMagnifierFrameNode());
9632 } else if (options.value().menuPolicy == MenuPolicy::HIDE) {
9633 if (selectOverlay_->IsUsingMouse()) {
9634 CloseSelectOverlay();
9635 } else {
9636 selectOverlay_->ProcessOverlay({ .menuIsShow = false, .animation = true });
9637 }
9638 } else if (options.value().menuPolicy == MenuPolicy::SHOW) {
9639 if (selectOverlay_->IsUsingMouse() || sourceType_ == SourceType::MOUSE) {
9640 selectionMenuOffsetByMouse_ = selectionMenuOffsetClick_;
9641 }
9642 selectOverlay_->ProcessOverlay({ .animation = true, .requestCode = REQUEST_RECREATE });
9643 IF_PRESENT(magnifierController_, RemoveMagnifierFrameNode());
9644 }
9645 }
9646
9647 void RichEditorPattern::BindSelectionMenu(TextResponseType type, TextSpanType richEditorType,
9648 std::function<void()>& menuBuilder, const SelectMenuParam& menuParam)
9649 {
9650 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "BindSelectionMenu spanType = %{public}d, responseType = %{public}d",
9651 richEditorType, type);
9652 TextPattern::BindSelectionMenu(richEditorType, type, menuBuilder, menuParam);
9653 }
9654
9655 RefPtr<NodePaintMethod> RichEditorPattern::CreateNodePaintMethod()
9656 {
9657 if (!overlayMod_) {
9658 auto scrollBar = GetScrollBar();
9659 if (scrollBar) {
9660 auto scrollBarModifier = AceType::MakeRefPtr<ScrollBarOverlayModifier>();
9661 scrollBarModifier->SetRect(scrollBar->GetActiveRect());
9662 scrollBarModifier->SetPositionMode(scrollBar->GetPositionMode());
9663 SetScrollBarOverlayModifier(scrollBarModifier);
9664 }
9665 SetEdgeEffect(EdgeEffect::FADE, GetAlwaysEnabled());
9666 SetEdgeEffect();
9667 overlayMod_ = AceType::MakeRefPtr<RichEditorOverlayModifier>(
9668 WeakClaim(this), GetScrollBarOverlayModifier(), GetScrollEdgeEffect());
9669 }
9670 return MakeRefPtr<RichEditorPaintMethod>(WeakClaim(this), ¶graphs_, baselineOffset_, contentMod_, overlayMod_);
9671 }
9672
9673 int32_t RichEditorPattern::GetHandleIndex(const Offset& offset) const
9674 {
9675 CHECK_NULL_RETURN(!isShowPlaceholder_, 0);
9676 return paragraphs_.GetIndex(Offset(offset.GetX() + contentRect_.GetX() - richTextRect_.GetX(),
9677 offset.GetY() + contentRect_.GetY() - richTextRect_.GetY()));
9678 }
9679
9680 std::vector<RectF> RichEditorPattern::GetTextBoxes()
9681 {
9682 std::vector<RectF> selectedRects;
9683 if (IsAiSelected()) {
9684 selectedRects = paragraphs_.GetRects(textSelector_.aiStart.value(), textSelector_.aiStart.value());
9685 } else {
9686 selectedRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
9687 }
9688 std::vector<RectF> res;
9689 res.reserve(selectedRects.size());
9690 for (auto&& rect : selectedRects) {
9691 res.emplace_back(rect);
9692 }
9693 if (!res.empty() && paragraphs_.IsSelectLineHeadAndUseLeadingMargin(textSelector_.GetTextStart())) {
9694 // To make drag screenshot include LeadingMarginPlaceholder when not single line
9695 if (res.front().GetY() != res.back().GetY()) {
9696 res.front().SetLeft(0.0f);
9697 }
9698 }
9699 return res;
9700 }
9701
9702 float RichEditorPattern::GetLineHeight() const
9703 {
9704 auto selectedRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
9705 CHECK_NULL_RETURN(selectedRects.size(), 0.0f);
9706 return selectedRects.front().Height();
9707 }
9708
9709 size_t RichEditorPattern::GetLineCount() const
9710 {
9711 CHECK_NULL_RETURN(!spans_.empty() || NeedShowPlaceholder(), 0);
9712 return paragraphs_.GetLineCount();
9713 }
9714
9715 TextLineMetrics RichEditorPattern::GetLineMetrics(int32_t lineNumber)
9716 {
9717 if (lineNumber < 0 || GetLineCount() == 0 || static_cast<uint32_t>(lineNumber) > GetLineCount() - 1) {
9718 TAG_LOGE(AceLogTag::ACE_RICH_TEXT,
9719 "GetLineMetrics failed, lineNumber not between 0 and max lines:%{public}d", lineNumber);
9720 return TextLineMetrics();
9721 }
9722 auto lineMetrics = paragraphs_.GetLineMetrics(lineNumber);
9723 const auto& textRect = GetTextRect();
9724 lineMetrics.x += textRect.GetX();
9725 lineMetrics.y += textRect.GetY();
9726 lineMetrics.baseline += textRect.GetY();
9727 return lineMetrics;
9728 }
9729
9730 std::vector<ParagraphManager::TextBox> RichEditorPattern::GetRectsForRange(
9731 int32_t start, int32_t end, RectHeightStyle heightStyle, RectWidthStyle widthStyle)
9732 {
9733 if (start < 0 || end < 0 || start > end) {
9734 return {};
9735 }
9736 std::vector<ParagraphManager::TextBox> textBoxes =
9737 paragraphs_.GetRectsForRange(start, end, heightStyle, widthStyle);
9738 const auto& textRect = richTextRect_;
9739 std::vector<ParagraphManager::TextBox> adjustedTextBoxes;
9740 for (auto& textBox : textBoxes) {
9741 ParagraphManager::TextBox adjustedTextBox = textBox;
9742 adjustedTextBox.rect_.SetLeft(textBox.rect_.Left() + textRect.Left());
9743 adjustedTextBox.rect_.SetTop(textBox.rect_.Top() + textRect.Top());
9744 adjustedTextBoxes.push_back(adjustedTextBox);
9745 }
9746 return adjustedTextBoxes;
9747 }
9748
9749 float RichEditorPattern::GetLetterSpacing() const
9750 {
9751 auto selectedRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
9752 CHECK_NULL_RETURN(!selectedRects.empty(), 0.0f);
9753 return selectedRects.front().Width();
9754 }
9755
9756 void RichEditorPattern::UpdateSelectMenuInfo(SelectMenuInfo& menuInfo)
9757 {
9758 bool isSupportCameraInput = false;
9759 #if defined(ENABLE_STANDARD_INPUT)
9760 auto inputMethod = MiscServices::InputMethodController::GetInstance();
9761 isSupportCameraInput =
9762 inputMethod && inputMethod->IsInputTypeSupported(MiscServices::InputType::CAMERA_INPUT);
9763 #endif
9764 menuInfo.showCameraInput = !IsSelected() && isSupportCameraInput && !customKeyboardBuilder_;
9765 if (textResponseType_.has_value()) {
9766 menuInfo.responseType = static_cast<int32_t>(textResponseType_.value());
9767 }
9768
9769 if (IsShowAIMenuOption() && !GetAIItemOption().empty()) {
9770 auto firstSpanItem = GetAIItemOption().begin()->second;
9771 menuInfo.aiMenuOptionType = firstSpanItem.type;
9772 return;
9773 }
9774 menuInfo.aiMenuOptionType = TextDataDetectType::INVALID;
9775 }
9776
9777 RectF RichEditorPattern::GetCaretRect() const
9778 {
9779 RectF rect;
9780 CHECK_NULL_RETURN(overlayMod_, rect);
9781 auto richEditorOverlay = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
9782 CHECK_NULL_RETURN(richEditorOverlay, rect);
9783 rect.SetOffset(richEditorOverlay->GetCaretOffset());
9784 rect.SetHeight(richEditorOverlay->GetCaretHeight());
9785 return rect;
9786 }
9787
9788 void RichEditorPattern::ScrollToSafeArea() const
9789 {
9790 auto pipeline = GetContext();
9791 CHECK_NULL_VOID(pipeline);
9792 if (pipeline->UsingCaretAvoidMode()) {
9793 // using TriggerAvoidOnCaretChange instead in CaretAvoidMode
9794 return;
9795 }
9796 auto textFieldManager = DynamicCast<TextFieldManagerNG>(pipeline->GetTextFieldManager());
9797 CHECK_NULL_VOID(textFieldManager);
9798 textFieldManager->ScrollTextFieldToSafeArea();
9799 }
9800
9801 void RichEditorPattern::InitScrollablePattern()
9802 {
9803 auto layoutProperty = GetLayoutProperty<RichEditorLayoutProperty>();
9804 CHECK_NULL_VOID(layoutProperty);
9805 auto barState = layoutProperty->GetDisplayModeValue(DisplayMode::AUTO);
9806 CHECK_NULL_VOID(!barDisplayMode_.has_value() || barDisplayMode_.value() != barState);
9807 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "setBarState=%{public}d", barState);
9808 barDisplayMode_ = barState;
9809 if (!GetScrollableEvent()) {
9810 AddScrollEvent();
9811 }
9812 SetAxis(Axis::VERTICAL);
9813 if (barState != DisplayMode::AUTO) {
9814 barState = DisplayMode::ON;
9815 }
9816 SetScrollBar(barState);
9817 auto scrollBar = GetScrollBar();
9818 if (scrollBar) {
9819 auto richEditorTheme = GetTheme<RichEditorTheme>();
9820 CHECK_NULL_VOID(richEditorTheme);
9821 scrollBar->SetMinHeight(richEditorTheme->GetScrollbarMinHeight());
9822 }
9823 if (overlayMod_) {
9824 UpdateScrollBarOffset();
9825 }
9826 auto& paddingProperty = layoutProperty->GetPaddingProperty();
9827 if (paddingProperty) {
9828 auto offsetY = paddingProperty->top.has_value() ? paddingProperty->top->GetDimension().ConvertToPx() : 0.0f;
9829 auto offsetX = paddingProperty->left.has_value() ? paddingProperty->left->GetDimension().ConvertToPx() : 0.0f;
9830 richTextRect_.SetOffset(OffsetF(offsetX, offsetY));
9831 }
9832 }
9833
9834 void RichEditorPattern::ProcessInnerPadding()
9835 {
9836 auto theme = GetTheme<RichEditorTheme>();
9837 CHECK_NULL_VOID(theme);
9838 auto host = GetHost();
9839 CHECK_NULL_VOID(host);
9840 auto layoutProperty = host->GetLayoutProperty<RichEditorLayoutProperty>();
9841 CHECK_NULL_VOID(layoutProperty);
9842 auto themePadding = theme->GetPadding();
9843 auto& paddingProp = layoutProperty->GetPaddingProperty();
9844 auto left = !paddingProp ? CalcLength(themePadding.Left()).GetDimension()
9845 : paddingProp->left.value_or(CalcLength(themePadding.Left())).GetDimension();
9846 auto top = !paddingProp ? CalcLength(themePadding.Top()).GetDimension()
9847 : paddingProp->top.value_or(CalcLength(themePadding.Top())).GetDimension();
9848 auto bottom = !paddingProp ? CalcLength(themePadding.Bottom()).GetDimension()
9849 : paddingProp->bottom.value_or(CalcLength(themePadding.Bottom())).GetDimension();
9850 auto right = !paddingProp ? CalcLength(themePadding.Right()).GetDimension()
9851 : paddingProp->right.value_or(CalcLength(themePadding.Right())).GetDimension();
9852 PaddingProperty paddings;
9853 paddings.top = NG::CalcLength(top);
9854 paddings.bottom = NG::CalcLength(bottom);
9855 paddings.left = NG::CalcLength(left);
9856 paddings.right = NG::CalcLength(right);
9857 layoutProperty->UpdatePadding(paddings);
9858 }
9859
9860 void RichEditorPattern::UpdateScrollStateAfterLayout(bool shouldDisappear)
9861 {
9862 bool hasTextOffsetChanged = false;
9863 if (GreatNotEqual(richTextRect_.GetY(), contentRect_.GetY())) {
9864 auto offset = richTextRect_.GetOffset();
9865 offset.AddY(contentRect_.GetY() - richTextRect_.GetY());
9866 richTextRect_.SetOffset(offset);
9867 hasTextOffsetChanged = true;
9868 }
9869 if (GreatNotEqual(richTextRect_.Height(), contentRect_.Height()) &&
9870 LessNotEqual(richTextRect_.Bottom(), contentRect_.Bottom())) {
9871 auto offset = richTextRect_.GetOffset();
9872 offset.AddY(contentRect_.Bottom() - richTextRect_.Bottom());
9873 richTextRect_.SetOffset(offset);
9874 hasTextOffsetChanged = true;
9875 }
9876 if (LessOrEqual(richTextRect_.Height(), contentRect_.Height()) &&
9877 LessNotEqual(richTextRect_.GetY(), contentRect_.GetY())) {
9878 richTextRect_.SetOffset(contentRect_.GetOffset());
9879 hasTextOffsetChanged = true;
9880 }
9881 if (hasTextOffsetChanged) {
9882 UpdateChildrenOffset();
9883 }
9884 StopScrollable();
9885 CheckScrollable();
9886 if (overlayMod_) {
9887 UpdateScrollBarOffset();
9888 }
9889 auto scrollBar = GetScrollBar();
9890 CHECK_NULL_VOID(scrollBar);
9891
9892 if (isFirstCallOnReady_) {
9893 isFirstCallOnReady_ = false;
9894 scrollBar->ScheduleDisappearDelayTask();
9895 return;
9896 }
9897 if (shouldDisappear) {
9898 scrollBar->ScheduleDisappearDelayTask();
9899 }
9900 }
9901
9902 bool RichEditorPattern::OnScrollCallback(float offset, int32_t source)
9903 {
9904 auto scrollBar = GetScrollBar();
9905 if (source == SCROLL_FROM_START) {
9906 IF_PRESENT(scrollBar, PlayScrollBarAppearAnimation());
9907 if (SelectOverlayIsOn()) {
9908 selectOverlay_->HideMenu(true);
9909 }
9910 CloseAIMenu();
9911 ScrollablePattern::RecordScrollEvent(Recorder::EventType::SCROLL_START);
9912 UIObserverHandler::GetInstance().NotifyScrollEventStateChange(
9913 AceType::WeakClaim(this), ScrollEventType::SCROLL_START);
9914 return true;
9915 }
9916 if (IsReachedBoundary(offset)) {
9917 return false;
9918 }
9919 if (scrollBar && source == SCROLL_FROM_JUMP) {
9920 scrollBar->PlayScrollBarAppearAnimation();
9921 scrollBar->ScheduleDisappearDelayTask();
9922 }
9923 auto newOffset = MoveTextRect(offset);
9924 MoveFirstHandle(newOffset);
9925 MoveSecondHandle(newOffset);
9926 dataDetectorAdapter_->aiSpanRects_.clear();
9927 return true;
9928 }
9929
9930 float RichEditorPattern::GetCrossOverHeight() const
9931 {
9932 if (!keyboardAvoidance_ || !contentChange_ || AceApplicationInfo::GetInstance().
9933 GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_FOURTEEN)) {
9934 return 0.0f;
9935 }
9936 auto pipeline = GetContext();
9937 CHECK_NULL_RETURN(pipeline, 0.0f);
9938 auto rootHeight = pipeline->GetRootHeight();
9939 auto keyboardY = rootHeight - pipeline->GetSafeAreaManager()->GetKeyboardInset().Length();
9940 if (GreatOrEqual(keyboardY, rootHeight)) {
9941 return 0.0f;
9942 }
9943 float height = contentRect_.Bottom();
9944 float frameY = parentGlobalOffset_.GetY() + contentRect_.GetY();
9945 float bottom = frameY + height;
9946 auto crossOverHeight = bottom - keyboardY;
9947 if (LessOrEqual(crossOverHeight, 0.0f)) {
9948 return 0.0f;
9949 }
9950 return crossOverHeight;
9951 }
9952
9953 float RichEditorPattern::MoveTextRect(float offset)
9954 {
9955 auto keyboardOffset = GetCrossOverHeight();
9956 if (GreatNotEqual(richTextRect_.Height(), contentRect_.Height() - keyboardOffset)) {
9957 if (GreatNotEqual(richTextRect_.GetY() + offset, contentRect_.GetY())) {
9958 offset = contentRect_.GetY() - richTextRect_.GetY();
9959 } else if (LessNotEqual(richTextRect_.Bottom() + offset, contentRect_.Bottom() - keyboardOffset)) {
9960 offset = contentRect_.Bottom() - keyboardOffset - richTextRect_.Bottom();
9961 }
9962 } else if (!NearEqual(richTextRect_.GetY(), contentRect_.GetY())) {
9963 offset = contentRect_.GetY() - richTextRect_.GetY();
9964 } else {
9965 return 0.0f;
9966 }
9967 if (NearEqual(offset, 0.0f)) {
9968 return offset;
9969 }
9970 scrollOffset_ = richTextRect_.GetY() + offset;
9971 richTextRect_.SetOffset(OffsetF(richTextRect_.GetX(), scrollOffset_));
9972 UpdateScrollBarOffset();
9973 UpdateChildrenOffset();
9974 if (auto host = GetContentHost(); host) {
9975 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
9976 }
9977 return offset;
9978 }
9979
9980 void RichEditorPattern::MoveFirstHandle(float offset)
9981 {
9982 if (SelectOverlayIsOn() && !NearEqual(offset, 0.0f)) {
9983 textSelector_.selectionBaseOffset.AddY(offset);
9984 auto firstHandleOffset = textSelector_.firstHandle.GetOffset();
9985 firstHandleOffset.AddY(offset);
9986 textSelector_.firstHandle.SetOffset(firstHandleOffset);
9987 selectOverlay_->UpdateFirstHandleOffset();
9988 }
9989 }
9990
9991 void RichEditorPattern::MoveSecondHandle(float offset)
9992 {
9993 if (SelectOverlayIsOn() && !NearEqual(offset, 0.0f)) {
9994 textSelector_.selectionDestinationOffset.AddY(offset);
9995 auto secondHandleOffset = textSelector_.secondHandle.GetOffset();
9996 secondHandleOffset.AddY(offset);
9997 textSelector_.secondHandle.SetOffset(secondHandleOffset);
9998 selectOverlay_->UpdateSecondHandleOffset();
9999 }
10000 }
10001
10002 void RichEditorPattern::SetNeedMoveCaretToContentRect()
10003 {
10004 CHECK_NULL_VOID(isRichEditorInit_);
10005 needMoveCaretToContentRect_ = true;
10006 }
10007
10008 void RichEditorPattern::MoveCaretToContentRect()
10009 {
10010 auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
10011 MoveCaretToContentRect(caretOffset, caretHeight);
10012 }
10013
10014 void RichEditorPattern::MoveCaretToContentRect(const OffsetF& caretOffset, float caretHeight)
10015 {
10016 auto keyboardOffset = GetCrossOverHeight();
10017 auto contentRect = GetTextContentRect();
10018 auto textRect = GetTextRect();
10019 auto scrollBar = GetScrollBar();
10020 if (scrollBar) {
10021 scrollBar->PlayScrollBarAppearAnimation();
10022 scrollBar->ScheduleDisappearDelayTask();
10023 }
10024 if (LessOrEqual(textRect.Height(), contentRect.Height() - keyboardOffset) || isShowPlaceholder_) {
10025 return;
10026 }
10027 if (LessNotEqual(contentRect.GetSize().Height(), caretHeight) &&
10028 !NearEqual(caretOffset.GetY() + caretHeight, contentRect.Bottom() - keyboardOffset)) {
10029 OnScrollCallback(contentRect.Bottom() - keyboardOffset - caretOffset.GetY() - caretHeight, SCROLL_FROM_NONE);
10030 }
10031 if (LessNotEqual(contentRect.GetSize().Height(), caretHeight)) {
10032 return;
10033 }
10034 if (LessNotEqual(caretOffset.GetY(), contentRect.GetY())) {
10035 if (LessOrEqual(caretOffset.GetX(), GetTextRect().GetX())) {
10036 OnScrollCallback(contentRect.GetY() - caretOffset.GetY() + caretHeight, SCROLL_FROM_NONE);
10037 } else {
10038 OnScrollCallback(contentRect.GetY() - caretOffset.GetY(), SCROLL_FROM_NONE);
10039 }
10040 } else if (GreatNotEqual(caretOffset.GetY() + caretHeight, contentRect.Bottom() - keyboardOffset)) {
10041 auto distance = contentRect.Bottom() - keyboardOffset - caretOffset.GetY() - caretHeight -
10042 CARET_BOTTOM_DISTANCE.ConvertToPx();
10043 OnScrollCallback(distance, SCROLL_FROM_NONE);
10044 }
10045 }
10046
10047 void RichEditorPattern::MoveCaretToContentRect(float offset, int32_t source)
10048 {
10049 float caretHeight = 0.0f;
10050 auto caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight);
10051 auto keyboardOffset = GetCrossOverHeight();
10052 auto contentRect = GetTextContentRect();
10053 auto distance = contentRect.Bottom() - keyboardOffset - caretOffset.GetY() - caretHeight - offset;
10054 OnScrollCallback(distance, source);
10055 }
10056
10057 bool RichEditorPattern::IsCaretInContentArea()
10058 {
10059 float caretHeight = 0.0f;
10060 auto caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight);
10061 auto keyboardOffset = GetCrossOverHeight();
10062 auto contentRect = GetTextContentRect();
10063 return GreatNotEqual(caretOffset.GetY() + caretHeight, contentRect.GetY())
10064 && LessNotEqual(caretOffset.GetY(), contentRect.Bottom() - keyboardOffset);
10065 }
10066
10067 void RichEditorPattern::UpdateScrollBarOffset()
10068 {
10069 if (!GetScrollBar() && !GetScrollBarProxy()) {
10070 return;
10071 }
10072 Size size(frameRect_.Width(), frameRect_.Height());
10073 auto verticalGap = frameRect_.Height() - contentRect_.Height();
10074 UpdateScrollBarRegion(
10075 contentRect_.GetY() - richTextRect_.GetY(), richTextRect_.Height() + verticalGap, size, Offset(0.0, 0.0));
10076 auto tmpHost = GetHost();
10077 CHECK_NULL_VOID(tmpHost);
10078 tmpHost->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
10079 }
10080
10081 void RichEditorPattern::OnScrollEndCallback()
10082 {
10083 auto scrollBar = GetScrollBar();
10084 if (scrollBar) {
10085 scrollBar->ScheduleDisappearDelayTask();
10086 }
10087 CHECK_NULL_VOID(!selectOverlay_->GetIsHandleMoving());
10088 if (IsSelectAreaVisible()) {
10089 auto info = selectOverlay_->GetSelectOverlayInfo();
10090 if (info && info->menuInfo.menuBuilder) {
10091 selectOverlay_->ProcessOverlay({ .animation = true });
10092 } else {
10093 selectOverlay_->UpdateMenuOffset();
10094 selectOverlay_->ShowMenu();
10095 }
10096 }
10097 if (AnimateStoped()) {
10098 ScrollablePattern::RecordScrollEvent(Recorder::EventType::SCROLL_STOP);
10099 UIObserverHandler::GetInstance().NotifyScrollEventStateChange(
10100 AceType::WeakClaim(this), ScrollEventType::SCROLL_STOP);
10101 }
10102 }
10103
10104 bool RichEditorPattern::IsSelectAreaVisible()
10105 {
10106 auto pipeline = GetContext();
10107 CHECK_NULL_RETURN(pipeline, false);
10108 auto safeAreaManager = pipeline->GetSafeAreaManager();
10109 CHECK_NULL_RETURN(safeAreaManager, false);
10110 auto keyboardInsert = safeAreaManager->GetKeyboardInset();
10111 auto selectArea = GetSelectArea(SelectRectsType::ALL_LINES);
10112
10113 return !selectArea.IsEmpty() && LessNotEqual(selectArea.Top(), keyboardInsert.start);
10114 }
10115
10116 bool RichEditorPattern::IsReachedBoundary(float offset)
10117 {
10118 auto keyboardOffset = GetCrossOverHeight();
10119 return (NearEqual(richTextRect_.GetY(), contentRect_.GetY()) && GreatNotEqual(offset, 0.0f)) ||
10120 (NearEqual(richTextRect_.GetY() + richTextRect_.Height(),
10121 contentRect_.GetY() + contentRect_.Height() - keyboardOffset) &&
10122 LessNotEqual(offset, 0.0f));
10123 }
10124
10125 void RichEditorPattern::CheckScrollable()
10126 {
10127 auto gestureHub = GetGestureEventHub();
10128 CHECK_NULL_VOID(gestureHub);
10129 scrollable_ = GetTextContentLength() > 0 && GreatNotEqual(richTextRect_.Height(), contentRect_.Height());
10130 SetScrollEnabled(scrollable_);
10131 }
10132
10133 void RichEditorPattern::UpdateChildrenOffset()
10134 {
10135 auto host = GetContentHost();
10136 CHECK_NULL_VOID(host);
10137 std::vector<int32_t> placeholderIndex;
10138 for (const auto& child : spans_) {
10139 if (!child) {
10140 continue;
10141 }
10142 if (AceType::InstanceOf<ImageSpanItem>(child) || AceType::InstanceOf<PlaceholderSpanItem>(child)) {
10143 placeholderIndex.emplace_back(child->placeholderIndex);
10144 }
10145 }
10146 if (spans_.empty() || placeholderIndex.empty()) {
10147 return;
10148 }
10149 size_t index = 0;
10150 std::vector<RectF> rectsForPlaceholders = paragraphs_.GetPlaceholderRects();
10151 auto childrenNodes = host->GetChildren();
10152 auto textOffset = GetTextRect().GetOffset();
10153 for (const auto& child : childrenNodes) {
10154 auto childNode = AceType::DynamicCast<FrameNode>(child);
10155 if (!childNode) {
10156 continue;
10157 }
10158 if (!(childNode->GetPattern<ImagePattern>() || childNode->GetPattern<PlaceholderSpanPattern>())) {
10159 continue;
10160 }
10161 if (isSpanStringMode_) {
10162 auto imageSpanNode = AceType::DynamicCast<ImageSpanNode>(child);
10163 if (imageSpanNode && imageSpanNode->GetSpanItem()) {
10164 index = static_cast<uint32_t>(imageSpanNode->GetSpanItem()->placeholderIndex);
10165 }
10166 }
10167 if (index >= rectsForPlaceholders.size()) {
10168 break;
10169 }
10170 auto rect = rectsForPlaceholders.at(index);
10171 auto geometryNode = childNode->GetGeometryNode();
10172 if (geometryNode) {
10173 geometryNode->SetMarginFrameOffset(textOffset + OffsetF(rect.Left(), rect.Top()));
10174 childNode->ForceSyncGeometryNode();
10175 childNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
10176 }
10177 ++index;
10178 }
10179 }
10180
10181 void RichEditorPattern::AutoScrollByEdgeDetection(AutoScrollParam param, OffsetF offset, EdgeDetectionStrategy strategy)
10182 {
10183 if (NearEqual(prevAutoScrollOffset_.GetY(), offset.GetY())) {
10184 return;
10185 }
10186 prevAutoScrollOffset_ = offset;
10187 auto contentRect = GetTextContentRect();
10188 auto isDragging = param.autoScrollEvent == AutoScrollEvent::DRAG;
10189 float edgeThreshold = isDragging ? AUTO_SCROLL_DRAG_EDGE_DISTANCE.ConvertToPx()
10190 : AUTO_SCROLL_EDGE_DISTANCE.ConvertToPx();
10191 auto maxHeight = isDragging ? frameRect_.Height() : contentRect.Height();
10192 if (GreatNotEqual(edgeThreshold * 2, maxHeight)) {
10193 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AutoScrollByEdgeDetection: hot area height is great than max height.");
10194 return;
10195 }
10196 float topEdgeThreshold = isDragging ? edgeThreshold : edgeThreshold + contentRect.GetY();
10197 float bottomThreshold = isDragging ? frameRect_.Height() - edgeThreshold : contentRect.Bottom() - edgeThreshold;
10198 if (param.autoScrollEvent == AutoScrollEvent::HANDLE) {
10199 auto handleTopOffset = offset;
10200 auto handleBottomOffset = OffsetF(offset.GetX(), offset.GetY() + param.handleRect.Height());
10201 if (GreatNotEqual(handleBottomOffset.GetY(), bottomThreshold)) {
10202 param.offset = bottomThreshold - handleBottomOffset.GetY();
10203 ScheduleAutoScroll(param);
10204 } else if (LessNotEqual(handleTopOffset.GetY(), topEdgeThreshold)) {
10205 param.offset = topEdgeThreshold - handleTopOffset.GetY();
10206 ScheduleAutoScroll(param);
10207 } else {
10208 StopAutoScroll();
10209 }
10210 return;
10211 }
10212 // drag and mouse
10213 if (GreatNotEqual(offset.GetY(), bottomThreshold)) {
10214 param.offset = isDragging ? -CalcDragSpeed(bottomThreshold, frameRect_.Height(), offset.GetY())
10215 : bottomThreshold - offset.GetY();
10216 ScheduleAutoScroll(param);
10217 } else if (LessNotEqual(offset.GetY(), topEdgeThreshold)) {
10218 param.offset = isDragging ? CalcDragSpeed(topEdgeThreshold, 0, offset.GetY())
10219 : topEdgeThreshold - offset.GetY();
10220 ScheduleAutoScroll(param);
10221 } else {
10222 StopAutoScroll();
10223 }
10224 }
10225
10226 float RichEditorPattern::CalcDragSpeed(float hotAreaStart, float hotAreaEnd, float point)
10227 {
10228 auto distanceRatio = (point - hotAreaStart) / (hotAreaEnd - hotAreaStart);
10229 auto speedFactor = Curves::SHARP->MoveInternal(distanceRatio);
10230 return ((MAX_DRAG_SCROLL_SPEED * speedFactor) / TIME_UNIT) * AUTO_SCROLL_INTERVAL;
10231 }
10232
10233 void RichEditorPattern::ScheduleAutoScroll(AutoScrollParam param)
10234 {
10235 if (GreatNotEqual(param.offset, 0.0f) && IsReachTop()) {
10236 return;
10237 }
10238 if (LessNotEqual(param.offset, 0.0f) && IsReachBottom()) {
10239 return;
10240 }
10241 auto context = GetContext();
10242 CHECK_NULL_VOID(context);
10243 auto taskExecutor = context->GetTaskExecutor();
10244 CHECK_NULL_VOID(taskExecutor);
10245 if (param.isFirstRun_) {
10246 param.isFirstRun_ = false;
10247 currentScrollParam_ = param;
10248 if (isAutoScrollRunning_) {
10249 return;
10250 }
10251 }
10252 autoScrollTask_.Reset([weak = WeakClaim(this)]() {
10253 auto client = weak.Upgrade();
10254 CHECK_NULL_VOID(client);
10255 client->OnAutoScroll(client->currentScrollParam_);
10256 if (client->IsReachTop() || client->IsReachBottom()) {
10257 client->StopAutoScroll();
10258 }
10259 });
10260 isAutoScrollRunning_ = true;
10261 taskExecutor->PostDelayedTask(autoScrollTask_, TaskExecutor::TaskType::UI, AUTO_SCROLL_INTERVAL,
10262 "ArkUIRichEditorScheduleAutoScroll");
10263 }
10264
10265 void RichEditorPattern::OnAutoScroll(AutoScrollParam param)
10266 {
10267 if (param.showScrollbar) {
10268 auto scrollBar = GetScrollBar();
10269 IF_PRESENT(scrollBar, PlayScrollBarAppearAnimation());
10270 param.showScrollbar = false;
10271 }
10272 CHECK_NULL_VOID(param.autoScrollEvent != AutoScrollEvent::NONE);
10273 auto newOffset = MoveTextRect(param.offset);
10274 switch (param.autoScrollEvent) {
10275 case AutoScrollEvent::CARET:
10276 break;
10277 case AutoScrollEvent::HANDLE: {
10278 param.isFirstHandle ? MoveSecondHandle(newOffset) : MoveFirstHandle(newOffset);
10279 selectOverlay_->OnHandleMove(param.handleRect, param.isFirstHandle);
10280 break;
10281 }
10282 case AutoScrollEvent::DRAG:
10283 break;
10284 case AutoScrollEvent::MOUSE: {
10285 auto textOffset = ConvertTouchOffsetToTextOffset(param.eventOffset);
10286 int32_t extend = (GetTextContentLength() == 0) ? 0 : paragraphs_.GetIndex(textOffset);
10287 UpdateSelector(textSelector_.baseOffset, extend);
10288 SetCaretPosition(std::max(textSelector_.baseOffset, extend));
10289 break;
10290 }
10291 default:
10292 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "Unsupported auto scroll event type");
10293 return;
10294 }
10295 CHECK_NULL_VOID(!NearEqual(newOffset, 0.0f));
10296 ScheduleAutoScroll(param);
10297 }
10298
10299 void RichEditorPattern::StopAutoScroll()
10300 {
10301 isAutoScrollRunning_ = false;
10302 autoScrollTask_.Cancel();
10303 prevAutoScrollOffset_ = OffsetF(0.0f, 0.0f);
10304 auto scrollBar = GetScrollBar();
10305 IF_PRESENT(scrollBar, ScheduleDisappearDelayTask());
10306 }
10307
10308 bool RichEditorPattern::NeedAiAnalysis(
10309 const CaretUpdateType targeType, const int32_t pos, const int32_t& spanStart, const std::string& content)
10310 {
10311 if (spanStart < 0) {
10312 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "NeedAiAnalysis -spanStart:%{public}d, return!", spanStart);
10313 return false;
10314 }
10315
10316 if (!InputAIChecker::NeedAIAnalysis(content.empty(), targeType, lastClickTimeStamp_ - lastAiPosTimeStamp_)) {
10317 return false;
10318 }
10319
10320 if (IsClickBoundary(pos) && targeType == CaretUpdateType::PRESSED) {
10321 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "NeedAiAnalysis IsClickBoundary, return!");
10322 return false;
10323 }
10324 EmojiRelation relation = GetEmojiRelation(pos);
10325 if (relation == EmojiRelation::IN_EMOJI || relation == EmojiRelation::MIDDLE_EMOJI ||
10326 relation == EmojiRelation::BEFORE_EMOJI) {
10327 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "NeedAiAnalysis emoji relation=%{public}d, return!", relation);
10328 return false;
10329 }
10330 return true;
10331 }
10332
10333 void RichEditorPattern::AdjustCursorPosition(int32_t& pos)
10334 {
10335 // the rich text has some spans, the pos is belong to the whole richtext content, should use (pos - spanStarint)
10336 int32_t spanStart = -1;
10337 // get the span text by the position, maybe text is empty
10338 std::string content = GetPositionSpansText(pos, spanStart);
10339
10340 if (NeedAiAnalysis(CaretUpdateType::PRESSED, pos, spanStart, content)) {
10341 int32_t aiPos = pos - spanStart;
10342 DataDetectorMgr::GetInstance().AdjustCursorPosition(aiPos, content, lastAiPosTimeStamp_, lastClickTimeStamp_);
10343 if (aiPos < 0) {
10344 return;
10345 }
10346 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "get ai pos:%{public}d--spanStart%{public}d", aiPos, spanStart);
10347 pos = aiPos + spanStart;
10348 }
10349 }
10350
10351 bool RichEditorPattern::AdjustWordSelection(int32_t& start, int32_t& end)
10352 {
10353 // the rich text has some spans, the pos is belong to the whole richtext content, should use (pos - spanStarint)
10354 int32_t spanStart = -1;
10355 // get the span text by the position, maybe text is empty
10356 std::string content = GetPositionSpansText(start, spanStart);
10357 if (NeedAiAnalysis(CaretUpdateType::DOUBLE_CLICK, start, spanStart, content)) {
10358 int32_t aiPosStart = start - spanStart;
10359 int32_t aiPosEnd = end - spanStart;
10360 DataDetectorMgr::GetInstance().AdjustWordSelection(aiPosStart, content, aiPosStart, aiPosEnd);
10361 if (aiPosStart < 0 || aiPosEnd < 0) {
10362 return false;
10363 }
10364
10365 start = std::min(aiPosStart + spanStart, GetTextContentLength());
10366 end = std::min(aiPosEnd + spanStart, GetTextContentLength());
10367 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "get ai selector [%{public}d--%{public}d]", start, end);
10368 return true;
10369 }
10370 return false;
10371 }
10372
10373 void RichEditorPattern::AdjustPlaceholderSelection(int32_t& start, int32_t& end, const Offset& touchPos)
10374 {
10375 CHECK_NULL_VOID(!spans_.empty());
10376 if (!IsTouchBeforeCaret(start, touchPos)) {
10377 return;
10378 }
10379 auto it = std::find_if(spans_.begin(), spans_.end(), [start](const RefPtr<SpanItem>& spanItem) {
10380 return spanItem->position == start;
10381 });
10382 if (it != spans_.end()) {
10383 // adjust selection if touch right of image or placeholder
10384 auto spanIndex = std::distance(spans_.begin(), it);
10385 auto spanNodeBefore = DynamicCast<FrameNode>(GetChildByIndex(spanIndex));
10386 if (spanNodeBefore && (spanNodeBefore->GetTag() == V2::IMAGE_ETS_TAG ||
10387 spanNodeBefore->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG)) {
10388 end = start;
10389 --start;
10390 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "get placeholder selector [%{public}d--%{public}d]", start, end);
10391 }
10392 }
10393 }
10394
10395 bool RichEditorPattern::IsTouchAtLineEnd(int32_t caretPos, const Offset& textOffset)
10396 {
10397 auto positionWithAffinity = paragraphs_.GetGlyphPositionAtCoordinate(textOffset);
10398 TextAffinity currentAffinity = positionWithAffinity.affinity_;
10399 return currentAffinity == TextAffinity::UPSTREAM && !IsTouchBeforeCaret(caretPos, textOffset);
10400 }
10401
10402 bool RichEditorPattern::IsTouchBeforeCaret(int32_t caretPos, const Offset& textOffset) {
10403 CHECK_NULL_RETURN(!spans_.empty(), false);
10404 float selectLineHeight = 0.0f;
10405 OffsetF caretOffsetUp = paragraphs_.ComputeCursorOffset(caretPos, selectLineHeight);
10406 auto needAdjustRect = RectF{ 0, caretOffsetUp.GetY(), caretOffsetUp.GetX(), selectLineHeight };
10407 return needAdjustRect.IsInRegion(PointF{ textOffset.GetX(), textOffset.GetY() });
10408 }
10409
10410 bool RichEditorPattern::IsClickBoundary(const int32_t position)
10411 {
10412 if (InputAIChecker::IsSingleClickAtBoundary(position, GetTextContentLength())) {
10413 return true;
10414 }
10415
10416 float height = 0;
10417 auto handleOffset = CalcCursorOffsetByPosition(position, height);
10418 if (InputAIChecker::IsMultiClickAtBoundary(handleOffset, TextPattern::GetTextRect())) {
10419 return true;
10420 }
10421 return false;
10422 }
10423
10424 std::string RichEditorPattern::GetPositionSpansText(int32_t position, int32_t& startSpan)
10425 {
10426 int32_t start = position - AI_TEXT_RANGE_LEFT;
10427 int32_t end = position + AI_TEXT_RANGE_RIGHT;
10428
10429 start = std::clamp(start, 0, GetTextContentLength());
10430 end = std::clamp(end, 0, GetTextContentLength());
10431 AdjustSelector(start, end);
10432 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "caret=%{public}d, range=[%{public}d,%{public}d]", position, start, end);
10433
10434 // get all the spans between start and end, then filter the valid text
10435 auto infos = GetSpansInfo(start, end, GetSpansMethod::ONSELECT);
10436 if (infos.GetSelection().resultObjects.empty()) {
10437 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "get spans text is null pos:%{public}d,return", position);
10438 return "";
10439 }
10440 auto list = infos.GetSelection().resultObjects;
10441
10442 std::stringstream sstream;
10443 for (const auto& obj : list) {
10444 if (obj.type != SelectSpanType::TYPESPAN) {
10445 if (obj.spanPosition.spanRange[0] == position) {
10446 startSpan = -1;
10447 return "";
10448 } else if (obj.spanPosition.spanRange[1] <= position) {
10449 sstream.str("");
10450 startSpan = -1;
10451 } else {
10452 break;
10453 }
10454 } else {
10455 if (startSpan < 0) {
10456 startSpan = obj.spanPosition.spanRange[0] + obj.offsetInSpan[0];
10457 }
10458 // we should use the wide string deal to avoid crash
10459 auto wideText = obj.valueString;
10460 int32_t textLen = static_cast<int32_t>(wideText.length());
10461 if (obj.offsetInSpan[0] < textLen && obj.offsetInSpan[1] <= textLen) {
10462 sstream << UtfUtils::Str16ToStr8(
10463 wideText.substr(obj.offsetInSpan[0], obj.offsetInSpan[1] - obj.offsetInSpan[0]));
10464 }
10465 }
10466 }
10467
10468 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "get spans text ret spanStart:%{public}d", startSpan);
10469 return sstream.str();
10470 }
10471
10472 bool RichEditorPattern::IsShowSelectMenuUsingMouse()
10473 {
10474 auto host = GetHost();
10475 CHECK_NULL_RETURN(host, false);
10476 auto pipeline = host->GetContext();
10477 CHECK_NULL_RETURN(pipeline, false);
10478 auto selectOverlayManager = pipeline->GetSelectOverlayManager();
10479 CHECK_NULL_RETURN(selectOverlayManager, false);
10480 return selectOverlayManager->GetSelectOverlayInfo().isUsingMouse;
10481 }
10482
10483 RefPtr<FocusHub> RichEditorPattern::GetFocusHub() const
10484 {
10485 auto host = GetHost();
10486 CHECK_NULL_RETURN(host, nullptr);
10487 auto focusHub = host->GetOrCreateFocusHub();
10488 return focusHub;
10489 }
10490
10491 void RichEditorPattern::HandleCursorOnDragMoved(const RefPtr<NotifyDragEvent>& notifyDragEvent)
10492 {
10493 auto host = GetHost();
10494 CHECK_NULL_VOID(host);
10495 if (HasFocus()) {
10496 if (!isCursorAlwaysDisplayed_) {
10497 isCursorAlwaysDisplayed_ = true;
10498 StartTwinkling();
10499 }
10500 if (SystemProperties::GetDebugEnabled()) {
10501 TAG_LOGD(AceLogTag::ACE_RICH_TEXT,
10502 "In OnDragMoved, the cursor has always Displayed in the textField, id:" SEC_PLD(%{public}d),
10503 SEC_PARAM(host->GetId()));
10504 }
10505 return;
10506 }
10507 TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
10508 "In OnDragMoved, the dragging node is moving in the richEditor, id:" SEC_PLD(%{public}d),
10509 SEC_PARAM(host->GetId()));
10510 auto focusHub = GetFocusHub();
10511 CHECK_NULL_VOID(focusHub);
10512 isOnlyRequestFocus_ = true;
10513 focusHub->RequestFocusImmediately();
10514 if (focusHub->IsCurrentFocus()) {
10515 ShowCaretWithoutTwinkling();
10516 }
10517 };
10518
10519 void RichEditorPattern::HandleCursorOnDragLeaved(const RefPtr<NotifyDragEvent>& notifyDragEvent)
10520 {
10521 auto host = GetHost();
10522 CHECK_NULL_VOID(host);
10523 TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
10524 "In OnDragLeaved, the dragging node has left from richEditor, id:" SEC_PLD(%{public}d),
10525 SEC_PARAM(host->GetId()));
10526 auto focusHub = GetFocusHub();
10527 CHECK_NULL_VOID(focusHub);
10528 focusHub->LostFocusToViewRoot();
10529 StopTwinkling();
10530 };
10531
10532 void RichEditorPattern::HandleCursorOnDragEnded(const RefPtr<NotifyDragEvent>& notifyDragEvent)
10533 {
10534 auto host = GetHost();
10535 CHECK_NULL_VOID(host);
10536 auto focusHub = GetFocusHub();
10537 CHECK_NULL_VOID(focusHub);
10538 StopAutoScroll();
10539 if (!isCursorAlwaysDisplayed_) {
10540 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "In OnDragEnded,"
10541 " the released location is not in the current richEditor, id:" SEC_PLD(%{public}d),
10542 SEC_PARAM(host->GetId()));
10543 IF_TRUE(HasFocus(), CloseKeyboard(false));
10544 focusHub->LostFocus();
10545 StopTwinkling();
10546 return;
10547 }
10548 TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
10549 "In OnDragEnded, the released location is in the current richEditor, id:" SEC_PLD(%{public}d),
10550 SEC_PARAM(host->GetId()));
10551 focusHub->LostFocusToViewRoot();
10552 isCursorAlwaysDisplayed_ = false;
10553 StopTwinkling();
10554 };
10555
10556 void RichEditorPattern::HandleOnDragStatusCallback(
10557 const DragEventType& dragEventType, const RefPtr<NotifyDragEvent>& notifyDragEvent)
10558 {
10559 ScrollablePattern::HandleOnDragStatusCallback(dragEventType, notifyDragEvent);
10560 switch (dragEventType) {
10561 case DragEventType::MOVE:
10562 HandleCursorOnDragMoved(notifyDragEvent);
10563 break;
10564 case DragEventType::LEAVE:
10565 HandleCursorOnDragLeaved(notifyDragEvent);
10566 break;
10567 case DragEventType::DROP:
10568 HandleCursorOnDragEnded(notifyDragEvent);
10569 break;
10570 default:
10571 break;
10572 }
10573 }
10574
10575 void RichEditorPattern::HandleOnCameraInput()
10576 {
10577 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnCameraInput");
10578 #if defined(ENABLE_STANDARD_INPUT)
10579 if (richEditTextChangeListener_ == nullptr) {
10580 richEditTextChangeListener_ = new OnTextChangedListenerImpl(WeakClaim(this));
10581 }
10582 auto inputMethod = MiscServices::InputMethodController::GetInstance();
10583 if (!inputMethod) {
10584 return;
10585 }
10586 StartTwinkling();
10587 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
10588 if (imeShown_) {
10589 inputMethod->StartInputTypeAsync(MiscServices::InputType::CAMERA_INPUT);
10590 } else {
10591 HandleOnEditChanged(true);
10592 auto optionalTextConfig = GetMiscTextConfig();
10593 CHECK_NULL_VOID(optionalTextConfig.has_value());
10594 MiscServices::TextConfig textConfig = optionalTextConfig.value();
10595 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnCameraInput set calling window id is : %{public}u",
10596 textConfig.windowId);
10597 #ifdef WINDOW_SCENE_SUPPORTED
10598 auto systemWindowId = GetSCBSystemWindowId();
10599 if (systemWindowId) {
10600 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "windowId From %{public}u to %{public}u.", textConfig.windowId,
10601 systemWindowId);
10602 textConfig.windowId = systemWindowId;
10603 }
10604 #endif
10605 auto ret = inputMethod->Attach(richEditTextChangeListener_, false, textConfig);
10606 if (ret == MiscServices::ErrorCode::NO_ERROR) {
10607 auto pipeline = GetContext();
10608 CHECK_NULL_VOID(pipeline);
10609 auto textFieldManager = AceType::DynamicCast<TextFieldManagerNG>(pipeline->GetTextFieldManager());
10610 CHECK_NULL_VOID(textFieldManager);
10611 textFieldManager->SetIsImeAttached(true);
10612 }
10613 inputMethod->StartInputType(MiscServices::InputType::CAMERA_INPUT);
10614 inputMethod->ShowTextInput();
10615 }
10616 CloseSelectOverlay();
10617 #endif
10618 #endif
10619 }
10620
10621 bool RichEditorPattern::CanStartAITask() const
10622 {
10623 return !isEditing_ && !spans_.empty() && TextPattern::CanStartAITask();
10624 }
10625
10626 bool RichEditorPattern::NeedShowAIDetect()
10627 {
10628 return !isEditing_ && !isShowPlaceholder_ && !spans_.empty() && TextPattern::NeedShowAIDetect();
10629 }
10630
10631 void RichEditorPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
10632 {
10633 /* no fixed attr below, just return */
10634 if (filter.IsFastFilter()) {
10635 return;
10636 }
10637 json->PutExtAttr("enableDataDetector", textDetectEnable_ ? "true" : "false", filter);
10638 json->PutExtAttr("dataDetectorConfig", dataDetectorAdapter_->textDetectConfigStr_.c_str(), filter);
10639 json->PutExtAttr("placeholder", GetPlaceHolderInJson().c_str(), filter);
10640 json->PutExtAttr("customKeyboard", GetCustomKeyboardInJson().c_str(), filter);
10641 json->PutExtAttr("bindSelectionMenu", GetBindSelectionMenuInJson().c_str(), filter);
10642 json->PutExtAttr("copyOptions", static_cast<int32_t>(copyOption_), filter);
10643 json->PutExtAttr("enablePreviewText", isTextPreviewSupported_ ? "true" : "false", filter);
10644 json->PutExtAttr("caretColor", GetCaretColor().ColorToString().c_str(), filter);
10645 json->PutExtAttr("selectedBackgroundColor", GetSelectedBackgroundColor().ColorToString().c_str(), filter);
10646 auto enterKeyType = static_cast<int32_t>(GetTextInputActionValue(GetDefaultTextInputAction()));
10647 json->PutExtAttr("enterKeyType", enterKeyType, filter);
10648 json->PutExtAttr("stopBackPress", isStopBackPress_ ? "true" : "false", filter);
10649 json->PutExtAttr("keyboardAppearance", static_cast<int32_t>(keyboardAppearance_), filter);
10650 json->PutExtAttr("maxLength", maxLength_.value_or(INT_MAX), filter);
10651 json->PutExtAttr("enableHapticFeedback", isEnableHapticFeedback_ ? "true" : "false", filter);
10652 json->PutExtAttr("barState", static_cast<int32_t>(GetBarDisplayMode()), filter);
10653 json->PutExtAttr("enableKeyboardOnFocus", needToRequestKeyboardOnFocus_ ? "true" : "false", filter);
10654 auto undoStyle = isStyledUndoSupported_ ? OHOS::Ace::UndoStyle::KEEP_STYLE : OHOS::Ace::UndoStyle::CLEAR_STYLE;
10655 json->PutExtAttr("undoStyle", static_cast<int32_t>(undoStyle), filter);
10656 json->PutExtAttr("enableAutoSpacing", isEnableAutoSpacing_ ? "true" : "false", filter);
10657 }
10658
10659 std::string RichEditorPattern::GetCustomKeyboardInJson() const
10660 {
10661 auto jsonValue = JsonUtil::Create(true);
10662 jsonValue->Put("supportAvoidance", keyboardAvoidance_ ? "true" : "false");
10663 return StringUtils::RestoreBackslash(jsonValue->ToString());
10664 }
10665
10666 void RichEditorPattern::FillPreviewMenuInJson(const std::unique_ptr<JsonValue>& jsonValue) const
10667 {
10668 IF_PRESENT(oneStepDragController_, FillJsonValue(jsonValue));
10669 }
10670
10671 std::string RichEditorPattern::GetPlaceHolderInJson() const
10672 {
10673 auto host = GetHost();
10674 CHECK_NULL_RETURN(host, "");
10675 auto layoutProperty = host->GetLayoutProperty<RichEditorLayoutProperty>();
10676 bool hasPlaceHolder = layoutProperty && layoutProperty->HasPlaceholder()
10677 && !layoutProperty->GetPlaceholder().value().empty();
10678 CHECK_NULL_RETURN(hasPlaceHolder, "");
10679 auto jsonValue = JsonUtil::Create(true);
10680 jsonValue->Put("value", UtfUtils::Str16ToStr8(layoutProperty->GetPlaceholderValue(u"")).c_str());
10681 auto jsonFont = JsonUtil::Create(true);
10682 jsonFont->Put("size", GetFontSizeWithThemeInJson(layoutProperty->GetPlaceholderFontSize()).c_str());
10683 jsonFont->Put("weight", GetFontWeightInJson(layoutProperty->GetPlaceholderFontWeight()).c_str());
10684 jsonFont->Put("family", GetFontFamilyInJson(layoutProperty->GetPlaceholderFontFamily()).c_str());
10685 jsonFont->Put("style", GetFontStyleInJson(layoutProperty->GetPlaceholderItalicFontStyle()).c_str());
10686 auto jsonStyle = JsonUtil::Create(true);
10687 jsonStyle->Put("font", jsonFont->ToString().c_str());
10688 jsonStyle->Put("fontColor", GetTextColorInJson(layoutProperty->GetPlaceholderTextColor()).c_str());
10689 jsonValue->Put("style", jsonStyle->ToString().c_str());
10690 return StringUtils::RestoreBackslash(jsonValue->ToString());
10691 }
10692
10693 std::string RichEditorPattern::GetTextColorInJson(const std::optional<Color>& value) const
10694 {
10695 CHECK_NULL_RETURN(!value, value->ColorToString());
10696 auto host = GetHost();
10697 CHECK_NULL_RETURN(host, "");
10698 auto pipeline = host->GetContext();
10699 CHECK_NULL_RETURN(pipeline, "");
10700 auto richEditorTheme = pipeline->GetTheme<RichEditorTheme>();
10701 CHECK_NULL_RETURN(richEditorTheme, "");
10702 Color textColor = richEditorTheme->GetTextStyle().GetTextColor();
10703 return textColor.ColorToString();
10704 }
10705
10706 void RichEditorPattern::GetCaretMetrics(CaretMetricsF& caretCaretMetric)
10707 {
10708 float caretHeight = 0.0f;
10709 OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight);
10710 auto host = GetHost();
10711 CHECK_NULL_VOID(host);
10712 auto offset = host->GetPaintRectOffsetNG(false, true);
10713 caretOffset += offset;
10714 caretCaretMetric.offset = caretOffset;
10715 caretCaretMetric.height = caretHeight;
10716 }
10717
10718 void RichEditorPattern::OnVirtualKeyboardAreaChanged()
10719 {
10720 CHECK_NULL_VOID(SelectOverlayIsOn() && !selectOverlay_->GetIsHandleMoving() && !selectOverlay_->GetIsHandleHidden());
10721 float selectLineHeight = 0.0f;
10722 textSelector_.selectionBaseOffset.SetX(
10723 CalcCursorOffsetByPosition(textSelector_.GetStart(), selectLineHeight).GetX());
10724 textSelector_.selectionDestinationOffset.SetX(
10725 CalcCursorOffsetByPosition(textSelector_.GetEnd(), selectLineHeight).GetX());
10726 CreateHandles();
10727 }
10728
10729 void RichEditorPattern::ResetDragOption()
10730 {
10731 auto gestureEventHub = GetGestureEventHub();
10732 CHECK_NULL_VOID(gestureEventHub);
10733 if (gestureEventHub->GetIsTextDraggable()) {
10734 CloseSelectOverlay();
10735 ResetSelection();
10736 }
10737 }
10738
10739 void RichEditorPattern::AdjustSelectRects(SelectRectsType pos, std::vector<RectF>& selectRects)
10740 {
10741 if (pos == SelectRectsType::LEFT_TOP_POINT) {
10742 selectRects.erase(std::next(selectRects.begin()), selectRects.end());
10743 selectRects.front().SetSize({0, 0});
10744 } else if (pos == SelectRectsType::RIGHT_BOTTOM_POINT) {
10745 selectRects.erase(selectRects.begin(), std::prev(selectRects.end()));
10746 selectRects.front().SetRect({selectRects.front().Right(), selectRects.front().Bottom()}, {0, 0});
10747 }
10748 }
10749
10750 RectF RichEditorPattern::GetSelectArea(SelectRectsType pos)
10751 {
10752 RectF rect;
10753 auto paintOffset = selectOverlay_->GetPaintOffsetWithoutTransform();
10754 auto selectRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
10755 auto contentRect = contentRect_;
10756 contentRect.SetOffset(contentRect.GetOffset() + paintOffset);
10757 auto host = GetHost();
10758 CHECK_NULL_RETURN(host, rect);
10759 auto parent = host->GetAncestorNodeOfFrame(false);
10760 contentRect = GetVisibleContentRect(parent, contentRect);
10761 AppendSelectRect(selectRects);
10762 if (selectRects.empty()) {
10763 CHECK_NULL_RETURN(overlayMod_, rect);
10764 auto richEditorOverlay = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
10765 CHECK_NULL_RETURN(richEditorOverlay, rect);
10766 auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
10767 auto caretWidth = GetCaretWidth();
10768 auto selectRect = RectF(caretOffset + paintOffset, SizeF(caretWidth, caretHeight));
10769 return selectRect.IntersectRectT(contentRect);
10770 }
10771 AdjustSelectRects(pos, selectRects);
10772 auto frontRect = selectRects.front();
10773 auto backRect = selectRects.back();
10774 float selectAreaRight = frontRect.Right();
10775 float selectAreaLeft = frontRect.Left();
10776 if (selectRects.size() != 1) {
10777 std::unordered_map<float, RectF> selectLineRect;
10778 for (const auto& box : selectRects) {
10779 auto combineLineRect = box;
10780 auto top = box.Top();
10781 if (selectLineRect.find(top) == selectLineRect.end()) {
10782 selectLineRect.insert({ top, combineLineRect });
10783 } else {
10784 combineLineRect = combineLineRect.CombineRectT(selectLineRect[top]);
10785 selectLineRect.insert({ top, combineLineRect });
10786 }
10787 selectAreaRight = std::max(selectAreaRight, combineLineRect.Right());
10788 selectAreaLeft = std::min(selectAreaLeft, combineLineRect.Left());
10789 }
10790 }
10791 RectF res = { selectAreaLeft + richTextRect_.GetX() + paintOffset.GetX(),
10792 frontRect.GetY() + richTextRect_.GetY() + paintOffset.GetY(), selectAreaRight - selectAreaLeft,
10793 backRect.Bottom() - frontRect.Top() };
10794 return res.IntersectRectT(contentRect);
10795 }
10796
10797 void RichEditorPattern::AppendSelectRect(std::vector<RectF>& selectRects)
10798 {
10799 CHECK_NULL_VOID(!selectOverlay_->IsSingleHandle());
10800 auto startPosition = textSelector_.GetTextStart();
10801 auto endPosition = textSelector_.GetTextEnd();
10802 CHECK_NULL_VOID(GetParagraphEndPosition(startPosition) != GetParagraphEndPosition(endPosition));
10803 if (paragraphs_.IsIndexAtParagraphEnd(startPosition + 1)) {
10804 selectRects.insert(selectRects.begin(), CreateNewLineRect(startPosition, false));
10805 }
10806 if (paragraphs_.IsIndexAtParagraphEnd(endPosition)) {
10807 selectRects.emplace_back(CreateNewLineRect(endPosition, true));
10808 }
10809 }
10810
10811 RectF RichEditorPattern::CreateNewLineRect(const int32_t position, const bool downStreamFirst)
10812 {
10813 auto paragraphInfo = paragraphs_.GetParagraphInfo(position);
10814 auto& style = paragraphInfo.paragraphStyle;
10815 auto height = 0.0f;
10816 auto offset = paragraphs_.ComputeCursorOffset(position, height, downStreamFirst, true);
10817 auto caretWidth = GetCaretWidth();
10818 RectF rect = RectF(offset.GetX(), offset.GetY(), 0, height);
10819 auto textAlign = TextBase::CheckTextAlignByDirection(style.align, style.direction);
10820 TextBase::UpdateSelectedBlankLineRect(rect, caretWidth, textAlign, GetTextContentRect().Width());
10821 return rect;
10822 }
10823
10824 bool RichEditorPattern::NeedShowPlaceholder() const
10825 {
10826 auto host = GetHost();
10827 CHECK_NULL_RETURN(host, false);
10828 auto layoutProperty = host->GetLayoutProperty<RichEditorLayoutProperty>();
10829 CHECK_NULL_RETURN(layoutProperty, false);
10830 return layoutProperty->HasPlaceholder() && !layoutProperty->GetPlaceholder().value().empty();
10831 }
10832
10833 bool RichEditorPattern::IsTouchInFrameArea(const PointF& touchPoint)
10834 {
10835 auto host = GetHost();
10836 CHECK_NULL_RETURN(host, false);
10837 auto viewPort = RectF(parentGlobalOffset_, frameRect_.GetSize());
10838 auto parent = host->GetAncestorNodeOfFrame(false);
10839 viewPort = GetVisibleContentRect(parent, viewPort);
10840 return viewPort.IsInRegion(touchPoint);
10841 }
10842
10843 bool RichEditorPattern::SetPlaceholder(std::vector<std::list<RefPtr<SpanItem>>>& spanItemList)
10844 {
10845 if (!spans_.empty()) {
10846 isShowPlaceholder_ = false;
10847 return false;
10848 }
10849 auto host = GetHost();
10850 CHECK_NULL_RETURN(host, false);
10851 auto layoutProperty = host->GetLayoutProperty<RichEditorLayoutProperty>();
10852 CHECK_NULL_RETURN(layoutProperty, false);
10853 if (!layoutProperty->HasPlaceholder() || layoutProperty->GetPlaceholder().value().empty()) {
10854 isShowPlaceholder_ = false;
10855 return false;
10856 }
10857 auto placeholderValue = layoutProperty->GetPlaceholder().value();
10858 auto placeholderNode = SpanNode::GetOrCreateSpanNode(ElementRegister::GetInstance()->MakeUniqueId());
10859 CHECK_NULL_RETURN(placeholderNode, false);
10860 if (layoutProperty->HasPlaceholderFontSize()) {
10861 placeholderNode->UpdateFontSize(layoutProperty->GetPlaceholderFontSize().value());
10862 }
10863 if (layoutProperty->HasPlaceholderFontWeight()) {
10864 placeholderNode->UpdateFontWeight(layoutProperty->GetPlaceholderFontWeight().value());
10865 }
10866 if (layoutProperty->HasPlaceholderFontFamily()) {
10867 placeholderNode->UpdateFontFamily(layoutProperty->GetPlaceholderFontFamily().value());
10868 }
10869 if (layoutProperty->HasPlaceholderItalicFontStyle()) {
10870 placeholderNode->UpdateItalicFontStyle(layoutProperty->GetPlaceholderItalicFontStyle().value());
10871 }
10872 if (layoutProperty->HasPlaceholderTextColor()) {
10873 placeholderNode->UpdateTextColorWithoutCheck(layoutProperty->GetPlaceholderTextColor().value());
10874 } else {
10875 auto theme = GetTheme<RichEditorTheme>();
10876 placeholderNode->UpdateTextColorWithoutCheck(theme ? theme->GetPlaceholderColor() : Color());
10877 }
10878
10879 auto spanItem = placeholderNode->GetSpanItem();
10880 CHECK_NULL_RETURN(spanItem, false);
10881 spanItem->content = placeholderValue;
10882 spanItemList.clear();
10883 spanItemList.push_back({ { {spanItem} } });
10884 isShowPlaceholder_ = true;
10885 return true;
10886 }
10887
10888 std::string RichEditorPattern::GetPlaceHolder() const
10889 {
10890 auto host = GetHost();
10891 CHECK_NULL_RETURN(host, "");
10892 auto layoutProperty = host->GetLayoutProperty<RichEditorLayoutProperty>();
10893 CHECK_NULL_RETURN(layoutProperty, "");
10894 return UtfUtils::Str16ToStr8(layoutProperty->GetPlaceholderValue(u""));
10895 }
10896
10897 Color RichEditorPattern::GetCaretColor() const
10898 {
10899 if (caretColor_.has_value()) {
10900 return caretColor_.value();
10901 }
10902 auto richEditorTheme = GetTheme<RichEditorTheme>();
10903 CHECK_NULL_RETURN(richEditorTheme, SYSTEM_CARET_COLOR);
10904 return richEditorTheme->GetCaretColor();
10905 }
10906
10907 Color RichEditorPattern::GetSelectedBackgroundColor() const
10908 {
10909 Color selectedBackgroundColor;
10910 if (selectedBackgroundColor_.has_value()) {
10911 selectedBackgroundColor = selectedBackgroundColor_.value();
10912 } else {
10913 auto richEditorTheme = GetTheme<RichEditorTheme>();
10914 CHECK_NULL_RETURN(richEditorTheme, SYSTEM_SELECT_BACKGROUND_COLOR);
10915 selectedBackgroundColor = richEditorTheme->GetSelectedBackgroundColor();
10916 }
10917 // Alpha == 255 Means completely opaque
10918 if (selectedBackgroundColor.GetAlpha() == COLOR_OPAQUE) {
10919 selectedBackgroundColor = selectedBackgroundColor.ChangeOpacity(DEFAILT_OPACITY);
10920 }
10921 return selectedBackgroundColor;
10922 }
10923
10924 void RichEditorPattern::HandleOnDragDropStyledString(const RefPtr<OHOS::Ace::DragEvent>& event, bool isCopy)
10925 {
10926 CHECK_NULL_VOID(event);
10927 auto data = event->GetData();
10928 CHECK_NULL_VOID(data);
10929 auto arr = UdmfClient::GetInstance()->GetSpanStringRecord(data);
10930 if (!arr.empty()) {
10931 auto spanStr = SpanString::DecodeTlv(arr);
10932 if (!spanStr->GetSpanItems().empty()) {
10933 if (isSpanStringMode_) {
10934 HandleOnDragInsertStyledString(spanStr, isCopy);
10935 return;
10936 }
10937 AddSpanByPasteData(spanStr, TextChangeReason::DRAG);
10938 return;
10939 }
10940 }
10941
10942 auto records = UdmfClient::GetInstance()->GetPlainTextRecords(data);
10943 if (records.empty()) {
10944 return;
10945 }
10946 std::string str;
10947 for (const auto& record : records) {
10948 str += record;
10949 }
10950 if (str.empty()) {
10951 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "text is empty.");
10952 return;
10953 }
10954 if (isSpanStringMode_) {
10955 InsertValueInStyledString(UtfUtils::Str8ToStr16(str), true);
10956 } else {
10957 HandleOnDragDropTextOperation(UtfUtils::Str8ToStr16(str), isDragSponsor_, isCopy);
10958 }
10959 }
10960
10961 void RichEditorPattern::HandleOnDragDrop(const RefPtr<OHOS::Ace::DragEvent>& event, bool isCopy)
10962 {
10963 auto host = GetHost();
10964 CHECK_NULL_VOID(host);
10965 auto eventHub = host->GetOrCreateEventHub<RichEditorEventHub>();
10966 CHECK_NULL_VOID(eventHub);
10967 TextCommonEvent textCommonEvent;
10968 if (textCommonEvent.IsPreventDefault()) {
10969 CloseSelectOverlay();
10970 ResetSelection();
10971 StartTwinkling();
10972 return;
10973 }
10974 HandleOnDragDropStyledString(event, isCopy);
10975 if (textSelector_.IsValid()) {
10976 CloseSelectOverlay();
10977 ResetSelection();
10978 }
10979 auto focusHub = host->GetOrCreateFocusHub();
10980 CHECK_NULL_VOID(focusHub);
10981 if (focusHub->IsCurrentFocus()) {
10982 StartTwinkling();
10983 }
10984 afterDragSelect_ = isMouseOrTouchPad(sourceTool_);
10985 releaseInDrop_ = true;
10986 }
10987
10988 void RichEditorPattern::DeleteForward(int32_t currentPosition, int32_t length)
10989 {
10990 RichEditorDeleteValue info;
10991 info.SetOffset(currentPosition);
10992 info.SetRichEditorDeleteDirection(RichEditorDeleteDirection::FORWARD);
10993 info.SetLength(length);
10994 CalcDeleteValueObj(currentPosition, length, info);
10995 DeleteByDeleteValueInfo(info);
10996 OnReportRichEditorEvent("onDeleteComplete");
10997 }
10998
10999 int32_t RichEditorPattern::HandleOnDragDeleteForward(int32_t currentPosition)
11000 {
11001 int32_t allDelLength = 0;
11002 SelectionInfo textSelectInfo = GetSpansInfo(dragRange_.first, dragRange_.second, GetSpansMethod::ONSELECT);
11003 std::list<ResultObject> dragResultObjects = textSelectInfo.GetSelection().resultObjects;
11004 auto dragLength = dragRange_.second - dragRange_.first;
11005 UndoRedoRecord styledRecord;
11006 styledRecord.restoreBuilderSpan = true;
11007 undoManager_->RecordSelectionBefore(TextRange{ currentPosition, currentPosition });
11008 undoManager_->UpdateRecordBeforeChange(dragRange_.first, dragLength, styledRecord);
11009 for (auto ri = dragResultObjects.rbegin(); ri != dragResultObjects.rend(); ++ri) {
11010 if (SelectSpanType::TYPESPAN == ri->type || (SelectSpanType::TYPEIMAGE == ri->type && ri->valueString != u" ")) {
11011 int32_t spanStart = ri->offsetInSpan[RichEditorSpanRange::RANGESTART];
11012 int32_t spanEnd = ri->offsetInSpan[RichEditorSpanRange::RANGEEND];
11013 int32_t reStart = ri->spanPosition.spanRange[RichEditorSpanRange::RANGESTART];
11014 int32_t delStart = reStart;
11015 if (spanStart > 0) {
11016 delStart += spanStart;
11017 }
11018 int32_t delLength = spanEnd - spanStart;
11019 DeleteForward(delStart, delLength);
11020 allDelLength += delLength;
11021 }
11022 }
11023 undoManager_->RecordOperationAfterChange(dragRange_.first, dragLength - allDelLength, styledRecord);
11024 return allDelLength;
11025 }
11026
11027 void RichEditorPattern::HandleOnDragDropTextOperation(const std::u16string& insertValue, bool isDeleteSelect, bool isCopy)
11028 {
11029 insertValueLength_ = static_cast<int32_t>(insertValue.length());
11030 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "DragDropText, len=%{public}d, del=%{public}d, copy=%{public}d, dragRange="
11031 "[%{public}d,%{public}d]", insertValueLength_, isDeleteSelect, isCopy, dragRange_.first, dragRange_.second);
11032 if (!isDeleteSelect || isCopy) {
11033 InsertValueByOperationType(insertValue, OperationType::DRAG);
11034 return;
11035 }
11036 int32_t currentPosition = caretPosition_;
11037 int32_t strLength = static_cast<int32_t>(insertValue.length());
11038 OperationRecord record;
11039 record.addText = insertValue;
11040 record.beforeCaretPosition = dragRange_.first;
11041 RichEditorChangeValue changeValue(TextChangeReason::DRAG);
11042 // drag not move, do not fire contentChange
11043 if (dragRange_.first <= currentPosition && currentPosition <= dragRange_.second) {
11044 lastCaretPosition_ = dragRange_.first;
11045 return;
11046 }
11047 CHECK_NULL_VOID(BeforeChangeText(changeValue, record, RecordType::DRAG));
11048 if (currentPosition < dragRange_.first) {
11049 InsertValueByOperationType(insertValue, OperationType::DRAG);
11050 dragRange_.first += strLength;
11051 dragRange_.second += strLength;
11052 HandleOnDragDeleteForward(currentPosition);
11053 lastCaretPosition_ = currentPosition;
11054 } else if (currentPosition > dragRange_.second) {
11055 InsertValueByOperationType(insertValue, OperationType::DRAG);
11056 int32_t delLength = HandleOnDragDeleteForward(currentPosition);
11057 caretPosition_ -= delLength;
11058 lastCaretPosition_ = currentPosition - strLength;
11059 }
11060
11061 AfterContentChange(changeValue);
11062 }
11063
11064 void RichEditorPattern::UndoDrag(const OperationRecord& record)
11065 {
11066 if (!record.addText.has_value() || record.deleteCaretPosition == -1) {
11067 return;
11068 }
11069 const auto& str = record.addText.value();
11070 int32_t length = static_cast<int32_t>(str.length());
11071 DeleteForward(record.beforeCaretPosition, length);
11072
11073 lastCaretPosition_ = caretPosition_;
11074 caretPosition_ = record.deleteCaretPosition;
11075 InsertValueOperation(str, nullptr, OperationType::DEFAULT);
11076 }
11077
11078 void RichEditorPattern::RedoDrag(const OperationRecord& record)
11079 {
11080 if (!record.addText.has_value() || record.deleteCaretPosition == -1) {
11081 return;
11082 }
11083 RichEditorChangeValue changeValue(TextChangeReason::REDO);
11084 CHECK_NULL_VOID(BeforeChangeText(changeValue, record, RecordType::REDO));
11085 const auto& str = record.addText.value();
11086 int32_t length = static_cast<int32_t>(str.length());
11087 DeleteForward(record.deleteCaretPosition, length);
11088 lastCaretPosition_ = caretPosition_;
11089 caretPosition_ = record.beforeCaretPosition;
11090 InsertValueOperation(str, nullptr, OperationType::DRAG);
11091 AfterContentChange(changeValue);
11092 }
11093
11094 bool RichEditorPattern::IsEditing()
11095 {
11096 return isEditing_;
11097 }
11098
11099 void RichEditorPattern::HandleOnEditChanged(bool isEditing)
11100 {
11101 CHECK_NULL_VOID(isEditing_ != isEditing);
11102 auto host = GetHost();
11103 CHECK_NULL_VOID(host);
11104 auto eventHub = host->GetOrCreateEventHub<RichEditorEventHub>();
11105 CHECK_NULL_VOID(eventHub);
11106
11107 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "editState->%{public}d", isEditing);
11108 isEditing_ = isEditing;
11109 eventHub->FireOnEditingChange(isEditing);
11110
11111 if (CanStartAITask()) {
11112 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "leave edit state, start AI task");
11113 dataDetectorAdapter_->StartAITask();
11114 } else {
11115 if (isEditing) {
11116 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "enter edit state, reset previewLongPress_");
11117 previewLongPress_ = false;
11118 }
11119 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
11120 }
11121 }
11122
11123 void RichEditorPattern::ResetKeyboardIfNeed()
11124 {
11125 bool needToResetKeyboard = false;
11126 auto currentAction = GetTextInputActionValue(GetDefaultTextInputAction());
11127 // When the enter key type changes, the keyboard needs to be reset.
11128 if (action_ != TextInputAction::UNSPECIFIED) {
11129 needToResetKeyboard = action_ != currentAction;
11130 }
11131 action_ = currentAction;
11132 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
11133 if (needToResetKeyboard) {
11134 // if keyboard attached or keyboard is shown, pull up keyboard again
11135 if (imeShown_ || isCustomKeyboardAttached_) {
11136 if (HasFocus()) {
11137 RequestKeyboard(false, true, true);
11138 }
11139 return;
11140 }
11141 #if defined(ENABLE_STANDARD_INPUT)
11142 auto inputMethod = MiscServices::InputMethodController::GetInstance();
11143 CHECK_NULL_VOID(inputMethod);
11144 MiscServices::Configuration config;
11145 config.SetEnterKeyType(static_cast<MiscServices::EnterKeyType>(action_));
11146 config.SetTextInputType(static_cast<MiscServices::TextInputType>(keyboard_));
11147 inputMethod->OnConfigurationChange(config);
11148 #endif
11149 }
11150 #else
11151 if (needToResetKeyboard && HasConnection()) {
11152 CloseSelectOverlay();
11153 ResetSelection();
11154 CloseKeyboard(false);
11155 RequestKeyboard(false, true, true);
11156 }
11157 #endif
11158 }
11159
11160 void RichEditorPattern::OnTextInputActionUpdate(TextInputAction value) {}
11161
11162 void RichEditorPattern::PerformAction(TextInputAction action, bool forceCloseKeyboard)
11163 {
11164 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "PerformAction, action=%{public}d", action);
11165 auto host = GetHost();
11166 CHECK_NULL_VOID(host);
11167 // When the Enter key is triggered, perform a line feed operation.
11168 if (action == TextInputAction::NEW_LINE) {
11169 InsertValue(u"\n", true);
11170 }
11171 // Enter key type callback
11172 TextFieldCommonEvent event;
11173 auto eventHub = host->GetOrCreateEventHub<RichEditorEventHub>();
11174 eventHub->FireOnSubmit(static_cast<int32_t>(action), event);
11175 // If the developer wants to keep editing, editing will not stop
11176 if (event.IsKeepEditable() || action == TextInputAction::NEW_LINE) {
11177 return;
11178 }
11179 // Exit the editing state
11180 StopEditing();
11181 }
11182
11183 void RichEditorPattern::StopEditing()
11184 {
11185 CHECK_NULL_VOID(HasFocus());
11186 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "StopEditing");
11187
11188 #ifdef IOS_PLATFORM
11189 CloseKeyboard(false);
11190 #endif
11191 // In order to avoid the physical keyboard being able to type, you need to make sure that you lose focus
11192 FocusHub::LostFocusToViewRoot();
11193 }
11194
11195 TextInputAction RichEditorPattern::GetDefaultTextInputAction() const
11196 {
11197 // As with TextInput, it is a line break by default
11198 return TextInputAction::NEW_LINE;
11199 }
11200
11201 void RichEditorPattern::GetChangeSpanStyle(RichEditorChangeValue& changeValue, std::optional<TextStyle>& spanTextStyle,
11202 std::optional<struct UpdateParagraphStyle>& spanParaStyle, std::optional<std::u16string>& urlAddress,
11203 const RefPtr<SpanNode>& spanNode, int32_t spanIndex, bool useTypingParaStyle)
11204 {
11205 auto originalSpans = changeValue.GetRichEditorOriginalSpans();
11206 if (spanIndex == 0 && originalSpans.size()) {
11207 const RichEditorAbstractSpanResult& firstInfo = originalSpans.front();
11208 const RichEditorAbstractSpanResult& lastInfo = originalSpans.back();
11209 int32_t firstLength = static_cast<int32_t>(firstInfo.GetValue().length());
11210 int32_t lastLength = static_cast<int32_t>(lastInfo.GetValue().length());
11211 if (firstInfo.GetEraseLength() == firstLength && lastInfo.GetEraseLength() == lastLength) {
11212 if (spans_.size() == originalSpans.size() ||
11213 static_cast<int32_t>(spans_.size()) == (lastInfo.GetSpanIndex() + 1)) {
11214 urlAddress.reset();
11215 return; // all spanNode be deleted, set default style
11216 }
11217 spanIndex = lastInfo.GetSpanIndex() + 1;
11218 } else if (firstInfo.GetEraseLength() == firstLength) {
11219 spanIndex = lastInfo.GetSpanIndex();
11220 }
11221 auto it = spans_.begin();
11222 std::advance(it, spanIndex);
11223 if ((*it)->unicode != 0 || DynamicCast<PlaceholderSpanItem>(*it)) {
11224 return; // is not a textSpan(Image/Symbol/other)
11225 }
11226 spanTextStyle = (*it)->GetTextStyle();
11227 if (!useTypingParaStyle) {
11228 struct UpdateParagraphStyle paraStyle;
11229 paraStyle.textAlign = (*it)->textLineStyle->GetTextAlign();
11230 paraStyle.leadingMargin = (*it)->textLineStyle->GetLeadingMargin();
11231 paraStyle.wordBreak = (*it)->textLineStyle->GetWordBreak();
11232 paraStyle.lineBreakStrategy = (*it)->textLineStyle->GetLineBreakStrategy();
11233 paraStyle.paragraphSpacing = (*it)->textLineStyle->GetParagraphSpacing();
11234 paraStyle.textVerticalAlign = (*it)->textLineStyle->GetTextVerticalAlign();
11235 spanParaStyle = paraStyle;
11236 }
11237 } else if (spanNode && spanNode->GetSpanItem()) {
11238 spanTextStyle = spanNode->GetSpanItem()->GetTextStyle();
11239 if (!useTypingParaStyle) {
11240 struct UpdateParagraphStyle paraStyle;
11241 paraStyle.textAlign = spanNode->GetTextAlign();
11242 paraStyle.leadingMargin = spanNode->GetLeadingMarginValue({});
11243 paraStyle.wordBreak = spanNode->GetWordBreak();
11244 paraStyle.lineBreakStrategy = spanNode->GetLineBreakStrategy();
11245 paraStyle.paragraphSpacing = spanNode->GetParagraphSpacing();
11246 paraStyle.textVerticalAlign = spanNode->GetTextVerticalAlign();
11247 spanParaStyle = paraStyle;
11248 }
11249 }
11250 }
11251
11252 void RichEditorPattern::GetReplacedSpan(RichEditorChangeValue& changeValue, int32_t& innerPosition,
11253 const std::u16string& insertValue, int32_t textIndex, std::optional<TextStyle> textStyle,
11254 std::optional<struct UpdateParagraphStyle> paraStyle, std::optional<std::u16string> urlAddress,
11255 bool isCreate, bool fixDel)
11256 {
11257 bool useTypingParaStyle = styleManager_->UseTypingParaStyle(spans_, changeValue);
11258 IF_TRUE(useTypingParaStyle, paraStyle = styleManager_->GetTypingParagraphStyle());
11259
11260 std::u16string originalStr;
11261 int32_t originalPos = 0;
11262 RefPtr<SpanItem> spanItem = fixDel ? GetDelPartiallySpanItem(changeValue, originalStr, originalPos) : nullptr;
11263 TextInsertValueInfo info;
11264 CalcInsertValueObj(info, textIndex, isCreate);
11265 int32_t spanIndex = info.GetSpanIndex();
11266 int32_t offsetInSpan = info.GetOffsetInSpan();
11267 auto host = GetContentHost();
11268 CHECK_NULL_VOID(host);
11269 auto uiNode = host->GetChildAtIndex(spanIndex);
11270 RefPtr<SpanNode> spanNode = DynamicCast<SpanNode>(uiNode);
11271 if (!isCreate && textIndex && uiNode && uiNode->GetTag() != V2::SPAN_ETS_TAG) {
11272 spanNode = nullptr;
11273 ++spanIndex; // select/create a new span When the span is not a textSpan(Image/Symbol/other)
11274 offsetInSpan = 0;
11275 spanNode = DynamicCast<SpanNode>(host->GetChildAtIndex(spanIndex));
11276 }
11277
11278 changeValue.SetRangeAfter({ innerPosition, innerPosition + insertValue.length()});
11279 std::u16string textTemp = insertValue;
11280 if (!textStyle && !isCreate && spanNode) {
11281 if (typingStyle_ && !HasSameTypingStyle(spanNode)) {
11282 textStyle = typingTextStyle_; // create a new span When have a different typingStyle
11283 bool insertInSpan = textIndex && offsetInSpan;
11284 spanIndex = insertInSpan ? spanIndex + 1 : spanIndex;
11285 offsetInSpan = 0;
11286 } else {
11287 textTemp = spanNode->GetSpanItem()->content;
11288 textTemp.insert(offsetInSpan, insertValue);
11289 urlAddress = spanNode->GetSpanItem()->urlAddress;
11290 }
11291 }
11292
11293 auto it = textTemp.find(LINE_SEPARATOR);
11294 bool containNextLine = it != std::u16string::npos && it != textTemp.size() - 1;
11295
11296 if (textStyle || containNextLine) { // SpanNode Fission
11297 GetReplacedSpanFission(changeValue, innerPosition, textTemp, spanIndex, offsetInSpan, textStyle, paraStyle,
11298 urlAddress);
11299 } else {
11300 std::optional<TextStyle> spanTextStyle = textStyle ? textStyle : typingTextStyle_;
11301 GetChangeSpanStyle(changeValue, spanTextStyle, paraStyle, urlAddress, spanNode, spanIndex, useTypingParaStyle);
11302 CreateSpanResult(changeValue, innerPosition, spanIndex, offsetInSpan, offsetInSpan + insertValue.length(),
11303 textTemp, spanTextStyle, paraStyle, urlAddress);
11304 innerPosition += static_cast<int32_t>(insertValue.length());
11305 }
11306
11307 if (spanItem) {
11308 spanItem->content = originalStr;
11309 spanItem->position = originalPos;
11310 }
11311 }
11312
11313 void RichEditorPattern::GetReplacedSpanFission(RichEditorChangeValue& changeValue, int32_t& innerPosition,
11314 std::u16string& content, int32_t startSpanIndex, int32_t offsetInSpan, std::optional<TextStyle> textStyle,
11315 std::optional<struct UpdateParagraphStyle> paraStyle, const std::optional<std::u16string>& urlAddress)
11316 {
11317 int spanIndex = startSpanIndex;
11318 auto index = content.find(LINE_SEPARATOR);
11319 while (index != std::u16string::npos) {
11320 auto textAfter = content.substr(index + 1);
11321 if (textAfter.empty()) {
11322 break;
11323 }
11324 auto textBefore = content.substr(0, index + 1);
11325 if (offsetInSpan != static_cast<int32_t>(textBefore.length())) {
11326 CreateSpanResult(changeValue, innerPosition, spanIndex, offsetInSpan, textBefore.length(),
11327 textBefore, textStyle, paraStyle, urlAddress);
11328 innerPosition += textBefore.length() - offsetInSpan;
11329 }
11330 content = textAfter;
11331 index = content.find(LINE_SEPARATOR);
11332 offsetInSpan = 0;
11333 ++spanIndex;
11334 }
11335 CreateSpanResult(changeValue, innerPosition, spanIndex, offsetInSpan, content.length(),
11336 content, textStyle, paraStyle, urlAddress);
11337 innerPosition += static_cast<int32_t>(content.length());
11338 }
11339
11340 void RichEditorPattern::CreateSpanResult(RichEditorChangeValue& changeValue, int32_t& innerPosition, int32_t spanIndex,
11341 int32_t offsetInSpan, int32_t endInSpan, std::u16string content, std::optional<TextStyle> textStyle,
11342 std::optional<struct UpdateParagraphStyle> paraStyle, const std::optional<std::u16string>& urlAddress)
11343 {
11344 RichEditorAbstractSpanResult retInfo;
11345 if (textStyle) {
11346 SetTextStyleToRet(retInfo, *textStyle);
11347 } else {
11348 SetThemeTextStyleToRet(retInfo);
11349 }
11350 IF_TRUE(urlAddress.has_value(), retInfo.SetUrlAddress(urlAddress.value()));
11351 retInfo.SetSpanIndex(spanIndex);
11352 if (!previewTextRecord_.newPreviewContent.empty()) {
11353 retInfo.SetPreviewText(previewTextRecord_.newPreviewContent);
11354 } else {
11355 retInfo.SetValue(content);
11356 }
11357 int32_t rangStart = std::max(0, innerPosition - offsetInSpan);
11358 retInfo.SetSpanRangeStart(rangStart);
11359 retInfo.SetSpanRangeEnd(rangStart + content.length());
11360 retInfo.SetOffsetInSpan(offsetInSpan);
11361 retInfo.SetEraseLength(endInSpan - offsetInSpan);
11362 SetParaStyleToRet(retInfo, paraStyle);
11363 changeValue.SetRichEditorReplacedSpans(retInfo);
11364 }
11365
11366 void RichEditorPattern::SetTextStyleToRet(RichEditorAbstractSpanResult& retInfo, const TextStyle& textStyle)
11367 {
11368 retInfo.SetTextDecoration(textStyle.GetTextDecorationFirst());
11369 retInfo.SetFontColor(textStyle.GetTextColor().ColorToString());
11370 retInfo.SetColor(textStyle.GetTextDecorationColor().ColorToString());
11371 retInfo.SetTextDecorationStyle(textStyle.GetTextDecorationStyle());
11372 retInfo.SetLineThicknessScale(textStyle.GetLineThicknessScale());
11373 retInfo.SetFontSize(textStyle.GetFontSize().ConvertToVp());
11374 retInfo.SetFontStyle(textStyle.GetFontStyle());
11375 TextStyleResult textStyleResult;
11376 textStyleResult.lineHeight = textStyle.GetLineHeight().ConvertToVp();
11377 textStyleResult.halfLeading = textStyle.GetHalfLeading();
11378 textStyleResult.letterSpacing = textStyle.GetLetterSpacing().ConvertToVp();
11379 textStyleResult.textShadows = textStyle.GetTextShadows();
11380 textStyleResult.textBackgroundStyle = textStyle.GetTextBackgroundStyle();
11381 retInfo.SetTextStyle(textStyleResult);
11382 retInfo.SetLineHeight(textStyle.GetLineHeight().ConvertToVp());
11383 retInfo.SetHalfLeading(textStyle.GetHalfLeading());
11384 retInfo.SetLetterspacing(textStyle.GetLetterSpacing().ConvertToVp());
11385 retInfo.SetFontFeature(textStyle.GetFontFeatures());
11386 std::string fontFamilyValue;
11387 auto fontFamily = textStyle.GetFontFamilies();
11388 for (const auto& str : fontFamily) {
11389 fontFamilyValue += str;
11390 }
11391 retInfo.SetFontFamily(fontFamilyValue);
11392 retInfo.SetFontWeight((int32_t)textStyle.GetFontWeight());
11393 }
11394
11395 void RichEditorPattern::SetThemeTextStyleToRet(RichEditorAbstractSpanResult& retInfo)
11396 {
11397 auto theme = GetTheme<RichEditorTheme>();
11398 TextStyle style = theme ? theme->GetTextStyle() : TextStyle();
11399 retInfo.SetFontColor(style.GetTextColor().ColorToString());
11400 retInfo.SetFontSize(Dimension(DEFAULT_TEXT_SIZE, DimensionUnit::FP).ConvertToVp());
11401 retInfo.SetFontStyle(OHOS::Ace::FontStyle::NORMAL);
11402 retInfo.SetFontWeight(static_cast<int32_t>(FontWeight::NORMAL));
11403 retInfo.SetTextDecoration(TextDecoration::NONE);
11404 retInfo.SetColor(style.GetTextColor().ColorToString());
11405 retInfo.SetFontFamily("HarmonyOS Sans");
11406 }
11407
11408 void RichEditorPattern::SetParaStyleToRet(RichEditorAbstractSpanResult& retInfo,
11409 std::optional<struct UpdateParagraphStyle> paraStyle)
11410 {
11411 CHECK_NULL_VOID(paraStyle);
11412 TextStyleResult textStyleResult = retInfo.GetTextStyle();
11413 textStyleResult.textAlign = static_cast<int32_t>(paraStyle->textAlign.value_or(TextAlign::START));
11414 if (paraStyle->leadingMargin) {
11415 textStyleResult.leadingMarginSize[0] = paraStyle->leadingMargin->size.Width().ToString();
11416 textStyleResult.leadingMarginSize[1] = paraStyle->leadingMargin->size.Height().ToString();
11417 }
11418 IF_TRUE(paraStyle->wordBreak.has_value(),
11419 textStyleResult.wordBreak = static_cast<int32_t>(paraStyle->wordBreak.value()));
11420 IF_TRUE(paraStyle->lineBreakStrategy.has_value(),
11421 textStyleResult.lineBreakStrategy = static_cast<int32_t>(paraStyle->lineBreakStrategy.value()));
11422 IF_TRUE(paraStyle->paragraphSpacing.has_value(), textStyleResult.paragraphSpacing =
11423 Dimension(paraStyle->paragraphSpacing.value().ConvertToFp(), DimensionUnit::FP));
11424 IF_TRUE(paraStyle->textVerticalAlign.has_value(), textStyleResult.textVerticalAlign =
11425 static_cast<int32_t>(paraStyle->textVerticalAlign.value()));
11426 retInfo.SetTextStyle(textStyleResult);
11427 }
11428
11429 void RichEditorPattern::CalcInsertValueObj(TextInsertValueInfo& info, int textIndex, bool isCreate)
11430 {
11431 if (spans_.empty()) {
11432 info.SetSpanIndex(0);
11433 info.SetOffsetInSpan(0);
11434 return;
11435 }
11436 auto it = std::find_if(
11437 spans_.begin(), spans_.end(), [caretPosition = textIndex](const RefPtr<SpanItem>& spanItem) {
11438 auto spanLength = static_cast<int32_t>(spanItem->content.length());
11439 if (spanLength == 0) {
11440 return spanItem->position == caretPosition;
11441 }
11442 return (spanItem->position - spanLength <= caretPosition) && (caretPosition <= spanItem->position);
11443 });
11444 if (it == spans_.end()) {
11445 info.SetSpanIndex(static_cast<int32_t>(spans_.size()) - 1);
11446 info.SetOffsetInSpan((*spans_.rbegin())->content.length());
11447 return;
11448 }
11449 if (textIndex && isCreate) {
11450 info.SetSpanIndex(std::distance(spans_.begin(), it) + 1);
11451 info.SetOffsetInSpan(0);
11452 return;
11453 }
11454 if ((*it)->content.back() == '\n' && (*it)->position == textIndex) { // next line/span begin
11455 info.SetSpanIndex(std::distance(spans_.begin(), it) + 1);
11456 info.SetOffsetInSpan(0);
11457 } else {
11458 info.SetSpanIndex(std::distance(spans_.begin(), it));
11459 int32_t spanStart = (*it)->position - static_cast<int32_t>((*it)->content.length());
11460 info.SetOffsetInSpan(textIndex - spanStart);
11461 }
11462 }
11463
11464 void RichEditorPattern::GetDeletedSpan(RichEditorChangeValue& changeValue, int32_t& innerPosition,
11465 int32_t length, RichEditorDeleteDirection direction)
11466 {
11467 RichEditorDeleteValue info;
11468 if (!textSelector_.SelectNothing()) {
11469 length = textSelector_.GetTextEnd() - textSelector_.GetTextStart();
11470 innerPosition = std::min(textSelector_.GetStart(), textSelector_.GetEnd());
11471 } else if (previewTextRecord_.NeedReplace()) {
11472 length = previewTextRecord_.replacedRange.end - previewTextRecord_.replacedRange.start;
11473 innerPosition = previewTextRecord_.replacedRange.start;
11474 } else {
11475 int32_t emojiLength = CalculateDeleteLength(length, (direction == RichEditorDeleteDirection::BACKWARD));
11476 if (direction == RichEditorDeleteDirection::BACKWARD) {
11477 innerPosition -= emojiLength;
11478 }
11479 if (length < emojiLength) {
11480 length = emojiLength;
11481 }
11482 }
11483
11484 info.SetOffset(innerPosition);
11485 info.SetRichEditorDeleteDirection(direction);
11486 info.SetLength(length);
11487 if (!spans_.empty()) {
11488 CalcDeleteValueObj(innerPosition, length, info);
11489 }
11490 if (!spans_.empty() || isAPI14Plus) {
11491 changeValue.SetRangeBefore({ innerPosition, innerPosition + length });
11492 changeValue.SetRangeAfter({ innerPosition, innerPosition });
11493 }
11494 const std::list<RichEditorAbstractSpanResult>& resultList = info.GetRichEditorDeleteSpans();
11495 for (auto& it : resultList) {
11496 if (it.GetType() == SpanResultType::TEXT) {
11497 changeValue.SetRichEditorOriginalSpans(it);
11498 } else if (it.GetType() == SpanResultType::SYMBOL && textSelector_.SelectNothing() &&
11499 previewTextRecord_.previewContent.empty()) {
11500 int32_t symbolStart = it.GetSpanRangeStart();
11501 changeValue.SetRichEditorOriginalSpans(it);
11502 changeValue.SetRangeBefore({ symbolStart, symbolStart + SYMBOL_SPAN_LENGTH });
11503 changeValue.SetRangeAfter({ symbolStart, symbolStart });
11504 }
11505 }
11506 }
11507
11508 RefPtr<SpanItem> RichEditorPattern::GetDelPartiallySpanItem(
11509 RichEditorChangeValue& changeValue, std::u16string& originalStr, int32_t& originalPos)
11510 {
11511 RefPtr<SpanItem> retItem = nullptr;
11512 if (changeValue.GetRichEditorOriginalSpans().size() == 0) {
11513 return retItem;
11514 }
11515 std::u16string textTemp;
11516 auto originalSpans = changeValue.GetRichEditorOriginalSpans();
11517 const RichEditorAbstractSpanResult& firstResult = originalSpans.front();
11518 auto it = spans_.begin();
11519 std::advance(it, firstResult.GetSpanIndex());
11520 retItem = *it;
11521 originalStr = retItem->content;
11522 originalPos = retItem->position;
11523 retItem->content.erase(firstResult.OffsetInSpan(), firstResult.GetEraseLength());
11524 retItem->position -= firstResult.GetEraseLength();
11525 if (firstResult.GetEraseLength() != static_cast<int32_t>(firstResult.GetValue().length())) {
11526 return retItem;
11527 }
11528
11529 if (firstResult.GetSpanIndex() == 0) {
11530 int32_t spanIndex = 0;
11531 for (auto& orgIt : originalSpans) {
11532 spanIndex = orgIt.GetSpanIndex();
11533 if (orgIt.GetEraseLength() != static_cast<int32_t>(orgIt.GetValue().length())) {
11534 // find the deleted(Partially) spanItem
11535 auto findIt = spans_.begin();
11536 std::advance(findIt, spanIndex);
11537 textTemp = (*findIt)->content;
11538 textTemp.erase(orgIt.OffsetInSpan(), orgIt.GetEraseLength());
11539 retItem->content = textTemp;
11540 retItem->position = textTemp.length();
11541 return retItem;
11542 }
11543 }
11544 if (spans_.size() == originalSpans.size() || static_cast<int32_t>(spans_.size()) == (spanIndex + 1)) {
11545 return retItem; // all spanNode be deleted
11546 }
11547 auto nextIt = spans_.begin();
11548 std::advance(nextIt, spanIndex + 1);
11549 if ((*nextIt)->unicode != 0 || DynamicCast<PlaceholderSpanItem>(*nextIt)) {
11550 return retItem; // is not a textSpan(Image/Symbol/other)
11551 }
11552 retItem->content = (*nextIt)->content;
11553 retItem->position = static_cast<int32_t>(retItem->content.length());
11554 }
11555 return retItem;
11556 }
11557
11558 bool RichEditorPattern::BeforeChangeText(RichEditorChangeValue& changeValue, const TextSpanOptions& options)
11559 {
11560 auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
11561 CHECK_NULL_RETURN(eventHub, false);
11562 if (!eventHub->HasOnWillChange() && !eventHub->HasOnDidChange()) {
11563 return true;
11564 }
11565 int32_t innerPosition = caretPosition_;
11566
11567 // AddTextSpan
11568 std::optional<TextStyle> textStyle = std::nullopt;
11569 if (options.style.has_value()) {
11570 textStyle = options.style;
11571 }
11572 if (options.offset.has_value()) {
11573 if (spans_.empty() || options.offset.value() < 0) {
11574 innerPosition = 0;
11575 } else if (options.offset.value() > GetTextContentLength()) {
11576 innerPosition = GetTextContentLength();
11577 } else {
11578 innerPosition = options.offset.value();
11579 }
11580 } else {
11581 innerPosition = GetTextContentLength();
11582 }
11583 // only add, do not delete
11584 changeValue.SetRangeBefore({ innerPosition, innerPosition });
11585 GetReplacedSpan(changeValue, innerPosition, options.value, innerPosition, textStyle, options.paraStyle,
11586 options.urlAddress, true);
11587
11588 CHECK_NULL_RETURN(eventHub->HasOnWillChange(), true);
11589 auto ret = eventHub->FireOnWillChange(changeValue);
11590 return ret;
11591 }
11592
11593 bool RichEditorPattern::BeforeAddImage(RichEditorChangeValue& changeValue,
11594 const ImageSpanOptions& options, int32_t insertIndex)
11595 {
11596 auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
11597 CHECK_NULL_RETURN(eventHub, false);
11598 if (!eventHub->HasOnWillChange() && !eventHub->HasOnDidChange()) {
11599 return true;
11600 }
11601 changeValue.SetRangeBefore({ insertIndex, insertIndex });
11602 changeValue.SetRangeAfter({ insertIndex, insertIndex + 1});
11603 RichEditorAbstractSpanResult retInfo;
11604 TextInsertValueInfo info;
11605 CalcInsertValueObj(info, insertIndex, true);
11606 int32_t spanIndex = info.GetSpanIndex();
11607 retInfo.SetSpanIndex(spanIndex);
11608 UpdateImageSpanResultByOptions(retInfo, options);
11609 retInfo.SetOffsetInSpan(0);
11610 retInfo.SetEraseLength(1);
11611 retInfo.SetSpanRangeStart(insertIndex);
11612 retInfo.SetSpanRangeEnd(insertIndex + 1);
11613 changeValue.SetRichEditorReplacedImageSpans(retInfo);
11614 CHECK_NULL_RETURN(eventHub->HasOnWillChange(), true);
11615 return eventHub->FireOnWillChange(changeValue);
11616 }
11617
11618 void RichEditorPattern::UpdateImageSpanResultByOptions(RichEditorAbstractSpanResult& retInfo,
11619 const ImageSpanOptions& options)
11620 {
11621 retInfo.SetSpanType(SpanResultType::IMAGE);
11622 if (options.image) {
11623 retInfo.SetValueResourceStr(*options.image);
11624 }
11625 if (options.imagePixelMap) {
11626 retInfo.SetValuePixelMap(*options.imagePixelMap);
11627 }
11628 if (options.imageAttribute.has_value()) {
11629 auto imgAttr = options.imageAttribute.value();
11630 if (imgAttr.size.has_value()) {
11631 retInfo.SetSizeWidth(imgAttr.size->width.value_or(CalcDimension()).ConvertToPx());
11632 retInfo.SetSizeHeight(imgAttr.size->height.value_or(CalcDimension()).ConvertToPx());
11633 }
11634 if (imgAttr.verticalAlign.has_value()) {
11635 retInfo.SetVerticalAlign(imgAttr.verticalAlign.value());
11636 }
11637 if (imgAttr.objectFit.has_value()) {
11638 retInfo.SetImageFit(imgAttr.objectFit.value());
11639 }
11640 if (imgAttr.marginProp.has_value()) {
11641 retInfo.SetMargin(imgAttr.marginProp.value().ToString());
11642 }
11643 if (imgAttr.borderRadius.has_value()) {
11644 retInfo.SetBorderRadius(imgAttr.borderRadius.value().ToString());
11645 }
11646 }
11647 }
11648
11649 bool RichEditorPattern::BeforeSpansChange(const UndoRedoRecord& record, bool isUndo)
11650 {
11651 auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
11652 CHECK_NULL_RETURN(eventHub, false);
11653 CHECK_NULL_RETURN(eventHub->HasOnWillChange(), true);
11654 RichEditorChangeValue changeValue(isUndo ? TextChangeReason::UNDO : TextChangeReason::REDO);
11655 auto rangeBefore = isUndo ? record.rangeAfter : record.rangeBefore;
11656 changeValue.SetRangeBefore(rangeBefore);
11657 auto rangeAfter = isUndo ? record.rangeBefore : record.rangeAfter;
11658 changeValue.SetRangeAfter(rangeAfter);
11659 auto optionsList = isUndo ? record.optionsListBefore : record.optionsListAfter;
11660 TextInsertValueInfo info;
11661 CalcInsertValueObj(info, rangeBefore.start, true);
11662 int32_t spanIndex = info.GetSpanIndex();
11663 for (const auto& option : optionsList.value_or(OptionsList{})) {
11664 std::visit([weak = WeakClaim(this), &changeValue, &spanIndex](const auto& specificOption) {
11665 auto pattern = weak.Upgrade();
11666 CHECK_NULL_VOID(pattern);
11667 using T = std::decay_t<decltype(specificOption)>;
11668 if constexpr (std::is_same_v<T, ImageSpanOptions>) {
11669 auto retInfo = pattern->GetResultByImageSpanOptions(specificOption, spanIndex);
11670 changeValue.SetRichEditorReplacedImageSpans(retInfo);
11671 } else if constexpr (std::is_same_v<T, TextSpanOptions>) {
11672 auto retInfo = pattern->GetResultByTextSpanOptions(specificOption, spanIndex);
11673 changeValue.SetRichEditorReplacedSpans(retInfo);
11674 } else if constexpr (std::is_same_v<T, SymbolSpanOptions>) {
11675 auto retInfo = pattern->GetResultBySymbolSpanOptions(specificOption, spanIndex);
11676 changeValue.SetRichEditorReplacedSymbolSpans(retInfo);
11677 } else if constexpr (std::is_same_v<T, BuilderSpanOptions>) {
11678 auto textOptions = TextSpanOptions{ .offset = specificOption.offset, .value = u" " };
11679 auto retInfo = pattern->GetResultByTextSpanOptions(textOptions, spanIndex);
11680 changeValue.SetRichEditorReplacedSpans(retInfo);
11681 }
11682 spanIndex++;
11683 }, option);
11684 }
11685 return eventHub->FireOnWillChange(changeValue);
11686 }
11687
11688 RichEditorAbstractSpanResult RichEditorPattern::GetResultByImageSpanOptions(const ImageSpanOptions& options,
11689 int32_t spanIndex)
11690 {
11691 RichEditorAbstractSpanResult retInfo;
11692 auto spanRangeStart = options.offset.value_or(GetTextContentLength());
11693 UpdateSpanResultRange(retInfo, spanIndex, { spanRangeStart, spanRangeStart + IMAGE_SPAN_LENGTH });
11694 UpdateImageSpanResultByOptions(retInfo, options);
11695 return retInfo;
11696 }
11697
11698 void RichEditorPattern::UpdateSpanResultRange(RichEditorAbstractSpanResult& retInfo, int32_t spanIndex,
11699 TextRange spanRange)
11700 {
11701 retInfo.SetSpanIndex(spanIndex);
11702 retInfo.SetOffsetInSpan(0);
11703 retInfo.SetEraseLength(spanRange.GetLength());
11704 retInfo.SetSpanRangeStart(spanRange.start);
11705 retInfo.SetSpanRangeEnd(spanRange.end);
11706 }
11707
11708 RichEditorAbstractSpanResult RichEditorPattern::GetResultByTextSpanOptions(const TextSpanOptions& options,
11709 int32_t spanIndex)
11710 {
11711 RichEditorAbstractSpanResult retInfo;
11712 auto spanRangeStart = options.offset.value_or(GetTextContentLength());
11713 UpdateSpanResultRange(retInfo, spanIndex, { spanRangeStart, spanRangeStart + options.value.length() });
11714 UpdateTextSpanResultByOptions(retInfo, options);
11715 return retInfo;
11716 }
11717
11718 void RichEditorPattern::UpdateTextSpanResultByOptions(RichEditorAbstractSpanResult& retInfo,
11719 const TextSpanOptions& options)
11720 {
11721 if (options.style.has_value()) {
11722 SetTextStyleToRet(retInfo, options.style.value());
11723 } else {
11724 SetThemeTextStyleToRet(retInfo);
11725 }
11726 auto urlAddress = options.urlAddress;
11727 IF_TRUE(urlAddress.has_value(), retInfo.SetUrlAddress(urlAddress.value()));
11728 retInfo.SetValue(options.value);
11729 SetParaStyleToRet(retInfo, options.paraStyle);
11730 }
11731
11732 RichEditorAbstractSpanResult RichEditorPattern::GetResultBySymbolSpanOptions(const SymbolSpanOptions& options,
11733 int32_t spanIndex)
11734 {
11735 RichEditorAbstractSpanResult retInfo;
11736 auto spanRangeStart = options.offset.value_or(GetTextContentLength());
11737 UpdateSpanResultRange(retInfo, spanIndex, { spanRangeStart, spanRangeStart + SYMBOL_SPAN_LENGTH });
11738 UpdateSymbolSpanResultByOptions(retInfo, options);
11739 return retInfo;
11740 }
11741
11742 void RichEditorPattern::UpdateSymbolSpanResultByOptions(RichEditorAbstractSpanResult& retInfo,
11743 const SymbolSpanOptions& options)
11744 {
11745 retInfo.SetValueString(std::to_string(options.symbolId));
11746 retInfo.SetSpanType(SpanResultType::SYMBOL);
11747 TextStyle style = options.style.value_or(TextStyle());
11748 retInfo.SetSymbolSpanStyle(SymbolSpanStyle(style));
11749 retInfo.SetValueResource(options.resourceObject);
11750 }
11751
11752 void RichEditorPattern::AfterSpansChange(const UndoRedoRecord& record, bool isUndo)
11753 {
11754 auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
11755 CHECK_NULL_VOID(eventHub);
11756 CHECK_NULL_VOID(eventHub->HasOnDidChange());
11757 RichEditorChangeValue changeValue(isUndo ? TextChangeReason::UNDO : TextChangeReason::REDO);
11758 auto rangeBefore = isUndo ? record.rangeAfter : record.rangeBefore;
11759 changeValue.SetRangeBefore(rangeBefore);
11760 auto rangeAfter = isUndo ? record.rangeBefore : record.rangeAfter;
11761 changeValue.SetRangeAfter(rangeAfter);
11762 eventHub->FireOnDidChange(changeValue);
11763 ReportAfterContentChangeEvent();
11764 }
11765
11766 void RichEditorPattern::FixMoveDownChange(RichEditorChangeValue& changeValue, int32_t delLength)
11767 {
11768 int32_t delSpanCount = 0;
11769 for (auto& it : changeValue.GetRichEditorOriginalSpans()) {
11770 if (it.GetEraseLength() == static_cast<int32_t>(it.GetValue().length())) {
11771 ++delSpanCount;
11772 }
11773 }
11774 for (auto& it : const_cast<std::vector<RichEditorAbstractSpanResult>&>(changeValue.GetRichEditorReplacedSpans())) {
11775 if (delSpanCount) {
11776 it.SetSpanIndex(it.GetSpanIndex() - delSpanCount);
11777 }
11778 }
11779 }
11780
11781 void RichEditorPattern::BeforeUndo(
11782 RichEditorChangeValue& changeValue, int32_t& innerPosition, const OperationRecord& record)
11783 {
11784 innerPosition = record.afterCaretPosition;
11785 if (record.addText.has_value() && record.deleteCaretPosition != -1) { // UndoDrag
11786 GetDeletedSpan(changeValue, innerPosition, record.addText.value_or(u"").length(),
11787 RichEditorDeleteDirection::FORWARD);
11788 innerPosition = record.deleteCaretPosition;
11789 GetReplacedSpan(changeValue, innerPosition, record.addText.value(), innerPosition, std::nullopt, std::nullopt);
11790 } else if (record.addText.has_value() && record.deleteText.has_value()) {
11791 GetDeletedSpan(changeValue, innerPosition, record.addText.value_or(u"").length(),
11792 RichEditorDeleteDirection::BACKWARD);
11793 GetReplacedSpan(
11794 changeValue, innerPosition, record.deleteText.value(), innerPosition, std::nullopt, std::nullopt);
11795 } else if (record.deleteText.has_value()) {
11796 GetReplacedSpan(
11797 changeValue, innerPosition, record.deleteText.value(), innerPosition, std::nullopt, std::nullopt);
11798 } else if (record.addText.has_value()) {
11799 GetDeletedSpan(changeValue, innerPosition, record.addText.value_or(u"").length(),
11800 RichEditorDeleteDirection::BACKWARD);
11801 }
11802 }
11803
11804 void RichEditorPattern::BeforeRedo(
11805 RichEditorChangeValue& changeValue, int32_t& innerPosition, const OperationRecord& record)
11806 {
11807 innerPosition = record.beforeCaretPosition - record.addText.value_or(u"").length();
11808 if (record.addText.has_value() && record.deleteCaretPosition != -1) { // RedoDrag
11809 innerPosition = record.deleteCaretPosition;
11810 GetDeletedSpan(changeValue, innerPosition, record.addText.value_or(u"").length(),
11811 RichEditorDeleteDirection::FORWARD);
11812 innerPosition = record.beforeCaretPosition;
11813 GetReplacedSpan(changeValue, innerPosition, record.addText.value(), innerPosition, std::nullopt, std::nullopt);
11814 } else if (record.addText.has_value() && record.deleteText.has_value()) {
11815 GetDeletedSpan(changeValue, innerPosition, record.deleteText.value_or(u"").length(),
11816 RichEditorDeleteDirection::FORWARD);
11817 GetReplacedSpan(changeValue, innerPosition, record.addText.value(), innerPosition, std::nullopt, std::nullopt);
11818 } else if (record.deleteText.has_value()) {
11819 innerPosition = record.beforeCaretPosition - record.deleteText.value_or(u"").length();
11820 GetDeletedSpan(changeValue, innerPosition, record.deleteText.value_or(u"").length(),
11821 RichEditorDeleteDirection::FORWARD);
11822 } else if (record.addText.has_value()) {
11823 innerPosition = std::min(innerPosition, record.afterCaretPosition);
11824 int32_t innerAddPosition = record.afterCaretPosition - static_cast<int32_t>(record.addText.value().length());
11825 if (changeValue.GetRichEditorOriginalSpans().empty()) {
11826 innerPosition = caretPosition_;
11827 innerAddPosition = caretPosition_;
11828 }
11829 GetReplacedSpan(changeValue, innerAddPosition, record.addText.value(), innerPosition,
11830 std::nullopt, std::nullopt);
11831 }
11832 }
11833
11834 void RichEditorPattern::BeforeDrag(
11835 RichEditorChangeValue& changeValue, int32_t& innerPosition, const OperationRecord& record)
11836 {
11837 std::u16string recordAddText = record.addText.value_or(u"");
11838 int length = recordAddText.length();
11839 int32_t nowPosition = innerPosition;
11840 std::optional<TextStyle> style = std::nullopt;
11841 if (typingStyle_.has_value() && typingTextStyle_.has_value()) {
11842 style = typingTextStyle_.value();
11843 }
11844 if (!isDragSponsor_) { // drag from outside
11845 GetReplacedSpan(
11846 changeValue, innerPosition, recordAddText, innerPosition, style, std::nullopt, std::nullopt, true, false);
11847 } else if (nowPosition < record.beforeCaretPosition + length) { // move up
11848 innerPosition = record.beforeCaretPosition;
11849 GetDeletedSpan(changeValue, innerPosition, length, RichEditorDeleteDirection::FORWARD);
11850 innerPosition = nowPosition;
11851 GetReplacedSpan(
11852 changeValue, innerPosition, recordAddText, nowPosition, style, std::nullopt, std::nullopt, true, false);
11853 } else { // move down
11854 innerPosition = record.beforeCaretPosition;
11855 GetDeletedSpan(changeValue, innerPosition, length, RichEditorDeleteDirection::FORWARD);
11856 innerPosition = nowPosition - length;
11857 GetReplacedSpan(
11858 changeValue, innerPosition, recordAddText, nowPosition, style, std::nullopt, std::nullopt, true, false);
11859 FixMoveDownChange(changeValue, length);
11860 }
11861 }
11862
11863 bool RichEditorPattern::BeforeChangeText(
11864 RichEditorChangeValue& changeValue, const OperationRecord& record, RecordType type, int32_t delLength)
11865 {
11866 int32_t innerPosition = caretPosition_;
11867 auto eventHub = GetOrCreateEventHub<RichEditorEventHub>();
11868 CHECK_NULL_RETURN(eventHub, false);
11869 if (!eventHub->HasOnWillChange() && !eventHub->HasOnDidChange()) {
11870 return true;
11871 }
11872
11873 if (RecordType::INSERT == type) {
11874 if (textSelector_.IsValid()) {
11875 GetDeletedSpan(changeValue, innerPosition,
11876 static_cast<int32_t>(textSelector_.GetTextEnd() - textSelector_.GetTextStart()));
11877 } else if (previewTextRecord_.NeedReplace()) {
11878 GetDeletedSpan(changeValue, innerPosition,
11879 static_cast<int32_t>(previewTextRecord_.replacedRange.end - previewTextRecord_.replacedRange.start));
11880 }
11881 if (record.addText.has_value()) {
11882 GetReplacedSpan(changeValue, innerPosition, record.addText.value(), innerPosition, std::nullopt, std::nullopt);
11883 }
11884 }
11885 if (RecordType::DEL_FORWARD == type) {
11886 innerPosition = record.beforeCaretPosition;
11887 GetDeletedSpan(changeValue, innerPosition, delLength, RichEditorDeleteDirection::FORWARD);
11888 }
11889 if (RecordType::DEL_BACKWARD == type) {
11890 innerPosition = record.beforeCaretPosition;
11891 GetDeletedSpan(changeValue, innerPosition, delLength, RichEditorDeleteDirection::BACKWARD);
11892 }
11893 if (RecordType::UNDO == type) {
11894 BeforeUndo(changeValue, innerPosition, record);
11895 }
11896 if (RecordType::REDO == type) {
11897 BeforeRedo(changeValue, innerPosition, record);
11898 }
11899 if (RecordType::DRAG == type) {
11900 BeforeDrag(changeValue, innerPosition, record);
11901 }
11902 bool isDelete = RecordType::DEL_FORWARD == type || RecordType::DEL_BACKWARD == type;
11903 if (changeValue.GetRichEditorOriginalSpans().empty() && !isDelete) {
11904 // only add, do not delete
11905 changeValue.SetRangeBefore({ caretPosition_, caretPosition_ });
11906 }
11907
11908 CHECK_NULL_RETURN(eventHub->HasOnWillChange(), true);
11909 auto ret = eventHub->FireOnWillChange(changeValue);
11910 return ret;
11911 }
11912
11913 OffsetF RichEditorPattern::GetTextPaintOffset() const
11914 {
11915 if (selectOverlay_->HasRenderTransform()) {
11916 return selectOverlay_->GetPaintRectOffsetWithTransform();
11917 }
11918 return GetPaintRectGlobalOffset();
11919 }
11920
11921 OffsetF RichEditorPattern::GetPaintRectGlobalOffset() const
11922 {
11923 auto host = GetHost();
11924 CHECK_NULL_RETURN(host, OffsetF(0.0f, 0.0f));
11925 auto pipeline = host->GetContextRefPtr();
11926 CHECK_NULL_RETURN(pipeline, OffsetF(0.0f, 0.0f));
11927 auto rootOffset = pipeline->GetRootRect().GetOffset();
11928 auto textPaintOffset = host->GetPaintRectOffsetNG(false, true);
11929 return textPaintOffset - rootOffset;
11930 }
11931
11932 void RichEditorPattern::HandlePointWithTransform(OffsetF& point)
11933 {
11934 auto host = GetHost();
11935 CHECK_NULL_VOID(host);
11936 PointF convertPoint = { point.GetX(), point.GetY() };
11937 auto parent = host;
11938 while (parent && (parent->GetTag() != V2::WINDOW_SCENE_ETS_TAG)) {
11939 auto renderContext = parent->GetRenderContext();
11940 CHECK_NULL_VOID(renderContext);
11941 auto paintOffset = renderContext->GetPaintRectWithoutTransform().GetOffset();
11942 if (parent != host) {
11943 convertPoint = convertPoint + paintOffset;
11944 }
11945 renderContext->GetPointTransform(convertPoint);
11946 parent = parent->GetAncestorNodeOfFrame(true);
11947 }
11948 point = { convertPoint.GetX(), convertPoint.GetY() };
11949 }
11950
11951 CaretOffsetInfo RichEditorPattern::GetCaretOffsetInfoByPosition(int32_t position)
11952 {
11953 CaretOffsetInfo caretInfo;
11954 int32_t currrentPosition = 0;
11955 if (position == -1) {
11956 currrentPosition = caretPosition_;
11957 } else {
11958 currrentPosition = position;
11959 }
11960 caretInfo.caretOffsetUp = CalcCursorOffsetByPosition(currrentPosition, caretInfo.caretHeightUp, false, false);
11961 caretInfo.caretOffsetDown = CalcCursorOffsetByPosition(currrentPosition, caretInfo.caretHeightDown, true, false);
11962 caretInfo.caretOffsetLine = CalcCursorOffsetByPosition(currrentPosition, caretInfo.caretHeightLine);
11963 return caretInfo;
11964 }
11965
11966 void RichEditorPattern::CalcLineSidesIndexByPosition(int32_t& startIndex, int32_t& endIndex)
11967 {
11968 Offset textStartOffset;
11969 Offset textEndOffset;
11970
11971 CHECK_NULL_VOID(overlayMod_);
11972 auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
11973 auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize());
11974 float textOffsetY = richTextRect_.GetY() - (minDet / 2.0);
11975 auto currentCaretOffsetOverlay = overlayMod->GetCaretOffset();
11976 textStartOffset = Offset(0, currentCaretOffsetOverlay.GetY() - textOffsetY);
11977 textEndOffset = Offset(richTextRect_.Width(), currentCaretOffsetOverlay.GetY() - textOffsetY);
11978 startIndex = paragraphs_.GetIndex(textStartOffset);
11979 endIndex = paragraphs_.GetIndex(textEndOffset);
11980 }
11981
11982 RectF RichEditorPattern::CalcLineInfoByPosition()
11983 {
11984 int32_t startIndex = 0;
11985 int32_t endIndex = 0;
11986
11987 CalcLineSidesIndexByPosition(startIndex, endIndex);
11988 if (startIndex == endIndex) {
11989 endIndex += 1;
11990 }
11991 auto selectedRects = paragraphs_.GetRects(startIndex, endIndex);
11992 CHECK_NULL_RETURN(selectedRects.size(), {});
11993 return selectedRects.front();
11994 }
11995
11996 int32_t RichEditorPattern::CalcMoveUpPos(float& leadingMarginOffset)
11997 {
11998 int32_t caretPosition;
11999 CaretOffsetInfo caretInfo;
12000 float textOffsetDownY = 0.0f;
12001 int32_t startIndex = 0;
12002 int32_t endIndex = 0;
12003 Offset textOffset;
12004
12005 caretInfo = GetCaretOffsetInfoByPosition();
12006 auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize());
12007 CHECK_NULL_RETURN(overlayMod_, 0);
12008 auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
12009 auto caretOffsetOverlay = overlayMod->GetCaretOffset();
12010 auto caretOffsetWidth = overlayMod->GetCaretWidth();
12011 bool cursorNotAtLineStart = NearEqual(caretOffsetOverlay.GetX(), caretInfo.caretOffsetUp.GetX(), caretOffsetWidth);
12012 float textOffsetY = richTextRect_.GetY() + (minDet / 2.0); // 2.0 Cursor one half at the center position
12013 CalcLineSidesIndexByPosition(startIndex, endIndex);
12014 auto rectLineInfo = CalcLineInfoByPosition();
12015 leadingMarginOffset = rectLineInfo.GetX();
12016 if (cursorNotAtLineStart) {
12017 textOffsetDownY = caretInfo.caretOffsetLine.GetY() - textOffsetY;
12018 // lm mean leadingMargin abbr
12019 auto lmSizeOffset = (endIndex - startIndex <= 1 && NearEqual(rectLineInfo.Width(), richTextRect_.Width()))
12020 ? rectLineInfo.GetX()
12021 : 0;
12022 textOffset = Offset(caretInfo.caretOffsetLine.GetX() - richTextRect_.GetX() + lmSizeOffset, textOffsetDownY);
12023 } else {
12024 textOffsetDownY = caretInfo.caretOffsetLine.GetY() + caretInfo.caretHeightLine - textOffsetY;
12025 textOffset = Offset(caretOffsetOverlay.GetX() - richTextRect_.GetX(), textOffsetDownY);
12026 }
12027 caretPosition = paragraphs_.GetIndex(textOffset);
12028 return caretPosition;
12029 }
12030
12031 int32_t RichEditorPattern::CalcMoveDownPos(float& leadingMarginOffset)
12032 {
12033 CaretOffsetInfo caretInfo;
12034 float textOffsetDownY = 0.0f;
12035 Offset textOffset;
12036 int32_t caretPositionEnd;
12037
12038 caretInfo = GetCaretOffsetInfoByPosition();
12039 auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize());
12040 CHECK_NULL_RETURN(overlayMod_, 0);
12041 auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
12042 auto caretOffsetOverlay = overlayMod->GetCaretOffset();
12043 auto caretOffsetWidth = overlayMod->GetCaretWidth();
12044 float textOffsetX = richTextRect_.GetX();
12045 float textOffsetY = richTextRect_.GetY() - (minDet / 2.0);
12046 bool cursorNotAtLineStart = NearEqual(caretOffsetOverlay.GetX(), caretInfo.caretOffsetUp.GetX(), caretOffsetWidth);
12047 // midle or enter
12048 auto rectLineInfo = CalcLineInfoByPosition();
12049 leadingMarginOffset = rectLineInfo.GetX();
12050 auto lineHeightDis = rectLineInfo.Height();
12051 // midle or end, first line start position,end line end position
12052 textOffsetDownY = caretInfo.caretOffsetLine.GetY() + caretInfo.caretHeightLine - textOffsetY;
12053 float lastLineTop = 0.0f;
12054 float paragraphSpacing = 0.0f;
12055 HandleCurrentPositionParagraphInfo(lastLineTop, paragraphSpacing);
12056 if (cursorNotAtLineStart || caretPosition_ == 0) {
12057 IF_TRUE(NearEqual(std::floor(caretInfo.caretOffsetLine.GetY()), std::floor(lastLineTop)),
12058 textOffsetDownY += paragraphSpacing);
12059 textOffset = Offset(caretInfo.caretOffsetLine.GetX() - textOffsetX, textOffsetDownY);
12060 } else {
12061 IF_TRUE(NearEqual(std::floor(caretInfo.caretOffsetLine.GetY() + lineHeightDis), std::floor(lastLineTop)),
12062 textOffsetDownY += paragraphSpacing);
12063 textOffsetDownY += lineHeightDis;
12064 textOffset = Offset(caretOffsetOverlay.GetX() - textOffsetX, textOffsetDownY);
12065 }
12066 caretPositionEnd = paragraphs_.GetIndex(textOffset);
12067 return caretPositionEnd;
12068 }
12069
12070 int32_t RichEditorPattern::CalcLineBeginPosition()
12071 {
12072 float caretHeight = 0.0f;
12073 OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight, false, false);
12074 auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
12075 auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize());
12076 auto textOffsetY = caretOffset.GetY() - textPaintOffset.GetY() + (minDet / 2.0);
12077 Offset textOffset = { 0, textOffsetY };
12078 auto newPos = paragraphs_.GetIndex(textOffset);
12079 return newPos;
12080 }
12081
12082 float RichEditorPattern::GetTextThemeFontSize()
12083 {
12084 auto theme = GetTheme<TextTheme>();
12085 CHECK_NULL_RETURN(theme, 0.0f);
12086 auto textStyle = theme->GetTextStyle();
12087 return textStyle.GetFontSize().ConvertToPx();
12088 }
12089
12090 int32_t RichEditorPattern::CalcLineEndPosition(int32_t index)
12091 {
12092 CaretOffsetInfo caretInfo;
12093 int32_t realCaretOffsetY = 0;
12094 int32_t realLastClickOffsetY = 0;
12095
12096 caretInfo = GetCaretOffsetInfoByPosition(index);
12097 if (NearEqual(richTextRect_.GetY(), contentRect_.GetY())) {
12098 realLastClickOffsetY = lastClickOffset_.GetY();
12099 realCaretOffsetY = caretInfo.caretOffsetDown.GetY();
12100 } else {
12101 auto scrollOffset =
12102 caretInfo.caretOffsetDown.GetY() - caretInfo.caretOffsetUp.GetY() + caretInfo.caretOffsetLine.GetY();
12103 realLastClickOffsetY = lastClickOffset_.GetY() + std::abs(richTextRect_.GetY()) + contentRect_.GetY();
12104 realCaretOffsetY = scrollOffset + std::abs(richTextRect_.GetY()) + contentRect_.GetY();
12105 }
12106 auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
12107 auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize());
12108 Offset textOffset;
12109 auto rectLineInfo = CalcLineInfoByPosition();
12110 float textWidth = richTextRect_.Width() + rectLineInfo.GetX();
12111 float textPaintOffsetY = textPaintOffset.GetY() - (minDet / 2.0);
12112 float textOffsetClickY = realLastClickOffsetY - textPaintOffsetY;
12113 float textOffsetDownY = realCaretOffsetY - textPaintOffsetY;
12114 if (lastClickOffset_.NonNegative()) {
12115 textOffset = { textWidth, textOffsetClickY };
12116 } else {
12117 textOffset = { textWidth, textOffsetDownY };
12118 }
12119 auto position = paragraphs_.GetIndex(textOffset);
12120 return position;
12121 }
12122
12123 int32_t RichEditorPattern::CalcSingleLineBeginPosition(int32_t fixedPos)
12124 {
12125 float caretHeightDown = 0.0f;
12126 OffsetF caretOffsetDown = CalcCursorOffsetByPosition(fixedPos, caretHeightDown, true, false);
12127 float caretHeightUp = 0.0f;
12128 OffsetF caretOffsetUp = CalcCursorOffsetByPosition(fixedPos, caretHeightUp, false, false);
12129 bool isCaretPosInLineEnd = !NearEqual(caretOffsetDown.GetX(), caretOffsetUp.GetX(), 0.5f);
12130
12131 auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
12132 CHECK_NULL_RETURN(overlayMod_, false);
12133 auto caretOffsetOverlay = overlayMod->GetCaretOffset();
12134 bool cursorNotAtLineStart = NearEqual(caretOffsetOverlay.GetX(), caretOffsetUp.GetX(), 0.5f);
12135
12136 Offset textOffset;
12137 if (!cursorNotAtLineStart && !lastClickOffset_.IsNegative()) {
12138 return fixedPos;
12139 } else if (isCaretPosInLineEnd && lastClickOffset_.IsNegative()) {
12140 return lastSelectionRange_.start_;
12141 } else {
12142 float caretHeight = 0.0f;
12143 OffsetF caretOffsetFixed = CalcCursorOffsetByPosition(fixedPos, caretHeight, false, false);
12144 textOffset = { 0, caretOffsetFixed.GetY() };
12145 }
12146 auto position = paragraphs_.GetIndex(textOffset);
12147 return position;
12148 }
12149
12150 int32_t RichEditorPattern::CalcSingleLineEndPosition(int32_t fixedPos)
12151 {
12152 auto rectLineInfo = CalcLineInfoByPosition();
12153 float textWidth = richTextRect_.Width() + rectLineInfo.GetX();
12154
12155 float caretHeightDown = 0.0f;
12156 OffsetF caretOffsetDown = CalcCursorOffsetByPosition(fixedPos, caretHeightDown, true, false);
12157 float caretHeightUp = 0.0f;
12158 OffsetF caretOffsetUp = CalcCursorOffsetByPosition(fixedPos, caretHeightUp, false, false);
12159 bool isCaretPosInLineEnd = !NearEqual(caretOffsetDown.GetX(), caretOffsetUp.GetX(), 0.5f);
12160
12161 auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
12162 CHECK_NULL_RETURN(overlayMod_, false);
12163 auto caretOffsetOverlay = overlayMod->GetCaretOffset();
12164 bool cursorNotAtLineStart = NearEqual(caretOffsetOverlay.GetX(), caretOffsetUp.GetX(), 0.5f);
12165
12166 Offset textOffset;
12167 if (isCaretPosInLineEnd && lastClickOffset_.IsNegative() && lastSelectionRange_.end_ > fixedPos) {
12168 return lastSelectionRange_.end_;
12169 } else if (!cursorNotAtLineStart && isCaretPosInLineEnd && fixedPos == caretPosition_) {
12170 textOffset = { textWidth, caretOffsetOverlay.GetY() };
12171 } else if (isCaretPosInLineEnd && lastSelectionRange_.end_ <= fixedPos) {
12172 CursorMoveLineEnd();
12173 return fixedPos;
12174 } else if (cursorNotAtLineStart && isCaretPosInLineEnd) {
12175 return fixedPos;
12176 } else {
12177 float caretHeight = 0.0f;
12178 OffsetF caretOffsetFixed = CalcCursorOffsetByPosition(fixedPos, caretHeight, false, false);
12179 textOffset = { textWidth, caretOffsetFixed.GetY() };
12180 }
12181 auto position = paragraphs_.GetIndex(textOffset);
12182 return position;
12183 }
12184
12185 bool RichEditorPattern::CursorMoveLineBegin()
12186 {
12187 int32_t currentPositionIndex = 0;
12188 if (textSelector_.SelectNothing()) {
12189 currentPositionIndex = caretPosition_;
12190 } else {
12191 currentPositionIndex = textSelector_.GetTextStart();
12192 }
12193 CloseSelectOverlay();
12194 ResetSelection();
12195 float caretHeightDown = 0.0f;
12196 Offset textOffset;
12197
12198 if (0 == currentPositionIndex) {
12199 SetCaretPosition(currentPositionIndex);
12200 StartTwinkling();
12201 return false;
12202 }
12203 OffsetF caretOffsetDown = CalcCursorOffsetByPosition(currentPositionIndex, caretHeightDown, true, false);
12204 auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
12205 auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize());
12206 float textPaintOffsetY = textPaintOffset.GetY() - (minDet / 2.0);
12207 if (lastClickOffset_.NonNegative()) {
12208 textOffset = { 0, lastClickOffset_.GetY() - textPaintOffsetY };
12209 } else {
12210 textOffset = { 0, caretOffsetDown.GetY() - textPaintOffsetY };
12211 }
12212 auto position = paragraphs_.GetIndex(textOffset);
12213 AdjustCursorPosition(position);
12214 SetCaretPosition(position);
12215 MoveCaretToContentRect();
12216 StartTwinkling();
12217 auto host = GetHost();
12218 IF_PRESENT(host, MarkDirtyNode(PROPERTY_UPDATE_RENDER));
12219 return true;
12220 }
12221
12222 bool RichEditorPattern::CursorMoveLineEnd()
12223 {
12224 int32_t position = 0;
12225 if (!textSelector_.SelectNothing()) {
12226 position = textSelector_.GetTextEnd();
12227 CaretOffsetInfo caretInfo = GetCaretOffsetInfoByPosition(position);
12228 bool cursorAtLineEnd = !NearEqual(caretInfo.caretOffsetUp.GetX(), caretInfo.caretOffsetDown.GetX(), 0.5f);
12229 bool cursorAfterNewLine = (position - 1) == GetParagraphEndPosition(position - 1);
12230 if (cursorAfterNewLine) {
12231 --position;
12232 } else if (!cursorAtLineEnd) {
12233 position = CalcLineEndPosition(textSelector_.GetTextEnd());
12234 }
12235 } else {
12236 position = CalcLineEndPosition();
12237 }
12238 CloseSelectOverlay();
12239 ResetSelection();
12240 float caretHeight = 0.0f;
12241 CHECK_NULL_RETURN(overlayMod_, false);
12242 auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
12243 SetCaretPosition(position);
12244 StartTwinkling();
12245 OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight, false, false);
12246 overlayMod->SetCaretOffsetAndHeight(caretOffset, caretHeight);
12247 SetLastClickOffset(caretOffset);
12248 caretAffinityPolicy_ = CaretAffinityPolicy::UPSTREAM_FIRST;
12249 MoveCaretToContentRect(caretOffset, caretHeight);
12250 auto host = GetHost();
12251 CHECK_NULL_RETURN(host, false);
12252 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
12253 return true;
12254 }
12255
12256 void RichEditorPattern::HandleSelectFontStyle(KeyCode code)
12257 {
12258 auto start = textSelector_.GetTextStart();
12259 auto end = textSelector_.GetTextEnd();
12260 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Change range:[%{public}d-%{public}d] font by key:%{public}d mode:%{public}d",
12261 start, end, code, isSpanStringMode_);
12262 CHECK_NULL_VOID(!textSelector_.SelectNothing());
12263 auto host = GetHost();
12264 CHECK_NULL_VOID(host);
12265 if (isSpanStringMode_) {
12266 UpdateSelectStyledStringStyle(start, end, code);
12267 } else {
12268 UpdateSelectSpanStyle(start, end, code);
12269 }
12270 StopTwinkling();
12271 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
12272 }
12273
12274 void RichEditorPattern::HandleOnShowMenu()
12275 {
12276 auto isSelectAreaVisible = IsSelectAreaVisible();
12277 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnShowMenu, selectAreaVisible=%{public}d,"
12278 "previewTextInputting=%{public}d,isDragging=%{public}d,isMoveCaret=%{public}d,isHandleMoving=%{public}d,"
12279 "isTouchSelecting=%{public}d,mouseStatus=%{public}d",
12280 isSelectAreaVisible, IsPreviewTextInputting(), IsDragging(), moveCaretState_.isMoveCaret,
12281 selectOverlay_->GetIsHandleMoving(), isTouchSelecting_, mouseStatus_);
12282 CHECK_NULL_VOID(isSelectAreaVisible && !IsPreviewTextInputting() && !IsDragging());
12283 CHECK_NULL_VOID(!moveCaretState_.isMoveCaret && !selectOverlay_->GetIsHandleMoving());
12284 CHECK_NULL_VOID(!isTouchSelecting_ && mouseStatus_ != MouseStatus::MOVE);
12285
12286 if (sourceType_ == SourceType::MOUSE) {
12287 if (!IsSelected()) {
12288 selectedType_ = TextSpanType::TEXT;
12289 }
12290 textResponseType_ = TextResponseType::RIGHT_CLICK;
12291 selectionMenuOffsetByMouse_ = GetCaretRect().GetOffset() + GetParentGlobalOffset();
12292 selectOverlay_->ProcessOverlay({ .animation = true });
12293 return;
12294 }
12295 if (!IsSelected()) {
12296 CreateAndShowSingleHandle();
12297 return;
12298 }
12299 if (SelectOverlayIsOn()) {
12300 selectOverlay_->SwitchToOverlayMode();
12301 if (selectOverlay_->NeedRefreshMenu()) {
12302 selectOverlay_->ProcessOverlay({ .animation = true, .requestCode = REQUEST_RECREATE });
12303 return;
12304 }
12305 selectOverlay_->UpdateMenuOffset();
12306 selectOverlay_->ShowMenu();
12307 return;
12308 }
12309 CalculateHandleOffsetAndShowOverlay();
12310 selectOverlay_->ProcessOverlay({ .animation = true });
12311 }
12312
12313 PositionType RichEditorPattern::GetPositionTypeFromLine()
12314 {
12315 auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
12316 CHECK_NULL_RETURN(overlayMod, PositionType::DEFAULT);
12317 auto currentCaretOffsetOverlay = overlayMod->GetCaretOffset();
12318 auto caretOffsetWidth = overlayMod->GetCaretWidth();
12319 int32_t currentParagraphStart = GetParagraphBeginPosition(caretPosition_);
12320 bool isParagraphStart = caretPosition_ == currentParagraphStart;
12321 CHECK_NULL_RETURN(!isParagraphStart, PositionType::PARAGRAPH_START);
12322 int32_t currentParagraphEnd = GetParagraphEndPosition(caretPosition_);
12323 bool isParagraphEnd = caretPosition_ == currentParagraphEnd;
12324 CHECK_NULL_RETURN(!isParagraphEnd, PositionType::PARAGRAPH_END);
12325 CaretOffsetInfo caretInfo = GetCaretOffsetInfoByPosition();
12326 bool isCaretAtLineMiddle = NearEqual(caretInfo.caretOffsetDown.GetX(), caretInfo.caretOffsetUp.GetX(), 0.5f);
12327 CHECK_NULL_RETURN(!isCaretAtLineMiddle, PositionType::DEFAULT);
12328 bool isCaretAtLineEnd =
12329 NearEqual(currentCaretOffsetOverlay.GetX(), caretInfo.caretOffsetUp.GetX(), caretOffsetWidth);
12330 CHECK_NULL_RETURN(!isCaretAtLineEnd, PositionType::LINE_END);
12331 return PositionType::LINE_START;
12332 }
12333
12334 int32_t RichEditorPattern::HandleSelectWrapper(CaretMoveIntent direction, int32_t fixedPos)
12335 {
12336 int32_t index = GetCaretPosition();
12337 switch (direction) {
12338 case CaretMoveIntent::Left:
12339 return CaretPositionSelectEmoji(CaretMoveIntent::Left);
12340 case CaretMoveIntent::Right:
12341 return CaretPositionSelectEmoji(CaretMoveIntent::Right);
12342 case CaretMoveIntent::Up:
12343 return HandleKbVerticalSelection(true);
12344 case CaretMoveIntent::Down:
12345 return HandleKbVerticalSelection(false);
12346 case CaretMoveIntent::LeftWord: {
12347 return GetLeftWordIndex(index);
12348 }
12349 case CaretMoveIntent::RightWord: {
12350 return GetRightWordIndex(index);
12351 }
12352 case CaretMoveIntent::ParagraghBegin:
12353 return paragraphs_.GetParagraphInfo(index - 1).start;
12354 case CaretMoveIntent::ParagraghEnd:
12355 return paragraphs_.GetParagraphInfo(index).end;
12356 case CaretMoveIntent::LineBegin:
12357 return CalcSingleLineBeginPosition(fixedPos);
12358 case CaretMoveIntent::LineEnd:
12359 return CalcSingleLineEndPosition(fixedPos);
12360 default:
12361 return NONE_SELECT_TYPE;
12362 }
12363 }
12364
12365 int32_t RichEditorPattern::HandleKbVerticalSelection(bool isUp)
12366 {
12367 float caretHeight = 0.0f;
12368 float newCaretHeight = 0.0f;
12369 float careOffsetY = 0.0f;
12370 int32_t newPos;
12371 Offset textOffset;
12372 OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight);
12373 auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize()) / 2.0;
12374 auto positionType = GetPositionTypeFromLine();
12375 if (isUp) {
12376 float selectStartHeight = 0.0f;
12377 OffsetF selectStartOffset = CalcCursorOffsetByPosition(textSelector_.GetTextStart(), selectStartHeight);
12378 careOffsetY = caretOffset.GetY() - GetTextRect().GetY() - minDet;
12379 textOffset = Offset(caretOffset.GetX() - GetTextRect().GetX(), careOffsetY);
12380 CHECK_NULL_RETURN(GreatNotEqual(textOffset.GetY(), 0), 0);
12381 newPos = paragraphs_.GetIndex(textOffset, true);
12382 OffsetF newCaretOffset = CalcCursorOffsetByPosition(newPos, newCaretHeight, true);
12383 CHECK_EQUAL_RETURN(!textSelector_.SelectNothing() && textSelector_.GetTextEnd() == caretPosition_ &&
12384 selectStartOffset.GetY() == newCaretOffset.GetY(), true, textSelector_.GetTextStart());
12385 } else {
12386 float selectEndHeight = 0.0f;
12387 OffsetF selectEndOffset = CalcCursorOffsetByPosition(textSelector_.GetEnd(), selectEndHeight);
12388 careOffsetY = caretOffset.GetY() - GetTextRect().GetY() + caretHeight + (minDet / 2.0);
12389 float lastLineTop = 0.0f;
12390 float paragraphSpacing = 0.0f;
12391 HandleCurrentPositionParagraphInfo(lastLineTop, paragraphSpacing);
12392 if (positionType == PositionType::LINE_START) {
12393 auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
12394 CHECK_NULL_RETURN(overlayMod, 0);
12395 auto caretOffsetOverlay = overlayMod->GetCaretOffset();
12396 auto rectLineInfo = CalcLineInfoByPosition();
12397 careOffsetY += rectLineInfo.Height();
12398 IF_TRUE(NearEqual(std::floor(caretOffset.GetY() + rectLineInfo.Height()), std::floor(lastLineTop)),
12399 careOffsetY += paragraphSpacing);
12400 textOffset = Offset(caretOffsetOverlay.GetX() - GetTextRect().GetX(), careOffsetY);
12401 } else {
12402 IF_TRUE(NearEqual(std::floor(caretOffset.GetY()), std::floor(lastLineTop)),
12403 careOffsetY += paragraphSpacing);
12404 textOffset = Offset(caretOffset.GetX() - GetTextRect().GetX(), careOffsetY);
12405 }
12406 auto height = paragraphs_.GetHeight();
12407 CHECK_NULL_RETURN(LessNotEqual(textOffset.GetY(), height), GetTextContentLength());
12408 newPos = paragraphs_.GetIndex(textOffset, true);
12409 OffsetF newCaretOffset = CalcCursorOffsetByPosition(newPos, newCaretHeight);
12410 CHECK_EQUAL_RETURN(!textSelector_.SelectNothing() && textSelector_.GetTextStart() == caretPosition_ &&
12411 selectEndOffset.GetY() == newCaretOffset.GetY(), true, textSelector_.GetTextEnd());
12412 }
12413 return newPos;
12414 }
12415
12416 void RichEditorPattern::HandleSelectFontStyleWrapper(KeyCode code, TextStyle& spanStyle)
12417 {
12418 switch (code) {
12419 case KeyCode::KEY_B:
12420 if (spanStyle.GetFontWeight() == Ace::FontWeight::BOLD) {
12421 spanStyle.SetFontWeight(Ace::FontWeight::NORMAL);
12422 } else {
12423 spanStyle.SetFontWeight(Ace::FontWeight::BOLD);
12424 }
12425 break;
12426 case KeyCode::KEY_I:
12427 if (spanStyle.GetFontStyle() == OHOS::Ace::FontStyle::ITALIC) {
12428 spanStyle.SetFontStyle(OHOS::Ace::FontStyle::NORMAL);
12429 } else {
12430 spanStyle.SetFontStyle(OHOS::Ace::FontStyle::ITALIC);
12431 }
12432 break;
12433 case KeyCode::KEY_U:
12434 if (spanStyle.GetTextDecorationFirst() == TextDecoration::UNDERLINE) {
12435 spanStyle.SetTextDecoration(TextDecoration::NONE);
12436 } else {
12437 spanStyle.SetTextDecoration(TextDecoration::UNDERLINE);
12438 }
12439 break;
12440 default:
12441 LOGW("Unsupported select operation for HandleSelectFrontStyle");
12442 return;
12443 }
12444 }
12445
12446 void RichEditorPattern::AIDeleteComb(int32_t start, int32_t end, int32_t& aiPosition, bool direction)
12447 {
12448 std::u16string selectTextContent;
12449 GetContentBySpans(selectTextContent);
12450 // get select content
12451 std::u16string selectData16 = selectTextContent.substr(static_cast<int32_t>(start), static_cast<int32_t>(end - start));
12452 std::string selectData = StringUtils::Str16ToStr8(selectData16);
12453 int32_t aiPosStart;
12454 int32_t aiPosEnd;
12455 int32_t caretPosition;
12456 int32_t size = 1;
12457
12458 if (direction) {
12459 caretPosition = end - start - size;
12460 DataDetectorMgr::GetInstance().AdjustWordSelection(caretPosition, selectData, aiPosStart, aiPosEnd);
12461 aiPosition = aiPosStart + start;
12462 } else {
12463 caretPosition = 0;
12464 DataDetectorMgr::GetInstance().AdjustWordSelection(caretPosition, selectData, aiPosStart, aiPosEnd);
12465 aiPosition = aiPosEnd + start;
12466 }
12467 if (aiPosStart < 0 || aiPosEnd < 0) {
12468 aiPosition = GetCaretPosition();
12469 }
12470 }
12471
12472 bool RichEditorPattern::HandleOnDeleteComb(bool backward)
12473 {
12474 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnDeleteComb backward=%{public}d", backward);
12475 undoManager_->RecordSelectionBefore();
12476 if (textSelector_.IsValid()) {
12477 CloseSelectOverlay();
12478 SetCaretPosition(textSelector_.GetTextStart());
12479 ResetSelection();
12480 }
12481 if (backward) {
12482 DeleteBackwardWord();
12483 } else {
12484 DeleteForwardWord();
12485 }
12486 undoManager_->ClearSelectionBefore();
12487 MoveCaretToContentRect();
12488 StartTwinkling();
12489 auto host = GetHost();
12490 CHECK_NULL_RETURN(host, false);
12491 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
12492 return true;
12493 }
12494
12495 void RichEditorPattern::DeleteBackwardWord()
12496 {
12497 CHECK_NULL_VOID(caretPosition_ != 0);
12498 int32_t startIndex = caretPosition_;
12499 int32_t spaceEndIndex = startIndex;
12500 AdjustIndexSkipSpace(spaceEndIndex, MoveDirection::BACKWARD);
12501 int32_t wordEndIndex = std::max(0, spaceEndIndex - 1);
12502 AdjustSelectorForSymbol(wordEndIndex, HandleType::FIRST, SelectorAdjustPolicy::INCLUDE);
12503 AdjustWordSelection(wordEndIndex, spaceEndIndex);
12504 DeleteBackward(startIndex - wordEndIndex, TextChangeReason::INPUT);
12505 }
12506
12507 void RichEditorPattern::DeleteForwardWord()
12508 {
12509 CHECK_NULL_VOID(caretPosition_ != GetTextContentLength());
12510 int32_t startIndex = caretPosition_;
12511 int32_t spaceEndIndex = startIndex;
12512 AdjustIndexSkipSpace(spaceEndIndex, MoveDirection::FORWARD);
12513 int32_t wordEndIndex = std::min(spaceEndIndex + 1, GetTextContentLength());
12514 AdjustWordSelection(spaceEndIndex, wordEndIndex);
12515 DeleteForward(wordEndIndex - startIndex, TextChangeReason::INPUT);
12516 }
12517
12518 const std::list<RefPtr<UINode>>& RichEditorPattern::GetAllChildren() const
12519 {
12520 childNodes_.clear();
12521 auto host = GetContentHost();
12522 CHECK_NULL_RETURN(host, childNodes_);
12523 auto children = host->GetChildren();
12524 for (const auto& child: children) {
12525 childNodes_.push_back(child);
12526 }
12527 return childNodes_;
12528 }
12529
12530 void RichEditorPattern::HandleTripleClickEvent(OHOS::Ace::GestureEvent& info)
12531 {
12532 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleTripleClickEvent");
12533 CHECK_EQUAL_VOID(IsPreviewTextInputting(), true);
12534 CHECK_EQUAL_VOID(IsDragging(), true);
12535 bool isMouseClickWithShift = shiftFlag_ && info.GetSourceDevice() == SourceType::MOUSE;
12536 CHECK_EQUAL_VOID(isMouseClickWithShift, true);
12537 auto focusHub = GetFocusHub();
12538 CHECK_NULL_VOID(focusHub);
12539 CHECK_EQUAL_VOID(focusHub->IsFocusable(), false);
12540 auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
12541 Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
12542 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
12543 int32_t pos = paragraphs_.GetIndex(textOffset);
12544
12545 int32_t start = 0;
12546 int32_t end = 0;
12547 auto& paragraphInfoList = paragraphs_.GetParagraphs();
12548 if (!paragraphInfoList.empty() && pos == paragraphInfoList.back().end) {
12549 start = paragraphInfoList.back().start;
12550 end = paragraphInfoList.back().end;
12551 } else {
12552 for (const auto& paragraph : paragraphInfoList) {
12553 if (pos >= paragraph.start && pos < paragraph.end) {
12554 start = paragraph.start;
12555 end = paragraph.end;
12556 break;
12557 }
12558 }
12559 }
12560 if (!paragraphInfoList.empty() && paragraphInfoList.back().end != end) {
12561 --end;
12562 }
12563 end = std::min(GetTextContentLength(), end);
12564 start = std::min(GetTextContentLength(), start);
12565 CHECK_EQUAL_VOID(start > end, true);
12566 TripleClickSection(info, start, end, pos);
12567 }
12568
12569 void RichEditorPattern::UpdateSelectionByTouchMove(const Offset& touchOffset)
12570 {
12571 // While previewing + long press and move, then shall select content.
12572 auto host = GetHost();
12573 CHECK_NULL_VOID(host);
12574
12575 Offset textOffset = ConvertTouchOffsetToTextOffset(touchOffset);
12576 auto positionWithAffinity = paragraphs_.GetGlyphPositionAtCoordinate(textOffset);
12577 SetCaretPositionWithAffinity(positionWithAffinity);
12578 MoveCaretToContentRect();
12579 int32_t currentPosition = GreatNotEqual(textOffset.GetY(), paragraphs_.GetHeight())
12580 ? GetTextContentLength()
12581 : caretPosition_;
12582 IF_TRUE(GetTextContentLength() > 0, SetMagnifierLocalOffset(touchOffset));
12583 auto [initSelectStart, initSelectEnd] = initSelector_;
12584 int32_t start = std::min(initSelectStart, currentPosition);
12585 int32_t end = std::max(initSelectEnd, currentPosition);
12586 if (start == textSelector_.GetTextStart()) {
12587 StartVibratorByIndexChange(end, textSelector_.GetTextEnd());
12588 } else {
12589 StartVibratorByIndexChange(start, textSelector_.GetTextStart());
12590 }
12591 HandleSelectionChange(start, end);
12592 TriggerAvoidOnCaretChange();
12593 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
12594 }
12595
12596 void RichEditorPattern::HideMenu()
12597 {
12598 selectOverlay_->HideMenu();
12599 }
12600
12601 void RichEditorPattern::OnSelectionMenuOptionsUpdate(const NG::OnCreateMenuCallback&& onCreateMenuCallback,
12602 const NG::OnMenuItemClickCallback&& onMenuItemClick, const NG::OnPrepareMenuCallback&& onPrepareMenuCallback)
12603 {
12604 selectOverlay_->OnSelectionMenuOptionsUpdate(
12605 std::move(onCreateMenuCallback), std::move(onMenuItemClick), std::move(onPrepareMenuCallback));
12606 }
12607
12608 bool RichEditorPattern::CheckTripClickEvent(GestureEvent& info)
12609 {
12610 clickInfo_.push_back(info.GetTimeStamp());
12611 if (clickInfo_.size() > MAX_CLICK) {
12612 clickInfo_.erase(clickInfo_.begin());
12613 }
12614 if (clickInfo_.size() == MAX_CLICK) {
12615 std::chrono::duration<float, std::ratio<1, InputAIChecker::SECONDS_TO_MILLISECONDS>>
12616 clickTimeIntervalOne = clickInfo_[1] - clickInfo_[0];
12617 std::chrono::duration<float, std::ratio<1, InputAIChecker::SECONDS_TO_MILLISECONDS>>
12618 clickTimeIntervalTwo = clickInfo_[2] - clickInfo_[1];
12619 if (clickTimeIntervalOne.count() < DOUBLE_CLICK_INTERVAL_MS
12620 && clickTimeIntervalTwo.count() < DOUBLE_CLICK_INTERVAL_MS) {
12621 return true;
12622 }
12623 }
12624 return false;
12625 }
12626
12627 void RichEditorPattern::TripleClickSection(GestureEvent& info, int32_t start, int32_t end, int32_t pos)
12628 {
12629 auto host = GetHost();
12630 CHECK_NULL_VOID(host);
12631 textSelector_.Update(start, end);
12632 if (IsShowHandle()) {
12633 SetCaretPositionWithAffinity({ end, TextAffinity::UPSTREAM });
12634 MoveCaretToContentRect();
12635 }
12636 UpdateSelectionType(GetSpansInfo(start, end, GetSpansMethod::ONSELECT));
12637 if (info.GetSourceDevice() == SourceType::TOUCH) {
12638 showSelect_ = true;
12639 RequestKeyboard(false, true, true);
12640 HandleOnEditChanged(true);
12641 CalculateHandleOffsetAndShowOverlay();
12642 selectOverlay_->ProcessOverlay({ .menuIsShow = !selectOverlay_->GetIsHandleMoving(), .animation = true });
12643 }
12644 if (info.GetSourceDevice() == SourceType::TOUCH && start == end) {
12645 selectOverlay_->SetIsSingleHandle(true);
12646 }
12647 if (textSelector_.SelectNothing()) {
12648 textSelector_.Update(pos, pos);
12649 } else {
12650 StopTwinkling();
12651 }
12652 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
12653 }
12654
12655 void RichEditorPattern::RequestKeyboardToEdit()
12656 {
12657 CHECK_NULL_VOID(!isEditing_ && HasFocus());
12658 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "request keyboard and enter edit");
12659 RequestKeyboard(false, true, true);
12660 HandleOnEditChanged(true);
12661 }
12662
12663 bool RichEditorPattern::IsResponseRegionExpandingNeededForStylus(const TouchEvent& touchEvent) const
12664 {
12665 if (touchEvent.sourceTool != SourceTool::PEN || touchEvent.type != TouchType::DOWN) {
12666 return false;
12667 }
12668 auto host = GetHost();
12669 CHECK_NULL_RETURN(host, false);
12670 auto focusHub = host->GetFocusHub();
12671 CHECK_NULL_RETURN(focusHub, false);
12672 if (!focusHub->IsFocusable() || !host->IsVisible()) {
12673 return false;
12674 }
12675 auto renderContext = host->GetRenderContext();
12676 CHECK_NULL_RETURN(renderContext, false);
12677 auto opacity = renderContext->GetOpacity();
12678 // if opacity is 0.0f, no need to hit frameNode.
12679 if (NearZero(opacity.value_or(1.0f))) {
12680 return false;
12681 }
12682 return true;
12683 }
12684
12685 RectF RichEditorPattern::ExpandDefaultResponseRegion(RectF& rect)
12686 {
12687 return rect + NG::SizeF(0, OHOS::Ace::HOT_AREA_ADJUST_SIZE.ConvertToPx() * OHOS::Ace::HOT_AREA_EXPAND_TIME) +
12688 NG::OffsetF(0, -OHOS::Ace::HOT_AREA_ADJUST_SIZE.ConvertToPx());
12689 }
12690
12691 bool RichEditorPattern::InsertOrDeleteSpace(int32_t index)
12692 {
12693 // delete or insert space
12694 if (index < 0 || index >= GetTextContentLength()) {
12695 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "index is invalid, index=%{public}d", index);
12696 return false;
12697 }
12698 bool success = SetCaretPosition(index);
12699 CHECK_NULL_RETURN(success, false);
12700 CloseSelectOverlay();
12701 ResetSelection();
12702
12703 auto curIt = GetSpanIter(index);
12704 if (curIt != spans_.end()) {
12705 std::u16string curText = (*curIt)->content;
12706 if ((*curIt)->spanItemType == SpanItemType::NORMAL
12707 && index >= (*curIt)->rangeStart && curText[index - (*curIt)->rangeStart] == u' ') {
12708 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "delete forward");
12709 DeleteForward(1, TextChangeReason::STYLUS);
12710 return true;
12711 }
12712 }
12713
12714 auto preIt = GetSpanIter(index - 1);
12715 if (preIt != spans_.end()) {
12716 std::u16string preText = (*preIt)->content;
12717 if ((*preIt)->spanItemType == SpanItemType::NORMAL
12718 && index - 1 >= (*preIt)->rangeStart && preText[index - 1 - (*preIt)->rangeStart] == u' ') {
12719 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "delete backward");
12720 DeleteBackward(1, TextChangeReason::STYLUS);
12721 return true;
12722 }
12723 }
12724
12725 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "insert value");
12726 InsertValueByOperationType(u" ", OperationType::STYLUS);
12727 return true;
12728 }
12729
12730 // only called by IME
12731 void RichEditorPattern::DeleteRange(int32_t start, int32_t end, bool isIME)
12732 {
12733 DeleteRange(start, end, isIME, TextChangeReason::INPUT);
12734 }
12735
12736 void RichEditorPattern::DeleteRange(int32_t start, int32_t end, bool isIME, TextChangeReason reason)
12737 {
12738 if (start > end) {
12739 std::swap(start, end);
12740 }
12741 start = std::max(0, start);
12742 end = std::min(GetTextContentLength(), end);
12743 if (start > GetTextContentLength() || end < 0 || start == end) {
12744 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "start=%{public}d, end=%{public}d, not in the range", start, end);
12745 return;
12746 }
12747 CHECK_NULL_VOID(!IsPreviewTextInputting());
12748 SetCaretPosition(start);
12749 auto length = end - start;
12750 if (isSpanStringMode_) {
12751 DeleteValueInStyledString(start, length, true, false);
12752 return;
12753 }
12754 OperationRecord record;
12755 record.beforeCaretPosition = caretPosition_;
12756 RichEditorChangeValue changeValue(reason);
12757 CHECK_NULL_VOID(BeforeChangeText(changeValue, record, RecordType::DEL_FORWARD, length));
12758 std::u16string textContent;
12759 GetContentBySpans(textContent);
12760 auto realEnd = std::clamp(caretPosition_ + length, 0, static_cast<int32_t>(textContent.length()));
12761 std::u16string deleteText = textContent.substr(
12762 static_cast<uint32_t>(std::clamp(caretPosition_, 0, static_cast<int32_t>(textContent.length()))),
12763 static_cast<uint32_t>(realEnd - caretPosition_));
12764 if (caretPosition_ != GetTextContentLength()) {
12765 RichEditorDeleteValue info;
12766 info.SetOffset(caretPosition_);
12767 info.SetRichEditorDeleteDirection(RichEditorDeleteDirection::FORWARD);
12768 info.SetLength(length);
12769 int32_t currentPosition = caretPosition_;
12770 if (!spans_.empty()) {
12771 CalcDeleteValueObj(currentPosition, length, info);
12772 bool doDelete = DoDeleteActions(currentPosition, length, info);
12773 CHECK_NULL_VOID(doDelete);
12774 }
12775 }
12776 CHECK_NULL_VOID(deleteText.length() != 0);
12777 ClearRedoOperationRecords();
12778 record.deleteText = deleteText;
12779 record.afterCaretPosition = caretPosition_;
12780 AddOperationRecord(record);
12781 AfterContentChange(changeValue);
12782 }
12783
12784 void RichEditorPattern::HandleOnPageUp()
12785 {
12786 HandlePageScroll(true);
12787 }
12788
12789 void RichEditorPattern::HandleOnPageDown()
12790 {
12791 HandlePageScroll(false);
12792 }
12793
12794 void RichEditorPattern::HandlePageScroll(bool isPageUp)
12795 {
12796 auto visibleRect = selectOverlay_->GetVisibleRect();
12797 float distance = isPageUp ? visibleRect.Height() : -visibleRect.Height();
12798 RectF curCaretRect = GetCaretRect();
12799 auto height = paragraphs_.GetHeight();
12800 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "PageScroll isPageUp:%{public}d distance:%{public}f paragraphsHeight:%{public}f",
12801 isPageUp, distance, height);
12802 CloseSelectOverlay();
12803 ResetSelection();
12804 OnScrollCallback(distance, SCROLL_FROM_JUMP);
12805 auto paintOffset = selectOverlay_->GetPaintOffsetWithoutTransform();
12806 float offsetY = isPageUp ? visibleRect.Top() : visibleRect.Bottom();
12807 auto localOffset = Offset(curCaretRect.GetX(), offsetY - paintOffset.GetY());
12808 auto textOffset = ConvertTouchOffsetToTextOffset(localOffset);
12809 auto positionWithAffinity = paragraphs_.GetGlyphPositionAtCoordinate(textOffset);
12810 // If scrolling to the first or last line, move the cursor to the beginning or end of the line
12811 if (isPageUp && LessOrEqual(textOffset.GetY(), 0)) {
12812 positionWithAffinity = PositionWithAffinity(0, TextAffinity::DOWNSTREAM);
12813 } else if (!isPageUp && GreatOrEqual(textOffset.GetY(), height)) {
12814 positionWithAffinity = PositionWithAffinity(GetTextContentLength(), TextAffinity::UPSTREAM);
12815 }
12816 SetCaretPositionWithAffinity(positionWithAffinity);
12817 IF_TRUE(isEditing_, StartTwinkling());
12818 }
12819
12820 TextStyle RichEditorPattern::GetDefaultTextStyle()
12821 {
12822 auto theme = GetTheme<RichEditorTheme>();
12823 TextStyle style = theme ? theme->GetTextStyle() : TextStyle();
12824 style.SetFontSize(Dimension(DEFAULT_TEXT_SIZE, DimensionUnit::FP));
12825 style.SetFontFeatures(ParseFontFeatureSettings("\"pnum\" 1"));
12826 style.SetFontFamilies({ "HarmonyOS Sans" });
12827 return style;
12828 }
12829
12830 bool RichEditorPattern::IsShowTranslate()
12831 {
12832 auto richEditorTheme = GetTheme<RichEditorTheme>();
12833 CHECK_NULL_RETURN(richEditorTheme, false);
12834 return richEditorTheme->GetTranslateIsSupport();
12835 }
12836
12837 bool RichEditorPattern::IsShowSearch()
12838 {
12839 auto richEditorTheme = GetTheme<RichEditorTheme>();
12840 CHECK_NULL_RETURN(richEditorTheme, false);
12841 return richEditorTheme->GetSearchIsSupport();
12842 }
12843
12844 bool RichEditorPattern::IsShowAIWrite()
12845 {
12846 CHECK_NULL_RETURN(!textSelector_.SelectNothing(), false);
12847 auto container = Container::Current();
12848 if (container && container->IsSceneBoardWindow()) {
12849 return false;
12850 }
12851
12852 if (copyOption_ == CopyOptions::None) {
12853 return false;
12854 }
12855 auto theme = GetTheme<RichEditorTheme>();
12856 CHECK_NULL_RETURN(theme, false);
12857 auto bundleName = theme->GetAIWriteBundleName();
12858 auto abilityName = theme->GetAIWriteAbilityName();
12859 if (bundleName.empty() || abilityName.empty()) {
12860 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "Failed to obtain AI write package name!");
12861 return false;
12862 }
12863 aiWriteAdapter_->SetBundleName(bundleName);
12864 aiWriteAdapter_->SetAbilityName(abilityName);
12865
12866 auto isAISupport = false;
12867 if (theme->GetAIWriteIsSupport() == "true") {
12868 isAISupport = true;
12869 }
12870
12871 auto host = GetHost();
12872 CHECK_NULL_RETURN(host, false);
12873 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Whether the device supports AI write: %{public}d, nodeId: %{public}d",
12874 isAISupport, host->GetId());
12875 return isAISupport;
12876 }
12877
12878 void RichEditorPattern::GetAIWriteInfo(AIWriteInfo& info)
12879 {
12880 CHECK_NULL_VOID(!textSelector_.SelectNothing());
12881 info.firstHandle = textSelector_.firstHandle.ToString();
12882 info.secondHandle = textSelector_.secondHandle.ToString();
12883 info.selectStart = textSelector_.GetTextStart();
12884 info.selectEnd = textSelector_.GetTextEnd();
12885 auto host = GetHost();
12886 CHECK_NULL_VOID(host);
12887 info.componentType = host->GetTag();
12888
12889 // serialize the sentenced-level text
12890 auto textSize = static_cast<int32_t>(GetTextForDisplay().length()) + placeholderCount_;
12891 RefPtr<SpanString> spanString = ToStyledString(0, textSize);
12892 auto contentAll = spanString->GetU16string();
12893 auto sentenceStart = 0;
12894 auto sentenceEnd = textSize;
12895 for (int32_t i = info.selectStart; i >= 0; --i) {
12896 if (aiWriteAdapter_->IsSentenceBoundary(contentAll[i])) {
12897 sentenceStart = i + 1;
12898 break;
12899 }
12900 }
12901 for (int32_t i = info.selectEnd; i < textSize; i++) {
12902 if (aiWriteAdapter_->IsSentenceBoundary(contentAll[i])) {
12903 sentenceEnd = i;
12904 break;
12905 }
12906 }
12907 info.start = info.selectStart - sentenceStart;
12908 info.end = info.selectEnd - sentenceStart;
12909 spanString = ToStyledString(sentenceStart, sentenceEnd);
12910 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "Sentence range=[%{public}d-%{public}d], content=" SEC_PLD(%{public}s),
12911 sentenceStart, sentenceEnd, SEC_PARAM(spanString->GetString().c_str()));
12912 spanString->EncodeTlv(info.sentenceBuffer);
12913
12914 // serialize the selected text
12915 spanString = ToStyledString(info.selectStart, info.selectEnd);
12916 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "Selected range=[%{public}d-%{public}d], content=" SEC_PLD(%{public}s),
12917 info.selectStart, info.selectEnd, SEC_PARAM(spanString->GetString().c_str()));
12918 spanString->EncodeTlv(info.selectBuffer);
12919 info.selectLength = static_cast<int32_t>(aiWriteAdapter_->GetSelectLengthOnlyText(spanString->GetU16string()));
12920 }
12921
12922 void RichEditorPattern::HandleOnAIWrite()
12923 {
12924 aiWriteAdapter_->SetAIWrite(true);
12925 AIWriteInfo info;
12926 GetAIWriteInfo(info);
12927 CloseSelectOverlay();
12928 ResetSelection();
12929 CloseKeyboard(false);
12930
12931 auto callback = [weak = WeakClaim(this), info](std::vector<uint8_t>& buffer) {
12932 auto pattern = weak.Upgrade();
12933 CHECK_NULL_VOID(pattern);
12934 pattern->HandleAIWriteResult(info.selectStart, info.selectEnd, buffer);
12935 auto aiWriteAdapter = pattern->aiWriteAdapter_;
12936 CHECK_NULL_VOID(aiWriteAdapter);
12937 aiWriteAdapter->CloseModalUIExtension();
12938 };
12939 auto pipeline = GetContext();
12940 CHECK_NULL_VOID(pipeline);
12941 aiWriteAdapter_->SetPipelineContext(WeakClaim(pipeline));
12942 aiWriteAdapter_->ShowModalUIExtension(info, callback);
12943 }
12944
12945 SymbolSpanOptions RichEditorPattern::GetSymbolSpanOptions(const RefPtr<SpanItem>& spanItem)
12946 {
12947 CHECK_NULL_RETURN(spanItem, {});
12948 TextStyle textStyle = GetDefaultTextStyle();
12949 UseSelfStyle(spanItem->fontStyle, spanItem->textLineStyle, textStyle, true);
12950 SymbolSpanOptions options;
12951 options.style = textStyle;
12952 options.offset = caretPosition_;
12953 options.resourceObject = spanItem->GetResourceObject();
12954 options.symbolId = spanItem->GetSymbolId();
12955 return options;
12956 }
12957
12958 void RichEditorPattern::ReplacePlaceholderWithCustomSpan(
12959 const RefPtr<SpanItem>& spanItem, size_t& index, size_t& textIndex)
12960 {
12961 if (isSpanStringMode_) {
12962 auto customSpanItem = DynamicCast<CustomSpanItem>(spanItem);
12963 auto customSpan = MakeRefPtr<CustomSpan>();
12964 if (customSpanItem->onMeasure.has_value()) {
12965 customSpan->SetOnMeasure(customSpanItem->onMeasure.value());
12966 }
12967 if (customSpanItem->onDraw.has_value()) {
12968 customSpan->SetOnDraw(customSpanItem->onDraw.value());
12969 }
12970 auto spanString = MakeRefPtr<MutableSpanString>(customSpan);
12971 InsertStyledStringByPaste(spanString);
12972 } else {
12973 auto customSpanItem = DynamicCast<PlaceholderSpanItem>(spanItem);
12974 CHECK_NULL_VOID(customSpanItem);
12975 auto customNode = customSpanItem->GetCustomNode();
12976 SpanOptionBase options;
12977 options.offset = caretPosition_;
12978 AddPlaceholderSpan(customNode, options, TextChangeReason::AI_WRITE);
12979 }
12980 textIndex = index + PLACEHOLDER_LENGTH;
12981 }
12982
12983 void RichEditorPattern::ReplacePlaceholderWithSymbolSpan(
12984 const RefPtr<SpanItem>& spanItem, size_t& index, size_t& textIndex)
12985 {
12986 auto options = GetSymbolSpanOptions(spanItem);
12987 options.offset = caretPosition_;
12988 AddSymbolSpan(options, TextChangeReason::AI_WRITE, false, caretPosition_);
12989 textIndex = index + PLACEHOLDER_LENGTH;
12990 }
12991
12992 void RichEditorPattern::ReplacePlaceholderWithImageSpan(
12993 const RefPtr<SpanItem>& spanItem, size_t& index, size_t& textIndex)
12994 {
12995 auto imageSpanItem = DynamicCast<ImageSpanItem>(spanItem);
12996 CHECK_NULL_VOID(imageSpanItem);
12997 auto options = imageSpanItem->options;
12998 options.offset = caretPosition_;
12999 if (isSpanStringMode_) {
13000 auto spanString = MakeRefPtr<SpanString>(options);
13001 InsertStyledStringByPaste(spanString);
13002 } else {
13003 AddImageSpan(options, TextChangeReason::AI_WRITE, true, caretPosition_, true);
13004 }
13005 textIndex = index + PLACEHOLDER_LENGTH;
13006 }
13007
13008 void RichEditorPattern::ReplacePlaceholderWithRawSpans(
13009 const RefPtr<SpanItem>& spanItem, size_t& index, size_t& textIndex)
13010 {
13011 switch (spanItem->spanItemType) {
13012 case SpanItemType::SYMBOL:
13013 ReplacePlaceholderWithSymbolSpan(spanItem, index, textIndex);
13014 return;
13015 case SpanItemType::CustomSpan:
13016 ReplacePlaceholderWithCustomSpan(spanItem, index, textIndex);
13017 return;
13018 case SpanItemType::IMAGE:
13019 ReplacePlaceholderWithImageSpan(spanItem, index, textIndex);
13020 return;
13021 default:
13022 return;
13023 }
13024 }
13025
13026 void RichEditorPattern::AddSpansAndReplacePlaceholder(RefPtr<SpanString>& spanString)
13027 {
13028 auto content = spanString->GetU16string();
13029 size_t textIndex = 0;
13030 size_t index = content.find(PLACEHOLDER_MARK);
13031
13032 while (index != std::u16string::npos) {
13033 if (textIndex < index) {
13034 auto subSpan = spanString->GetSubSpanString(textIndex, index - textIndex);
13035 AddSpanByPasteData(subSpan, TextChangeReason::AI_WRITE);
13036 }
13037 auto key = content.substr(index, PLACEHOLDER_LENGTH);
13038 if (placeholderSpansMap_.find(key) == placeholderSpansMap_.end()) {
13039 index = content.find(PLACEHOLDER_MARK, index + 1);
13040 continue;
13041 }
13042 auto spanItem = placeholderSpansMap_[key];
13043 if (!spanItem) {
13044 index = content.find(PLACEHOLDER_MARK, index + 1);
13045 continue;
13046 }
13047 ReplacePlaceholderWithRawSpans(spanItem, index, textIndex);
13048 index = content.find(PLACEHOLDER_MARK, index + 1);
13049 }
13050 if (textIndex < content.length()) {
13051 auto subSpan = spanString->GetSubSpanString(textIndex, content.length() - textIndex);
13052 AddSpanByPasteData(subSpan, TextChangeReason::AI_WRITE);
13053 }
13054 }
13055
13056 void RichEditorPattern::InsertSpanByBackData(RefPtr<SpanString>& spanString)
13057 {
13058 CHECK_NULL_VOID(spanString);
13059 if (textSelector_.IsValid()) {
13060 SetCaretPosition(textSelector_.GetTextStart());
13061 DeleteForward(textSelector_.GetTextStart(), textSelector_.GetTextEnd() - textSelector_.GetTextStart());
13062 ResetSelection();
13063 }
13064 if (placeholderSpansMap_.empty()) {
13065 AddSpanByPasteData(spanString, TextChangeReason::AI_WRITE);
13066 } else {
13067 AddSpansAndReplacePlaceholder(spanString);
13068 }
13069 StartTwinkling();
13070 auto host = GetHost();
13071 CHECK_NULL_VOID(host);
13072 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
13073 host->MarkModifyDone();
13074 }
13075
13076 void RichEditorPattern::HandleAIWriteResult(int32_t start, int32_t end, std::vector<uint8_t>& buffer)
13077 {
13078 RefPtr<SpanString> spanString = SpanString::DecodeTlv(buffer);
13079 if (spanString->GetSpanItems().empty()) {
13080 return;
13081 }
13082 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "Backfilling results range=[%{public}d--%{public}d], content=" SEC_PLD(%{public}s),
13083 start, end, SEC_PARAM(spanString->GetString().c_str()));
13084
13085 textSelector_.Update(start, end);
13086 auto length = end - start;
13087 CHECK_NULL_VOID(length > 0);
13088 DeleteBackward(length, TextChangeReason::AI_WRITE);
13089 InsertSpanByBackData(spanString);
13090 BeforeIMEInsertValue(UtfUtils::Str8ToStr16(spanString->GetString()));
13091 InsertValueByOperationType(u"", OperationType::AI_WRITE);
13092 }
13093
13094 bool RichEditorPattern::IsTextEditableForStylus() const
13095 {
13096 CHECK_NULL_RETURN(!customKeyboardBuilder_, false);
13097 auto host = GetHost();
13098 CHECK_NULL_RETURN(host, false);
13099 auto focusHub = host->GetFocusHub();
13100 CHECK_NULL_RETURN(focusHub, false);
13101 if (!focusHub->IsFocusable() || !host->IsVisible()) {
13102 return false;
13103 }
13104 auto renderContext = host->GetRenderContext();
13105 CHECK_NULL_RETURN(renderContext, false);
13106 auto opacity = renderContext->GetOpacity();
13107 // if opacity is 0.0f, no need to hit frameNode.
13108 if (NearZero(opacity.value_or(1.0f))) {
13109 return false;
13110 }
13111 return true;
13112 }
13113
13114 void RichEditorPattern::DumpInfo(std::unique_ptr<JsonValue>& json)
13115 {
13116 if (customKeyboardBuilder_) {
13117 json->Put("CustomKeyboard, Attached", std::to_string(isCustomKeyboardAttached_).c_str());
13118 }
13119 auto host = GetHost();
13120 CHECK_NULL_VOID(host);
13121 auto richEditorTheme = GetTheme<RichEditorTheme>();
13122 CHECK_NULL_VOID(richEditorTheme);
13123 json->Put("caret offset", GetCaretRect().GetOffset().ToString().c_str());
13124 json->Put("caret height",
13125 std::to_string(NearZero(GetCaretRect().Height()) ? richEditorTheme->GetDefaultCaretHeight().ConvertToPx()
13126 : GetCaretRect().Height())
13127 .c_str());
13128 json->Put("text rect", richTextRect_.ToString().c_str());
13129 json->Put("content rect", contentRect_.ToString().c_str());
13130 auto richEditorPaintOffset = host->GetPaintRectOffsetNG(false, true);
13131 bool hasRenderTransform = selectOverlay_->HasRenderTransform();
13132 if (hasRenderTransform) {
13133 richEditorPaintOffset = selectOverlay_->GetPaintOffsetWithoutTransform();
13134 }
13135 json->Put("hasRenderTransform", std::to_string(hasRenderTransform).c_str());
13136 json->Put("richEditorPaintOffset", richEditorPaintOffset.ToString().c_str());
13137 auto selectOverlayInfo = selectOverlay_->GetSelectOverlayInfo();
13138 CHECK_NULL_VOID(selectOverlayInfo);
13139 json->Put("selectOverlay info", selectOverlayInfo->ToString().c_str());
13140 }
13141
13142 RectF RichEditorPattern::GetCaretRelativeRect()
13143 {
13144 CHECK_NULL_RETURN(caretTwinkling_, RectF(-1, -1, -1, -1));
13145 auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
13146 CHECK_NULL_RETURN(overlayMod_, RectF(0, 0, 0, 0));
13147 auto caretWidth = DynamicCast<RichEditorOverlayModifier>(overlayMod_)->GetCaretWidth();
13148 return RectF(caretOffset.GetX(), caretOffset.GetY(), caretWidth, caretHeight);
13149 }
13150
13151 void RichEditorPattern::OnReportRichEditorEvent(const std::string& event)
13152 {
13153 std::string value = RICHEDITOR + event;
13154 UiSessionManager::GetInstance()->ReportComponentChangeEvent(EVENT, value);
13155 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "nodeId:[%{public}d] RichEditor reportComponentChangeEvent %{public}s", frameId_,
13156 event.c_str());
13157 }
13158
13159 float RichEditorPattern::GetCaretWidth()
13160 {
13161 return static_cast<float>(CARET_WIDTH.ConvertToPx());
13162 }
13163
13164 #if defined(IOS_PLATFORM)
13165 const TextEditingValue& RichEditorPattern::GetInputEditingValue() const
13166 {
13167 static TextEditingValue value;
13168 value.text.clear();
13169 if (!spans_.empty()) {
13170 std::string text;
13171 for (const auto& span : spans_) {
13172 if (!span) {
13173 continue;
13174 }
13175 if (span->spanItemType == SpanItemType::NORMAL) {
13176 text.append(UtfUtils::Str16ToStr8(span->content));
13177 } else {
13178 text.append(" ");
13179 }
13180 }
13181 value.text = text;
13182 }
13183 if (textSelector_.IsValid()) {
13184 value.selection.Update(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
13185 } else {
13186 value.MoveToPosition(caretPosition_);
13187 }
13188 return value;
13189 }
13190 #endif
13191
13192 bool RichEditorPattern::IsSelectAll()
13193 {
13194 return textSelector_.GetTextStart() == 0 && textSelector_.GetTextEnd() == GetTextContentLength();
13195 }
13196
13197 void RichEditorPattern::OnAccessibilityEventTextChange(const std::string& changeType, const std::string& changeString)
13198 {
13199 auto pipeline = GetContext();
13200 CHECK_NULL_VOID(pipeline);
13201 auto host = GetHost();
13202 CHECK_NULL_VOID(host);
13203 AccessibilityEvent event;
13204 event.type = AccessibilityEventType::TEXT_CHANGE;
13205 event.nodeId = host->GetAccessibilityId();
13206 event.extraEventInfo.insert({changeType, changeString});
13207 pipeline->SendEventToAccessibilityWithNode(event, GetHost());
13208 }
13209
13210 void RichEditorPattern::ReportComponentChangeEvent() {
13211 #if !defined(PREVIEW) && !defined(ACE_UNITTEST) && defined(OHOS_PLATFORM)
13212 std::string str;
13213 if (isSpanStringMode_) {
13214 CHECK_NULL_VOID(styledString_);
13215 str = styledString_->GetString();
13216 } else {
13217 std::u16string u16Str;
13218 GetContentBySpans(u16Str);
13219 str = UtfUtils::Str16DebugToStr8(u16Str);
13220 }
13221 auto value = InspectorJsonUtil::Create();
13222 CHECK_NULL_VOID(value);
13223 value->Put("text", str.c_str());
13224 UiSessionManager::GetInstance()->ReportComponentChangeEvent(frameId_, "event", value);
13225 SEC_TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "nodeId:[%{public}d] RichEditor reportComponentChangeEvent %{public}zu",
13226 frameId_, str.length());
13227 #endif
13228 }
13229 } // namespace OHOS::Ace::NG
13230