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