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