1 /*
2 * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "core/components_ng/pattern/rich_editor/rich_editor_pattern.h"
16
17 #include <algorithm>
18 #include <chrono>
19 #include <cstddef>
20 #include <cstdint>
21 #include <functional>
22 #include <iterator>
23 #include <sstream>
24 #include <string>
25 #include <utility>
26
27 #include "interfaces/inner_api/ui_session/ui_session_manager.h"
28 #include "adapter/ohos/capability/clipboard/clipboard_impl.h"
29 #include "base/geometry/offset.h"
30 #include "base/i18n/localization.h"
31 #include "base/log/ace_trace.h"
32 #include "base/log/dump_log.h"
33 #include "base/log/log_wrapper.h"
34 #include "base/memory/ace_type.h"
35 #include "base/utils/string_utils.h"
36 #include "base/utils/utf_helper.h"
37 #include "base/utils/utils.h"
38 #include "core/common/ai/data_detector_mgr.h"
39 #include "core/common/clipboard/paste_data.h"
40 #include "core/common/container.h"
41 #include "core/common/container_scope.h"
42 #include "core/common/ime/text_input_client.h"
43 #include "core/common/share/text_share_adapter.h"
44 #include "core/common/stylus/stylus_detector_mgr.h"
45 #include "core/common/vibrator/vibrator_utils.h"
46 #include "core/components/common/layout/constants.h"
47 #include "core/components/common/properties/text_style_parser.h"
48 #include "core/components_ng/base/inspector_filter.h"
49 #include "core/components_ng/base/observer_handler.h"
50 #include "core/components_ng/base/view_stack_processor.h"
51 #include "core/components_ng/event/event_hub.h"
52 #include "core/components_ng/event/gesture_event_hub.h"
53 #include "core/components_ng/event/long_press_event.h"
54 #include "core/components_ng/pattern/image/image_pattern.h"
55 #include "core/components_ng/pattern/overlay/keyboard_base_pattern.h"
56 #include "core/components_ng/pattern/rich_editor/one_step_drag_controller.h"
57 #include "core/components_ng/pattern/rich_editor/color_mode_processor.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_utils.h"
62 #include "core/components_ng/pattern/rich_editor_drag/rich_editor_drag_pattern.h"
63 #include "core/components_ng/pattern/text_field/text_field_manager.h"
64 #include "core/components_ng/pattern/text_field/text_input_ai_checker.h"
65
66 #ifndef ACE_UNITTEST
67 #ifdef ENABLE_STANDARD_INPUT
68 #include "refbase.h"
69
70 #include "core/components_ng/pattern/text_field/on_text_changed_listener_impl.h"
71 #endif
72 #endif
73
74 #include "core/common/udmf/udmf_client.h"
75
76 #ifdef WINDOW_SCENE_SUPPORTED
77 #include "core/components_ng/pattern/window_scene/helper/window_scene_helper.h"
78 #endif
79
80 #ifdef ENABLE_ROSEN_BACKEND
81 #include "core/components/custom_paint/rosen_render_custom_paint.h"
82 #endif
83
84 namespace OHOS::Ace::NG {
85
86 namespace {
87 #if defined(ENABLE_STANDARD_INPUT)
88 // should be moved to theme
89 constexpr float CARET_WIDTH = 1.5f;
90 constexpr float DEFAULT_CARET_HEIGHT = 18.5f;
91 constexpr Dimension KEYBOARD_AVOID_OFFSET = 24.0_vp;
92 #endif
93 constexpr int32_t IMAGE_SPAN_LENGTH = 1;
94 constexpr int32_t SYMBOL_SPAN_LENGTH = 2;
95 constexpr uint32_t RICH_EDITOR_TWINKLING_INTERVAL_MS = 500;
96 constexpr uint32_t RICH_EDITOR_TWINKLING_INTERVAL_MS_DEBUG = 3000;
97 constexpr int32_t AUTO_SCROLL_INTERVAL = 15;
98 constexpr Dimension CARET_BOTTOM_DISTANCE = 16.0_vp;
99 constexpr Dimension AUTO_SCROLL_EDGE_DISTANCE = 15.0_vp;
100 constexpr Dimension AUTO_SCROLL_DRAG_EDGE_DISTANCE = 58.0_vp;
101 constexpr float MAX_DRAG_SCROLL_SPEED = 2400.0f;
102 constexpr float TIME_UNIT = 1000.0f;
103 constexpr float DOUBLE_CLICK_INTERVAL_MS = 300.0f;
104 constexpr uint32_t RECORD_MAX_LENGTH = 20;
105 constexpr float DEFAILT_OPACITY = 0.2f;
106 constexpr int64_t COLOR_OPAQUE = 255;
107 constexpr int32_t MAX_CLICK = 3;
108
109 constexpr Color SYSTEM_CARET_COLOR = Color(0xff007dff);
110 constexpr Color SYSTEM_SELECT_BACKGROUND_COLOR = Color(0x33007dff);
111
112 const auto MAGNIFIER_ANIMATION_CURVE = AceType::MakeRefPtr<InterpolatingSpring>(0.0f, 1.0f, 228.0f, 30.0f);
113 constexpr int32_t MAGNIFIER_ANIMATION_DURATION = 100;
114
115 constexpr int32_t ERROR_BAD_PARAMETERS = -1;
116 constexpr char PREVIEW_STYLE_NORMAL[] = "normal";
117 constexpr char PREVIEW_STYLE_UNDERLINE[] = "underline";
118 const std::u16string LINE_SEPARATOR = u"\n";
119 // hen do ai anaylsis, we should limit the left an right limit of the string
120 constexpr static int32_t AI_TEXT_RANGE_LEFT = 50;
121 constexpr static int32_t AI_TEXT_RANGE_RIGHT = 50;
122 constexpr static int32_t NONE_SELECT_TYPE = -1;
123
124 constexpr float RICH_DEFAULT_SHADOW_COLOR = 0x33000000;
125 constexpr float RICH_DEFAULT_ELEVATION = 120.0f;
126 constexpr int32_t CUSTOM_CONTENT_LENGTH = 1;
127 constexpr int32_t SYMBOL_CONTENT_LENGTH = 2;
128 constexpr int32_t PLACEHOLDER_LENGTH = 6;
129 const std::u16string PLACEHOLDER_MARK = u"{ return (span->urlOnRelease); };
133 } // namespace
134
RichEditorPattern()135 RichEditorPattern::RichEditorPattern() :
136 #ifndef ACE_UNITTEST
137 isAPI14Plus(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_FOURTEEN)),
138 #else
139 isAPI14Plus(true),
140 #endif
141 isAPI16Plus(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_SIXTEEN)),
142 isAPI18Plus(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)),
143 isAPI20Plus(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWENTY))
144 {
145 selectOverlay_ = AceType::MakeRefPtr<RichEditorSelectOverlay>(WeakClaim(this));
146 magnifierController_ = MakeRefPtr<MagnifierController>(WeakClaim(this));
147 styledString_ = MakeRefPtr<MutableSpanString>(u"");
148 styledString_->SetSpanWatcher(WeakClaim(this));
149 twinklingInterval_ = SystemProperties::GetDebugEnabled()
150 ? RICH_EDITOR_TWINKLING_INTERVAL_MS_DEBUG : RICH_EDITOR_TWINKLING_INTERVAL_MS;
151 floatingCaretState_.UpdateOriginCaretColor(GetDisplayColorMode());
152 }
153
~RichEditorPattern()154 RichEditorPattern::~RichEditorPattern()
155 {
156 if (isCustomKeyboardAttached_) {
157 CloseCustomKeyboard();
158 }
159 }
160
SetStyledString(const RefPtr<SpanString> & value)161 void RichEditorPattern::SetStyledString(const RefPtr<SpanString>& value)
162 {
163 if (GetTextContentLength() > maxLength_.value_or(INT_MAX)) {
164 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "SetStyledString: Reach the maxLength. maxLength=%{public}d", maxLength_.value_or(INT_MAX));
165 return;
166 }
167 CHECK_NULL_VOID(value && styledString_);
168 auto subValue = value;
169 if (value->GetLength() != styledString_->GetLength() && value->GetLength() > maxLength_.value_or(INT_MAX)) {
170 auto subLength = CalculateTruncationLength(value->GetU16string(), maxLength_.value_or(INT_MAX));
171 if (subLength == 0) {
172 IF_TRUE(IsPreviewTextInputting() && !previewTextRecord_.previewTextExiting, NotifyExitTextPreview(true));
173 return;
174 }
175 subValue = value->GetSubSpanString(0, subLength);
176 }
177 IF_TRUE(IsPreviewTextInputting() && !previewTextRecord_.previewTextExiting, NotifyExitTextPreview(true));
178 CloseSelectOverlay();
179 ResetSelection();
180 styledString_->RemoveCustomSpan();
181 auto length = styledString_->GetLength();
182 styledString_->ReplaceSpanString(0, length, subValue);
183 SetCaretPosition(styledString_->GetLength());
184 SetNeedMoveCaretToContentRect();
185 auto host = GetHost();
186 CHECK_NULL_VOID(host);
187 styledString_->AddCustomSpan();
188 styledString_->SetFramNode(host);
189 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
190 ForceTriggerAvoidOnCaretChange();
191 }
192
UpdateSpanItems(const std::list<RefPtr<NG::SpanItem>> & spanItems)193 void RichEditorPattern::UpdateSpanItems(const std::list<RefPtr<NG::SpanItem>>& spanItems)
194 {
195 SetSpanItemChildren(spanItems);
196 ProcessStyledString();
197 }
198
ProcessStyledString()199 void RichEditorPattern::ProcessStyledString()
200 {
201 auto host = GetHost();
202 CHECK_NULL_VOID(host);
203 std::u16string textCache = textForDisplay_;
204 textForDisplay_.clear();
205 dataDetectorAdapter_->textForAI_.clear();
206 host->Clean();
207 RemoveEmptySpanItems();
208 hasUrlSpan_ = false;
209 for (const auto& span : spans_) {
210 if (!span) {
211 continue;
212 }
213 auto imageSpan = DynamicCast<ImageSpanItem>(span);
214 if (imageSpan) {
215 MountImageNode(imageSpan);
216 dataDetectorAdapter_->textForAI_ += u'\n';
217 } else {
218 dataDetectorAdapter_->textForAI_ += span->content;
219 }
220 textForDisplay_ += span->content;
221 auto [spanStart, spanEnd] = span->interval;
222 span->rangeStart = spanStart;
223 span->position = spanEnd;
224
225 if (span->urlOnRelease) {
226 hasUrlSpan_ = true;
227 }
228 }
229 if (textForDisplay_ != textCache) {
230 dataDetectorAdapter_->aiDetectInitialized_ = false;
231 }
232 if (CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
233 dataDetectorAdapter_->StartAITask();
234 }
235 }
236
MountImageNode(const RefPtr<ImageSpanItem> & imageItem)237 void RichEditorPattern::MountImageNode(const RefPtr<ImageSpanItem>& imageItem)
238 {
239 auto host = GetHost();
240 CHECK_NULL_VOID(host);
241 CHECK_NULL_VOID(imageItem);
242 auto options = imageItem->options;
243 auto imageNode = ImageSpanNode::GetOrCreateSpanNode(V2::IMAGE_ETS_TAG,
244 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<ImagePattern>(); });
245 auto pattern = imageNode->GetPattern<ImagePattern>();
246 CHECK_NULL_VOID(pattern);
247 if (options.imagePixelMap.has_value()) {
248 pattern->SetSyncLoad(true);
249 } else if (options.imageAttribute.has_value()) {
250 pattern->SetSyncLoad(options.imageAttribute.value().syncLoad);
251 }
252 auto index = host->GetChildren().size();
253 imageNodes.push_back(imageNode);
254 imageNode->MountToParent(host, index);
255 HandleImageDrag(imageNode);
256 SetImageLayoutProperty(imageNode, options);
257 imageItem->imageNodeId = imageNode->GetId();
258 imageNode->SetImageItem(imageItem);
259 }
260
SetImageLayoutProperty(RefPtr<ImageSpanNode> imageNode,const ImageSpanOptions & options)261 void RichEditorPattern::SetImageLayoutProperty(RefPtr<ImageSpanNode> imageNode, const ImageSpanOptions& options)
262 {
263 CHECK_NULL_VOID(imageNode);
264 auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
265 CHECK_NULL_VOID(imageLayoutProperty);
266 std::function<ImageSourceInfo()> createSourceInfoFunc = CreateImageSourceInfo(options);
267 imageLayoutProperty->UpdateImageSourceInfo(createSourceInfoFunc());
268 if (options.imageAttribute.has_value()) {
269 auto imgAttr = options.imageAttribute.value();
270 if (imgAttr.size.has_value()) {
271 imageLayoutProperty->UpdateUserDefinedIdealSize(imgAttr.size->GetSize());
272 }
273 if (imgAttr.verticalAlign.has_value()) {
274 imageLayoutProperty->UpdateVerticalAlign(imgAttr.verticalAlign.value());
275 }
276 if (imgAttr.objectFit.has_value()) {
277 imageLayoutProperty->UpdateImageFit(imgAttr.objectFit.value());
278 }
279 if (imgAttr.marginProp.has_value()) {
280 imageLayoutProperty->UpdateMargin(imgAttr.marginProp.value());
281 }
282 if (imgAttr.paddingProp.has_value()) {
283 imageLayoutProperty->UpdatePadding(imgAttr.paddingProp.value());
284 }
285 if (imgAttr.borderRadius.has_value()) {
286 auto imageRenderCtx = imageNode->GetRenderContext();
287 imageRenderCtx->UpdateBorderRadius(imgAttr.borderRadius.value());
288 imageRenderCtx->SetClipToBounds(true);
289 }
290 auto paintProperty = imageNode->GetPaintProperty<ImageRenderProperty>();
291 if (imgAttr.colorFilterMatrix.has_value() && paintProperty) {
292 paintProperty->UpdateColorFilter(imgAttr.colorFilterMatrix.value());
293 paintProperty->ResetDrawingColorFilter();
294 } else if (imgAttr.drawingColorFilter.has_value() && paintProperty) {
295 paintProperty->UpdateDrawingColorFilter(imgAttr.drawingColorFilter.value());
296 paintProperty->ResetColorFilter();
297 }
298 }
299 imageNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
300 imageNode->MarkModifyDone();
301 IF_PRESENT(oneStepDragController_, MarkDirtyNode(WeakClaim(RawPtr(imageNode))));
302 }
303
InsertValueInStyledStringMore(RefPtr<SpanString> insertStyledString,int32_t changeStart,int32_t changeLength,std::u16string & subValue,bool needReplaceInTextPreview)304 void RichEditorPattern::InsertValueInStyledStringMore(RefPtr<SpanString> insertStyledString, int32_t changeStart,
305 int32_t changeLength, std::u16string& subValue, bool needReplaceInTextPreview)
306 {
307 bool isSingleHandleMoving = selectOverlay_->IsSingleHandleMoving();
308 if (textSelector_.IsValid()) {
309 ResetSelection();
310 }
311 CloseSelectOverlay();
312 if (insertStyledString) {
313 styledString_->InsertSpanString(changeStart, insertStyledString);
314 } else {
315 styledString_->InsertString(changeStart, subValue);
316 }
317 SetCaretPosition(changeStart + static_cast<int32_t>(subValue.length()), !needReplaceInTextPreview);
318 IF_TRUE((!caretVisible_ || isSingleHandleMoving) && HasFocus(), StartTwinkling());
319 auto host = GetHost();
320 CHECK_NULL_VOID(host);
321 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
322 host->MarkModifyDone();
323 AfterStyledStringChange(changeStart, changeLength, subValue);
324 }
325
InsertValueInStyledString(const std::u16string & insertValue,bool calledByImf)326 void RichEditorPattern::InsertValueInStyledString(const std::u16string& insertValue, bool calledByImf)
327 {
328 CHECK_NULL_VOID(styledString_);
329 IF_TRUE(calledByImf && previewTextRecord_.IsValid(), FinishTextPreviewInner());
330 int32_t changeStart = caretPosition_;
331 int32_t changeLength = 0;
332 if (textSelector_.IsValid()) {
333 changeStart = textSelector_.GetTextStart();
334 changeLength = textSelector_.GetTextEnd() - changeStart;
335 }
336 auto subValue = insertValue;
337 if (!ProcessTextTruncationOperation(subValue, calledByImf)) {
338 return;
339 }
340 auto needReplaceInTextPreview = (previewTextRecord_.needReplacePreviewText || previewTextRecord_.needReplaceText) &&
341 previewTextRecord_.replacedRange.end - previewTextRecord_.replacedRange.start > 0;
342 if (needReplaceInTextPreview) {
343 changeStart= previewTextRecord_.replacedRange.start;
344 changeLength = previewTextRecord_.replacedRange.end - previewTextRecord_.replacedRange.start;
345 }
346 bool isPreventChange = false;
347 RefPtr<SpanString> insertStyledString = nullptr;
348 if (typingStyle_.has_value() && typingTextStyle_.has_value()) {
349 insertStyledString = CreateStyledStringByTextStyle(subValue, typingStyle_.value(), typingTextStyle_.value());
350 isPreventChange = !BeforeStyledStringChange(changeStart, changeLength, insertStyledString);
351 } else {
352 isPreventChange = !BeforeStyledStringChange(changeStart, changeLength, subValue);
353 }
354 CHECK_NULL_VOID(!isPreventChange || previewTextRecord_.needReplacePreviewText);
355 if (changeLength > 0 && (subValue.length() > 0 || !calledByImf)) {
356 auto start = needReplaceInTextPreview ? previewTextRecord_.replacedRange.start : caretPosition_;
357 auto isUpdateCaret = !needReplaceInTextPreview;
358 DeleteValueInStyledString(start, changeLength, false, isUpdateCaret);
359 }
360 InsertValueInStyledStringMore(insertStyledString, changeStart, changeLength, subValue, needReplaceInTextPreview);
361 }
362
CreateStyledStringByTextStyle(const std::u16string & insertValue,const struct UpdateSpanStyle & updateSpanStyle,const TextStyle & textStyle)363 RefPtr<SpanString> RichEditorPattern::CreateStyledStringByTextStyle(
364 const std::u16string& insertValue, const struct UpdateSpanStyle& updateSpanStyle, const TextStyle& textStyle)
365 {
366 auto styledString = AceType::MakeRefPtr<SpanString>(insertValue);
367 auto length = styledString->GetLength();
368 std::vector<RefPtr<SpanBase>> spans;
369 spans.push_back(CreateFontSpanByTextStyle(updateSpanStyle, textStyle, length));
370 spans.push_back(CreateDecorationSpanByTextStyle(updateSpanStyle, textStyle, length));
371 if (updateSpanStyle.updateTextShadows.has_value()) {
372 spans.push_back(AceType::MakeRefPtr<TextShadowSpan>(textStyle.GetTextShadows(), 0, length));
373 }
374 if (updateSpanStyle.updateLineHeight.has_value()) {
375 spans.push_back(AceType::MakeRefPtr<LineHeightSpan>(textStyle.GetLineHeight(), 0, length));
376 }
377 if (updateSpanStyle.updateHalfLeading.has_value()) {
378 spans.push_back(AceType::MakeRefPtr<HalfLeadingSpan>(textStyle.GetHalfLeading(), 0, length));
379 }
380 if (updateSpanStyle.updateLetterSpacing.has_value()) {
381 spans.push_back(AceType::MakeRefPtr<LetterSpacingSpan>(textStyle.GetLetterSpacing(), 0, length));
382 }
383 if (updateSpanStyle.updateTextBackgroundStyle.has_value()) {
384 spans.push_back(AceType::MakeRefPtr<BackgroundColorSpan>(textStyle.GetTextBackgroundStyle(), 0, length));
385 }
386 styledString->BindWithSpans(spans);
387 return styledString;
388 }
389
CreateFontSpanByTextStyle(const struct UpdateSpanStyle & updateSpanStyle,const TextStyle & textStyle,int32_t length)390 RefPtr<FontSpan> RichEditorPattern::CreateFontSpanByTextStyle(
391 const struct UpdateSpanStyle& updateSpanStyle, const TextStyle& textStyle, int32_t length)
392 {
393 Font font;
394 if (updateSpanStyle.updateFontWeight.has_value()) {
395 font.fontWeight = textStyle.GetFontWeight();
396 }
397 if (updateSpanStyle.updateFontSize.has_value()) {
398 font.fontSize = textStyle.GetFontSize();
399 }
400 if (updateSpanStyle.updateItalicFontStyle.has_value()) {
401 font.fontStyle = textStyle.GetFontStyle();
402 }
403 if (updateSpanStyle.updateFontFamily.has_value()) {
404 font.fontFamilies = textStyle.GetFontFamilies();
405 }
406 if (updateSpanStyle.updateTextColor.has_value()) {
407 font.fontColor = textStyle.GetTextColor();
408 }
409 return AceType::MakeRefPtr<FontSpan>(font, 0, length);
410 }
411
CreateDecorationSpanByTextStyle(const struct UpdateSpanStyle & updateSpanStyle,const TextStyle & textStyle,int32_t length)412 RefPtr<DecorationSpan> RichEditorPattern::CreateDecorationSpanByTextStyle(
413 const struct UpdateSpanStyle& updateSpanStyle, const TextStyle& textStyle, int32_t length)
414 {
415 TextDecoration type = TextDecoration::NONE;
416 std::optional<Color> colorOption;
417 std::optional<TextDecorationStyle> styleOption;
418 if (updateSpanStyle.updateTextDecoration.has_value()) {
419 type = textStyle.GetTextDecoration();
420 }
421 if (updateSpanStyle.updateTextDecorationColor.has_value()) {
422 colorOption = textStyle.GetTextDecorationColor();
423 }
424 if (updateSpanStyle.updateTextDecorationStyle.has_value()) {
425 styleOption = textStyle.GetTextDecorationStyle();
426 }
427 return AceType::MakeRefPtr<DecorationSpan>(type, colorOption, styleOption, 0, length);
428 }
429
DeleteBackwardInStyledString(int32_t length)430 void RichEditorPattern::DeleteBackwardInStyledString(int32_t length)
431 {
432 DeleteValueInStyledString(caretPosition_ - length, length);
433 }
434
DeleteForwardInStyledString(int32_t length,bool isIME)435 void RichEditorPattern::DeleteForwardInStyledString(int32_t length, bool isIME)
436 {
437 DeleteValueInStyledString(caretPosition_, length, isIME);
438 }
439
DeleteValueInStyledString(int32_t start,int32_t length,bool isIME,bool isUpdateCaret)440 void RichEditorPattern::DeleteValueInStyledString(int32_t start, int32_t length, bool isIME, bool isUpdateCaret)
441 {
442 CHECK_NULL_VOID(styledString_);
443 if (!textSelector_.SelectNothing()) {
444 start = textSelector_.GetTextStart();
445 length = textSelector_.GetTextEnd() - textSelector_.GetTextStart();
446 }
447 auto range = TextEmojiProcessor::CalSubU16stringRange(start, length, styledString_->GetU16string(), true, true);
448 start = range.startIndex;
449 length = range.endIndex - range.startIndex;
450 bool isPreventChange = isIME && !BeforeStyledStringChange(start, length, u"");
451 TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
452 "deleteInSS, start=%{public}d, length=%{public}d, isPreventChange=%{public}d, "
453 "isPreviewTextInputting=%{public}d",
454 start, length, isPreventChange, IsPreviewTextInputting());
455 CHECK_NULL_VOID(!isPreventChange || IsPreviewTextInputting());
456 bool isSingleHandleMoving = selectOverlay_->IsSingleHandleMoving();
457 if (textSelector_.IsValid()) {
458 CloseSelectOverlay();
459 ResetSelection();
460 }
461 styledString_->RemoveString(start, length);
462 if (isUpdateCaret) {
463 SetCaretPosition(start, !isModifyingContent_);
464 }
465 if ((!caretVisible_ || isSingleHandleMoving) && HasFocus()) {
466 StartTwinkling();
467 if (!isEditing_ && isIME) {
468 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "previewLongPress_ is true, before RequestKeyboard");
469 RequestKeyboard(false, true, true);
470 HandleOnEditChanged(true);
471 previewLongPress_ = false;
472 }
473 }
474 if (isIME) {
475 AfterStyledStringChange(start, length, u"");
476 }
477 auto host = GetHost();
478 CHECK_NULL_VOID(host);
479 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
480 host->MarkModifyDone();
481 }
482
BeforeStyledStringChange(int32_t start,int32_t length,const std::u16string & string)483 bool RichEditorPattern::BeforeStyledStringChange(int32_t start, int32_t length, const std::u16string& string)
484 {
485 auto eventHub = GetEventHub<RichEditorEventHub>();
486 CHECK_NULL_RETURN(eventHub, true);
487 CHECK_NULL_RETURN(eventHub->HasOnStyledStringWillChange(), true);
488 auto styledString = AceType::MakeRefPtr<SpanString>(string);
489 auto stringLength = styledString->GetLength();
490 auto changeStart = std::clamp(start, 0, GetTextContentLength());
491 if (stringLength != 0) {
492 auto lastStyles = styledString_->GetSpans(changeStart - 1, 1);
493 for (auto && style : lastStyles) {
494 if (!style) {
495 continue;
496 }
497 auto spanType = style->GetSpanType();
498 if (spanType == SpanType::Image || spanType == SpanType::CustomSpan) {
499 continue;
500 }
501 auto span = style->GetSubSpan(0, stringLength);
502 styledString->AddSpan(span);
503 }
504 }
505 return BeforeStyledStringChange(changeStart, length, styledString);
506 }
507
BeforeStyledStringChange(int32_t start,int32_t length,const RefPtr<SpanString> & styledString)508 bool RichEditorPattern::BeforeStyledStringChange(int32_t start, int32_t length, const RefPtr<SpanString>& styledString)
509 {
510 auto eventHub = GetEventHub<RichEditorEventHub>();
511 CHECK_NULL_RETURN(eventHub, true);
512 CHECK_NULL_RETURN(eventHub->HasOnStyledStringWillChange(), true);
513 auto replaceMentString = AceType::MakeRefPtr<MutableSpanString>(u"");
514 replaceMentString->AppendSpanString(styledString);
515 StyledStringChangeValue changeValue;
516 auto changeStart = std::clamp(start, 0, GetTextContentLength());
517 auto changeEnd = std::clamp(changeStart + length, 0, GetTextContentLength());
518 changeValue.SetRangeBefore({ changeStart, changeEnd });
519 changeValue.SetReplacementString(replaceMentString);
520 if (!previewTextRecord_.newPreviewContent.empty()) {
521 auto previewTextStyledString = AceType::MakeRefPtr<MutableSpanString>(previewTextRecord_.newPreviewContent);
522 changeValue.SetPreviewText(previewTextStyledString);
523 }
524 return eventHub->FireOnStyledStringWillChange(changeValue);
525 }
526
AfterStyledStringChange(int32_t start,int32_t length,const std::u16string & string)527 void RichEditorPattern::AfterStyledStringChange(int32_t start, int32_t length, const std::u16string& string)
528 {
529 auto eventHub = GetEventHub<RichEditorEventHub>();
530 CHECK_NULL_VOID(eventHub);
531 if (eventHub->HasOnStyledStringDidChange()) {
532 StyledStringChangeValue changeValue;
533 auto changeStart = std::clamp(start, 0, GetTextContentLength());
534 auto changeEnd = changeStart + length;
535 auto stringLength = static_cast<int32_t>(string.length());
536 auto stringEnd = changeStart + stringLength;
537 changeValue.SetRangeBefore({ changeStart, changeEnd });
538 changeValue.SetRangeAfter({ changeStart, stringEnd });
539 eventHub->FireOnStyledStringDidChange(changeValue);
540 }
541 ForceTriggerAvoidOnCaretChange();
542 }
543
OnModifyDone()544 void RichEditorPattern::OnModifyDone()
545 {
546 Pattern::CheckLocalized();
547 auto host = GetHost();
548 CHECK_NULL_VOID(host);
549 auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
550 copyOption_ = layoutProperty->GetCopyOption().value_or(CopyOptions::Distributed);
551 auto context = host->GetContext();
552 CHECK_NULL_VOID(context);
553 ResetKeyboardIfNeed();
554 context->AddOnAreaChangeNode(host->GetId());
555 if (!clipboard_ && context) {
556 clipboard_ = ClipboardProxy::GetInstance()->GetClipboard(context->GetTaskExecutor());
557 }
558 instanceId_ = context->GetInstanceId();
559 InitMouseEvent();
560 InitAISpanHoverEvent();
561 auto focusHub = host->GetOrCreateFocusHub();
562 CHECK_NULL_VOID(focusHub);
563 InitFocusEvent(focusHub);
564 auto gestureEventHub = host->GetOrCreateGestureEventHub();
565 InitClickEvent(gestureEventHub);
566 InitLongPressEvent(gestureEventHub);
567 InitTouchEvent();
568 InitPanEvent();
569 HandleEnabled();
570 ProcessInnerPadding();
571 InitScrollablePattern();
572 SetAccessibilityAction();
573 selectOverlay_->SetMenuTranslateIsSupport(IsShowTranslate());
574 selectOverlay_->SetIsSupportMenuSearch(IsShowSearch());
575 if (host->IsDraggable()) {
576 InitDragDropEvent();
577 AddDragFrameNodeToManager(host);
578 } else {
579 ClearDragDropEvent();
580 RemoveDragFrameNodeFromManager(host);
581 }
582 Register2DragDropManager();
583 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
584
585 auto eventHub = host->GetEventHub<EventHub>();
586 CHECK_NULL_VOID(eventHub);
587 bool enabledCache = eventHub->IsEnabled();
588 if (textDetectEnable_ && enabledCache != enabled_) {
589 enabled_ = enabledCache;
590 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
591 }
592 SetIsEnableSubWindowMenu();
593 }
594
HandleEnabled()595 void RichEditorPattern::HandleEnabled()
596 {
597 auto host = GetHost();
598 CHECK_NULL_VOID(host);
599 auto renderContext = host->GetRenderContext();
600 CHECK_NULL_VOID(renderContext);
601 if (IsDisabled()) {
602 auto richEditorTheme = GetTheme<RichEditorTheme>();
603 CHECK_NULL_VOID(richEditorTheme);
604 auto disabledAlpha = richEditorTheme->GetDisabledAlpha();
605 renderContext->OnOpacityUpdate(disabledAlpha);
606 } else {
607 auto opacity = renderContext->GetOpacity().value_or(1.0);
608 renderContext->OnOpacityUpdate(opacity);
609 }
610 }
611
BeforeCreateLayoutWrapper()612 void RichEditorPattern::BeforeCreateLayoutWrapper()
613 {
614 ACE_SCOPED_TRACE("RichEditorBeforeCreateLayoutWrapper");
615 if (!isSpanStringMode_) {
616 TextPattern::PreCreateLayoutWrapper();
617 hasUrlSpan_ = std::any_of(spans_.begin(), spans_.end(), URL_SPAN_FILTER);
618 } else if (contentMod_) {
619 contentMod_->ContentChange();
620 }
621 }
622
UpdateMagnifierStateAfterLayout(bool frameSizeChange)623 void RichEditorPattern::UpdateMagnifierStateAfterLayout(bool frameSizeChange)
624 {
625 CHECK_NULL_VOID(!selectOverlay_->GetIsHandleMoving());
626 if (frameSizeChange && magnifierController_ && magnifierController_->GetMagnifierNodeExist()) {
627 ResetTouchSelectState();
628 ResetTouchAndMoveCaretState();
629 magnifierController_->RemoveMagnifierFrameNode();
630 }
631 }
632
UpdateGestureHotZone(const RefPtr<LayoutWrapper> & dirty)633 void RichEditorPattern::UpdateGestureHotZone(const RefPtr<LayoutWrapper>& dirty)
634 {
635 const auto& geometryNode = dirty->GetGeometryNode();
636 auto paddingSize = geometryNode->GetPaddingSize();
637 auto paddingOffset = geometryNode->GetPaddingOffset() - geometryNode->GetFrameOffset();
638
639 auto hotZoneWidth = Dimension(paddingSize.Width());
640 auto hotZoneHeight = Dimension(paddingSize.Height());
641 auto hotZoneOffset = DimensionOffset(Offset(paddingOffset.GetX(), paddingOffset.GetY()));
642
643 auto gestureHub = GetGestureEventHub();
644 CHECK_NULL_VOID(gestureHub);
645 gestureHub->SetResponseRegion({ { hotZoneWidth, hotZoneHeight, hotZoneOffset } });
646 }
647
ClearOnFocusTextField(FrameNode * node)648 void RichEditorPattern::ClearOnFocusTextField(FrameNode* node)
649 {
650 CHECK_NULL_VOID(isAPI14Plus && node);
651 auto context = node->GetContextRefPtr();
652 CHECK_NULL_VOID(context);
653 auto textFieldManager = DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
654 IF_PRESENT(textFieldManager, ClearOnFocusTextField(node->GetId()));
655 }
656
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)657 bool RichEditorPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
658 {
659 CHECK_NULL_RETURN(!config.skipMeasure && !dirty->SkipMeasureContent(), false);
660 auto originalFrameRect = frameRect_;
661 frameRect_ = dirty->GetGeometryNode()->GetFrameRect();
662 auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
663 CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
664 auto richEditorLayoutAlgorithm =
665 DynamicCast<RichEditorLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
666 CHECK_NULL_RETURN(richEditorLayoutAlgorithm, false);
667 UpdateParentOffsetAndOverlay();
668 richTextRect_ = richEditorLayoutAlgorithm->GetTextRect();
669 UpdateTextFieldManager(Offset(parentGlobalOffset_.GetX(), parentGlobalOffset_.GetY()), frameRect_.Height());
670 bool ret = TextPattern::OnDirtyLayoutWrapperSwap(dirty, config);
671 UpdateScrollStateAfterLayout(config.frameSizeChange);
672 UpdateMagnifierStateAfterLayout(config.frameSizeChange);
673 IF_TRUE(!isRichEditorInit_, FireOnReady());
674 MoveCaretOnLayoutSwap();
675 HandleTasksOnLayoutSwap();
676 HandleSelectOverlayOnLayoutSwap();
677 IF_TRUE(originalFrameRect.GetSize() != frameRect_.GetSize(), {
678 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "frame size change");
679 TriggerAvoidOnCaretChangeNextFrame();
680 });
681 IF_TRUE(!isModifyingContent_, UpdateCaretInfoToController());
682 auto host = GetHost();
683 CHECK_NULL_RETURN(host, ret);
684 auto context = host->GetRenderContext();
685 CHECK_NULL_RETURN(context, ret);
686 if (context->GetClipEdge().has_value()) {
687 auto geometryNode = host->GetGeometryNode();
688 auto frameOffset = geometryNode->GetFrameOffset();
689 auto frameSize = geometryNode->GetFrameSize();
690 auto height = static_cast<float>(paragraphs_.GetHeight() + std::fabs(baselineOffset_));
691 if (!context->GetClipEdge().value() && LessNotEqual(frameSize.Height(), height)) {
692 RectF boundsRect(frameOffset.GetX(), frameOffset.GetY(), frameSize.Width(), height);
693 CHECK_NULL_RETURN(overlayMod_, ret);
694 overlayMod_->SetBoundsRect(boundsRect);
695 }
696 }
697 caretUpdateType_ = CaretUpdateType::NONE;
698 IF_PRESENT(oneStepDragController_, HandleDirtyNodes());
699 UpdateGestureHotZone(dirty);
700 if (afterDragSelect_) {
701 UpdateSelectionAndHandleVisibility();
702 afterDragSelect_ = false;
703 }
704 releaseInDrop_ = false;
705 return ret;
706 }
707
UpdateSelectionAndHandleVisibility()708 void RichEditorPattern::UpdateSelectionAndHandleVisibility()
709 {
710 auto start = recoverStart_;
711 auto end = recoverEnd_;
712 if (textSelector_.GetStart() == start && textSelector_.GetEnd() == end) {
713 return;
714 }
715 auto host = GetHost();
716 CHECK_NULL_VOID(host);
717 if (isMouseOrTouchPad(sourceTool_) && releaseInDrop_) {
718 start = lastCaretPosition_;
719 end = insertValueLength_ + lastCaretPosition_;
720 }
721 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "UpdateSelectionAndHandleVisibility range=[%{public}d--%{public}d]", start, end);
722 textSelector_.Update(start, end);
723
724 if (!isMouseOrTouchPad(sourceTool_)) {
725 if (!selectOverlay_->IsBothHandlesShow() && !selectOverlay_->SelectOverlayIsCreating()) {
726 showSelect_ = true;
727 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
728 CalculateHandleOffsetAndShowOverlay();
729 selectOverlay_->ProcessOverlay({.menuIsShow = false, .animation = false});
730 }
731 }
732 FireOnSelectionChange(start, end, true);
733 }
734
HandleSelectOverlayOnLayoutSwap()735 void RichEditorPattern::HandleSelectOverlayOnLayoutSwap()
736 {
737 bool needToRefreshSelectOverlay = textSelector_.IsValid() && SelectOverlayIsOn() && !IsPreviewTextInputting();
738 CHECK_NULL_VOID(needToRefreshSelectOverlay);
739 auto overlayTask = [weak = WeakClaim(this)]() {
740 auto pattern = weak.Upgrade();
741 CHECK_NULL_VOID(pattern);
742 auto selectOverlay = pattern->selectOverlay_;
743 IF_PRESENT(selectOverlay, UpdateSelectOverlayOnAreaChanged());
744 };
745 if (AnimationUtils::IsImplicitAnimationOpen()) {
746 auto pipeline = GetContext();
747 CHECK_NULL_VOID(pipeline);
748 pipeline->AddAfterRenderTask(overlayTask);
749 } else {
750 overlayTask();
751 }
752 }
753
FireOnReady()754 void RichEditorPattern::FireOnReady()
755 {
756 auto eventHub = GetEventHub<RichEditorEventHub>();
757 CHECK_NULL_VOID(eventHub);
758 eventHub->FireOnReady();
759 ClearOperationRecords();
760 isFirstCallOnReady_ = true;
761 isRichEditorInit_ = true;
762 }
763
MoveCaretOnLayoutSwap()764 void RichEditorPattern::MoveCaretOnLayoutSwap()
765 {
766 MoveCaretAfterTextChange();
767 if (needMoveCaretToContentRect_ || isEditing_) {
768 MoveCaretToContentRect();
769 needMoveCaretToContentRect_ = false;
770 }
771 }
772
CreateImageSourceInfo(const ImageSpanOptions & options)773 std::function<ImageSourceInfo()> RichEditorPattern::CreateImageSourceInfo(const ImageSpanOptions& options)
774 {
775 std::string src;
776 RefPtr<PixelMap> pixMap = nullptr;
777 std::string bundleName;
778 std::string moduleName;
779 if (options.image.has_value()) {
780 src = options.image.value();
781 }
782 if (options.imagePixelMap.has_value()) {
783 pixMap = options.imagePixelMap.value();
784 }
785 if (options.bundleName.has_value()) {
786 bundleName = options.bundleName.value();
787 }
788 if (options.moduleName.has_value()) {
789 moduleName = options.moduleName.value();
790 }
791 auto createSourceInfoFunc = [src, noPixMap = !options.imagePixelMap.has_value(),
792 isUriPureNumber = options.isUriPureNumber.value_or(false), pixMap, bundleName,
793 moduleName]() -> ImageSourceInfo {
794 ImageSourceInfo info;
795 #if defined(PIXEL_MAP_SUPPORTED)
796 if (noPixMap) {
797 info = ImageSourceInfo { src, bundleName, moduleName };
798 } else {
799 info = ImageSourceInfo(pixMap);
800 }
801 #else
802 info = ImageSourceInfo { src, bundleName, moduleName };
803 #endif
804 info.SetIsUriPureNumber(isUriPureNumber);
805 return info;
806 };
807 return std::move(createSourceInfoFunc);
808 }
809
GetTextContentLength()810 int32_t RichEditorPattern::GetTextContentLength()
811 {
812 if (isSpanStringMode_ && styledString_) {
813 return styledString_->GetLength();
814 }
815 if (!spans_.empty()) {
816 auto it = spans_.rbegin();
817 return (*it)->position;
818 }
819 return 0;
820 }
821
SetPreviewMenuParam(TextSpanType spanType,std::function<void ()> & builder,const SelectMenuParam & menuParam)822 void RichEditorPattern::SetPreviewMenuParam(TextSpanType spanType, std::function<void()>& builder, const SelectMenuParam& menuParam)
823 {
824 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "SetPreviewMenuParam, spanType=%{public}d", spanType);
825 if (!oneStepDragController_) {
826 oneStepDragController_ = std::make_unique<OneStepDragController>(WeakClaim(this));
827 }
828 oneStepDragController_->SetMenuParam(spanType, builder, menuParam);
829 }
830
HandleImageDrag(const RefPtr<ImageSpanNode> & imageNode)831 void RichEditorPattern::HandleImageDrag(const RefPtr<ImageSpanNode>& imageNode)
832 {
833 DisableDrag(imageNode);
834 IF_PRESENT(oneStepDragController_, EnableOneStepDrag(TextSpanType::IMAGE, imageNode));
835 }
836
DisableDrag(const RefPtr<ImageSpanNode> & imageNode)837 void RichEditorPattern::DisableDrag(const RefPtr<ImageSpanNode>& imageNode)
838 {
839 // Disable the image itself event
840 imageNode->SetDraggable(false);
841 auto gesture = imageNode->GetOrCreateGestureEventHub();
842 CHECK_NULL_VOID(gesture);
843 gesture->InitDragDropEvent();
844 gesture->SetDragEvent(nullptr, { PanDirection::DOWN }, 0, Dimension(0));
845 }
846
SetGestureOptions(UserGestureOptions options,RefPtr<SpanItem> spanItem)847 void RichEditorPattern::SetGestureOptions(UserGestureOptions options, RefPtr<SpanItem> spanItem)
848 {
849 IF_TRUE(options.onClick, spanItem->SetOnClickEvent(std::move(options.onClick)));
850 IF_TRUE(options.onLongPress, spanItem->SetLongPressEvent(std::move(options.onLongPress)));
851 IF_TRUE(options.onDoubleClick, spanItem->SetDoubleClickEvent(std::move(options.onDoubleClick)));
852 }
853
AddSpanHoverEvent(RefPtr<SpanItem> spanItem,const RefPtr<FrameNode> & frameNode,const SpanOptionBase & options)854 void RichEditorPattern::AddSpanHoverEvent(
855 RefPtr<SpanItem> spanItem, const RefPtr<FrameNode>& frameNode, const SpanOptionBase& options)
856 {
857 auto onHoverFunc = options.userMouseOption.onHover;
858 CHECK_NULL_VOID(spanItem && frameNode && onHoverFunc);
859 auto tag = frameNode->GetTag();
860 spanItem->SetHoverEvent(std::move(onHoverFunc));
861 auto eventHub = frameNode->GetEventHub<EventHub>();
862 CHECK_NULL_VOID(eventHub);
863 auto inputHub = eventHub->GetOrCreateInputEventHub();
864 CHECK_NULL_VOID(inputHub);
865 auto hoverTask = [weak = WeakClaim(Referenced::RawPtr(spanItem)), tag](bool isHover, HoverInfo& info) {
866 auto item = weak.Upgrade();
867 if (item && item->onHover) {
868 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "[%{public}s] onHover status :[%{public}d]", tag.c_str(), isHover);
869 item->onHover(isHover, info);
870 }
871 };
872 auto hoverEvent = MakeRefPtr<InputEvent>(std::move(hoverTask));
873 inputHub->AddOnHoverEvent(hoverEvent);
874 }
875
AddImageSpan(const ImageSpanOptions & options,bool isPaste,int32_t index,bool updateCaret)876 int32_t RichEditorPattern::AddImageSpan(const ImageSpanOptions& options, bool isPaste, int32_t index,
877 bool updateCaret)
878 {
879 if (GetTextContentLength() >= maxLength_.value_or(INT_MAX)) {
880 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "AddImageSpan: Reach the maxLength. maxLength=%{public}d", maxLength_.value_or(INT_MAX));
881 return 0;
882 }
883 auto host = GetHost();
884 CHECK_NULL_RETURN(host, -1);
885 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AddImageSpan, opts=%{public}s, updateCaret=%{public}d",
886 options.ToString().c_str(), updateCaret);
887 NotifyExitTextPreview(false);
888 auto imageNode = ImageSpanNode::GetOrCreateSpanNode(V2::IMAGE_ETS_TAG,
889 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<ImagePattern>(); });
890 auto pattern = imageNode->GetPattern<ImagePattern>();
891 CHECK_NULL_RETURN(pattern, -1);
892 pattern->SetSyncLoad(true);
893 int32_t insertIndex = options.offset.value_or(GetTextContentLength());
894 insertIndex = std::min(insertIndex, GetTextContentLength());
895 RichEditorChangeValue changeValue;
896 CHECK_NULL_RETURN(BeforeAddImage(changeValue, options, insertIndex), -1);
897
898 HandleImageDrag(imageNode);
899 AddOprationWhenAddImage(options.offset.value_or(static_cast<int32_t>(GetTextContentLength())));
900 int32_t spanIndex = TextSpanSplit(insertIndex);
901 IF_TRUE(spanIndex == -1, spanIndex = static_cast<int32_t>(host->GetChildren().size()));
902
903 imageNodes.push_back(imageNode);
904 imageNode->MountToParent(host, spanIndex);
905 auto renderContext = imageNode->GetRenderContext();
906 IF_PRESENT(renderContext, SetNeedAnimateFlag(false));
907 SetImageLayoutProperty(imageNode, options);
908 auto spanItem = imageNode->GetSpanItem();
909 // The length of the imageSpan defaults to the length of a character to calculate the position
910 spanItem->content = u" ";
911 spanItem->SetImageSpanOptions(options);
912 spanItem->spanItemType = SpanItemType::IMAGE;
913 AddSpanItem(spanItem, spanIndex);
914 SetGestureOptions(options.userGestureOption, spanItem);
915 auto userMouseOption = options.userMouseOption;
916 if (userMouseOption.onHover) {
917 spanItem->onHover_ = std::move(userMouseOption.onHover);
918 hoverableNodes.push_back(imageNode);
919 }
920 placeholderCount_++;
921 if (updateCaret) {
922 SetCaretPosition(insertIndex + spanItem->content.length());
923 SetNeedMoveCaretToContentRect();
924 }
925 ResetSelectionAfterAddSpan(isPaste);
926 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
927 host->MarkModifyDone();
928 AfterContentChange(changeValue);
929 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "end");
930 return spanIndex;
931 }
932
CreateHoverInfo(const MouseInfo & info)933 HoverInfo RichEditorPattern::CreateHoverInfo(const MouseInfo& info)
934 {
935 HoverInfo hoverInfo;
936 hoverInfo.SetType(info.GetType());
937 hoverInfo.SetTimeStamp(info.GetTimeStamp());
938 hoverInfo.SetTarget(info.GetTarget());
939 hoverInfo.SetSourceDevice(info.GetSourceDevice());
940 hoverInfo.SetForce(info.GetForce());
941 IF_TRUE(info.GetTiltX(), hoverInfo.SetTiltX(info.GetTiltX().value()));
942 IF_TRUE(info.GetTiltY(), hoverInfo.SetTiltY(info.GetTiltY().value()));
943 hoverInfo.SetSourceTool(info.GetSourceTool());
944 hoverInfo.SetDeviceId(info.GetDeviceId());
945 hoverInfo.SetTargetDisplayId(info.GetTargetDisplayId());
946 hoverInfo.SetStopPropagation(info.IsStopPropagation());
947 hoverInfo.SetPreventDefault(info.IsPreventDefault());
948 hoverInfo.SetPatternName(info.GetPatternName());
949 hoverInfo.SetPressedKeyCodes(info.GetPressedKeyCodes());
950 hoverInfo.SetIsPostEventResult(info.GetIsPostEventResult());
951 hoverInfo.SetPostEventNodeId(info.GetPostEventNodeId());
952 return hoverInfo;
953 }
954
HandleImageHoverEvent(const MouseInfo & mouseInfo)955 void RichEditorPattern::HandleImageHoverEvent(const MouseInfo& mouseInfo)
956 {
957 CHECK_NULL_VOID(mouseInfo.GetAction() == MouseAction::MOVE && !isMousePressed_);
958 ACE_SCOPED_TRACE("RichEditorHandleImageHoverEvent");
959 PointF mouseOffset = { mouseInfo.GetLocalLocation().GetX(), mouseInfo.GetLocalLocation().GetY() };
960 HoverInfo info = CreateHoverInfo(mouseInfo);
961 for (auto it = hoverableNodes.begin(); it != hoverableNodes.end();) {
962 auto spanNode = it->Upgrade();
963 if (!spanNode) {
964 it = hoverableNodes.erase(it);
965 continue;
966 }
967 const auto& imageSpanItem = spanNode->GetSpanItem();
968 if (!imageSpanItem || !imageSpanItem->onHover_) {
969 it = hoverableNodes.erase(it);
970 continue;
971 }
972 const auto& geoNode = spanNode->GetGeometryNode();
973 CHECK_NULL_CONTINUE(geoNode);
974 const auto& imageRect = geoNode->GetFrameRect();
975 if (!imageRect.IsInRegion(mouseOffset)) {
976 ++it;
977 continue;
978 }
979 if (!lastHoverSpanItem_) {
980 imageSpanItem->onHover_(true, info);
981 lastHoverSpanItem_ = imageSpanItem;
982 lastHoverInfo_ = info;
983 return;
984 }
985 CHECK_NULL_VOID(Referenced::RawPtr(lastHoverSpanItem_) != Referenced::RawPtr(imageSpanItem));
986 imageSpanItem->onHover_(true, info);
987 lastHoverSpanItem_->onHover_(false, info);
988 lastHoverSpanItem_ = imageSpanItem;
989 lastHoverInfo_ = info;
990 return;
991 }
992
993 if (lastHoverSpanItem_) {
994 lastHoverSpanItem_->onHover_(false, info);
995 lastHoverSpanItem_.Reset();
996 }
997 }
998
AddOprationWhenAddImage(int32_t beforeCaretPos)999 void RichEditorPattern::AddOprationWhenAddImage(int32_t beforeCaretPos)
1000 {
1001 OperationRecord record;
1002 record.beforeCaretPosition = beforeCaretPos;
1003 record.addText = u" ";
1004 ClearRedoOperationRecords();
1005 record.afterCaretPosition = record.beforeCaretPosition + 1;
1006 AddOperationRecord(record);
1007 }
1008
ResetSelectionAfterAddSpan(bool isPaste)1009 void RichEditorPattern::ResetSelectionAfterAddSpan(bool isPaste)
1010 {
1011 if (isPaste || !textSelector_.IsValid()) {
1012 return;
1013 }
1014 CloseSelectOverlay();
1015 ResetSelection();
1016 if (isEditing_ && !caretVisible_) {
1017 StartTwinkling();
1018 }
1019 }
1020
AddSpanItem(const RefPtr<SpanItem> & item,int32_t offset)1021 void RichEditorPattern::AddSpanItem(const RefPtr<SpanItem>& item, int32_t offset)
1022 {
1023 auto host = GetHost();
1024 CHECK_NULL_VOID(host);
1025 if (offset == -1) {
1026 offset = static_cast<int32_t>(host->GetChildren().size());
1027 }
1028 offset = std::clamp(offset, 0, static_cast<int32_t>(host->GetChildren().size()) - 1);
1029 auto it = spans_.begin();
1030 std::advance(it, offset);
1031 spans_.insert(it, item);
1032 UpdateSpanPosition();
1033 }
1034
OnAttachToFrameNode()1035 void RichEditorPattern::OnAttachToFrameNode()
1036 {
1037 TextPattern::OnAttachToFrameNode();
1038 richEditorInstanceId_ = Container::CurrentIdSafely();
1039 auto frameNode = GetHost();
1040 CHECK_NULL_VOID(frameNode);
1041 frameId_ = frameNode->GetId();
1042 frameNode->GetRenderContext()->UpdateClipEdge(true);
1043 frameNode->GetRenderContext()->SetClipToFrame(true);
1044 StylusDetectorMgr::GetInstance()->AddTextFieldFrameNode(frameNode, WeakClaim(this));
1045 auto context = GetContext();
1046 CHECK_NULL_VOID(context);
1047 context->AddWindowSizeChangeCallback(frameId_);
1048 }
1049
OnDetachFromFrameNode(FrameNode * node)1050 void RichEditorPattern::OnDetachFromFrameNode(FrameNode* node)
1051 {
1052 TextPattern::OnDetachFromFrameNode(node);
1053 ScrollablePattern::OnDetachFromFrameNode(node);
1054 ClearOnFocusTextField(node);
1055 auto context = GetContext();
1056 IF_PRESENT(context, RemoveWindowSizeChangeCallback(frameId_));
1057 }
1058
AddPlaceholderSpan(const RefPtr<UINode> & customNode,const SpanOptionBase & options)1059 int32_t RichEditorPattern::AddPlaceholderSpan(const RefPtr<UINode>& customNode, const SpanOptionBase& options)
1060 {
1061 if (GetTextContentLength() >= maxLength_.value_or(INT_MAX)) {
1062 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "AddPlaceholderSpan: Reach the maxLength. maxLength=%{public}d", maxLength_.value_or(INT_MAX));
1063 return 0;
1064 }
1065 CHECK_NULL_RETURN(customNode, 0);
1066 auto host = GetHost();
1067 CHECK_NULL_RETURN(host, 0);
1068 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AddPlaceholderSpan");
1069 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "options=%{public}s", options.ToString().c_str());
1070 NotifyExitTextPreview(false);
1071 auto placeholderSpanNode = PlaceholderSpanNode::GetOrCreateSpanNode(V2::PLACEHOLDER_SPAN_ETS_TAG,
1072 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<PlaceholderSpanPattern>(); });
1073 CHECK_NULL_RETURN(placeholderSpanNode, 0);
1074 customNode->MountToParent(placeholderSpanNode);
1075 SetSelfAndChildDraggableFalse(customNode);
1076 IF_PRESENT(oneStepDragController_, EnableOneStepDrag(TextSpanType::BUILDER, placeholderSpanNode));
1077 auto focusHub = placeholderSpanNode->GetOrCreateFocusHub();
1078 focusHub->SetFocusable(false);
1079 int32_t insertIndex = options.offset.value_or(GetTextContentLength());
1080 int32_t spanIndex = TextSpanSplit(insertIndex);
1081 if (spanIndex == -1) {
1082 spanIndex = static_cast<int32_t>(host->GetChildren().size());
1083 }
1084 builderNodes.push_back(placeholderSpanNode);
1085 placeholderSpanNode->MountToParent(host, spanIndex);
1086 auto renderContext = placeholderSpanNode->GetRenderContext();
1087 IF_PRESENT(renderContext, SetNeedAnimateFlag(false));
1088 auto spanItem = placeholderSpanNode->GetSpanItem();
1089 spanItem->content = u" ";
1090 spanItem->SetCustomNode(customNode);
1091 spanItem->dragBackgroundColor_ = options.dragBackgroundColor;
1092 spanItem->isDragShadowNeeded_ = options.isDragShadowNeeded;
1093 AddSpanItem(spanItem, spanIndex);
1094 placeholderCount_++;
1095 SetCaretPosition(insertIndex + spanItem->content.length());
1096 ResetSelectionAfterAddSpan(false);
1097 auto placeholderPipelineContext = placeholderSpanNode->GetContext();
1098 IF_PRESENT(placeholderPipelineContext, SetDoKeyboardAvoidAnimate(false));
1099 SetNeedMoveCaretToContentRect();
1100 AddOnPlaceholderHoverEvent(placeholderSpanNode);
1101 placeholderSpanNode->MarkModifyDone();
1102 placeholderSpanNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1103 host->MarkModifyDone();
1104 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1105 return spanIndex;
1106 }
1107
AddOnPlaceholderHoverEvent(const RefPtr<PlaceholderSpanNode> & placeholderSpanNode)1108 void RichEditorPattern::AddOnPlaceholderHoverEvent(const RefPtr<PlaceholderSpanNode>& placeholderSpanNode)
1109 {
1110 CHECK_NULL_VOID(placeholderSpanNode);
1111 auto inputHub = placeholderSpanNode->GetOrCreateInputEventHub();
1112 CHECK_NULL_VOID(inputHub);
1113 auto hoverTask = [weak = WeakClaim(this)](bool isHover) {
1114 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "placeholder, on hover event isHover=%{public}d", isHover);
1115 auto pattern = weak.Upgrade();
1116 CHECK_NULL_VOID(pattern);
1117 pattern->OnPlaceholderHover(isHover);
1118 };
1119 auto hoverEvent = MakeRefPtr<InputEvent>(std::move(hoverTask));
1120 inputHub->AddOnHoverEvent(hoverEvent);
1121 }
1122
OnPlaceholderHover(bool isHover)1123 void RichEditorPattern::OnPlaceholderHover(bool isHover)
1124 {
1125 auto host = GetHost();
1126 CHECK_NULL_VOID(host);
1127 auto pipeline = GetContext();
1128 CHECK_NULL_VOID(pipeline);
1129 auto nodeId = host->GetId();
1130 if (isHover) {
1131 pipeline->FreeMouseStyleHoldNode(nodeId);
1132 } else {
1133 pipeline->FreeMouseStyleHoldNode();
1134 ChangeMouseStyle(MouseFormat::TEXT_CURSOR);
1135 }
1136 }
1137
SetSelfAndChildDraggableFalse(const RefPtr<UINode> & customNode)1138 void RichEditorPattern::SetSelfAndChildDraggableFalse(const RefPtr<UINode>& customNode)
1139 {
1140 CHECK_NULL_VOID(customNode);
1141 auto frameNode = DynamicCast<FrameNode>(customNode);
1142 if (frameNode) {
1143 auto gestureEventHub = frameNode->GetOrCreateGestureEventHub();
1144 IF_PRESENT(gestureEventHub, SetDragForbiddenForcely(true));
1145 }
1146 for (const auto& child : customNode->GetChildren()) {
1147 SetSelfAndChildDraggableFalse(child);
1148 }
1149 }
1150
AddTextSpan(TextSpanOptions options,bool isPaste,int32_t index)1151 int32_t RichEditorPattern::AddTextSpan(TextSpanOptions options, bool isPaste, int32_t index)
1152 {
1153 if (GetTextContentLength() >= maxLength_.value_or(INT_MAX)) {
1154 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "AddTextSpan: Reach the maxLength. maxLength=%{public}d", maxLength_.value_or(INT_MAX));
1155 return 0;
1156 }
1157 auto length = CalculateTruncationLength(options.value, maxLength_.value_or(INT_MAX) - GetTextContentLength());
1158 if (length == 0) {
1159 return -1;
1160 }
1161 options.value = options.value.substr(0, length);
1162 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AddTextSpan, opts=%{public}s", ToBriefString(options).c_str());
1163 SEC_TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AddTextSpan, opts=%{public}s", options.ToString().c_str());
1164 AdjustAddPosition(options);
1165 NotifyExitTextPreview();
1166 OperationRecord record;
1167 auto textContentLength = GetTextContentLength();
1168 if (options.offset.has_value()) {
1169 options.offset = std::clamp(options.offset.value(), 0, textContentLength);
1170 }
1171 record.beforeCaretPosition = std::clamp(options.offset.value_or(textContentLength), 0, textContentLength);
1172 record.addText = options.value;
1173 RichEditorChangeValue changeValue;
1174 CHECK_NULL_RETURN(BeforeChangeText(changeValue, options), -1);
1175 ClearRedoOperationRecords();
1176 record.afterCaretPosition = record.beforeCaretPosition + static_cast<int32_t>(options.value.length());
1177 AddOperationRecord(record);
1178 auto ret = AddTextSpanOperation(options, isPaste, index, false);
1179 SetNeedMoveCaretToContentRect();
1180 if (!previewTextRecord_.IsValid()) {
1181 AfterContentChange(changeValue);
1182 }
1183 return ret;
1184 }
1185
AdjustAddPosition(TextSpanOptions & options)1186 void RichEditorPattern::AdjustAddPosition(TextSpanOptions& options)
1187 {
1188 CHECK_NULL_VOID(IsPreviewTextInputting() && options.offset.has_value());
1189 auto& offset = options.offset.value();
1190 auto delta = offset - previewTextRecord_.startOffset;
1191 offset -= std::min(std::max(0, delta), previewTextRecord_.endOffset - previewTextRecord_.startOffset);
1192 }
1193
AddTextSpanOperation(const TextSpanOptions & options,bool isPaste,int32_t index,bool needLeadingMargin,bool updateCaretPosition)1194 int32_t RichEditorPattern::AddTextSpanOperation(
1195 const TextSpanOptions& options, bool isPaste, int32_t index, bool needLeadingMargin, bool updateCaretPosition)
1196 {
1197 auto host = GetHost();
1198 CHECK_NULL_RETURN(host, -1);
1199
1200 auto* stack = ViewStackProcessor::GetInstance();
1201 auto nodeId = stack->ClaimNodeId();
1202 auto spanNode = SpanNode::GetOrCreateSpanNode(nodeId);
1203
1204 int32_t spanIndex = 0;
1205 int32_t offset = -1;
1206 if (options.offset.has_value()) {
1207 offset = TextSpanSplit(options.offset.value(), needLeadingMargin);
1208 if (offset == -1) {
1209 spanIndex = static_cast<int32_t>(host->GetChildren().size());
1210 } else {
1211 spanIndex = offset;
1212 }
1213 spanNode->MountToParent(host, offset);
1214 } else if (index != -1) {
1215 spanNode->MountToParent(host, index);
1216 spanIndex = index;
1217 } else {
1218 spanIndex = static_cast<int32_t>(host->GetChildren().size());
1219 spanNode->MountToParent(host);
1220 }
1221 auto textStyle = options.style;
1222 if (options.urlAddress.has_value() && options.useThemeFontColor && textStyle.has_value()) {
1223 textStyle.value().SetTextColor(GetUrlSpanColor());
1224 }
1225 auto spanItem = spanNode->GetSpanItem();
1226 spanItem->SetTextStyle(textStyle);
1227 spanItem->useThemeFontColor = options.useThemeFontColor;
1228 spanItem->useThemeDecorationColor = options.useThemeDecorationColor;
1229 UpdateSpanNode(spanNode, options);
1230 AddSpanItem(spanItem, offset);
1231 if (!options.style.has_value()) {
1232 SetDefaultColor(spanNode);
1233 }
1234 if (options.paraStyle) {
1235 UpdateParagraphStyle(spanNode, *options.paraStyle);
1236 }
1237 SetGestureOptions(options.userGestureOption, spanItem);
1238 if (updateCaretPosition && !previewTextRecord_.IsValid()) {
1239 if (options.offset.has_value()) {
1240 SetCaretPosition(options.offset.value() + options.value.length());
1241 } else {
1242 SetCaretPosition(GetTextContentLength());
1243 }
1244 }
1245 ResetSelectionAfterAddSpan(isPaste);
1246 SpanNodeFission(spanNode);
1247 return spanIndex;
1248 }
1249
UpdateSpanNode(RefPtr<SpanNode> spanNode,const TextSpanOptions & options)1250 void RichEditorPattern::UpdateSpanNode(RefPtr<SpanNode> spanNode, const TextSpanOptions& options)
1251 {
1252 spanNode->UpdateContent(options.value);
1253 if (options.style.has_value()) {
1254 const TextStyle& textStyle = options.style.value();
1255 spanNode->UpdateTextColorWithoutCheck(textStyle.GetTextColor());
1256 spanNode->UpdateFontSize(textStyle.GetFontSize());
1257 spanNode->UpdateItalicFontStyle(textStyle.GetFontStyle());
1258 spanNode->UpdateFontWeight(textStyle.GetFontWeight());
1259 spanNode->UpdateFontFamily(textStyle.GetFontFamilies());
1260 spanNode->UpdateTextDecoration(textStyle.GetTextDecoration());
1261 spanNode->UpdateTextDecorationColorWithoutCheck(textStyle.GetTextDecorationColor());
1262 spanNode->UpdateTextDecorationStyle(textStyle.GetTextDecorationStyle());
1263 spanNode->UpdateTextShadow(textStyle.GetTextShadows());
1264 spanNode->UpdateHalfLeading(textStyle.GetHalfLeading());
1265 spanNode->UpdateLineHeight(textStyle.GetLineHeight());
1266 spanNode->UpdateLetterSpacing(textStyle.GetLetterSpacing());
1267 spanNode->UpdateFontFeature(textStyle.GetFontFeatures());
1268 UpdateTextBackgroundStyle(spanNode, textStyle.GetTextBackgroundStyle());
1269 }
1270 UpdateUrlStyle(spanNode, options.urlAddress);
1271 }
1272
UpdateTextBackgroundStyle(RefPtr<SpanNode> & spanNode,const std::optional<TextBackgroundStyle> & style)1273 void RichEditorPattern::UpdateTextBackgroundStyle(
1274 RefPtr<SpanNode>& spanNode, const std::optional<TextBackgroundStyle>& style)
1275 {
1276 CHECK_NULL_VOID(style.has_value());
1277 TextBackgroundStyle backgroundStyle = style.value();
1278 backgroundStyle.needCompareGroupId = true;
1279 backgroundStyle.groupId = ElementRegister::GetInstance()->MakeUniqueId();
1280 spanNode->UpdateTextBackgroundFromParent(backgroundStyle);
1281 }
1282
UpdateUrlStyle(RefPtr<SpanNode> & spanNode,const std::optional<std::u16string> & urlAddressOpt)1283 void RichEditorPattern::UpdateUrlStyle(RefPtr<SpanNode>& spanNode, const std::optional<std::u16string>& urlAddressOpt)
1284 {
1285 CHECK_NULL_VOID(spanNode && urlAddressOpt.has_value());
1286 auto& spanItem = spanNode->GetSpanItem();
1287 CHECK_NULL_VOID(spanItem);
1288 auto& urlAddress = urlAddressOpt.value();
1289
1290 // handle url span callback
1291 std::function<void()> urlOnRelease;
1292 if (!urlAddress.empty()) {
1293 urlOnRelease = [add = UtfUtils::Str16ToStr8(urlAddress)]() {
1294 auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
1295 CHECK_NULL_VOID(pipelineContext);
1296 pipelineContext->HyperlinkStartAbility(add);
1297 };
1298 }
1299 spanItem->SetUrlOnReleaseEvent(std::move(urlOnRelease));
1300 spanItem->urlAddress = urlAddress;
1301
1302 // handle url span color
1303 CHECK_NULL_VOID(spanItem->useThemeFontColor);
1304 if (urlAddress.empty()) {
1305 auto theme = GetTheme<RichEditorTheme>();
1306 CHECK_NULL_VOID(theme);
1307 const auto& themeTextStyle = theme->GetTextStyle();
1308 spanNode->UpdateTextColor(themeTextStyle.GetTextColor());
1309 } else {
1310 spanNode->UpdateTextColor(GetUrlSpanColor());
1311 }
1312 }
1313
AddSymbolSpan(const SymbolSpanOptions & options,bool isPaste,int32_t index)1314 int32_t RichEditorPattern::AddSymbolSpan(const SymbolSpanOptions& options, bool isPaste, int32_t index)
1315 {
1316 if (GetTextContentLength() >= maxLength_.value_or(INT_MAX) - 1) {
1317 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "AddSymbolSpan: Reach the maxLength. maxLength=%{public}d", maxLength_.value_or(INT_MAX));
1318 return 0;
1319 }
1320 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "options=%{public}s", options.ToString().c_str());
1321 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "isPaste=%{public}d, index=%{public}d", isPaste, index);
1322
1323 NotifyExitTextPreview(false);
1324 RichEditorChangeValue changeValue;
1325 CHECK_NULL_RETURN(BeforeAddSymbol(changeValue, options), -1);
1326 OperationRecord record;
1327 record.beforeCaretPosition = options.offset.value_or(static_cast<int32_t>(GetTextContentLength()));
1328 record.addText = u" ";
1329 ClearRedoOperationRecords();
1330 record.afterCaretPosition = record.beforeCaretPosition + 1;
1331 AddOperationRecord(record);
1332 auto ret = AddSymbolSpanOperation(options, isPaste, index);
1333 SetNeedMoveCaretToContentRect();
1334 AfterContentChange(changeValue);
1335 return ret;
1336 }
1337
AddSymbolSpanOperation(const SymbolSpanOptions & options,bool isPaste,int32_t index)1338 int32_t RichEditorPattern::AddSymbolSpanOperation(const SymbolSpanOptions& options, bool isPaste, int32_t index)
1339 {
1340 auto host = GetHost();
1341 CHECK_NULL_RETURN(host, -1);
1342
1343 auto* stack = ViewStackProcessor::GetInstance();
1344 auto nodeId = stack->ClaimNodeId();
1345 auto spanNode = SpanNode::GetOrCreateSpanNode(V2::SYMBOL_SPAN_ETS_TAG, nodeId);
1346
1347 int32_t insertIndex = options.offset.value_or(GetTextContentLength());
1348 int32_t spanIndex = TextSpanSplit(insertIndex);
1349 if (spanIndex == -1) {
1350 spanIndex = static_cast<int32_t>(host->GetChildren().size());
1351 }
1352 spanNode->MountToParent(host, spanIndex);
1353 spanNode->UpdateContent(options.symbolId);
1354 if (options.style.has_value()) {
1355 spanNode->UpdateFontSize(options.style.value().GetFontSize());
1356 spanNode->UpdateFontWeight(options.style.value().GetFontWeight());
1357 spanNode->UpdateSymbolColorList(options.style.value().GetSymbolColorList());
1358 spanNode->UpdateSymbolRenderingStrategy(options.style.value().GetRenderStrategy());
1359 spanNode->UpdateSymbolEffectStrategy(options.style.value().GetEffectStrategy());
1360 spanNode->UpdateSymbolType(options.style.value().GetSymbolType());
1361 spanNode->UpdateFontFamily(options.style.value().GetFontFamilies());
1362 }
1363 auto spanItem = spanNode->GetSpanItem();
1364 spanItem->content = u" ";
1365 spanItem->spanItemType = SpanItemType::SYMBOL;
1366 spanItem->SetSymbolId(options.symbolId);
1367 spanItem->SetTextStyle(options.style);
1368 spanItem->SetResourceObject(options.resourceObject);
1369 AddSpanItem(spanItem, spanIndex);
1370 SetCaretPosition(insertIndex + spanItem->content.length());
1371 ResetSelectionAfterAddSpan(false);
1372 SpanNodeFission(spanNode);
1373 return spanIndex;
1374 }
1375
BeforeAddSymbol(RichEditorChangeValue & changeValue,const SymbolSpanOptions & options)1376 bool RichEditorPattern::BeforeAddSymbol(RichEditorChangeValue& changeValue, const SymbolSpanOptions& options)
1377 {
1378 auto eventHub = GetEventHub<RichEditorEventHub>();
1379 CHECK_NULL_RETURN(eventHub, false);
1380 CHECK_NULL_RETURN(eventHub->HasOnWillChange(), true);
1381
1382 int32_t contentLength = GetTextContentLength();
1383 int32_t insertIndex = options.offset.value_or(contentLength);
1384 insertIndex = std::clamp(insertIndex, 0, contentLength);
1385
1386 changeValue.SetRangeBefore({ insertIndex, insertIndex });
1387 changeValue.SetRangeAfter({ insertIndex, insertIndex + SYMBOL_SPAN_LENGTH });
1388 RichEditorAbstractSpanResult retInfo;
1389 TextInsertValueInfo info;
1390 CalcInsertValueObj(info, insertIndex, true);
1391 int32_t spanIndex = info.GetSpanIndex();
1392 retInfo.SetSpanIndex(spanIndex);
1393 retInfo.SetOffsetInSpan(0);
1394 retInfo.SetValueString(std::to_string(options.symbolId));
1395 retInfo.SetEraseLength(SYMBOL_SPAN_LENGTH);
1396 retInfo.SetSpanRangeStart(insertIndex);
1397 retInfo.SetSpanRangeEnd(insertIndex + SYMBOL_SPAN_LENGTH);
1398 retInfo.SetSpanType(SpanResultType::SYMBOL);
1399
1400 TextStyle style = options.style.value_or(TextStyle());
1401 retInfo.SetSymbolSpanStyle(SymbolSpanStyle(style));
1402 retInfo.SetValueResource(options.resourceObject);
1403
1404 changeValue.SetRichEditorReplacedSymbolSpans(retInfo);
1405 auto ret = eventHub->FireOnWillChange(changeValue);
1406 return ret;
1407 }
1408
AfterContentChange(RichEditorChangeValue & changeValue)1409 void RichEditorPattern::AfterContentChange(RichEditorChangeValue& changeValue)
1410 {
1411 auto eventHub = GetEventHub<RichEditorEventHub>();
1412 if (eventHub && eventHub->HasOnDidChange()) {
1413 eventHub->FireOnDidChange(changeValue);
1414 }
1415 ForceTriggerAvoidOnCaretChange();
1416 }
1417
SpanNodeFission(RefPtr<SpanNode> & spanNode)1418 void RichEditorPattern::SpanNodeFission(RefPtr<SpanNode>& spanNode)
1419 {
1420 auto spanItem = spanNode->GetSpanItem();
1421 auto wContent = spanItem->content;
1422 auto spanStart = spanItem->position - wContent.length();
1423 for (size_t i = 0; i < wContent.length(); i++) {
1424 if (wContent[i] == '\n') {
1425 TextSpanSplit(static_cast<int32_t>(spanStart + i + 1));
1426 }
1427 }
1428 UpdateSpanPosition();
1429 }
1430
DeleteSpans(const RangeOptions & options)1431 void RichEditorPattern::DeleteSpans(const RangeOptions& options)
1432 {
1433 IF_TRUE(previewTextRecord_.previewTextExiting, TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "exiting preview"));
1434 IF_TRUE(IsPreviewTextInputting() && !previewTextRecord_.previewTextExiting, NotifyExitTextPreview());
1435 auto length = GetTextContentLength();
1436 int32_t start = options.start.value_or(0);
1437 int32_t end = options.end.value_or(length);
1438 if (start > end) {
1439 std::swap(start, end);
1440 }
1441 start = std::max(0, start);
1442 end = std::min(length, end);
1443 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "delete spans range=[%{public}d, %{public}d]", start, end);
1444 if (start > length || end < 0 || start == end) {
1445 return;
1446 }
1447 AdjustSelector(start, end);
1448 OperationRecord record;
1449 record.beforeCaretPosition = start;
1450 std::u16string u16valueString;
1451 GetContentBySpans(u16valueString);
1452 record.deleteText = u16valueString.substr(start, end - start);
1453 RichEditorChangeValue changeValue;
1454 changeValue.SetRangeBefore({ start, end });
1455 changeValue.SetRangeAfter({ start, start });
1456 if (auto eventHub = GetEventHub<RichEditorEventHub>(); eventHub) {
1457 CHECK_NULL_VOID(eventHub->FireOnWillChange(changeValue));
1458 }
1459 ClearRedoOperationRecords();
1460 record.afterCaretPosition = start;
1461 AddOperationRecord(record);
1462 DeleteSpansOperation(start, end);
1463 AfterContentChange(changeValue);
1464 }
1465
DeleteSpansOperation(int32_t start,int32_t end)1466 void RichEditorPattern::DeleteSpansOperation(int32_t start, int32_t end)
1467 {
1468 auto startInfo = GetSpanPositionInfo(start);
1469 auto endInfo = GetSpanPositionInfo(end - 1);
1470 if (startInfo.spanIndex_ == endInfo.spanIndex_) {
1471 DeleteSpanByRange(start, end, startInfo);
1472 } else {
1473 DeleteSpansByRange(start, end, startInfo, endInfo);
1474 }
1475 RemoveEmptySpanItems();
1476 if (textSelector_.IsValid()) {
1477 SetCaretPosition(textSelector_.GetTextStart());
1478 CloseSelectOverlay();
1479 ResetSelection();
1480 }
1481 SetCaretOffset(start);
1482 auto host = GetHost();
1483 CHECK_NULL_VOID(host);
1484 if (host->GetChildren().empty() || GetTextContentLength() == 0) {
1485 SetCaretPosition(0);
1486 textForDisplay_.clear();
1487 }
1488 UpdateSpanPosition();
1489 }
1490
RemoveEmptySpanItems()1491 void RichEditorPattern::RemoveEmptySpanItems()
1492 {
1493 for (auto it = spans_.begin(); it != spans_.end();) {
1494 if ((*it)->content.empty()) {
1495 it = spans_.erase(it);
1496 } else {
1497 ++it;
1498 }
1499 }
1500 }
1501
RemoveEmptySpanNodes()1502 void RichEditorPattern::RemoveEmptySpanNodes()
1503 {
1504 auto host = GetHost();
1505 CHECK_NULL_VOID(host);
1506 auto& spanNodes = host->GetChildren();
1507 for (auto it = spanNodes.begin(); it != spanNodes.end();) {
1508 auto spanNode = AceType::DynamicCast<SpanNode>(*it);
1509 if (!spanNode) {
1510 ++it;
1511 continue;
1512 }
1513 if (spanNode->GetSpanItem()->content.empty()) {
1514 it = host->RemoveChild(spanNode);
1515 } else {
1516 ++it;
1517 }
1518 }
1519 }
1520
RemoveEmptySpans()1521 void RichEditorPattern::RemoveEmptySpans()
1522 {
1523 RemoveEmptySpanItems();
1524 RemoveEmptySpanNodes();
1525 }
1526
DeleteSpanByRange(int32_t start,int32_t end,SpanPositionInfo info)1527 void RichEditorPattern::DeleteSpanByRange(int32_t start, int32_t end, SpanPositionInfo info)
1528 {
1529 auto host = GetHost();
1530 CHECK_NULL_VOID(host);
1531 auto childrens = host->GetChildren();
1532 auto it = childrens.begin();
1533 std::advance(it, info.spanIndex_);
1534 CHECK_NULL_VOID(it != childrens.end());
1535 if (start == info.spanStart_ && end == info.spanEnd_) {
1536 ClearContent(*it);
1537 host->RemoveChild(*it);
1538 } else {
1539 auto spanNode = DynamicCast<SpanNode>(*it);
1540 CHECK_NULL_VOID(spanNode);
1541 auto spanItem = spanNode->GetSpanItem();
1542 auto beforStr = spanItem->content.substr(0, start - info.spanStart_);
1543 auto endStr = spanItem->content.substr(end - info.spanStart_);
1544 spanNode->UpdateContent(beforStr + endStr);
1545 }
1546 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1547 host->MarkModifyDone();
1548 }
1549
DeleteSpansByRange(int32_t start,int32_t end,SpanPositionInfo startInfo,SpanPositionInfo endInfo)1550 void RichEditorPattern::DeleteSpansByRange(
1551 int32_t start, int32_t end, SpanPositionInfo startInfo, SpanPositionInfo endInfo)
1552 {
1553 auto host = GetHost();
1554 CHECK_NULL_VOID(host);
1555 auto childrens = host->GetChildren();
1556 CHECK_NULL_VOID(!childrens.empty());
1557
1558 auto itStart = childrens.begin();
1559 if (startInfo.spanIndex_ >= static_cast<int32_t>(childrens.size())) {
1560 std::advance(itStart, static_cast<int32_t>(childrens.size()) - 1);
1561 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "startInfo.spanIndex_ is larger than childrens size");
1562 } else {
1563 std::advance(itStart, startInfo.spanIndex_);
1564 }
1565 CHECK_NULL_VOID(itStart != childrens.end());
1566 auto saveStartSpan = (start == startInfo.spanStart_) ? 0 : 1;
1567 if (saveStartSpan) {
1568 auto spanNodeStart = DynamicCast<SpanNode>(*itStart);
1569 CHECK_NULL_VOID(spanNodeStart);
1570 auto spanItemStart = spanNodeStart->GetSpanItem();
1571 auto beforStr = spanItemStart->content.substr(0, start - startInfo.spanStart_);
1572 spanNodeStart->UpdateContent(beforStr);
1573 }
1574 auto itEnd = childrens.begin();
1575 std::advance(itEnd, endInfo.spanIndex_);
1576 auto delEndSpan = (end == endInfo.spanEnd_) ? 1 : 0;
1577 if (!delEndSpan) {
1578 auto spanNodeEnd = DynamicCast<SpanNode>(*itEnd);
1579 CHECK_NULL_VOID(spanNodeEnd);
1580 auto spanItemEnd = spanNodeEnd->GetSpanItem();
1581 auto endStr = spanItemEnd->content.substr(end - endInfo.spanStart_, endInfo.spanEnd_ - end);
1582 spanNodeEnd->UpdateContent(endStr);
1583 }
1584 auto startIter = childrens.begin();
1585 std::advance(startIter, startInfo.spanIndex_ + saveStartSpan);
1586 auto endIter = childrens.begin();
1587 std::advance(endIter, endInfo.spanIndex_);
1588 for (auto iter = startIter; iter != endIter; ++iter) {
1589 ClearContent(*iter);
1590 host->RemoveChild(*iter);
1591 }
1592 if (endIter != childrens.end() && delEndSpan) {
1593 ClearContent(*endIter);
1594 host->RemoveChild(*endIter);
1595 }
1596 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1597 host->MarkModifyDone();
1598 }
1599
GetLeftTextOfCursor(int32_t number)1600 std::u16string RichEditorPattern::GetLeftTextOfCursor(int32_t number)
1601 {
1602 if (number > caretPosition_) {
1603 number = caretPosition_;
1604 }
1605 auto start = caretPosition_;
1606 if (IsSelected()) {
1607 start = std::min(textSelector_.GetStart(), textSelector_.GetEnd());
1608 }
1609 return GetSelectedText(start - number, start);
1610 }
1611
GetRightTextOfCursor(int32_t number)1612 std::u16string RichEditorPattern::GetRightTextOfCursor(int32_t number)
1613 {
1614 auto end = caretPosition_;
1615 if (IsSelected()) {
1616 end = std::max(textSelector_.GetStart(), textSelector_.GetEnd());
1617 }
1618 return GetSelectedText(end, end + number);
1619 }
1620
GetTextIndexAtCursor()1621 int32_t RichEditorPattern::GetTextIndexAtCursor()
1622 {
1623 return caretPosition_;
1624 }
1625
ClearContent(const RefPtr<UINode> & child)1626 void RichEditorPattern::ClearContent(const RefPtr<UINode>& child)
1627 {
1628 CHECK_NULL_VOID(child);
1629 if (child->GetTag() == V2::SPAN_ETS_TAG) {
1630 auto spanNode = DynamicCast<SpanNode>(child);
1631 if (spanNode) {
1632 spanNode->UpdateContent(u"");
1633 spanNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1634 }
1635 return;
1636 }
1637 auto imageSpanNode = DynamicCast<ImageSpanNode>(child);
1638 if (imageSpanNode) {
1639 imageSpanNode->GetSpanItem()->content.clear();
1640 imageSpanNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1641 return;
1642 }
1643 auto placeholderSpanNode = DynamicCast<PlaceholderSpanNode>(child);
1644 if (placeholderSpanNode) {
1645 placeholderSpanNode->GetSpanItem()->content.clear();
1646 placeholderSpanNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1647 }
1648 }
1649
GetSpanPositionInfo(int32_t position)1650 SpanPositionInfo RichEditorPattern::GetSpanPositionInfo(int32_t position)
1651 {
1652 SpanPositionInfo spanPositionInfo(-1, -1, -1, -1);
1653 CHECK_NULL_RETURN(!spans_.empty(), spanPositionInfo);
1654 position = std::clamp(position, 0, GetTextContentLength());
1655 // find the spanItem where the position is
1656 auto it = std::find_if(spans_.begin(), spans_.end(), [position](const RefPtr<SpanItem>& spanItem) {
1657 return (spanItem->position - static_cast<int32_t>(spanItem->content.length()) <= position) &&
1658 (position < spanItem->position);
1659 });
1660 if (it != spans_.end() && (*it)->unicode != 0 && (*it)->position - caretPosition_ + moveLength_ == 1) {
1661 it++;
1662 moveLength_++;
1663 position++;
1664 }
1665
1666 // the position is at the end
1667 if (it == spans_.end()) {
1668 return spanPositionInfo;
1669 }
1670
1671 spanPositionInfo.spanIndex_ = std::distance(spans_.begin(), it);
1672 int32_t contentLen = static_cast<int32_t>((*it)->content.length());
1673 spanPositionInfo.spanStart_ = (*it)->position - contentLen;
1674 spanPositionInfo.spanEnd_ = (*it)->position;
1675 spanPositionInfo.spanOffset_ = position - spanPositionInfo.spanStart_;
1676 return spanPositionInfo;
1677 }
1678
CopyTextSpanStyle(RefPtr<SpanNode> & source,RefPtr<SpanNode> & target,bool needLeadingMargin)1679 void RichEditorPattern::CopyTextSpanStyle(RefPtr<SpanNode>& source, RefPtr<SpanNode>& target, bool needLeadingMargin)
1680 {
1681 CopyTextSpanFontStyle(source, target);
1682 CopyTextSpanLineStyle(source, target, needLeadingMargin);
1683 CopyTextSpanUrlStyle(source, target);
1684 }
1685
CopyTextSpanFontStyle(RefPtr<SpanNode> & source,RefPtr<SpanNode> & target)1686 void RichEditorPattern::CopyTextSpanFontStyle(RefPtr<SpanNode>& source, RefPtr<SpanNode>& target)
1687 {
1688 CHECK_NULL_VOID(source);
1689 CHECK_NULL_VOID(source->GetTag() == V2::SPAN_ETS_TAG);
1690 CHECK_NULL_VOID(target);
1691 COPY_SPAN_STYLE_IF_PRESENT(source, target, FontSize);
1692 COPY_SPAN_STYLE_IF_PRESENT(source, target, TextColor);
1693 COPY_SPAN_STYLE_IF_PRESENT(source, target, ItalicFontStyle);
1694 COPY_SPAN_STYLE_IF_PRESENT(source, target, FontWeight);
1695 COPY_SPAN_STYLE_IF_PRESENT(source, target, FontFamily);
1696 COPY_SPAN_STYLE_IF_PRESENT(source, target, TextDecoration);
1697 COPY_SPAN_STYLE_IF_PRESENT(source, target, TextDecorationColor);
1698 COPY_SPAN_STYLE_IF_PRESENT(source, target, TextDecorationStyle);
1699 COPY_SPAN_STYLE_IF_PRESENT(source, target, TextCase);
1700 COPY_SPAN_STYLE_IF_PRESENT(source, target, LineHeight);
1701 COPY_SPAN_STYLE_IF_PRESENT(source, target, HalfLeading);
1702 COPY_SPAN_STYLE_IF_PRESENT(source, target, LetterSpacing);
1703 COPY_SPAN_STYLE_IF_PRESENT(source, target, FontFeature);
1704 COPY_SPAN_STYLE_IF_PRESENT(source, target, TextShadow);
1705 target->GetSpanItem()->useThemeFontColor = source->GetSpanItem()->useThemeFontColor;
1706 target->GetSpanItem()->useThemeDecorationColor = source->GetSpanItem()->useThemeDecorationColor;
1707 UpdateTextBackgroundStyle(target, source->GetTextBackgroundStyle());
1708 }
1709
CopyTextSpanLineStyle(RefPtr<SpanNode> & source,RefPtr<SpanNode> & target,bool needLeadingMargin)1710 void RichEditorPattern::CopyTextSpanLineStyle(
1711 RefPtr<SpanNode>& source, RefPtr<SpanNode>& target, bool needLeadingMargin)
1712 {
1713 CHECK_NULL_VOID(source);
1714 CHECK_NULL_VOID(target);
1715 COPY_SPAN_STYLE_IF_PRESENT(source, target, TextAlign);
1716 COPY_SPAN_STYLE_IF_PRESENT(source, target, WordBreak);
1717 COPY_SPAN_STYLE_IF_PRESENT(source, target, LineBreakStrategy);
1718 COPY_SPAN_STYLE_IF_PRESENT(source, target, ParagraphSpacing);
1719 needLeadingMargin |= previewTextRecord_.previewTextHasStarted;
1720 if (source->HasLeadingMargin()) {
1721 auto leadingMargin = source->GetLeadingMarginValue({});
1722 if (!needLeadingMargin) {
1723 leadingMargin.pixmap.Reset();
1724 }
1725 target->UpdateLeadingMargin(leadingMargin);
1726 }
1727 }
1728
CopyTextSpanUrlStyle(RefPtr<SpanNode> & source,RefPtr<SpanNode> & target)1729 void RichEditorPattern::CopyTextSpanUrlStyle(RefPtr<SpanNode>& source, RefPtr<SpanNode>& target)
1730 {
1731 CHECK_NULL_VOID(source && source->GetTag() == V2::SPAN_ETS_TAG);
1732 CHECK_NULL_VOID(target && target->GetTag() == V2::SPAN_ETS_TAG);
1733 const auto& sourceSpanItem = source->GetSpanItem();
1734 const auto& targetSpanItem = target->GetSpanItem();
1735 CHECK_NULL_VOID(sourceSpanItem && targetSpanItem);
1736 targetSpanItem->urlOnRelease = sourceSpanItem->urlOnRelease;
1737 targetSpanItem->urlAddress = sourceSpanItem->urlAddress;
1738 }
1739
CopyGestureOption(const RefPtr<SpanNode> & source,RefPtr<SpanNode> & target)1740 void RichEditorPattern::CopyGestureOption(const RefPtr<SpanNode>& source, RefPtr<SpanNode>& target)
1741 {
1742 CHECK_NULL_VOID(source);
1743 CHECK_NULL_VOID(target);
1744 auto sourceItem = source->GetSpanItem();
1745 CHECK_NULL_VOID(sourceItem);
1746 auto targetItem = target->GetSpanItem();
1747 CHECK_NULL_VOID(targetItem);
1748
1749 if (sourceItem->onClick) {
1750 auto tmpClickFunc = sourceItem->onClick;
1751 targetItem->SetOnClickEvent(std::move(tmpClickFunc));
1752 }
1753 if (sourceItem->onLongPress) {
1754 auto tmpLongPressFunc = sourceItem->onLongPress;
1755 targetItem->SetLongPressEvent(std::move(tmpLongPressFunc));
1756 }
1757 if (sourceItem->onDoubleClick) {
1758 auto tmpDoubleClickFunc = sourceItem->onDoubleClick;
1759 targetItem->SetDoubleClickEvent(std::move(tmpDoubleClickFunc));
1760 }
1761 if (sourceItem->onHover) {
1762 auto tmpHoverFunc = sourceItem->onHover;
1763 targetItem->SetHoverEvent(std::move(tmpHoverFunc));
1764 }
1765 }
1766
TextSpanSplit(int32_t position,bool needLeadingMargin)1767 int32_t RichEditorPattern::TextSpanSplit(int32_t position, bool needLeadingMargin)
1768 {
1769 CHECK_NULL_RETURN(!spans_.empty(), -1);
1770
1771 SpanPositionInfo positionInfo = GetSpanPositionInfo(position);
1772 int32_t spanIndex = positionInfo.spanIndex_;
1773 int32_t spanStart = positionInfo.spanStart_;
1774 int32_t spanEnd = positionInfo.spanEnd_;
1775 int32_t offsetInSpan = positionInfo.spanOffset_;
1776 TAG_LOGD(AceLogTag::ACE_RICH_TEXT,
1777 "position=%{public}d, spanIndex=%{public}d, spanRange=[%{public}d,%{public}d], offsetInSpan=%{public}d",
1778 position, spanIndex, spanStart, spanEnd, offsetInSpan);
1779
1780 CHECK_NULL_RETURN((offsetInSpan > 0), spanIndex);
1781
1782 auto host = GetHost();
1783 CHECK_NULL_RETURN(host, -1);
1784 auto spanNode = DynamicCast<SpanNode>(host->GetChildAtIndex(spanIndex));
1785 CHECK_NULL_RETURN(spanNode, -1);
1786
1787 auto spanItem = spanNode->GetSpanItem();
1788 auto spanItemContent = spanItem->content;
1789 offsetInSpan = std::min(offsetInSpan, static_cast<int32_t>(spanItemContent.length()));
1790
1791 spanNode->UpdateContent(spanItemContent.substr(0, offsetInSpan));
1792 spanItem->position = spanStart + offsetInSpan;
1793
1794 auto nodeId = ViewStackProcessor::GetInstance()->ClaimNodeId();
1795 auto newSpanNode = SpanNode::GetOrCreateSpanNode(nodeId);
1796 CHECK_NULL_RETURN(newSpanNode, -1);
1797
1798 CopyTextSpanStyle(spanNode, newSpanNode, needLeadingMargin);
1799 CopyGestureOption(spanNode, newSpanNode);
1800 newSpanNode->UpdateContent(spanItemContent.substr(offsetInSpan));
1801 newSpanNode->MountToParent(host, spanIndex + 1);
1802
1803 auto newSpanItem = newSpanNode->GetSpanItem();
1804 newSpanItem->rangeStart = spanStart + offsetInSpan;
1805 newSpanItem->position = spanEnd;
1806
1807 auto spanIter = spans_.begin();
1808 std::advance(spanIter, spanIndex + 1);
1809 spans_.insert(spanIter, newSpanItem);
1810
1811 return spanIndex + 1;
1812 }
1813
GetCaretPosition()1814 int32_t RichEditorPattern::GetCaretPosition()
1815 {
1816 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "GetCaretPosition");
1817 return caretPosition_;
1818 }
1819
SetCaretOffset(int32_t caretPosition)1820 bool RichEditorPattern::SetCaretOffset(int32_t caretPosition)
1821 {
1822 if (IsPreviewTextInputting()) {
1823 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "intercept operation in previewText state");
1824 return false;
1825 }
1826 int32_t inputCaretPosition = caretPosition;
1827 AdjustSelector(caretPosition, HandleType::SECOND);
1828 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "setCaretOffset, in=%{public}d, afterAdjust=%{public}d",
1829 inputCaretPosition, caretPosition);
1830 bool success = SetCaretPosition(caretPosition);
1831 auto host = GetHost();
1832 CHECK_NULL_RETURN(host, false);
1833 auto focusHub = host->GetOrCreateFocusHub();
1834 CHECK_NULL_RETURN(focusHub, false);
1835 if (focusHub->IsCurrentFocus()) {
1836 isCursorAlwaysDisplayed_ = false;
1837 StartTwinkling();
1838 }
1839 CloseSelectOverlay();
1840 ResetSelection();
1841 return success;
1842 }
1843
CalcCursorOffsetByPosition(int32_t position,float & selectLineHeight,bool downStreamFirst,bool needLineHighest)1844 OffsetF RichEditorPattern::CalcCursorOffsetByPosition(
1845 int32_t position, float& selectLineHeight, bool downStreamFirst, bool needLineHighest)
1846 {
1847 selectLineHeight = 0.0f;
1848 auto host = GetHost();
1849 CHECK_NULL_RETURN(host, OffsetF(0, 0));
1850 auto pipeline = host->GetContext();
1851 CHECK_NULL_RETURN(pipeline, OffsetF(0, 0));
1852 auto rootOffset = pipeline->GetRootRect().GetOffset();
1853 auto textPaintOffset = richTextRect_.GetOffset();
1854 needLineHighest |= IsCustomSpanInCaretPos(position, downStreamFirst);
1855 auto startOffset = paragraphs_.ComputeCursorOffset(position, selectLineHeight, downStreamFirst, needLineHighest);
1856 auto children = host->GetChildren();
1857 if (NearZero(selectLineHeight)) {
1858 if (children.empty() || GetTextContentLength() == 0) {
1859 return textPaintOffset - rootOffset;
1860 }
1861 if (std::all_of(children.begin(), children.end(), [](RefPtr<UINode>& node) {
1862 CHECK_NULL_RETURN(node, false);
1863 return (node->GetTag() == V2::IMAGE_ETS_TAG || node->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG);
1864 })) {
1865 bool isTail = false;
1866 auto it = children.begin();
1867 if (position >= static_cast<int32_t>(children.size())) {
1868 std::advance(it, (static_cast<int32_t>(children.size()) - 1));
1869 isTail = true;
1870 } else {
1871 std::advance(it, position);
1872 }
1873 if (it == children.end()) {
1874 return startOffset;
1875 }
1876 auto imageNode = DynamicCast<FrameNode>(*it);
1877 if (imageNode) {
1878 auto geometryNode = imageNode->GetGeometryNode();
1879 CHECK_NULL_RETURN(geometryNode, OffsetF(0.0f, 0.0f));
1880 startOffset = geometryNode->GetMarginFrameOffset();
1881 selectLineHeight = geometryNode->GetMarginFrameSize().Height();
1882 startOffset += isTail ? OffsetF(geometryNode->GetMarginFrameSize().Width(), 0.0f) : OffsetF(0.0f, 0.0f);
1883 }
1884 return startOffset;
1885 }
1886 }
1887 auto caretOffset = startOffset + textPaintOffset + rootOffset;
1888 CHECK_NULL_RETURN(overlayMod_, caretOffset);
1889 caretOffset.SetX(std::clamp(caretOffset.GetX(), 0.0f, richTextRect_.Right()));
1890 return caretOffset;
1891 }
1892
HandleCurrentPositionParagraphInfo(float & lastLineTop,float & paragraphSpacing)1893 void RichEditorPattern::HandleCurrentPositionParagraphInfo(float& lastLineTop, float& paragraphSpacing)
1894 {
1895 auto paragraphInfo = paragraphs_.GetParagrahInfo(caretPosition_);
1896 paragraphSpacing = paragraphInfo.paragraphStyle.paragraphSpacing.ConvertToPx();
1897 float lastLineHeight = 0.0f;
1898 CHECK_EQUAL_VOID(paragraphInfo.end - 1 >= paragraphInfo.start, false);
1899 lastLineTop = CalcCursorOffsetByPosition(paragraphInfo.end -1, lastLineHeight, true, true).GetY();
1900 }
1901
IsCustomSpanInCaretPos(int32_t position,bool downStreamFirst)1902 bool RichEditorPattern::IsCustomSpanInCaretPos(int32_t position, bool downStreamFirst)
1903 {
1904 CHECK_NULL_RETURN((isSpanStringMode_ && styledString_), false);
1905 auto start = downStreamFirst ? position : position - 1;
1906 start = std::clamp(start, 0, GetTextContentLength());
1907 auto lastStyles = styledString_->GetSpans(start, 1);
1908 for (auto& style : lastStyles) {
1909 if (style && style->GetSpanType() == SpanType::CustomSpan) {
1910 return true;
1911 }
1912 }
1913 return false;
1914 }
1915
SetCaretPositionWithAffinity(PositionWithAffinity positionWithAffinity)1916 void RichEditorPattern::SetCaretPositionWithAffinity(PositionWithAffinity positionWithAffinity)
1917 {
1918 auto currentPosition = static_cast<int32_t>(positionWithAffinity.position_);
1919 SetCaretPosition(currentPosition);
1920 caretAffinityPolicy_ = (positionWithAffinity.affinity_ == TextAffinity::UPSTREAM)
1921 ? CaretAffinityPolicy::UPSTREAM_FIRST
1922 : CaretAffinityPolicy::DOWNSTREAM_FIRST;
1923 }
1924
SetCaretPosition(int32_t pos,bool needNotifyImf)1925 bool RichEditorPattern::SetCaretPosition(int32_t pos, bool needNotifyImf)
1926 {
1927 auto correctPos = std::clamp(pos, 0, GetTextContentLength());
1928 IF_TRUE(!isModifyingContent_, AdjustSelector(correctPos, HandleType::SECOND));
1929 ResetLastClickOffset();
1930 caretAffinityPolicy_ = CaretAffinityPolicy::DEFAULT;
1931 CHECK_NULL_RETURN((pos == correctPos), false);
1932 if (caretPosition_ != correctPos) {
1933 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "caret:%{public}d->%{public}d", caretPosition_, correctPos);
1934 lastCaretPosition_ = caretPosition_;
1935 caretPosition_ = correctPos;
1936 FireOnSelectionChange(caretPosition_);
1937 if (caretChangeListener_) {
1938 caretChangeListener_(caretPosition_);
1939 }
1940 }
1941 if (needNotifyImf) {
1942 UpdateCaretInfoToController();
1943 }
1944 return true;
1945 }
1946
FireOnSelectionChange(const int32_t caretPosition)1947 void RichEditorPattern::FireOnSelectionChange(const int32_t caretPosition)
1948 {
1949 if (!textSelector_.SelectNothing() || !caretTwinkling_) {
1950 return;
1951 }
1952 FireOnSelectionChange(caretPosition, caretPosition);
1953 }
1954
FireOnSelectionChange(const TextSelector & selector)1955 void RichEditorPattern::FireOnSelectionChange(const TextSelector& selector)
1956 {
1957 if (selector.SelectNothing()) {
1958 return;
1959 }
1960 FireOnSelectionChange(selector.GetStart(), selector.GetEnd());
1961 }
1962
FireOnSelectionChange(int32_t start,int32_t end,bool isForced)1963 void RichEditorPattern::FireOnSelectionChange(int32_t start, int32_t end, bool isForced)
1964 {
1965 auto eventHub = GetEventHub<RichEditorEventHub>();
1966 CHECK_NULL_VOID(eventHub);
1967 CHECK_NULL_VOID(isForced || HasFocus() || dataDetectorAdapter_->hasClickedMenuOption_);
1968 bool isSingleHandle = selectOverlay_->IsSingleHandle();
1969 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "onSelectionChange, range=[%{public}d,%{public}d], isTwinkling=%{public}d, "
1970 "isSingleHandle=%{public}d", start, end, caretTwinkling_, isSingleHandle);
1971 if (start < 0 || end < 0) {
1972 return;
1973 }
1974 if (start == end && !caretTwinkling_ && !isSingleHandle) {
1975 return;
1976 }
1977 if (start > end) {
1978 std::swap(start, end);
1979 }
1980 auto range = SelectionRangeInfo(start, end);
1981 if (range == lastSelectionRange_) {
1982 return;
1983 }
1984 lastSelectionRange_ = std::move(range);
1985 eventHub->FireOnSelectionChange(&range);
1986 }
1987
GetCaretVisible() const1988 bool RichEditorPattern::GetCaretVisible() const
1989 {
1990 return caretVisible_;
1991 }
1992
OnWindowHide()1993 void RichEditorPattern::OnWindowHide()
1994 {
1995 ScrollablePattern::OnWindowHide();
1996 }
1997
SetUpdateSpanStyle(struct UpdateSpanStyle updateSpanStyle)1998 void RichEditorPattern::SetUpdateSpanStyle(struct UpdateSpanStyle updateSpanStyle)
1999 {
2000 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "SetUpdateSpanStyle");
2001 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "updateSpanStyle=%{public}s", updateSpanStyle.ToString().c_str());
2002 updateSpanStyle_ = updateSpanStyle;
2003 }
2004
GetUpdateSpanStyle()2005 UpdateSpanStyle RichEditorPattern::GetUpdateSpanStyle()
2006 {
2007 return updateSpanStyle_;
2008 }
2009
SetTypingStyle(std::optional<struct UpdateSpanStyle> typingStyle,std::optional<TextStyle> textStyle)2010 void RichEditorPattern::SetTypingStyle(std::optional<struct UpdateSpanStyle> typingStyle,
2011 std::optional<TextStyle> textStyle)
2012 {
2013 typingStyle_ = typingStyle;
2014 typingTextStyle_ = textStyle;
2015 if (typingStyle_.has_value() && typingTextStyle_.has_value()) {
2016 IF_TRUE(typingStyle_->updateTextBackgroundStyle,
2017 typingStyle_->updateTextBackgroundStyle->needCompareGroupId = false);
2018 auto textBackgroundStyle = typingTextStyle_->GetTextBackgroundStyle();
2019 if (textBackgroundStyle) {
2020 textBackgroundStyle->needCompareGroupId = false;
2021 typingTextStyle_->SetTextBackgroundStyle(textBackgroundStyle);
2022 }
2023 }
2024 presetParagraph_ = nullptr;
2025 if (spans_.empty() || !previewTextRecord_.previewContent.empty()) {
2026 auto host = GetHost();
2027 CHECK_NULL_VOID(host);
2028 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2029 }
2030 }
2031
GetTypingStyle()2032 std::optional<struct UpdateSpanStyle> RichEditorPattern::GetTypingStyle()
2033 {
2034 return typingStyle_;
2035 }
2036
UpdateFontFeatureTextStyle(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle & updateSpanStyle,TextStyle & textStyle)2037 void RichEditorPattern::UpdateFontFeatureTextStyle(
2038 RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle& updateSpanStyle, TextStyle& textStyle)
2039 {
2040 if (updateSpanStyle.updateFontFeature.has_value()) {
2041 spanNode->UpdateFontFeature(textStyle.GetFontFeatures());
2042 }
2043 }
2044
UpdateTextStyle(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle updateSpanStyle,TextStyle textStyle)2045 void RichEditorPattern::UpdateTextStyle(
2046 RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle updateSpanStyle, TextStyle textStyle)
2047 {
2048 CHECK_NULL_VOID(spanNode->GetTag() == V2::SPAN_ETS_TAG);
2049 auto host = GetHost();
2050 CHECK_NULL_VOID(host);
2051 UpdateFontFeatureTextStyle(spanNode, updateSpanStyle, textStyle);
2052 if (updateSpanStyle.updateTextColor.has_value()) {
2053 spanNode->UpdateTextColorWithoutCheck(textStyle.GetTextColor());
2054 spanNode->GetSpanItem()->useThemeFontColor = false;
2055 }
2056 if (updateSpanStyle.updateLineHeight.has_value()) {
2057 spanNode->UpdateLineHeight(textStyle.GetLineHeight());
2058 }
2059 if (updateSpanStyle.updateHalfLeading.has_value()) {
2060 spanNode->UpdateHalfLeading(textStyle.GetHalfLeading());
2061 }
2062 if (updateSpanStyle.updateLetterSpacing.has_value()) {
2063 spanNode->UpdateLetterSpacing(textStyle.GetLetterSpacing());
2064 }
2065 if (updateSpanStyle.updateFontSize.has_value()) {
2066 spanNode->UpdateFontSize(textStyle.GetFontSize());
2067 }
2068 if (updateSpanStyle.updateItalicFontStyle.has_value()) {
2069 spanNode->UpdateItalicFontStyle(textStyle.GetFontStyle());
2070 }
2071 if (updateSpanStyle.updateFontWeight.has_value()) {
2072 spanNode->UpdateFontWeight(textStyle.GetFontWeight());
2073 }
2074 if (updateSpanStyle.updateFontFamily.has_value()) {
2075 spanNode->UpdateFontFamily(textStyle.GetFontFamilies());
2076 }
2077 UpdateDecoration(spanNode, updateSpanStyle, textStyle);
2078 if (updateSpanStyle.updateTextShadows.has_value()) {
2079 spanNode->UpdateTextShadow(textStyle.GetTextShadows());
2080 }
2081 if (updateSpanStyle.updateTextBackgroundStyle.has_value()) {
2082 UpdateTextBackgroundStyle(spanNode, textStyle.GetTextBackgroundStyle());
2083 }
2084 UpdateUrlStyle(spanNode, updateSpanStyle.updateUrlAddress);
2085
2086 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2087 host->MarkModifyDone();
2088 }
2089
UpdateDecoration(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle & updateSpanStyle,TextStyle & textStyle)2090 void RichEditorPattern::UpdateDecoration(
2091 RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle& updateSpanStyle, TextStyle& textStyle)
2092 {
2093 if (updateSpanStyle.updateTextDecoration.has_value()) {
2094 spanNode->UpdateTextDecoration(textStyle.GetTextDecoration());
2095 spanNode->GetSpanItem()->useThemeDecorationColor = false;
2096 }
2097 if (updateSpanStyle.updateTextDecorationColor.has_value()) {
2098 spanNode->UpdateTextDecorationColorWithoutCheck(textStyle.GetTextDecorationColor());
2099 }
2100 if (updateSpanStyle.updateTextDecorationStyle.has_value()) {
2101 spanNode->UpdateTextDecorationStyle(textStyle.GetTextDecorationStyle());
2102 }
2103 }
2104
UpdateSymbolStyle(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle updateSpanStyle,TextStyle textStyle)2105 void RichEditorPattern::UpdateSymbolStyle(
2106 RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle updateSpanStyle, TextStyle textStyle)
2107 {
2108 CHECK_NULL_VOID(spanNode->GetTag() == V2::SYMBOL_SPAN_ETS_TAG);
2109 auto host = GetHost();
2110 CHECK_NULL_VOID(host);
2111 UpdateFontFeatureTextStyle(spanNode, updateSpanStyle, textStyle);
2112 if (updateSpanStyle.updateSymbolFontSize.has_value()) {
2113 spanNode->UpdateFontSize(updateSpanStyle.updateSymbolFontSize.value());
2114 }
2115 if (updateSpanStyle.updateSymbolFontWeight.has_value()) {
2116 spanNode->UpdateFontWeight(updateSpanStyle.updateSymbolFontWeight.value());
2117 }
2118 if (updateSpanStyle.updateSymbolColor.has_value()) {
2119 spanNode->UpdateSymbolColorList(updateSpanStyle.updateSymbolColor.value());
2120 }
2121 if (updateSpanStyle.updateSymbolRenderingStrategy.has_value()) {
2122 spanNode->UpdateSymbolRenderingStrategy(updateSpanStyle.updateSymbolRenderingStrategy.value());
2123 }
2124 if (updateSpanStyle.updateSymbolEffectStrategy.has_value()) {
2125 spanNode->UpdateSymbolEffectStrategy(updateSpanStyle.updateSymbolEffectStrategy.value());
2126 }
2127 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2128 host->MarkModifyDone();
2129 }
2130
HasSameTypingStyle(const RefPtr<SpanNode> & spanNode)2131 bool RichEditorPattern::HasSameTypingStyle(const RefPtr<SpanNode>& spanNode)
2132 {
2133 auto spanItem = spanNode->GetSpanItem();
2134 CHECK_NULL_RETURN(spanItem, false);
2135 auto spanTextStyle = spanItem->GetTextStyle();
2136 if (spanTextStyle.has_value() && typingTextStyle_.has_value()) {
2137 return spanTextStyle.value() == typingTextStyle_.value();
2138 } else {
2139 return !(spanTextStyle.has_value() || typingTextStyle_.has_value());
2140 }
2141 }
2142
UpdateImageStyle(RefPtr<FrameNode> & imageNode,const ImageSpanAttribute & imageStyle)2143 void RichEditorPattern::UpdateImageStyle(RefPtr<FrameNode>& imageNode, const ImageSpanAttribute& imageStyle)
2144 {
2145 CHECK_NULL_VOID(imageNode);
2146 CHECK_NULL_VOID(imageNode->GetTag() == V2::IMAGE_ETS_TAG);
2147 auto host = GetHost();
2148 CHECK_NULL_VOID(host);
2149 auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
2150 CHECK_NULL_VOID(imageLayoutProperty);
2151 if (updateSpanStyle_.updateImageWidth.has_value() || updateSpanStyle_.updateImageHeight.has_value()) {
2152 imageLayoutProperty->UpdateUserDefinedIdealSize(imageStyle.size->GetSize());
2153 }
2154 if (updateSpanStyle_.updateImageFit.has_value()) {
2155 imageLayoutProperty->UpdateImageFit(imageStyle.objectFit.value());
2156 }
2157 if (updateSpanStyle_.updateImageVerticalAlign.has_value()) {
2158 imageLayoutProperty->UpdateVerticalAlign(imageStyle.verticalAlign.value());
2159 }
2160 if (updateSpanStyle_.borderRadius.has_value()) {
2161 auto imageRenderCtx = imageNode->GetRenderContext();
2162 imageRenderCtx->UpdateBorderRadius(imageStyle.borderRadius.value());
2163 imageRenderCtx->SetClipToBounds(true);
2164 }
2165 if (updateSpanStyle_.marginProp.has_value()) {
2166 imageLayoutProperty->UpdateMargin(imageStyle.marginProp.value());
2167 }
2168 UpdateImageAttribute(imageNode, imageStyle);
2169 imageNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2170 imageNode->MarkModifyDone();
2171 IF_PRESENT(oneStepDragController_, MarkDirtyNode(WeakClaim((ImageSpanNode*) RawPtr(imageNode))));
2172 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2173 host->MarkModifyDone();
2174 }
2175
UpdateImageAttribute(RefPtr<FrameNode> & imageNode,const ImageSpanAttribute & imageStyle)2176 void RichEditorPattern::UpdateImageAttribute(RefPtr<FrameNode>& imageNode, const ImageSpanAttribute& imageStyle)
2177 {
2178 CHECK_NULL_VOID(imageNode);
2179 auto node = DynamicCast<ImageSpanNode>(imageNode);
2180 CHECK_NULL_VOID(node);
2181 auto imageSpanItem = DynamicCast<ImageSpanItem>(node->GetSpanItem());
2182 CHECK_NULL_VOID(imageSpanItem);
2183 imageSpanItem->options.imageAttribute = imageStyle;
2184 imageSpanItem->MarkDirty();
2185 }
2186
SymbolSpanUpdateStyle(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle updateSpanStyle,TextStyle textStyle)2187 bool RichEditorPattern::SymbolSpanUpdateStyle(
2188 RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle updateSpanStyle, TextStyle textStyle)
2189 {
2190 if (spanNode->GetTag() == V2::SYMBOL_SPAN_ETS_TAG) {
2191 UpdateSymbolStyle(spanNode, updateSpanStyle_, textStyle);
2192 return true;
2193 }
2194 return false;
2195 }
2196
UpdateSpanStyle(int32_t start,int32_t end,const TextStyle & textStyle,const ImageSpanAttribute & imageStyle)2197 void RichEditorPattern::UpdateSpanStyle(
2198 int32_t start, int32_t end, const TextStyle& textStyle, const ImageSpanAttribute& imageStyle)
2199 {
2200 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "updateSpanStyle, [%{public}d,%{public}d], %{public}s",
2201 start, end, ToBriefString(textStyle, imageStyle, updateSpanStyle_).c_str());
2202 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "textStyle=%{public}s, imageStyle=%{public}s",
2203 textStyle.ToString().c_str(), imageStyle.ToString().c_str());
2204 auto host = GetHost();
2205 CHECK_NULL_VOID(host);
2206 AdjustSelector(start, end);
2207 int32_t spanStart = 0;
2208 int32_t spanEnd = 0;
2209 for (auto it = host->GetChildren().begin(); it != host->GetChildren().end(); ++it) {
2210 auto spanNode = DynamicCast<SpanNode>(*it);
2211 auto imageNode = DynamicCast<FrameNode>(*it);
2212 if (!spanNode) {
2213 if (spanEnd != 0) {
2214 spanStart = spanEnd;
2215 }
2216 spanEnd = spanStart + 1;
2217 } else {
2218 spanNode->GetSpanItem()->GetIndex(spanStart, spanEnd);
2219 }
2220 if (spanEnd < start) {
2221 continue;
2222 }
2223
2224 if (spanStart >= start && spanEnd <= end) {
2225 if (spanNode) {
2226 UpdateSymbolStyle(spanNode, updateSpanStyle_, textStyle);
2227 UpdateTextStyle(spanNode, updateSpanStyle_, textStyle);
2228 } else {
2229 UpdateImageStyle(imageNode, imageStyle);
2230 }
2231 if (spanEnd == end) {
2232 break;
2233 }
2234 } else if ((spanStart < start && start < spanEnd) || (spanStart < end && end < spanEnd)) {
2235 if (SymbolSpanUpdateStyle(spanNode, updateSpanStyle_, textStyle)) {
2236 continue;
2237 }
2238 auto index = spanStart < start && start < spanEnd ? start : end;
2239 TextSpanSplit(index, true);
2240 --it;
2241 } else if (spanStart >= end) {
2242 break;
2243 }
2244 }
2245 }
2246
GetChildByIndex(int32_t index) const2247 RefPtr<UINode> RichEditorPattern::GetChildByIndex(int32_t index) const
2248 {
2249 auto host = GetHost();
2250 CHECK_NULL_RETURN(host, nullptr);
2251 return host->GetChildAtIndex(index);
2252 }
2253
SetResultObjectText(ResultObject & resultObject,const RefPtr<SpanItem> & spanItem)2254 void RichEditorPattern::SetResultObjectText(ResultObject& resultObject, const RefPtr<SpanItem>& spanItem)
2255 {
2256 CHECK_NULL_VOID(spanItem);
2257 resultObject.valueString = spanItem->content;
2258 if (spanItem->rangeStart <= previewTextRecord_.startOffset && spanItem->position >= previewTextRecord_.endOffset) {
2259 resultObject.previewText = previewTextRecord_.previewContent;
2260 }
2261 resultObject.urlAddress = spanItem->urlAddress;
2262 }
2263
GetContentBySpans(std::u16string & u16Str)2264 void RichEditorPattern::GetContentBySpans(std::u16string& u16Str)
2265 {
2266 uint32_t length = 1;
2267 for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
2268 length += (*iter)->content.length();
2269 }
2270 u16Str.reserve(length);
2271
2272 for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
2273 u16Str.append((*iter)->content);
2274 }
2275 }
2276
SetSelectSpanStyle(int32_t start,int32_t end,KeyCode code,bool isStart)2277 void RichEditorPattern::SetSelectSpanStyle(int32_t start, int32_t end, KeyCode code, bool isStart)
2278 {
2279 TextStyle spanStyle;
2280 struct UpdateSpanStyle updateSpanStyle;
2281 ImageSpanAttribute imageStyle;
2282 auto it = std::find_if(spans_.begin(), spans_.end(), [start](const RefPtr<SpanItem>& spanItem) {
2283 return (spanItem->rangeStart <= start) && (start < spanItem->position);
2284 });
2285 if (it == spans_.end()) {
2286 return;
2287 }
2288 std::optional<TextStyle> spanTextStyle = (*it)->GetTextStyle();
2289 if (spanTextStyle.has_value()) {
2290 spanStyle = spanTextStyle.value();
2291 }
2292 HandleSelectFontStyleWrapper(code, spanStyle);
2293 updateSpanStyle.updateTextColor = spanStyle.GetTextColor();
2294 updateSpanStyle.updateFontSize = spanStyle.GetFontSize();
2295 updateSpanStyle.updateItalicFontStyle = spanStyle.GetFontStyle();
2296 updateSpanStyle.updateFontWeight = spanStyle.GetFontWeight();
2297 updateSpanStyle.updateFontFamily = spanStyle.GetFontFamilies();
2298 updateSpanStyle.updateTextDecoration = spanStyle.GetTextDecoration();
2299 if (!isStart) {
2300 auto updateSpanStyle_ = GetUpdateSpanStyle();
2301 switch (code) {
2302 case KeyCode::KEY_B:
2303 updateSpanStyle.updateFontWeight = updateSpanStyle_.updateFontWeight;
2304 spanStyle.SetFontWeight(updateSpanStyle_.updateFontWeight.value());
2305 break;
2306 case KeyCode::KEY_I:
2307 updateSpanStyle.updateItalicFontStyle = updateSpanStyle_.updateItalicFontStyle;
2308 spanStyle.SetFontStyle(updateSpanStyle_.updateItalicFontStyle.value());
2309 break;
2310 case KeyCode::KEY_U:
2311 updateSpanStyle.updateTextDecoration = updateSpanStyle_.updateTextDecoration;
2312 spanStyle.SetTextDecoration(updateSpanStyle_.updateTextDecoration.value());
2313 break;
2314 default:
2315 LOGW("Unsupported select operation for HandleSelectFontStyleWrapper");
2316 return;
2317 }
2318 }
2319 SetUpdateSpanStyle(updateSpanStyle);
2320 UpdateSpanStyle(start, end, spanStyle, imageStyle);
2321 }
2322
GetSelectSpansPositionInfo(int32_t & start,int32_t & end,SpanPositionInfo & startPositionSpanInfo,SpanPositionInfo & endPositionSpanInfo)2323 void RichEditorPattern::GetSelectSpansPositionInfo(
2324 int32_t& start, int32_t& end, SpanPositionInfo& startPositionSpanInfo, SpanPositionInfo& endPositionSpanInfo)
2325 {
2326 bool isText = false;
2327 auto host = GetHost();
2328 CHECK_NULL_VOID(host);
2329 std::find_if(spans_.begin(), spans_.end(), [&start, &end, &isText](const RefPtr<SpanItem>& spanItem) {
2330 if ((spanItem->rangeStart <= start) && (start < spanItem->position) && start < end) {
2331 if (spanItem->spanItemType == NG::SpanItemType::NORMAL && spanItem->unicode == 0) {
2332 isText = true;
2333 return true;
2334 }
2335 start += spanItem->content.length();
2336 }
2337 return false;
2338 });
2339 CHECK_EQUAL_VOID(isText, false);
2340 std::find_if(spans_.rbegin(), spans_.rend(), [&end](const RefPtr<SpanItem>& spanItem) {
2341 if ((spanItem->rangeStart < end) && (end <= spanItem->position)) {
2342 if (spanItem->spanItemType == NG::SpanItemType::NORMAL && spanItem->unicode == 0) {
2343 return true;
2344 }
2345 end = spanItem->rangeStart;
2346 }
2347 return false;
2348 });
2349 startPositionSpanInfo = GetSpanPositionInfo(start);
2350 startPositionSpanInfo.spanIndex_ =
2351 std::clamp(startPositionSpanInfo.spanIndex_, 0, static_cast<int32_t>(host->GetChildren().size()) - 1);
2352 if (end == GetTextContentLength()) {
2353 endPositionSpanInfo.spanIndex_ = spans_.size() - 1;
2354 auto spanIter = spans_.begin();
2355 endPositionSpanInfo.spanIndex_ =
2356 std::clamp(endPositionSpanInfo.spanIndex_, 0, static_cast<int32_t>(host->GetChildren().size()) - 1);
2357 std::advance(spanIter, endPositionSpanInfo.spanIndex_);
2358 auto contentLen = (*spanIter)->content.length();
2359 endPositionSpanInfo.spanStart_ = (*spanIter)->position - contentLen;
2360 endPositionSpanInfo.spanEnd_ = (*spanIter)->position;
2361 endPositionSpanInfo.spanOffset_ = contentLen;
2362 } else {
2363 endPositionSpanInfo = GetSpanPositionInfo(end);
2364 }
2365 if (endPositionSpanInfo.spanIndex_ == -1) {
2366 endPositionSpanInfo = startPositionSpanInfo;
2367 }
2368 }
2369
GetSpanNodeIter(int32_t index)2370 std::list<RefPtr<UINode>>::const_iterator RichEditorPattern::GetSpanNodeIter(int32_t index)
2371 {
2372 auto host = GetHost();
2373 CHECK_NULL_RETURN(host, {});
2374 auto spanNodeIter = host->GetChildren().begin();
2375 std::advance(spanNodeIter, index);
2376 return spanNodeIter;
2377 }
2378
GetSelectSpanSplit(SpanPositionInfo & startPositionSpanInfo,SpanPositionInfo & endPositionSpanInfo)2379 std::list<SpanPosition> RichEditorPattern::GetSelectSpanSplit(
2380 SpanPositionInfo& startPositionSpanInfo, SpanPositionInfo& endPositionSpanInfo)
2381 {
2382 std::list<SpanPosition> resultObjects;
2383 int32_t spanIndex = 0;
2384 auto itStart = GetSpanNodeIter(startPositionSpanInfo.spanIndex_);
2385 auto itEnd = GetSpanNodeIter(endPositionSpanInfo.spanIndex_);
2386 auto itEndNext = GetSpanNodeIter(endPositionSpanInfo.spanIndex_ + 1);
2387 for (auto itSelect = itStart; itSelect != itEndNext; itSelect++) {
2388 SpanPosition resultObject;
2389 auto spanNode = DynamicCast<SpanNode>(*itSelect);
2390 if (!spanNode || spanNode->GetTag() != V2::SPAN_ETS_TAG) {
2391 continue;
2392 }
2393 auto spanItem = spanNode->GetSpanItem();
2394 if (itSelect == itStart) {
2395 if (startPositionSpanInfo.spanOffset_ == 0) {
2396 resultObject.spanRange[RichEditorSpanRange::RANGESTART] = startPositionSpanInfo.spanStart_;
2397 } else {
2398 resultObject.spanRange[RichEditorSpanRange::RANGESTART] =
2399 startPositionSpanInfo.spanStart_ + startPositionSpanInfo.spanOffset_;
2400 }
2401 resultObject.spanRange[RichEditorSpanRange::RANGEEND] = startPositionSpanInfo.spanEnd_;
2402 resultObject.spanIndex = spanIndex;
2403 spanIndex++;
2404 resultObjects.emplace_back(resultObject);
2405 continue;
2406 }
2407 if (itSelect == itEnd) {
2408 resultObject.spanRange[RichEditorSpanRange::RANGESTART] = endPositionSpanInfo.spanStart_;
2409 if (endPositionSpanInfo.spanOffset_ == static_cast<int32_t>(spanItem->content.size())) {
2410 resultObject.spanRange[RichEditorSpanRange::RANGEEND] = endPositionSpanInfo.spanEnd_;
2411 } else {
2412 resultObject.spanRange[RichEditorSpanRange::RANGEEND] =
2413 endPositionSpanInfo.spanStart_ + endPositionSpanInfo.spanOffset_;
2414 }
2415 resultObject.spanIndex = spanIndex;
2416 spanIndex++;
2417 resultObjects.emplace_back(resultObject);
2418 continue;
2419 }
2420 resultObject.spanRange[RichEditorSpanRange::RANGESTART] =
2421 spanItem->position - spanItem->content.length();
2422 resultObject.spanRange[RichEditorSpanRange::RANGEEND] = spanItem->position;
2423 resultObject.spanIndex = spanIndex;
2424 spanIndex++;
2425 resultObjects.emplace_back(resultObject);
2426 }
2427 return resultObjects;
2428 }
2429
GetSelectSpanInfo(int32_t start,int32_t end)2430 std::list<SpanPosition> RichEditorPattern::GetSelectSpanInfo(int32_t start, int32_t end)
2431 {
2432 SpanPositionInfo startPositionSpanInfo(-1, -1, -1, -1);
2433 SpanPositionInfo endPositionSpanInfo(-1, -1, -1, -1);
2434 std::list<SpanPosition> resultObjects;
2435 int32_t spanIndex = 0;
2436 GetSelectSpansPositionInfo(start, end, startPositionSpanInfo, endPositionSpanInfo);
2437 CHECK_EQUAL_RETURN(startPositionSpanInfo.spanStart_, -1, resultObjects);
2438 if (startPositionSpanInfo.spanIndex_ == endPositionSpanInfo.spanIndex_) {
2439 SpanPosition resultObject;
2440 resultObject.spanRange[RichEditorSpanRange::RANGESTART] = start;
2441 resultObject.spanRange[RichEditorSpanRange::RANGEEND] = end;
2442 resultObject.spanIndex = spanIndex;
2443 resultObjects.emplace_back(resultObject);
2444 } else {
2445 resultObjects = GetSelectSpanSplit(startPositionSpanInfo, endPositionSpanInfo);
2446 }
2447 return resultObjects;
2448 }
2449
GetSpansInfoByRange(int32_t start,int32_t end)2450 SelectionInfo RichEditorPattern::GetSpansInfoByRange(int32_t start, int32_t end)
2451 {
2452 auto selectionInfo = GetSpansInfo(start, end , GetSpansMethod::GETSPANS);
2453 auto& resultObjects = selectionInfo.GetSelectionRef().resultObjects;
2454 for (auto& resObj : resultObjects) {
2455 CHECK_NULL_CONTINUE(resObj.type == SelectSpanType::TYPESPAN);
2456 auto uiNode = GetChildByIndex(resObj.spanPosition.spanIndex);
2457 auto spanNode = DynamicCast<SpanNode>(uiNode);
2458 CHECK_NULL_CONTINUE(spanNode);
2459 auto fontFamily = spanNode->GetFontFamily();
2460 IF_TRUE(!fontFamily, resObj.textStyle.fontFamily.clear());
2461 }
2462 return selectionInfo;
2463 }
2464
UpdateSelectSpanStyle(int32_t start,int32_t end,KeyCode code)2465 void RichEditorPattern::UpdateSelectSpanStyle(int32_t start, int32_t end, KeyCode code)
2466 {
2467 std::list<SpanPosition> resultObjects;
2468 resultObjects = GetSelectSpanInfo(start, end);
2469 bool isFirstText = true;
2470 for (auto& spanStyleIter : resultObjects) {
2471 SetSelectSpanStyle(spanStyleIter.spanRange[RichEditorSpanRange::RANGESTART],
2472 spanStyleIter.spanRange[RichEditorSpanRange::RANGEEND], code, isFirstText);
2473 isFirstText = false;
2474 }
2475 }
2476
CheckStyledStringRangeValid(int32_t start,int32_t length)2477 bool RichEditorPattern::CheckStyledStringRangeValid(int32_t start, int32_t length)
2478 {
2479 if (!styledString_ || !styledString_->CheckRange(start, length)) {
2480 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "range:[%{public}d-%{public}d] is invalid or styledString is null",
2481 start, start + length);
2482 return false;
2483 }
2484 return true;
2485 }
2486
UpdateSelectStyledStringStyle(int32_t start,int32_t end,KeyCode code)2487 void RichEditorPattern::UpdateSelectStyledStringStyle(int32_t start, int32_t end, KeyCode code)
2488 {
2489 CHECK_NULL_VOID(CheckStyledStringRangeValid(start, end - start));
2490 Font updateFont;
2491 bool isFirstSpanStylePresent;
2492 switch (code) {
2493 case KeyCode::KEY_B: {
2494 auto firstFontSpan = DynamicCast<FontSpan>(styledString_->GetSpan(start, 1, SpanType::Font));
2495 isFirstSpanStylePresent = firstFontSpan && firstFontSpan->GetFont().fontWeight == FontWeight::BOLD;
2496 updateFont.fontWeight = isFirstSpanStylePresent ? FontWeight::NORMAL : FontWeight::BOLD;
2497 UpdateStyledStringFontStyle(start, end, updateFont);
2498 break;
2499 }
2500 case KeyCode::KEY_I: {
2501 auto firstFontSpan = DynamicCast<FontSpan>(styledString_->GetSpan(start, 1, SpanType::Font));
2502 isFirstSpanStylePresent = firstFontSpan && firstFontSpan->GetFont().fontStyle == OHOS::Ace::FontStyle::ITALIC;
2503 updateFont.fontStyle = isFirstSpanStylePresent ? OHOS::Ace::FontStyle::NORMAL : OHOS::Ace::FontStyle::ITALIC;
2504 UpdateStyledStringFontStyle(start, end, updateFont);
2505 break;
2506 }
2507 case KeyCode::KEY_U: {
2508 auto firstDecorationSpan = DynamicCast<DecorationSpan>(styledString_->GetSpan(start, 1, SpanType::Decoration));
2509 isFirstSpanStylePresent =
2510 firstDecorationSpan && firstDecorationSpan->GetTextDecorationType() == TextDecoration::UNDERLINE;
2511 auto updateDecorationType = isFirstSpanStylePresent ? TextDecoration::NONE : TextDecoration::UNDERLINE;
2512 UpdateStyledStringDecorationType(start, end, updateDecorationType);
2513 break;
2514 }
2515 default:
2516 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "Unsupported key code for UpdateSelectStyledStringStyle");
2517 return;
2518 }
2519 }
2520
2521 template<typename T>
UpdateSpansStyleInRange(int32_t start,int32_t end,const RefPtr<SpanBase> & baseSpan,std::function<RefPtr<T> (const RefPtr<T> &)> && updateSpanFunc)2522 void RichEditorPattern::UpdateSpansStyleInRange(int32_t start, int32_t end, const RefPtr<SpanBase>& baseSpan,
2523 std::function<RefPtr<T>(const RefPtr<T>&)>&& updateSpanFunc)
2524 {
2525 auto length = end - start;
2526 CHECK_NULL_VOID(CheckStyledStringRangeValid(start, length));
2527 CHECK_NULL_VOID(baseSpan);
2528 auto spanType = baseSpan->GetSpanType();
2529 std::vector<RefPtr<SpanBase>> updateSpans;
2530 updateSpans.push_back(baseSpan);
2531 auto originalSpans = styledString_->GetSpans(start, length, spanType);
2532 for (auto& originalSpan : originalSpans) {
2533 auto originalTypedSpan = DynamicCast<T>(originalSpan);
2534 CHECK_NULL_CONTINUE(originalTypedSpan)
2535 updateSpans.push_back(updateSpanFunc(originalTypedSpan));
2536 }
2537 paragraphCache_.Clear();
2538 styledString_->BindWithSpans(updateSpans);
2539 styledString_->NotifySpanWatcher();
2540 }
2541
UpdateStyledStringFontStyle(int32_t start,int32_t end,const Font & font)2542 void RichEditorPattern::UpdateStyledStringFontStyle(int32_t start, int32_t end, const Font& font)
2543 {
2544 auto fontSpan = AceType::MakeRefPtr<FontSpan>(font, start, end);
2545 auto updateFontSpanFunc = [&font](const RefPtr<FontSpan>& oriFontSpan) -> RefPtr<FontSpan> {
2546 CHECK_NULL_RETURN(oriFontSpan, nullptr);
2547 auto fontStyle = oriFontSpan->GetFont();
2548 if (font.fontStyle.has_value()) {
2549 fontStyle.fontStyle = font.fontStyle.value();
2550 }
2551 if (font.fontWeight.has_value()) {
2552 fontStyle.fontWeight = font.fontWeight.value();
2553 }
2554 return AceType::MakeRefPtr<FontSpan>(fontStyle, oriFontSpan->GetStartIndex(), oriFontSpan->GetEndIndex());
2555 };
2556 UpdateSpansStyleInRange<FontSpan>(start, end, fontSpan, updateFontSpanFunc);
2557 }
2558
UpdateStyledStringDecorationType(int32_t start,int32_t end,const TextDecoration & type)2559 void RichEditorPattern::UpdateStyledStringDecorationType(int32_t start, int32_t end, const TextDecoration& type)
2560 {
2561 std::optional<Color> colorOption;
2562 std::optional<TextDecorationStyle> styleOption;
2563 auto decorationSpan = AceType::MakeRefPtr<DecorationSpan>(type, colorOption, styleOption, start, end);
2564 auto updateDecorationSpanFunc = [&type](const RefPtr<DecorationSpan>& oriDecorationSpan) -> RefPtr<DecorationSpan> {
2565 CHECK_NULL_RETURN(oriDecorationSpan, nullptr);
2566 auto decorationColor = oriDecorationSpan->GetColor();
2567 auto decorationStyle = oriDecorationSpan->GetTextDecorationStyle();
2568 return AceType::MakeRefPtr<DecorationSpan>(type, decorationColor, decorationStyle,
2569 oriDecorationSpan->GetStartIndex(), oriDecorationSpan->GetEndIndex());
2570 };
2571 UpdateSpansStyleInRange<DecorationSpan>(start, end, decorationSpan, updateDecorationSpanFunc);
2572 }
2573
CloseSystemMenu()2574 void RichEditorPattern::CloseSystemMenu()
2575 {
2576 if (!SelectOverlayIsOn()) {
2577 return;
2578 }
2579 auto selectOverlayInfo = selectOverlay_->GetSelectOverlayInfo();
2580 if (selectOverlayInfo && !selectOverlayInfo->menuInfo.menuBuilder) {
2581 CloseSelectOverlay();
2582 }
2583 }
2584
SetAccessibilityAction()2585 void RichEditorPattern::SetAccessibilityAction()
2586 {
2587 auto host = GetHost();
2588 CHECK_NULL_VOID(host);
2589 auto property = host->GetAccessibilityProperty<AccessibilityProperty>();
2590 CHECK_NULL_VOID(property);
2591 property->SetActionSetSelection([weakPtr = WeakClaim(this)](int32_t start, int32_t end, bool isForward) {
2592 TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
2593 "Accessibility SetSelection, range=[%{public}d,%{public}d], isForward=%{public}d", start, end, isForward);
2594 const auto& pattern = weakPtr.Upgrade();
2595 CHECK_NULL_VOID(pattern);
2596 pattern->SetSelection(start, end, std::nullopt, isForward);
2597 });
2598
2599 property->SetActionSetIndex([weakPtr = WeakClaim(this)](int32_t index) {
2600 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Accessibility SetCaretOffset, index=%{public}d", index);
2601 const auto& pattern = weakPtr.Upgrade();
2602 CHECK_NULL_VOID(pattern);
2603 pattern->SetCaretOffset(index);
2604 });
2605
2606 property->SetActionGetIndex([weakPtr = WeakClaim(this)]() -> int32_t {
2607 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Accessibility GetCaretPosition");
2608 const auto& pattern = weakPtr.Upgrade();
2609 CHECK_NULL_RETURN(pattern, -1);
2610 return pattern->GetCaretPosition();
2611 });
2612 SetAccessibilityEditAction();
2613 }
2614
SetAccessibilityEditAction()2615 void RichEditorPattern::SetAccessibilityEditAction()
2616 {
2617 auto host = GetHost();
2618 CHECK_NULL_VOID(host);
2619 auto property = host->GetAccessibilityProperty<AccessibilityProperty>();
2620 CHECK_NULL_VOID(property);
2621 property->SetActionSetText([weakPtr = WeakClaim(this)](const std::string& value) {
2622 std::u16string u16Value = UtfUtils::Str8ToStr16(value);
2623 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "EditAction setText, length: %{public}d",
2624 static_cast<int32_t>(u16Value.length()));
2625 const auto& pattern = weakPtr.Upgrade();
2626 CHECK_NULL_VOID(pattern);
2627 pattern->InsertValue(u16Value);
2628 });
2629
2630 property->SetActionCopy([weakPtr = WeakClaim(this)]() {
2631 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "EditAction copy");
2632 const auto& pattern = weakPtr.Upgrade();
2633 CHECK_NULL_VOID(pattern);
2634 pattern->HandleOnCopy();
2635 pattern->CloseSelectionMenu();
2636 });
2637
2638 property->SetActionCut([weakPtr = WeakClaim(this)]() {
2639 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "EditAction cut");
2640 const auto& pattern = weakPtr.Upgrade();
2641 CHECK_NULL_VOID(pattern);
2642 pattern->HandleOnCut();
2643 });
2644
2645 property->SetActionPaste([weakPtr = WeakClaim(this)]() {
2646 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "EditAction paste");
2647 const auto& pattern = weakPtr.Upgrade();
2648 CHECK_NULL_VOID(pattern);
2649 pattern->HandleOnPaste();
2650 pattern->CloseSelectionMenu();
2651 });
2652
2653 property->SetActionClearSelection([weakPtr = WeakClaim(this)]() {
2654 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "EditAction clearSelection");
2655 const auto& pattern = weakPtr.Upgrade();
2656 CHECK_NULL_VOID(pattern);
2657 pattern->CloseSelectionMenu();
2658 pattern->ResetSelection();
2659 pattern->StartTwinkling();
2660 });
2661 }
2662
IsAccessibilityClick()2663 bool RichEditorPattern::IsAccessibilityClick()
2664 {
2665 auto host = GetHost();
2666 CHECK_NULL_RETURN(host, false);
2667 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
2668 CHECK_NULL_RETURN(accessibilityProperty, false);
2669 return accessibilityProperty->GetAccessibilityFocusState();
2670 }
2671
GetParagraphInfo(int32_t start,int32_t end)2672 std::vector<ParagraphInfo> RichEditorPattern::GetParagraphInfo(int32_t start, int32_t end)
2673 {
2674 std::vector<ParagraphInfo> res;
2675 auto spanNodes = GetParagraphNodes(start, end);
2676 CHECK_NULL_RETURN(!spanNodes.empty(), {});
2677
2678 auto&& firstSpan = spanNodes.front()->GetSpanItem();
2679 auto paraStart = firstSpan->position - static_cast<int32_t>(firstSpan->content.length());
2680
2681 for (auto it = spanNodes.begin(); it != spanNodes.end(); ++it) {
2682 if (it == std::prev(spanNodes.end()) || (*it)->GetSpanItem()->content.back() == u'\n') {
2683 ParagraphInfo info;
2684 auto lm = (*it)->GetLeadingMarginValue({});
2685 std::optional<double> spacingOpt;
2686 if (auto spacing = (*it)->GetParagraphSpacing(); spacing.has_value()) {
2687 spacingOpt = spacing.value().ConvertToFp();
2688 }
2689 res.emplace_back(ParagraphInfo {
2690 .leadingMarginPixmap = lm.pixmap,
2691 .leadingMarginSize = { lm.size.Width().ToString(),
2692 lm.size.Height().ToString() },
2693 .textAlign = static_cast<int32_t>((*it)->GetTextAlignValue(TextAlign::START)),
2694 .wordBreak = static_cast<int32_t>((*it)->GetWordBreakValue(WordBreak::BREAK_WORD)),
2695 .lineBreakStrategy = static_cast<int32_t>((*it)->GetLineBreakStrategyValue(LineBreakStrategy::GREEDY)),
2696 .paragraphSpacing = spacingOpt,
2697 .range = { paraStart, (*it)->GetSpanItem()->position },
2698 });
2699 paraStart = (*it)->GetSpanItem()->position;
2700 }
2701 }
2702
2703 return res;
2704 }
2705
GetParagraphLength(const std::list<RefPtr<UINode>> & spans) const2706 int32_t RichEditorPattern::GetParagraphLength(const std::list<RefPtr<UINode>>& spans) const
2707 {
2708 if (spans.empty()) {
2709 return 0;
2710 }
2711 int32_t imageSpanCnt = 0;
2712 for (auto it = spans.rbegin(); it != spans.rend(); ++it) {
2713 auto spanNode = DynamicCast<SpanNode>(*it);
2714 if (spanNode) {
2715 return spanNode->GetSpanItem()->position + imageSpanCnt;
2716 }
2717 ++imageSpanCnt;
2718 }
2719 return imageSpanCnt;
2720 }
2721
GetParagraphNodes(int32_t start,int32_t end) const2722 std::vector<RefPtr<SpanNode>> RichEditorPattern::GetParagraphNodes(int32_t start, int32_t end) const
2723 {
2724 CHECK_NULL_RETURN(start != end, {});
2725 auto host = GetHost();
2726 CHECK_NULL_RETURN(host, {});
2727 CHECK_NULL_RETURN(!host->GetChildren().empty(), {});
2728
2729 const auto& spans = host->GetChildren();
2730 int32_t length = GetParagraphLength(spans);
2731 std::vector<RefPtr<SpanNode>> res;
2732
2733 if (start >= length) {
2734 return res;
2735 }
2736
2737 auto headIt = spans.begin();
2738 auto flagNode = headIt;
2739 bool isEnd = false;
2740 int32_t spanEnd = -1;
2741 while (flagNode != spans.end()) {
2742 auto spanNode = DynamicCast<SpanNode>(*flagNode);
2743 if (spanNode) {
2744 auto&& info = spanNode->GetSpanItem();
2745 spanEnd = info->position;
2746 isEnd = info->content.back() == u'\n';
2747 } else {
2748 ++spanEnd;
2749 isEnd = false;
2750 }
2751 flagNode++;
2752 if (spanEnd > start) {
2753 break;
2754 }
2755 if (isEnd) {
2756 headIt = flagNode;
2757 }
2758 }
2759 while (headIt != flagNode) {
2760 auto spanNode = DynamicCast<SpanNode>(*headIt);
2761 if (spanNode) {
2762 res.emplace_back(spanNode);
2763 }
2764 headIt++;
2765 }
2766 while (flagNode != spans.end() && (spanEnd < end || !isEnd)) {
2767 auto spanNode = DynamicCast<SpanNode>(*flagNode);
2768 if (spanNode) {
2769 res.emplace_back(spanNode);
2770 auto&& info = spanNode->GetSpanItem();
2771 spanEnd = info->position;
2772 isEnd = info->content.back() == u'\n';
2773 } else {
2774 ++spanEnd;
2775 isEnd = false;
2776 }
2777 flagNode++;
2778 }
2779
2780 return res;
2781 }
2782
UpdateParagraphStyle(int32_t start,int32_t end,const struct UpdateParagraphStyle & style)2783 void RichEditorPattern::UpdateParagraphStyle(int32_t start, int32_t end, const struct UpdateParagraphStyle& style)
2784 {
2785 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "updateParagraphStyle, range=[%{public}d,%{public}d]", start, end);
2786 auto spanNodes = GetParagraphNodes(start, end);
2787 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "spanNode cnt=%{public}d, style=%{public}s",
2788 static_cast<int32_t>(spanNodes.size()), style.ToString().c_str());
2789 for (const auto& spanNode : spanNodes) {
2790 UpdateParagraphStyle(spanNode, style);
2791 }
2792 }
2793
UpdateParagraphStyle(RefPtr<SpanNode> spanNode,const struct UpdateParagraphStyle & style)2794 void RichEditorPattern::UpdateParagraphStyle(RefPtr<SpanNode> spanNode, const struct UpdateParagraphStyle& style)
2795 {
2796 CHECK_NULL_VOID(spanNode);
2797 spanNode->UpdateTextAlign(style.textAlign.value_or(TextAlign::START));
2798 spanNode->UpdateWordBreak(style.wordBreak.value_or(WordBreak::BREAK_WORD));
2799 spanNode->UpdateLineBreakStrategy(style.lineBreakStrategy.value_or(LineBreakStrategy::GREEDY));
2800 if (style.paragraphSpacing.has_value()) {
2801 spanNode->UpdateParagraphSpacing(style.paragraphSpacing.value());
2802 } else {
2803 spanNode->ResetParagraphSpacing();
2804 }
2805 if (style.leadingMargin.has_value()) {
2806 spanNode->GetSpanItem()->leadingMargin = *style.leadingMargin;
2807 spanNode->UpdateLeadingMargin(*style.leadingMargin);
2808 }
2809 IF_PRESENT(GetHost(), MarkDirtyNode(PROPERTY_UPDATE_MEASURE));
2810 }
2811
ScheduleCaretTwinkling()2812 void RichEditorPattern::ScheduleCaretTwinkling()
2813 {
2814 ContainerScope scope(richEditorInstanceId_);
2815 auto host = GetHost();
2816 CHECK_NULL_VOID(host);
2817 auto context = host->GetContext();
2818 CHECK_NULL_VOID(context);
2819
2820 if (!context->GetTaskExecutor()) {
2821 return;
2822 }
2823
2824 if (isCursorAlwaysDisplayed_) {
2825 return;
2826 }
2827
2828 auto weak = WeakClaim(this);
2829 caretTwinklingTask_.Reset([weak, instanceId = richEditorInstanceId_] {
2830 ContainerScope scope(instanceId);
2831 auto client = weak.Upgrade();
2832 CHECK_NULL_VOID(client);
2833 client->OnCaretTwinkling();
2834 });
2835 auto taskExecutor = context->GetTaskExecutor();
2836 CHECK_NULL_VOID(taskExecutor);
2837 taskExecutor->PostDelayedTask(caretTwinklingTask_, TaskExecutor::TaskType::UI, twinklingInterval_,
2838 "ArkUIRichEditorScheduleCaretTwinkling");
2839 }
2840
StartTwinkling()2841 void RichEditorPattern::StartTwinkling()
2842 {
2843 caretTwinklingTask_.Cancel();
2844 caretVisible_ = true;
2845 auto tmpHost = GetHost();
2846 CHECK_NULL_VOID(tmpHost);
2847 tmpHost->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2848 ScheduleCaretTwinkling();
2849 // Fire on selecion change when caret invisible -> visible
2850 if (!caretTwinkling_) {
2851 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "StartTwinkling");
2852 caretTwinkling_ = true;
2853 FireOnSelectionChange(caretPosition_, caretPosition_);
2854 }
2855 }
2856
ShowCaretWithoutTwinkling()2857 void RichEditorPattern::ShowCaretWithoutTwinkling()
2858 {
2859 isCursorAlwaysDisplayed_ = true;
2860 StartTwinkling();
2861 }
2862
OnCaretTwinkling()2863 void RichEditorPattern::OnCaretTwinkling()
2864 {
2865 caretTwinklingTask_.Cancel();
2866 caretVisible_ = !caretVisible_;
2867 GetHost()->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2868 ScheduleCaretTwinkling();
2869 }
2870
StopTwinkling()2871 void RichEditorPattern::StopTwinkling()
2872 {
2873 if (caretTwinkling_) {
2874 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "StopTwinkling");
2875 }
2876 caretTwinkling_ = false;
2877 isCursorAlwaysDisplayed_ = false;
2878 caretTwinklingTask_.Cancel();
2879 if (caretVisible_) {
2880 caretVisible_ = false;
2881 GetHost()->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2882 }
2883 }
2884
HandleClickEvent(GestureEvent & info)2885 void RichEditorPattern::HandleClickEvent(GestureEvent& info)
2886 {
2887 if (selectOverlay_->GetIsHandleMoving() || isMouseSelect_) {
2888 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "click rejected, isHandleMoving=%{public}d, isMouseSelect=%{public}d",
2889 selectOverlay_->GetIsHandleMoving(), isMouseSelect_);
2890 return;
2891 }
2892 auto focusHub = GetFocusHub();
2893 CHECK_NULL_VOID(focusHub);
2894 if (!focusHub->IsFocusable()) {
2895 return;
2896 }
2897
2898 if (!HasFocus() && !focusHub->IsFocusOnTouch().value_or(true)) {
2899 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleClickEvent fail when IsFocusOnTouch false");
2900 CloseSelectOverlay();
2901 StopTwinkling();
2902 return;
2903 }
2904
2905 selectionMenuOffsetClick_ = OffsetF(
2906 static_cast<float>(info.GetGlobalLocation().GetX()), static_cast<float>(info.GetGlobalLocation().GetY()));
2907 if (dataDetectorAdapter_->hasClickedAISpan_) {
2908 dataDetectorAdapter_->hasClickedAISpan_ = false;
2909 }
2910 multipleClickRecognizer_->Start(info);
2911 if (multipleClickRecognizer_->IsTripleClick()) {
2912 HandleTripleClickEvent(info);
2913 } else if (multipleClickRecognizer_->IsDoubleClick()) {
2914 HandleDoubleClickEvent(info);
2915 } else {
2916 HandleSingleClickEvent(info);
2917 NotifyCaretChange();
2918 }
2919 }
2920
HandleClickSelection(const OHOS::Ace::GestureEvent & info)2921 bool RichEditorPattern::HandleClickSelection(const OHOS::Ace::GestureEvent& info)
2922 {
2923 CHECK_NULL_RETURN(!selectOverlay_->GetIsHandleMoving(), true);
2924 if (SelectOverlayIsOn()) {
2925 selectOverlay_->SwitchToOverlayMode();
2926 selectOverlay_->ToggleMenu();
2927 } else {
2928 CalculateHandleOffsetAndShowOverlay();
2929 selectOverlay_->ProcessOverlay({.animation = true, .requestCode = REQUEST_RECREATE});
2930 }
2931 return true;
2932 }
2933
IsClickEventOnlyForMenuToggle(const OHOS::Ace::GestureEvent & info)2934 bool RichEditorPattern::IsClickEventOnlyForMenuToggle(const OHOS::Ace::GestureEvent& info)
2935 {
2936 CHECK_NULL_RETURN(info.GetSourceDevice() != SourceType::MOUSE, false);
2937 // In preview state or single handle showing, clicking handle has toggled the menu display
2938 bool hasHandledMenuToggleByClick =
2939 selectOverlay_->IsClickAtHandle(info) && (!isEditing_ || selectOverlay_->IsSingleHandleShow());
2940 CHECK_NULL_RETURN(!hasHandledMenuToggleByClick, true);
2941 if (showSelect_ && BetweenSelection(info.GetGlobalLocation())) {
2942 return HandleClickSelection(info);
2943 }
2944 return false;
2945 }
2946
HandleSingleClickEvent(OHOS::Ace::GestureEvent & info)2947 void RichEditorPattern::HandleSingleClickEvent(OHOS::Ace::GestureEvent& info)
2948 {
2949 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "handleSingleClick");
2950 hasClicked_ = true;
2951 lastClickTimeStamp_ = info.GetTimeStamp();
2952 CHECK_NULL_VOID(!IsClickEventOnlyForMenuToggle(info));
2953 CHECK_NULL_VOID(!HandleUrlSpanClickEvent(info));
2954
2955 Offset textOffset = ConvertTouchOffsetToTextOffset(info.GetLocalLocation());
2956 IF_TRUE(!isMousePressed_, HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY())));
2957
2958 if (dataDetectorAdapter_->hasClickedAISpan_ || dataDetectorAdapter_->pressedByLeftMouse_) {
2959 IF_TRUE(SelectOverlayIsOn(), selectOverlay_->HideMenu());
2960 return;
2961 }
2962
2963 HandleUserClickEvent(info);
2964 CHECK_NULL_VOID(!info.IsPreventDefault());
2965 bool isMouseClick = info.GetSourceDevice() == SourceType::MOUSE;
2966 bool isMouseClickWithShift = shiftFlag_ && isMouseClick && !IsPreviewTextInputting();
2967 if (textSelector_.IsValid() && !isMouseSelect_ && !isMouseClickWithShift) {
2968 CloseSelectOverlay();
2969 ResetSelection();
2970 }
2971 moveCaretState_.Reset();
2972 caretUpdateType_ = CaretUpdateType::PRESSED;
2973 CHECK_NULL_VOID(overlayMod_);
2974 RectF lastCaretRect = GetCaretRect();
2975 int32_t lastCaretPosition = caretPosition_;
2976 bool isCaretTwinkling = caretTwinkling_;
2977 auto position = paragraphs_.GetIndex(textOffset);
2978 AdjustCursorPosition(position);
2979 if (auto focusHub = GetFocusHub(); focusHub) {
2980 IF_TRUE(!isMouseClick || (blockPress_ && !isMouseClickWithShift), SetCaretPosition(position));
2981 IF_TRUE(isMouseClickWithShift, HandleShiftSelect(position));
2982 if (focusHub->IsCurrentFocus()) {
2983 HandleOnEditChanged(true);
2984 }
2985 if (focusHub->RequestFocusImmediately()) {
2986 IF_TRUE(!shiftFlag_ || textSelector_.SelectNothing(), StartTwinkling());
2987 RequestKeyboard(false, true, true, info.GetSourceDevice());
2988 }
2989 }
2990 UseHostToUpdateTextFieldManager();
2991 CalcCaretInfoByClick(info.GetLocalLocation());
2992 CHECK_NULL_VOID(!isMouseClick);
2993 if (IsShowSingleHandleByClick(info, lastCaretPosition, lastCaretRect, isCaretTwinkling)) {
2994 CreateAndShowSingleHandle();
2995 }
2996 }
2997
GetTextOffset(const Offset & localLocation,const RectF & contentRect)2998 PointF RichEditorPattern::GetTextOffset(const Offset &localLocation, const RectF &contentRect)
2999 {
3000 PointF textOffset = {static_cast<float>(localLocation.GetX()) - GetTextRect().GetX(),
3001 static_cast<float>(localLocation.GetY()) - GetTextRect().GetY()};
3002 return textOffset;
3003 }
3004
GetSelectedRects(int32_t start,int32_t end)3005 std::vector<RectF> RichEditorPattern::GetSelectedRects(int32_t start, int32_t end)
3006 {
3007 return paragraphs_.GetRects(start, end);
3008 }
3009
ConvertTouchOffsetToTextOffset(const Offset & touchOffset)3010 Offset RichEditorPattern::ConvertTouchOffsetToTextOffset(const Offset& touchOffset)
3011 {
3012 richTextRect_.SetTop(richTextRect_.GetY() - std::min(baselineOffset_, 0.0f));
3013 richTextRect_.SetHeight(richTextRect_.Height() - std::max(baselineOffset_, 0.0f));
3014 return touchOffset - Offset(richTextRect_.GetX(), richTextRect_.GetY());
3015 }
3016
IsShowSingleHandleByClick(const OHOS::Ace::GestureEvent & info,int32_t lastCaretPosition,const RectF & lastCaretRect,bool isCaretTwinkling)3017 bool RichEditorPattern::IsShowSingleHandleByClick(
3018 const OHOS::Ace::GestureEvent& info, int32_t lastCaretPosition, const RectF& lastCaretRect, bool isCaretTwinkling)
3019 {
3020 auto isAccessibilityClick = IsAccessibilityClick();
3021 if (!isCaretTwinkling || (info.GetSourceDevice() == SourceType::MOUSE) || isAccessibilityClick) {
3022 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "isCaretTwinkling=%{public}d,sourceType=%{public}d,"
3023 "isAccessibilityClick=%{public}d", isCaretTwinkling, info.GetSourceDevice(), isAccessibilityClick);
3024 return false;
3025 }
3026 auto offset = info.GetLocalLocation();
3027 Offset textOffset = ConvertTouchOffsetToTextOffset(offset);
3028 auto position = (GetTextContentLength() == 0) ? 0 : paragraphs_.GetIndex(textOffset);
3029 if (position != lastCaretPosition) {
3030 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "clickCaretPosition=%{public}d but lastCaretPosition=%{public}d",
3031 position, lastCaretPosition);
3032 return false;
3033 }
3034 auto paragraphEndPos = GetParagraphEndPosition(lastCaretPosition);
3035 if (lastCaretPosition == paragraphEndPos || IsTouchAtLineEnd(lastCaretPosition, textOffset)) {
3036 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "repeat click lineEnd or paragraphEndPos=%{public}d", paragraphEndPos);
3037 return true;
3038 }
3039 return RepeatClickCaret(offset, lastCaretRect);
3040 }
3041
RepeatClickCaret(const Offset & offset,const RectF & lastCaretRect)3042 bool RichEditorPattern::RepeatClickCaret(const Offset& offset, const RectF& lastCaretRect)
3043 {
3044 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "caretTwinkling=%{public}d offset=%{public}s lastCaretRect=%{public}s",
3045 caretTwinkling_, offset.ToString().c_str(), lastCaretRect.ToString().c_str());
3046 CHECK_NULL_RETURN(caretTwinkling_, false);
3047 auto lastCaretHeight = lastCaretRect.Height();
3048 auto handleHotZone = selectOverlay_->GetHandleHotZoneRadius();
3049 auto caretHotZoneRect =
3050 RectF(lastCaretRect.GetX() - handleHotZone, lastCaretRect.GetY(), handleHotZone * 2, lastCaretHeight);
3051 return caretHotZoneRect.IsInRegion(PointF(offset.GetX(), offset.GetY()));
3052 }
3053
CreateAndShowSingleHandle()3054 void RichEditorPattern::CreateAndShowSingleHandle()
3055 {
3056 if (IsPreviewTextInputting()) {
3057 return;
3058 }
3059 textResponseType_ = TextResponseType::LONG_PRESS;
3060 selectOverlay_->SetIsSingleHandle(true);
3061 textSelector_.Update(caretPosition_);
3062 CalculateHandleOffsetAndShowOverlay();
3063 UpdateSelectionType(GetSpansInfo(caretPosition_, caretPosition_, GetSpansMethod::ONSELECT));
3064 selectOverlay_->ProcessOverlay({ .animation = true });
3065 }
3066
MoveCaretAndStartFocus(const Offset & textOffset)3067 void RichEditorPattern::MoveCaretAndStartFocus(const Offset& textOffset)
3068 {
3069 auto position = paragraphs_.GetIndex(textOffset);
3070 AdjustCursorPosition(position);
3071
3072 auto focusHub = GetFocusHub();
3073 if (focusHub) {
3074 SetCaretPosition(position);
3075 if (focusHub->RequestFocusImmediately()) {
3076 IF_TRUE(!shiftFlag_ || textSelector_.SelectNothing(), StartTwinkling());
3077 if (overlayMod_) {
3078 RequestKeyboard(false, true, true);
3079 }
3080 HandleOnEditChanged(true);
3081 }
3082 }
3083 UseHostToUpdateTextFieldManager();
3084 }
3085
HandleDoubleClickEvent(OHOS::Ace::GestureEvent & info)3086 void RichEditorPattern::HandleDoubleClickEvent(OHOS::Ace::GestureEvent& info)
3087 {
3088 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleDoubleClickEvent");
3089 caretUpdateType_ = CaretUpdateType::DOUBLE_CLICK;
3090 HandleDoubleClickOrLongPress(info);
3091 caretUpdateType_ = CaretUpdateType::NONE;
3092 }
3093
HandleUserGestureEvent(GestureEvent & info,std::function<bool (RefPtr<SpanItem> item,GestureEvent & info)> && gestureFunc)3094 bool RichEditorPattern::HandleUserGestureEvent(
3095 GestureEvent& info, std::function<bool(RefPtr<SpanItem> item, GestureEvent& info)>&& gestureFunc)
3096 {
3097 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleUserGestureEvent");
3098 RectF textContentRect = contentRect_;
3099 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
3100 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
3101 if (!textContentRect.IsInRegion(PointF(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY())) ||
3102 spans_.empty()) {
3103 return false;
3104 }
3105 PointF textOffset = { info.GetLocalLocation().GetX() - GetTextRect().GetX(),
3106 info.GetLocalLocation().GetY() - GetTextRect().GetY() };
3107 int32_t start = 0;
3108 bool isParagraphHead = true;
3109 Offset paragraphOffset(0, 0);
3110 for (const auto& item : spans_) {
3111 if (!item) {
3112 continue;
3113 }
3114 std::vector<RectF> selectedRects = paragraphs_.GetRects(start, item->position);
3115 start = item->position;
3116 if (isParagraphHead && !selectedRects.empty()) {
3117 if (item->leadingMargin.has_value()) {
3118 auto addWidth = item->leadingMargin.value().size.Width();
3119 selectedRects[0].SetLeft(selectedRects[0].GetX() - addWidth.ConvertToPx());
3120 selectedRects[0].SetWidth(selectedRects[0].GetSize().Width() + addWidth.ConvertToPx());
3121 }
3122 paragraphOffset.SetX(selectedRects[0].GetOffset().GetX());
3123 paragraphOffset.SetY(selectedRects[0].GetOffset().GetY());
3124 isParagraphHead = false;
3125 }
3126 if (!isParagraphHead && item->content.back() == '\n') {
3127 isParagraphHead = true;
3128 }
3129 for (auto&& rect : selectedRects) {
3130 if (!rect.IsInRegion(textOffset)) {
3131 continue;
3132 }
3133 info = info.SetScreenLocation(
3134 Offset(textOffset.GetX() - paragraphOffset.GetX(), textOffset.GetY() - paragraphOffset.GetY()));
3135 return gestureFunc(item, info);
3136 }
3137 }
3138 return false;
3139 }
3140
ClickAISpan(const PointF & textOffset,const AISpan & aiSpan)3141 bool RichEditorPattern::ClickAISpan(const PointF& textOffset, const AISpan& aiSpan)
3142 {
3143 auto calculateHandleFunc = [weak = WeakClaim(this)]() {
3144 auto pattern = weak.Upgrade();
3145 CHECK_NULL_VOID(pattern);
3146 pattern->CalculateHandleOffsetAndShowOverlay();
3147 };
3148 auto showSelectOverlayFunc = [weak = WeakClaim(this)](const RectF& firstHandle, const RectF& secondHandle) {
3149 auto pattern = weak.Upgrade();
3150 CHECK_NULL_VOID(pattern);
3151 pattern->SetCaretPosition(pattern->textSelector_.destinationOffset);
3152 auto focusHub = pattern->GetFocusHub();
3153 CHECK_NULL_VOID(focusHub);
3154 focusHub->RequestFocusImmediately();
3155 IF_TRUE(!pattern->isEditing_, pattern->CloseKeyboard(true));
3156 pattern->ShowSelectOverlay(firstHandle, secondHandle);
3157 };
3158
3159 std::vector<RectF> aiRects = paragraphs_.GetRects(aiSpan.start, aiSpan.end);
3160 for (auto&& rect : aiRects) {
3161 if (rect.IsInRegion(textOffset)) {
3162 dataDetectorAdapter_->clickedAISpan_ = aiSpan;
3163 if (leftMousePress_) {
3164 dataDetectorAdapter_->pressedByLeftMouse_ = true;
3165 return true;
3166 }
3167 dataDetectorAdapter_->hasClickedAISpan_ = true;
3168 ShowAIEntityMenu(aiSpan, calculateHandleFunc, showSelectOverlayFunc);
3169 return true;
3170 }
3171 }
3172 return false;
3173 }
3174
GetStartAndEnd(int32_t start,const RefPtr<SpanItem> & item)3175 std::pair<int32_t, int32_t> RichEditorPattern::GetStartAndEnd(int32_t start, const RefPtr<SpanItem>& item)
3176 {
3177 return isSpanStringMode_
3178 ? TextPattern::GetStartAndEnd(start, item)
3179 : std::make_pair(item->rangeStart, item->position);
3180 }
3181
HandleUrlSpanClickEvent(const GestureEvent & info)3182 bool RichEditorPattern::HandleUrlSpanClickEvent(const GestureEvent& info)
3183 {
3184 RectF textContentRect = contentRect_;
3185 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
3186 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
3187
3188 CheckClickedOnSpanOrText(textContentRect, info.GetLocalLocation());
3189 auto clickedSpanPosition = GetClickedSpanPosition();
3190 if (LessNotEqual(clickedSpanPosition, 0)) {
3191 return false;
3192 }
3193 auto iter = spans_.begin();
3194 std::advance(iter, clickedSpanPosition);
3195 RefPtr<SpanItem> span;
3196 if (iter == spans_.end()) {
3197 span = spans_.back();
3198 } else {
3199 span = *iter;
3200 }
3201 if (span && span->urlOnRelease) {
3202 span->urlOnRelease();
3203 return true;
3204 }
3205 return false;
3206 }
3207
HandleUserClickEvent(GestureEvent & info)3208 bool RichEditorPattern::HandleUserClickEvent(GestureEvent& info)
3209 {
3210 auto clickFunc = [](RefPtr<SpanItem> item, GestureEvent& info) -> bool {
3211 if (item && item->onClick) {
3212 item->onClick(info);
3213 return true;
3214 }
3215 return false;
3216 };
3217 return HandleUserGestureEvent(info, std::move(clickFunc));
3218 }
3219
CalcCaretInfoByClick(const Offset & touchOffset)3220 void RichEditorPattern::CalcCaretInfoByClick(const Offset& touchOffset)
3221 {
3222 auto textRect = GetTextRect();
3223 textRect.SetTop(textRect.GetY() - std::min(baselineOffset_, 0.0f));
3224 textRect.SetHeight(textRect.Height() - std::max(baselineOffset_, 0.0f));
3225 Offset textOffset = { touchOffset.GetX() - textRect.GetX(), touchOffset.GetY() - textRect.GetY() };
3226 auto [lastClickOffset, caretHeight] = CalcAndRecordLastClickCaretInfo(textOffset);
3227 CHECK_NULL_VOID(overlayMod_);
3228 DynamicCast<RichEditorOverlayModifier>(overlayMod_)->SetCaretOffsetAndHeight(lastClickOffset, caretHeight);
3229 MoveCaretToContentRect();
3230 }
3231
CalcAndRecordLastClickCaretInfo(const Offset & textOffset)3232 std::pair<OffsetF, float> RichEditorPattern::CalcAndRecordLastClickCaretInfo(const Offset& textOffset)
3233 {
3234 // get the caret position
3235 auto positionWithAffinity = paragraphs_.GetGlyphPositionAtCoordinate(textOffset);
3236 auto position = static_cast<int32_t>(positionWithAffinity.position_);
3237 // get the caret offset when click
3238 float caretHeight = 0.0f;
3239 auto lastClickOffset = paragraphs_.ComputeCursorInfoByClick(position, caretHeight,
3240 OffsetF(static_cast<float>(textOffset.GetX()), static_cast<float>(textOffset.GetY())));
3241
3242 lastClickOffset += richTextRect_.GetOffset();
3243 if (isShowPlaceholder_) {
3244 auto [caretOffset, preferredHeight] = CalculateEmptyValueCaretRect();
3245 lastClickOffset = caretOffset;
3246 }
3247 SetLastClickOffset(lastClickOffset);
3248 caretAffinityPolicy_ = (positionWithAffinity.affinity_ == TextAffinity::UPSTREAM)
3249 ? CaretAffinityPolicy::UPSTREAM_FIRST
3250 : CaretAffinityPolicy::DOWNSTREAM_FIRST;
3251 return std::make_pair(lastClickOffset, caretHeight);
3252 }
3253
InitClickEvent(const RefPtr<GestureEventHub> & gestureHub)3254 void RichEditorPattern::InitClickEvent(const RefPtr<GestureEventHub>& gestureHub)
3255 {
3256 CHECK_NULL_VOID(!clickEventInitialized_);
3257 auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
3258 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "click callback, sourceType=%{public}d", info.GetSourceDevice());
3259 auto pattern = weak.Upgrade();
3260 CHECK_NULL_VOID(pattern);
3261 pattern->sourceType_ = info.GetSourceDevice();
3262 pattern->HandleClickEvent(info);
3263 };
3264 auto clickListener = MakeRefPtr<ClickEvent>(std::move(clickCallback));
3265 gestureHub->AddClickAfterEvent(clickListener);
3266 clickEventInitialized_ = true;
3267 }
3268
InitFocusEvent(const RefPtr<FocusHub> & focusHub)3269 void RichEditorPattern::InitFocusEvent(const RefPtr<FocusHub>& focusHub)
3270 {
3271 CHECK_NULL_VOID(!focusEventInitialized_);
3272 auto focusTask = [weak = WeakClaim(this)]() {
3273 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "rich editor in focus");
3274 auto pattern = weak.Upgrade();
3275 CHECK_NULL_VOID(pattern);
3276 pattern->HandleFocusEvent();
3277 };
3278 focusHub->SetOnFocusInternal(focusTask);
3279 auto blurTask = [weak = WeakClaim(this)]() {
3280 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "rich editor in blur");
3281 auto pattern = weak.Upgrade();
3282 CHECK_NULL_VOID(pattern);
3283 pattern->HandleBlurEvent();
3284 };
3285 focusHub->SetOnBlurInternal(blurTask);
3286 focusEventInitialized_ = true;
3287 auto keyTask = [weak = WeakClaim(this)](const KeyEvent& keyEvent) -> bool {
3288 auto pattern = weak.Upgrade();
3289 CHECK_NULL_RETURN(pattern, false);
3290 return pattern->OnKeyEvent(keyEvent);
3291 };
3292 focusHub->SetOnKeyEventInternal(std::move(keyTask));
3293 }
3294
GetBlurReason()3295 BlurReason RichEditorPattern::GetBlurReason()
3296 {
3297 auto host = GetHost();
3298 CHECK_NULL_RETURN(host, BlurReason::FOCUS_SWITCH);
3299 auto curFocusHub = host->GetFocusHub();
3300 CHECK_NULL_RETURN(curFocusHub, BlurReason::FOCUS_SWITCH);
3301 return curFocusHub->GetBlurReason();
3302 }
3303
HandleBlurEvent()3304 void RichEditorPattern::HandleBlurEvent()
3305 {
3306 auto reason = GetBlurReason();
3307 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleBlurEvent/%{public}d, blur reason=%{public}d", frameId_, reason);
3308 auto host = GetHost();
3309 ClearOnFocusTextField(RawPtr(host));
3310 host.Reset();
3311 IF_PRESENT(multipleClickRecognizer_, Stop());
3312 CHECK_NULL_VOID(showSelect_ || !IsSelected());
3313 isLongPress_ = false;
3314 ResetTouchSelectState();
3315 shiftFlag_ = false;
3316 moveCaretState_.Reset();
3317 floatingCaretState_.Reset();
3318 StopTwinkling();
3319 // The pattern handles blurevent, Need to close the softkeyboard first.
3320 if ((customKeyboardBuilder_ && isCustomKeyboardAttached_) || reason == BlurReason::FRAME_DESTROY) {
3321 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "RichEditor Blur, Close Keyboard.");
3322 CloseSelectOverlay();
3323 ResetSelection();
3324 CloseKeyboard(false);
3325 }
3326 if (magnifierController_) {
3327 magnifierController_->RemoveMagnifierFrameNode();
3328 }
3329 if (IsSelected() && reason == BlurReason::FOCUS_SWITCH) {
3330 CloseSelectOverlay();
3331 ResetSelection();
3332 } else if (IsSelected()) {
3333 selectOverlay_->HideMenu(true);
3334 } else {
3335 CloseSelectOverlay();
3336 }
3337 if (reason != BlurReason::WINDOW_BLUR) {
3338 lastSelectionRange_.reset();
3339 }
3340 HandleOnEditChanged(false);
3341 }
3342
HandleFocusEvent()3343 void RichEditorPattern::HandleFocusEvent()
3344 {
3345 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleFocusEvent/%{public}d", frameId_);
3346 blockKbInFloatingWindow_= false;
3347 UseHostToUpdateTextFieldManager();
3348 if (previewLongPress_ || isOnlyRequestFocus_) {
3349 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleFocusEvent, previewLongPress=%{public}d,"
3350 "OnlyRequestFocus=%{public}d", previewLongPress_, isOnlyRequestFocus_);
3351 isOnlyRequestFocus_ = false;
3352 return;
3353 }
3354 SetIsEnableSubWindowMenu();
3355 if (textSelector_.SelectNothing()) {
3356 StartTwinkling();
3357 }
3358 auto host = GetHost();
3359 if (host) {
3360 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
3361 }
3362
3363 bool clickAIMenu = dataDetectorAdapter_->hasClickedMenuOption_;
3364 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "longPress=%{public}d, clickAIMenu=%{public}d", isLongPress_, clickAIMenu);
3365 bool bindKeyboard = !isLongPress_ && !clickAIMenu;
3366 CHECK_NULL_VOID(bindKeyboard);
3367
3368 auto windowMode = GetWindowMode();
3369 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "requestKeyboard=%{public}d, windowMode=%{public}d, rightButton=%{public}d",
3370 needToRequestKeyboardOnFocus_, windowMode, usingMouseRightButton_);
3371
3372 bool needShowSoftKeyboard = needToRequestKeyboardOnFocus_;
3373 needShowSoftKeyboard &= !usingMouseRightButton_; // do not show kb when mouseRightClick
3374
3375 if (windowMode == WindowMode::WINDOW_MODE_FLOATING) {
3376 blockKbInFloatingWindow_ = needShowSoftKeyboard;
3377 needShowSoftKeyboard = false;
3378 }
3379
3380 RequestKeyboard(false, true, needShowSoftKeyboard);
3381 HandleOnEditChanged(true);
3382 }
3383
OnFocusNodeChange(FocusReason focusReason)3384 void RichEditorPattern::OnFocusNodeChange(FocusReason focusReason)
3385 {
3386 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "OnFocusNodeChange/%{public}d, reason=%{public}d, blockKbInFloating=%{public}d",
3387 frameId_, focusReason, blockKbInFloatingWindow_);
3388 CHECK_NULL_VOID(blockKbInFloatingWindow_);
3389 blockKbInFloatingWindow_= false;
3390 CHECK_NULL_VOID(GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING);
3391 CHECK_NULL_VOID(focusReason != FocusReason::WINDOW_FOCUS);
3392 CHECK_NULL_VOID(HasFocus() && isEditing_);
3393 bool clickAIMenu = dataDetectorAdapter_->hasClickedMenuOption_;
3394 bool bindKeyboard = !isLongPress_ && !clickAIMenu;
3395 CHECK_NULL_VOID(bindKeyboard);
3396 CHECK_NULL_VOID(needToRequestKeyboardOnFocus_ && !usingMouseRightButton_);
3397
3398 RequestKeyboard(false, true, true);
3399 }
3400
GetWindowMode()3401 WindowMode RichEditorPattern::GetWindowMode()
3402 {
3403 auto pipelineContext = GetContext();
3404 CHECK_NULL_RETURN(pipelineContext, WindowMode::WINDOW_MODE_UNDEFINED);
3405 auto windowManager = pipelineContext->GetWindowManager();
3406 CHECK_NULL_RETURN(windowManager, WindowMode::WINDOW_MODE_UNDEFINED);
3407 return windowManager->GetWindowMode();
3408 }
3409
GetIsMidScene()3410 bool RichEditorPattern::GetIsMidScene()
3411 {
3412 auto context = GetContext();
3413 CHECK_NULL_RETURN(context, false);
3414 auto windowManager = context->GetWindowManager();
3415 CHECK_NULL_RETURN(windowManager, false);
3416 bool isMidScene = false;
3417 int32_t ret = windowManager->GetIsMidScene(isMidScene);
3418 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "GetIsMidScene ret=%{public}d", ret);
3419 return isMidScene;
3420 }
3421
UseHostToUpdateTextFieldManager()3422 void RichEditorPattern::UseHostToUpdateTextFieldManager()
3423 {
3424 auto host = GetHost();
3425 CHECK_NULL_VOID(host);
3426 auto context = host->GetContext();
3427 CHECK_NULL_VOID(context);
3428 auto globalOffset = host->GetPaintRectOffsetNG(false, true) - context->GetRootRect().GetOffset();
3429 UpdateTextFieldManager(Offset(globalOffset.GetX(), globalOffset.GetY()), frameRect_.Height());
3430 }
3431
OnVisibleChange(bool isVisible)3432 void RichEditorPattern::OnVisibleChange(bool isVisible)
3433 {
3434 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "isVisible=%{public}d", isVisible);
3435 TextPattern::OnVisibleChange(isVisible);
3436 StopTwinkling();
3437 CloseSelectOverlay();
3438 ResetSelection();
3439 if (!isVisible && HasFocus()) {
3440 CloseKeyboard(false);
3441 }
3442 }
3443
CloseKeyboard(bool forceClose)3444 bool RichEditorPattern::CloseKeyboard(bool forceClose)
3445 {
3446 if (customKeyboardBuilder_ && isCustomKeyboardAttached_) {
3447 return CloseCustomKeyboard();
3448 }
3449 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "Request close soft keyboard.");
3450 #if defined(ENABLE_STANDARD_INPUT)
3451 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
3452 if (!imeAttached_ && !forceClose) {
3453 return false;
3454 }
3455 #endif
3456 auto inputMethod = MiscServices::InputMethodController::GetInstance();
3457 CHECK_NULL_RETURN(inputMethod, false);
3458 inputMethod->HideTextInput();
3459 inputMethod->Close();
3460 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
3461 imeAttached_ = false;
3462 #endif
3463 #else
3464 if (HasConnection()) {
3465 connection_->Close(GetInstanceId());
3466 connection_ = nullptr;
3467 }
3468 #endif
3469 return true;
3470 }
3471
HandleDraggableFlag(bool isTouchSelectArea)3472 void RichEditorPattern::HandleDraggableFlag(bool isTouchSelectArea)
3473 {
3474 if (copyOption_ != CopyOptions::None && isTouchSelectArea) {
3475 bool isContentDraggalbe = JudgeContentDraggable();
3476 if (isContentDraggalbe) {
3477 dragBoxes_ = GetTextBoxes();
3478 }
3479 SetIsTextDraggable(isContentDraggalbe);
3480 } else {
3481 SetIsTextDraggable(false);
3482 }
3483 }
3484
SetIsTextDraggable(bool isTextDraggable)3485 void RichEditorPattern::SetIsTextDraggable(bool isTextDraggable)
3486 {
3487 auto gestureHub = GetGestureEventHub();
3488 IF_PRESENT(gestureHub, SetIsTextDraggable(isTextDraggable));
3489 }
3490
JudgeContentDraggable()3491 bool RichEditorPattern::JudgeContentDraggable()
3492 {
3493 if (!IsSelected() || copyOption_ == CopyOptions::None) {
3494 return false ;
3495 }
3496 auto selectInfo = GetSpansInfo(textSelector_.GetTextStart(), textSelector_.GetTextEnd(), GetSpansMethod::ONSELECT);
3497 auto selResult = selectInfo.GetSelection().resultObjects;
3498 auto iter = std::find_if(selResult.begin(), selResult.end(), [](ResultObject& obj) { return obj.isDraggable; });
3499 return iter != selResult.end();
3500 }
3501
CalculateCaretOffsetAndHeight()3502 std::pair<OffsetF, float> RichEditorPattern::CalculateCaretOffsetAndHeight()
3503 {
3504 OffsetF caretOffset;
3505 float caretHeight = 0.0f;
3506 auto caretPosition = caretPosition_;
3507 float caretHeightUp = 0.0f;
3508 auto caretBoundaryRect = GetCaretBoundaryRect();
3509 OffsetF caretOffsetUp = CalcCursorOffsetByPosition(caretPosition, caretHeightUp, false, false);
3510 if (isShowPlaceholder_) {
3511 auto textAlign = GetTextAlignByDirection();
3512 IF_TRUE(textAlign == TextAlign::END, caretOffsetUp.SetX(caretBoundaryRect.Right()));
3513 return { caretOffsetUp, caretHeightUp };
3514 }
3515 if (GetTextContentLength() <= 0) {
3516 constexpr float DEFAULT_CARET_HEIGHT = 18.5f;
3517 auto [caretOffset, preferredHeight] = CalculateEmptyValueCaretRect();
3518 caretHeight = typingTextStyle_.has_value() ? preferredHeight
3519 : static_cast<float>(Dimension(DEFAULT_CARET_HEIGHT, DimensionUnit::VP).ConvertToPx());
3520 return { caretOffset, caretHeight };
3521 }
3522 float caretHeightDown = 0.0f;
3523 OffsetF caretOffsetDown = CalcCursorOffsetByPosition(caretPosition, caretHeightDown, true, false);
3524 bool isCaretPosInLineEnd = !NearEqual(caretOffsetDown.GetX(), caretOffsetUp.GetX(), 0.5f);
3525 bool isShowCaretDown = isCaretPosInLineEnd;
3526 if ((caretAffinityPolicy_ != CaretAffinityPolicy::DEFAULT) && isCaretPosInLineEnd) {
3527 // show caret by click
3528 isShowCaretDown = (caretAffinityPolicy_ == CaretAffinityPolicy::DOWNSTREAM_FIRST);
3529 }
3530 caretOffset = isShowCaretDown ? caretOffsetDown : caretOffsetUp;
3531 caretHeight = isShowCaretDown ? caretHeightDown : caretHeightUp;
3532 // Handle caret offset at the right boundary of the content rect
3533 if (GreatOrEqual(caretOffset.GetX(), caretBoundaryRect.Right())) {
3534 auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
3535 CHECK_NULL_RETURN(overlayModifier, std::make_pair(caretOffset, caretHeight));
3536 auto caretWidth = overlayModifier->GetCaretWidth();
3537 caretOffset.SetX(caretOffset.GetX() - caretWidth);
3538 }
3539 return std::make_pair(caretOffset, caretHeight);
3540 }
3541
CalculateEmptyValueCaretRect()3542 std::pair<OffsetF, float> RichEditorPattern::CalculateEmptyValueCaretRect()
3543 {
3544 OffsetF offset;
3545 auto textAlign = GetTextAlignByDirection();
3546 switch (textAlign) {
3547 case TextAlign::START:
3548 offset.SetX(contentRect_.GetX());
3549 break;
3550 case TextAlign::CENTER:
3551 offset.SetX(contentRect_.GetX() + contentRect_.Width() / 2.0f);
3552 break;
3553 case TextAlign::END: {
3554 auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
3555 auto caretWidth = overlayModifier ? overlayModifier->GetCaretWidth() : 0.0f;
3556 offset.SetX(contentRect_.Right() - caretWidth);
3557 break;
3558 }
3559 default:
3560 break;
3561 }
3562 auto offsetY = richTextRect_.GetY();
3563 float caretHeight = 0.0f;
3564 if (!presetParagraph_) {
3565 PreferredParagraph();
3566 }
3567 if (presetParagraph_) {
3568 CaretMetricsF caretCaretMetric;
3569 presetParagraph_->CalcCaretMetricsByPosition(1, caretCaretMetric, TextAffinity::UPSTREAM, false);
3570 offsetY += caretCaretMetric.offset.GetY();
3571 caretHeight = caretCaretMetric.height;
3572 }
3573 offset.SetY(offsetY);
3574 return std::make_pair(offset, caretHeight);
3575 }
3576
UpdateModifierCaretOffsetAndHeight()3577 void RichEditorPattern::UpdateModifierCaretOffsetAndHeight()
3578 {
3579 CHECK_NULL_VOID(overlayMod_);
3580 auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
3581 CHECK_NULL_VOID(overlayModifier);
3582 auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
3583 overlayModifier->SetCaretOffsetAndHeight(caretOffset, caretHeight);
3584 }
3585
NotifyCaretChange()3586 void RichEditorPattern::NotifyCaretChange()
3587 {
3588 CHECK_NULL_VOID(!IsSelected());
3589 TriggerAvoidOnCaretChange();
3590 }
3591
GetTextAlignByDirection()3592 TextAlign RichEditorPattern::GetTextAlignByDirection()
3593 {
3594 auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
3595 CHECK_NULL_RETURN(layoutProperty, TextAlign::START);
3596 auto textAlign = layoutProperty->GetTextAlignValue(TextAlign::START);
3597 auto direction = layoutProperty->GetNonAutoLayoutDirection();
3598 if (direction == TextDirection::RTL) {
3599 if (textAlign == TextAlign::START) {
3600 textAlign = TextAlign::END;
3601 } else {
3602 textAlign = TextAlign::START;
3603 }
3604 }
3605 return textAlign;
3606 }
3607
HandleLongPress(GestureEvent & info)3608 void RichEditorPattern::HandleLongPress(GestureEvent& info)
3609 {
3610 CHECK_NULL_VOID(!selectOverlay_->GetIsHandleMoving());
3611 auto focusHub = GetFocusHub();
3612 CHECK_NULL_VOID(focusHub);
3613 if (!focusHub->IsFocusable()) {
3614 return;
3615 }
3616 if (info.GetFingerList().size() > 1) {
3617 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "More than one finger detected, ignoring this long press event");
3618 return;
3619 }
3620 if (sourceType_ == SourceType::MOUSE && hasUrlSpan_) {
3621 HandleUrlSpanShowShadow(info.GetLocalLocation(), info.GetGlobalLocation(), GetUrlPressColor());
3622 }
3623
3624 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleLongPress");
3625 moveCaretState_.Reset();
3626 caretUpdateType_ = CaretUpdateType::LONG_PRESSED;
3627 selectionMenuOffsetClick_ = OffsetF(
3628 static_cast<float>(info.GetGlobalLocation().GetX()), static_cast<float>(info.GetGlobalLocation().GetY()));
3629 HandleDoubleClickOrLongPress(info);
3630 caretUpdateType_ = CaretUpdateType::NONE;
3631 }
3632
HandleUrlSpanShowShadow(const Offset & localLocation,const Offset & globalOffset,const Color & color)3633 bool RichEditorPattern::HandleUrlSpanShowShadow(const Offset& localLocation, const Offset& globalOffset, const Color& color)
3634 {
3635 RectF textContentRect = contentRect_;
3636 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
3637 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
3638
3639 auto localLocationOffset = localLocation;
3640 if (selectOverlay_->HasRenderTransform()) {
3641 localLocationOffset = ConvertGlobalToLocalOffset(globalOffset);
3642 }
3643
3644 PointF textOffset = {static_cast<float>(localLocationOffset.GetX()) - GetTextRect().GetX(),
3645 static_cast<float>(localLocationOffset.GetY()) - GetTextRect().GetY()};
3646 return ShowShadow(textOffset, color);
3647 }
3648
HandleDoubleClickOrLongPress(GestureEvent & info)3649 void RichEditorPattern::HandleDoubleClickOrLongPress(GestureEvent& info)
3650 {
3651 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "caretUpdateType=%{public}d", caretUpdateType_);
3652 if (IsPreviewTextInputting()) {
3653 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "do not handle DoubleClickOrLongPress in previewTextInputting");
3654 return;
3655 }
3656 if (IsDragging()) {
3657 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "do not handle DoubleClickOrLongPress during drag");
3658 return;
3659 }
3660 auto host = GetHost();
3661 CHECK_NULL_VOID(host);
3662 textResponseType_ = TextResponseType::LONG_PRESS;
3663 if (caretUpdateType_ == CaretUpdateType::LONG_PRESSED) {
3664 HandleUserLongPressEvent(info);
3665 } else if (caretUpdateType_ == CaretUpdateType::DOUBLE_CLICK) {
3666 HandleUserDoubleClickEvent(info);
3667 }
3668 bool isDoubleClick = caretUpdateType_== CaretUpdateType::DOUBLE_CLICK;
3669 if (isDoubleClick && info.GetSourceTool() == SourceTool::FINGER && IsSelected()) {
3670 showSelect_ = true;
3671 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3672 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle);
3673 }
3674 bool isLongPressSelectArea = BetweenSelection(info.GetGlobalLocation()) && !isDoubleClick;
3675 HandleDraggableFlag(isLongPressSelectArea);
3676 bool isLongPressByMouse = isMousePressed_ && caretUpdateType_== CaretUpdateType::LONG_PRESSED;
3677 if (isLongPressSelectArea && !isLongPressByMouse) {
3678 StartVibratorByLongPress();
3679 }
3680 bool isMouseClickWithShift = shiftFlag_ && info.GetSourceDevice() == SourceType::MOUSE;
3681 bool isInterceptEvent = isLongPressSelectArea || isLongPressByMouse || isMouseClickWithShift;
3682 if (isInterceptEvent) {
3683 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "intercept longPressReason:[%{public}d, %{public}d] shiftSelect:%{public}d",
3684 isLongPressSelectArea, isLongPressByMouse, isMouseClickWithShift);
3685 return;
3686 }
3687 HandleDoubleClickOrLongPress(info, host);
3688 if (IsSelected()) {
3689 TriggerAvoidOnCaretChangeNextFrame();
3690 } else {
3691 ForceTriggerAvoidOnCaretChange(true);
3692 }
3693 }
3694
ConvertGlobalToLocalOffset(const Offset & globalOffset)3695 Offset RichEditorPattern::ConvertGlobalToLocalOffset(const Offset& globalOffset)
3696 {
3697 auto localPoint = OffsetF(globalOffset.GetX(), globalOffset.GetY());
3698 selectOverlay_->RevertLocalPointWithTransform(localPoint);
3699 return Offset(localPoint.GetX(), localPoint.GetY());
3700 }
3701
HandleSelect(GestureEvent & info,int32_t selectStart,int32_t selectEnd)3702 void RichEditorPattern::HandleSelect(GestureEvent& info, int32_t selectStart, int32_t selectEnd)
3703 {
3704 initSelector_ = { selectStart, selectEnd };
3705 if (IsSelected()) {
3706 showSelect_ = true;
3707 }
3708 FireOnSelect(selectStart, selectEnd);
3709 SetCaretPositionWithAffinity({ selectEnd, TextAffinity::UPSTREAM });
3710 MoveCaretToContentRect();
3711 CalculateHandleOffsetAndShowOverlay();
3712 if (IsShowSelectMenuUsingMouse()) {
3713 CloseSelectOverlay();
3714 }
3715 selectionMenuOffset_ = info.GetGlobalLocation();
3716 }
3717
HandleDoubleClickOrLongPress(GestureEvent & info,RefPtr<FrameNode> host)3718 void RichEditorPattern::HandleDoubleClickOrLongPress(GestureEvent& info, RefPtr<FrameNode> host)
3719 {
3720 auto focusHub = host->GetOrCreateFocusHub();
3721 CHECK_NULL_VOID(focusHub);
3722 isLongPress_ = true;
3723 auto localOffset = info.GetLocalLocation();
3724 if (selectOverlay_->HasRenderTransform()) {
3725 localOffset = ConvertGlobalToLocalOffset(info.GetGlobalLocation());
3726 }
3727 auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
3728 Offset textOffset = { localOffset.GetX() - textPaintOffset.GetX(), localOffset.GetY() - textPaintOffset.GetY() };
3729 if (caretUpdateType_ == CaretUpdateType::LONG_PRESSED) {
3730 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "LONG_PRESSED and isEditing=%{public}d", isEditing_);
3731 if (textSelector_.IsValid()) {
3732 CloseSelectOverlay();
3733 ResetSelection();
3734 }
3735 StartVibratorByLongPress();
3736 editingLongPress_ = isEditing_;
3737 previewLongPress_ = !isEditing_;
3738 }
3739 focusHub->RequestFocusImmediately();
3740 InitSelection(textOffset);
3741 auto selectEnd = textSelector_.GetTextEnd();
3742 auto selectStart = textSelector_.GetTextStart();
3743 HandleSelect(info, selectStart, selectEnd);
3744 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3745 if (overlayMod_ && caretUpdateType_ == CaretUpdateType::DOUBLE_CLICK) {
3746 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "double click. shall enter edit state.set 1");
3747 HandleOnEditChanged(true);
3748 RequestKeyboard(false, true, true);
3749 }
3750 bool isDoubleClickByMouse =
3751 info.GetSourceDevice() == SourceType::MOUSE && caretUpdateType_ == CaretUpdateType::DOUBLE_CLICK;
3752 bool isShowSelectOverlay = !isDoubleClickByMouse && caretUpdateType_ != CaretUpdateType::LONG_PRESSED;
3753 if (isShowSelectOverlay) {
3754 selectOverlay_->ProcessOverlay({ .menuIsShow = !selectOverlay_->GetIsHandleMoving(), .animation = true });
3755 StopTwinkling();
3756 } else if (selectStart == selectEnd && isDoubleClickByMouse) {
3757 StartTwinkling();
3758 } else {
3759 StopTwinkling();
3760 }
3761 }
3762
StartVibratorByLongPress()3763 void RichEditorPattern::StartVibratorByLongPress()
3764 {
3765 CHECK_NULL_VOID(isEnableHapticFeedback_);
3766 VibratorUtils::StartVibraFeedback("longPress.light");
3767 }
3768
HandleUserLongPressEvent(GestureEvent & info)3769 bool RichEditorPattern::HandleUserLongPressEvent(GestureEvent& info)
3770 {
3771 auto longPressFunc = [](RefPtr<SpanItem> item, GestureEvent& info) -> bool {
3772 if (item && item->onLongPress) {
3773 item->onLongPress(info);
3774 return true;
3775 }
3776 return false;
3777 };
3778 return HandleUserGestureEvent(info, std::move(longPressFunc));
3779 }
3780
HandleUserDoubleClickEvent(GestureEvent & info)3781 bool RichEditorPattern::HandleUserDoubleClickEvent(GestureEvent& info)
3782 {
3783 auto doubleClickFunc = [](RefPtr<SpanItem> item, GestureEvent& info) -> bool {
3784 if (item && item->onDoubleClick) {
3785 item->onDoubleClick(info);
3786 return true;
3787 }
3788 return false;
3789 };
3790 return HandleUserGestureEvent(info, std::move(doubleClickFunc));
3791 }
3792
HandleMenuCallbackOnSelectAll(bool isShowMenu)3793 void RichEditorPattern::HandleMenuCallbackOnSelectAll(bool isShowMenu)
3794 {
3795 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleMenuCallbackOnSelectAll");
3796 auto textSize = GetTextContentLength();
3797 textSelector_.Update(0, textSize);
3798 CalculateHandleOffsetAndShowOverlay();
3799 if (selectOverlay_->IsUsingMouse()) {
3800 CloseSelectOverlay();
3801 }
3802 IF_TRUE(IsSelected(), StopTwinkling());
3803 auto selectOverlayInfo = selectOverlay_->GetSelectOverlayInfo();
3804 if (selectOverlayInfo && selectOverlay_->IsUsingMouse()) {
3805 textResponseType_ = static_cast<TextResponseType>(selectOverlayInfo->menuInfo.responseType.value_or(0));
3806 } else {
3807 textResponseType_ = TextResponseType::LONG_PRESS;
3808 }
3809 selectMenuInfo_.showCopyAll = false;
3810 auto host = GetHost();
3811 CHECK_NULL_VOID(host);
3812 FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
3813 showSelect_ = true;
3814 if (!selectOverlay_->IsUsingMouse()) {
3815 selectOverlay_->ProcessOverlay({ .menuIsShow = isShowMenu, .animation = true });
3816 }
3817 SetCaretPosition(textSize);
3818 MoveCaretToContentRect();
3819 TriggerAvoidOnCaretChangeNextFrame();
3820 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3821 }
3822
InitLongPressEvent(const RefPtr<GestureEventHub> & gestureHub)3823 void RichEditorPattern::InitLongPressEvent(const RefPtr<GestureEventHub>& gestureHub)
3824 {
3825 CHECK_NULL_VOID(!longPressEvent_);
3826 auto longPressCallback = [weak = WeakClaim(this)](GestureEvent& info) {
3827 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "long press callback, sourceType=%{public}d", info.GetSourceDevice());
3828 auto pattern = weak.Upgrade();
3829 CHECK_NULL_VOID(pattern);
3830 pattern->sourceType_ = info.GetSourceDevice();
3831 pattern->HandleLongPress(info);
3832 };
3833 longPressEvent_ = MakeRefPtr<LongPressEvent>(std::move(longPressCallback));
3834 gestureHub->SetLongPressEvent(longPressEvent_);
3835
3836 auto onTextSelectorChange = [weak = WeakClaim(this), &selector = textSelector_]() {
3837 auto pattern = weak.Upgrade();
3838 CHECK_NULL_VOID(pattern);
3839 if (!selector.SelectNothing()) {
3840 pattern->StopTwinkling();
3841 }
3842 IF_PRESENT(pattern->oneStepDragController_,
3843 SetEnableEventResponse(selector, pattern->imageNodes, pattern->builderNodes));
3844 pattern->FireOnSelectionChange(selector);
3845 auto frameNode = pattern->GetHost();
3846 CHECK_NULL_VOID(frameNode);
3847 frameNode->OnAccessibilityEvent(AccessibilityEventType::TEXT_SELECTION_UPDATE);
3848 };
3849 textSelector_.SetOnAccessibility(std::move(onTextSelectorChange));
3850 }
3851
UpdateSelector(int32_t start,int32_t end)3852 void RichEditorPattern::UpdateSelector(int32_t start, int32_t end)
3853 {
3854 AdjustSelector(start, end);
3855 textSelector_.Update(start, end);
3856 }
3857
AdjustSelector(int32_t & start,int32_t & end,SelectorAdjustPolicy policy)3858 void RichEditorPattern::AdjustSelector(int32_t& start, int32_t& end, SelectorAdjustPolicy policy)
3859 {
3860 AdjustSelector(start, HandleType::FIRST, policy);
3861 AdjustSelector(end, HandleType::SECOND, policy);
3862 }
3863
AdjustSelector(int32_t & index,HandleType handleType,SelectorAdjustPolicy policy)3864 void RichEditorPattern::AdjustSelector(int32_t& index, HandleType handleType, SelectorAdjustPolicy policy)
3865 {
3866 bool isAdjust = AdjustSelectorForSymbol(index, handleType, policy);
3867 CHECK_NULL_VOID(!isAdjust);
3868 AdjustSelectorForEmoji(index, handleType, policy);
3869 }
3870
AdjustSelectorForSymbol(int32_t & index,HandleType handleType,SelectorAdjustPolicy policy)3871 bool RichEditorPattern::AdjustSelectorForSymbol(int32_t& index, HandleType handleType, SelectorAdjustPolicy policy)
3872 {
3873 auto it = GetSpanIter(index);
3874 CHECK_NULL_RETURN((it != spans_.end()), false);
3875 auto spanItem = *it;
3876 CHECK_NULL_RETURN(spanItem, false);
3877
3878 auto spanStart = spanItem->rangeStart;
3879 auto spanEnd = spanItem->position;
3880 if (spanItem->unicode != 0 && spanItem->Contains(index)) {
3881 auto it = SELECTOR_ADJUST_DIR_MAP.find({ handleType, policy });
3882 index = (it->second == MoveDirection::BACKWARD) ? spanStart : spanEnd;
3883 return true;
3884 }
3885 return false;
3886 }
3887
GetEmojiRelation(int index)3888 EmojiRelation RichEditorPattern::GetEmojiRelation(int index)
3889 {
3890 auto it = GetSpanIter(index);
3891 CHECK_NULL_RETURN((it != spans_.end()), EmojiRelation::NO_EMOJI);
3892 auto spanItem = *it;
3893 CHECK_NULL_RETURN(spanItem, EmojiRelation::NO_EMOJI);
3894 int32_t emojiStartIndex;
3895 int32_t emojiEndIndex;
3896 return TextEmojiProcessor::GetIndexRelationToEmoji(index - spanItem->rangeStart, spanItem->content,
3897 emojiStartIndex, emojiEndIndex);
3898 }
3899
AdjustSelectorForEmoji(int & index,HandleType handleType,SelectorAdjustPolicy policy)3900 bool RichEditorPattern::AdjustSelectorForEmoji(int& index, HandleType handleType, SelectorAdjustPolicy policy)
3901 {
3902 auto it = GetSpanIter(index);
3903 CHECK_NULL_RETURN((it != spans_.end()), false);
3904 auto spanItem = *it;
3905 CHECK_NULL_RETURN(spanItem, false);
3906
3907 int32_t emojiStartIndex;
3908 int32_t emojiEndIndex;
3909 int32_t spanStart = spanItem->rangeStart;
3910 EmojiRelation relation = TextEmojiProcessor::GetIndexRelationToEmoji(index - spanStart, spanItem->content,
3911 emojiStartIndex, emojiEndIndex);
3912 if (relation != EmojiRelation::IN_EMOJI && relation != EmojiRelation::MIDDLE_EMOJI) {
3913 // no need adjusting when index is not warpped in emojis
3914 return false;
3915 }
3916 int32_t start = 0;
3917 int32_t end = 0;
3918 bool isBoundaryGet = paragraphs_.GetWordBoundary(index, start, end); // boundary from engine
3919 if (isBoundaryGet) {
3920 if (handleType == HandleType::FIRST) {
3921 index = start;
3922 } else {
3923 if (index > start) {
3924 // index to emoji, move index to end of emoji, double check "in emoji state"
3925 index = end;
3926 }
3927 }
3928 } else {
3929 if (relation == EmojiRelation::IN_EMOJI) {
3930 int32_t indexInSpan = (handleType == HandleType::FIRST) ? emojiStartIndex : emojiEndIndex;
3931 index = spanItem->rangeStart + indexInSpan;
3932 }
3933 }
3934 TAG_LOGD(AceLogTag::ACE_RICH_TEXT,
3935 "index=%{public}d, handleType=%{public}d, emojiRange=[%{public}d,%{public}d] isBoundaryGet=%{public}d "\
3936 "boundary=[%{public}d, %{public}d]",
3937 index, handleType, emojiStartIndex, emojiEndIndex, isBoundaryGet, start, end);
3938 return true;
3939 }
3940
GetSpanIter(int32_t index)3941 std::list<RefPtr<SpanItem>>::iterator RichEditorPattern::GetSpanIter(int32_t index)
3942 {
3943 return std::find_if(spans_.begin(), spans_.end(), [index](const RefPtr<SpanItem>& spanItem) {
3944 return spanItem->rangeStart <= index && index < spanItem->position;
3945 });
3946 }
3947
GetSpanType(int32_t index)3948 SpanItemType RichEditorPattern::GetSpanType(int32_t index)
3949 {
3950 auto it = GetSpanIter(index);
3951 CHECK_NULL_RETURN((it != spans_.end()), SpanItemType::NORMAL);
3952 auto& spanItem = *it;
3953 CHECK_NULL_RETURN(spanItem, SpanItemType::NORMAL);
3954 return spanItem->spanItemType;
3955 }
3956
GetGlyphPositionAtCoordinate(int32_t x,int32_t y)3957 PositionWithAffinity RichEditorPattern::GetGlyphPositionAtCoordinate(int32_t x, int32_t y)
3958 {
3959 Offset offset(x, y);
3960 return paragraphs_.GetGlyphPositionAtCoordinate(ConvertTouchOffsetToTextOffset(offset));
3961 }
3962
InitDragDropEvent()3963 void RichEditorPattern::InitDragDropEvent()
3964 {
3965 auto host = GetHost();
3966 CHECK_NULL_VOID(host);
3967 auto gestureHub = host->GetOrCreateGestureEventHub();
3968 CHECK_NULL_VOID(gestureHub);
3969 gestureHub->InitDragDropEvent();
3970 gestureHub->SetThumbnailCallback(GetThumbnailCallback());
3971 auto eventHub = host->GetEventHub<EventHub>();
3972 CHECK_NULL_VOID(eventHub);
3973 auto onDragMove = [weakPtr = WeakClaim(this)](
3974 const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& extraParams) {
3975 auto pattern = weakPtr.Upgrade();
3976 CHECK_NULL_VOID(pattern);
3977 pattern->showSelect_ = false;
3978 pattern->OnDragMove(event);
3979 };
3980 eventHub->SetOnDragMove(std::move(onDragMove));
3981 OnDragStartAndEnd();
3982 onDragDropAndLeave();
3983 }
3984
OnDragStartAndEnd()3985 void RichEditorPattern::OnDragStartAndEnd()
3986 {
3987 auto host = GetHost();
3988 CHECK_NULL_VOID(host);
3989 auto eventHub = host->GetEventHub<EventHub>();
3990 CHECK_NULL_VOID(eventHub);
3991 auto onDragStart = [weakPtr = WeakClaim(this)](const RefPtr<OHOS::Ace::DragEvent>& event,
3992 const std::string& extraParams) -> NG::DragDropInfo {
3993 NG::DragDropInfo itemInfo;
3994 auto pattern = weakPtr.Upgrade();
3995 CHECK_NULL_RETURN(pattern, itemInfo);
3996 return pattern->HandleDragStart(event, extraParams);
3997 };
3998 eventHub->SetDefaultOnDragStart(std::move(onDragStart));
3999 auto onDragEnd = [weakPtr = WeakClaim(this), scopeId = Container::CurrentId()](
4000 const RefPtr<OHOS::Ace::DragEvent>& event) {
4001 ContainerScope scope(scopeId);
4002 auto pattern = weakPtr.Upgrade();
4003 CHECK_NULL_VOID(pattern);
4004 pattern->isDragSponsor_ = false;
4005 pattern->dragRange_ = { 0, 0 };
4006 pattern->showSelect_ = true;
4007 pattern->StopAutoScroll();
4008 pattern->ClearRedoOperationRecords();
4009 pattern->OnDragEnd(event);
4010 };
4011 eventHub->SetOnDragEnd(std::move(onDragEnd));
4012 }
4013
HandleDragStart(const RefPtr<Ace::DragEvent> & event,const std::string & extraParams)4014 NG::DragDropInfo RichEditorPattern::HandleDragStart(const RefPtr<Ace::DragEvent>& event, const std::string& extraParams)
4015 {
4016 if (!isDragSponsor_) {
4017 isDragSponsor_ = true;
4018 dragRange_ = { textSelector_.GetTextStart(), textSelector_.GetTextEnd() };
4019 }
4020 sourceTool_ = event ? event->GetSourceTool() : SourceTool::UNKNOWN;
4021 timestamp_ = std::chrono::system_clock::now().time_since_epoch().count();
4022 auto eventHub = GetEventHub<RichEditorEventHub>();
4023 CHECK_NULL_RETURN(eventHub, {});
4024 eventHub->SetTimestamp(timestamp_);
4025 showSelect_ = false;
4026 auto dropInfo = OnDragStart(event, extraParams);
4027 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleDragStart dragStatus=%{public}d", status_);
4028 return dropInfo;
4029 }
4030
onDragDropAndLeave()4031 void RichEditorPattern::onDragDropAndLeave()
4032 {
4033 auto host = GetHost();
4034 CHECK_NULL_VOID(host);
4035 auto eventHub = host->GetEventHub<EventHub>();
4036 CHECK_NULL_VOID(eventHub);
4037 auto onDragDrop = [weakPtr = WeakClaim(this), scopeId = Container::CurrentId()](
4038 const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& value) {
4039 ContainerScope scope(scopeId);
4040 auto pattern = weakPtr.Upgrade();
4041 CHECK_NULL_VOID(pattern);
4042 auto host = pattern->GetHost();
4043 CHECK_NULL_VOID(host);
4044 auto eventHub = host->GetEventHub<EventHub>();
4045 pattern->sourceTool_ = event ? event->GetSourceTool() : SourceTool::UNKNOWN;
4046 CHECK_NULL_VOID(eventHub && eventHub->IsEnabled());
4047 bool isCopy = false;
4048 if (pattern->status_ == Status::DRAGGING) {
4049 CHECK_NULL_VOID(event);
4050 auto gesturePressedCodes = event->GetPressedKeyCodes();
4051 if ((gesturePressedCodes.size() == 1) && ((gesturePressedCodes[0] == KeyCode::KEY_CTRL_LEFT) ||
4052 (gesturePressedCodes[0] == KeyCode::KEY_CTRL_RIGHT))) {
4053 isCopy = true;
4054 }
4055 }
4056 pattern->status_ = Status::ON_DROP;
4057 pattern->HandleOnDragDrop(event, isCopy);
4058 pattern->status_ = Status::NONE;
4059 };
4060 eventHub->SetOnDrop(std::move(onDragDrop));
4061 auto onDragDragLeave = [weakPtr = WeakClaim(this), scopeId = Container::CurrentId()](
4062 const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& value) {
4063 ContainerScope scope(scopeId);
4064 auto pattern = weakPtr.Upgrade();
4065 IF_PRESENT(pattern, StopAutoScroll());
4066 };
4067 eventHub->SetOnDragLeave(onDragDragLeave);
4068 }
4069
ClearDragDropEvent()4070 void RichEditorPattern::ClearDragDropEvent()
4071 {
4072 auto host = GetHost();
4073 CHECK_NULL_VOID(host);
4074 SetIsTextDraggable(false);
4075 auto eventHub = host->GetEventHub<EventHub>();
4076 CHECK_NULL_VOID(eventHub);
4077 eventHub->SetOnDragStart(nullptr);
4078 eventHub->SetOnDragEnter(nullptr);
4079 eventHub->SetOnDragMove(nullptr);
4080 eventHub->SetOnDragLeave(nullptr);
4081 eventHub->SetOnDragEnd(nullptr);
4082 eventHub->SetOnDrop(nullptr);
4083 }
4084
OnDragMove(const RefPtr<OHOS::Ace::DragEvent> & event)4085 void RichEditorPattern::OnDragMove(const RefPtr<OHOS::Ace::DragEvent>& event)
4086 {
4087 auto theme = GetTheme<RichEditorTheme>();
4088 CHECK_NULL_VOID(theme);
4089 auto touchX = event->GetX();
4090 auto touchY = event->GetY();
4091 auto textRect = GetTextRect();
4092 textRect.SetTop(textRect.GetY() - std::min(baselineOffset_, 0.0f));
4093 Offset textOffset = { touchX - textRect.GetX() - GetParentGlobalOffset().GetX(),
4094 touchY - textRect.GetY() - GetParentGlobalOffset().GetY() - theme->GetInsertCursorOffset().ConvertToPx() };
4095 auto position = isShowPlaceholder_? 0 : paragraphs_.GetIndex(textOffset);
4096 ResetSelection();
4097 CloseSelectOverlay();
4098 SetCaretPosition(position);
4099 CalcAndRecordLastClickCaretInfo(textOffset);
4100 auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
4101 CHECK_NULL_VOID(overlayMod_);
4102 auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
4103 overlayModifier->SetCaretOffsetAndHeight(caretOffset, caretHeight);
4104
4105 auto host = GetHost();
4106 CHECK_NULL_VOID(host);
4107 if (host->GetDragPreviewOption().enableEdgeAutoScroll) {
4108 AutoScrollParam param = { .autoScrollEvent = AutoScrollEvent::DRAG, .showScrollbar = true };
4109 auto localOffset = OffsetF(touchX, touchY) - parentGlobalOffset_;
4110 AutoScrollByEdgeDetection(param, localOffset, EdgeDetectionStrategy::IN_BOUNDARY);
4111 } else if (isAutoScrollRunning_) {
4112 StopAutoScroll();
4113 }
4114 }
4115
OnDragEnd(const RefPtr<Ace::DragEvent> & event)4116 void RichEditorPattern::OnDragEnd(const RefPtr<Ace::DragEvent>& event)
4117 {
4118 ResetDragRecordSize(-1);
4119 if (status_ == Status::DRAGGING) {
4120 status_ = Status::NONE;
4121 }
4122 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "OnDragEnd dragStatus=%{public}d", status_);
4123 ResetDragSpanItems();
4124 CHECK_NULL_VOID(!recoverDragResultObjects_.empty());
4125 UpdateSpanItemDragStatus(recoverDragResultObjects_, false);
4126 recoverDragResultObjects_.clear();
4127 auto focusHub = GetFocusHub();
4128 if (event && focusHub && event->GetResult() != DragRet::DRAG_SUCCESS && focusHub->IsFocusable()) {
4129 afterDragSelect_ = true;
4130 HandleSelectionChange(recoverStart_, recoverEnd_);
4131 showSelect_ = true;
4132 CalculateHandleOffsetAndShowOverlay();
4133 ResetSelection();
4134 }
4135 auto host = GetHost();
4136 IF_PRESENT(host, MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF));
4137 }
4138
ToStyledString(int32_t start,int32_t end)4139 RefPtr<SpanString> RichEditorPattern::ToStyledString(int32_t start, int32_t end)
4140 {
4141 auto length = GetTextContentLength();
4142 int32_t realStart = (start == -1) ? 0 : std::clamp(start, 0, length);
4143 int32_t realEnd = (end == -1) ? length : std::clamp(end, 0, length);
4144 if (realStart > realEnd) {
4145 std::swap(realStart, realEnd);
4146 }
4147 RefPtr<SpanString> spanString = MakeRefPtr<SpanString>(u"");
4148 if (aiWriteAdapter_->GetAIWrite()) {
4149 SetSubSpansWithAIWrite(spanString, realStart, realEnd);
4150 } else {
4151 SetSubSpans(spanString, realStart, realEnd);
4152 }
4153 SetSubMap(spanString);
4154 return spanString;
4155 }
4156
FromStyledString(const RefPtr<SpanString> & spanString)4157 SelectionInfo RichEditorPattern::FromStyledString(const RefPtr<SpanString>& spanString)
4158 {
4159 std::list<ResultObject> resultObjects;
4160 int32_t start = 0;
4161 int32_t end = 0;
4162 if (spanString && !spanString->GetSpanItems().empty()) {
4163 auto spans = spanString->GetSpanItems();
4164 int32_t index = 0;
4165 std::for_each(spans.begin(), spans.end(),
4166 [&index, &resultObjects, weak = WeakClaim(this)](RefPtr<SpanItem>& item) {
4167 CHECK_NULL_VOID(item);
4168 auto pattern = weak.Upgrade();
4169 CHECK_NULL_VOID(pattern);
4170 auto obj = item->GetSpanResultObject(item->interval.first, item->interval.second);
4171 if (AceType::InstanceOf<ImageSpanItem>(item)) {
4172 obj.imageStyle = pattern->GetImageStyleBySpanItem(item);
4173 } else if (!AceType::InstanceOf<CustomSpanItem>(item)) {
4174 obj.textStyle = pattern->GetTextStyleBySpanItem(item);
4175 }
4176 obj.spanPosition.spanIndex = index;
4177 ++index;
4178 if (obj.isInit) {
4179 resultObjects.emplace_back(obj);
4180 }
4181 });
4182 if (spans.back()) {
4183 end = spans.back()->interval.second;
4184 }
4185 if (spans.front()) {
4186 start = spans.front()->interval.first;
4187 }
4188 }
4189 SelectionInfo selection;
4190 selection.SetSelectionEnd(end);
4191 selection.SetSelectionStart(start);
4192 selection.SetResultObjectList(resultObjects);
4193 return selection;
4194 }
4195
GetTextStyleBySpanItem(const RefPtr<SpanItem> & spanItem)4196 TextStyleResult RichEditorPattern::GetTextStyleBySpanItem(const RefPtr<SpanItem>& spanItem)
4197 {
4198 TextStyleResult textStyle;
4199 CHECK_NULL_RETURN(spanItem, textStyle);
4200 auto theme = GetTheme<RichEditorTheme>();
4201 TextStyle style = theme ? theme->GetTextStyle() : TextStyle();
4202 if (spanItem->fontStyle) {
4203 textStyle.fontColor = spanItem->fontStyle->GetTextColor().value_or(style.GetTextColor()).ColorToString();
4204 textStyle.fontSize =
4205 spanItem->fontStyle->GetFontSize().value_or(Dimension(DEFAULT_TEXT_SIZE, DimensionUnit::FP)).ConvertToFp();
4206 textStyle.fontStyle =
4207 static_cast<int32_t>(spanItem->fontStyle->GetItalicFontStyle().value_or(OHOS::Ace::FontStyle::NORMAL));
4208 textStyle.fontWeight = static_cast<int32_t>(spanItem->fontStyle->GetFontWeight().value_or(FontWeight::NORMAL));
4209 std::string fontFamilyValue;
4210 const std::vector<std::string> defaultFontFamily = { "HarmonyOS Sans" };
4211 auto fontFamily = spanItem->fontStyle->GetFontFamily().value_or(defaultFontFamily);
4212 for (const auto& str : fontFamily) {
4213 fontFamilyValue += str;
4214 fontFamilyValue += ",";
4215 }
4216 fontFamilyValue = fontFamilyValue.substr(0, fontFamilyValue.size() ? fontFamilyValue.size() - 1 : 0);
4217 textStyle.fontFamily = !fontFamilyValue.empty() ? fontFamilyValue : defaultFontFamily.front();
4218 textStyle.decorationType =
4219 static_cast<int32_t>(spanItem->fontStyle->GetTextDecoration().value_or(TextDecoration::NONE));
4220 textStyle.decorationColor =
4221 spanItem->fontStyle->GetTextDecorationColor().value_or(style.GetTextDecorationColor()).ColorToString();
4222 textStyle.decorationStyle =
4223 static_cast<int32_t>(spanItem->fontStyle->GetTextDecorationStyle().value_or(TextDecorationStyle::SOLID));
4224 textStyle.fontFeature = spanItem->fontStyle->GetFontFeature().value_or(ParseFontFeatureSettings("\"pnum\" 1"));
4225 textStyle.letterSpacing = spanItem->fontStyle->GetLetterSpacing().value_or(Dimension()).ConvertToFp();
4226 }
4227 if (spanItem->textLineStyle) {
4228 textStyle.lineHeight = spanItem->textLineStyle->GetLineHeight().value_or(Dimension()).ConvertToFp();
4229 textStyle.halfLeading = spanItem->textLineStyle->GetHalfLeading().value_or(false);
4230 textStyle.lineSpacing = spanItem->textLineStyle->GetLineSpacing().value_or(Dimension()).ConvertToFp();
4231 textStyle.textAlign = static_cast<int32_t>(spanItem->textLineStyle->GetTextAlign().value_or(TextAlign::START));
4232 auto lm = spanItem->textLineStyle->GetLeadingMargin();
4233 if (lm.has_value()) {
4234 textStyle.leadingMarginSize[RichEditorLeadingRange::LEADING_START] = lm.value().size.Width().ToString();
4235 textStyle.leadingMarginSize[RichEditorLeadingRange::LEADING_END] = lm.value().size.Height().ToString();
4236 }
4237 textStyle.wordBreak =
4238 static_cast<int32_t>(spanItem->textLineStyle->GetWordBreak().value_or(WordBreak::BREAK_WORD));
4239 textStyle.lineBreakStrategy =
4240 static_cast<int32_t>(spanItem->textLineStyle->GetLineBreakStrategy().value_or(LineBreakStrategy::GREEDY));
4241 textStyle.paragraphSpacing = spanItem->textLineStyle->GetParagraphSpacing();
4242 }
4243 textStyle.textBackgroundStyle = spanItem->backgroundStyle;
4244 return textStyle;
4245 }
4246
GetImageStyleBySpanItem(const RefPtr<SpanItem> & spanItem)4247 ImageStyleResult RichEditorPattern::GetImageStyleBySpanItem(const RefPtr<SpanItem>& spanItem)
4248 {
4249 ImageStyleResult imageStyle;
4250 auto imageSpanItem = DynamicCast<ImageSpanItem>(spanItem);
4251 CHECK_NULL_RETURN(imageSpanItem, imageStyle);
4252 auto imageAttributeOp = imageSpanItem->options.imageAttribute;
4253 CHECK_NULL_RETURN(imageAttributeOp.has_value(), imageStyle);
4254 auto imageSizeOp = imageAttributeOp->size;
4255 if (imageSizeOp.has_value() && imageSizeOp->width.has_value() && imageSizeOp->height.has_value()) {
4256 imageStyle.size[RichEditorImageSize::SIZEWIDTH] = imageSizeOp->width->ConvertToPx();
4257 imageStyle.size[RichEditorImageSize::SIZEHEIGHT] = imageSizeOp->height->ConvertToPx();
4258 }
4259 if (imageAttributeOp->verticalAlign.has_value()) {
4260 imageStyle.verticalAlign = static_cast<int32_t>(imageAttributeOp->verticalAlign.value());
4261 }
4262 if (imageAttributeOp->objectFit.has_value()) {
4263 imageStyle.objectFit = static_cast<int32_t>(imageAttributeOp->objectFit.value());
4264 }
4265 if (imageAttributeOp->marginProp.has_value()) {
4266 imageStyle.margin = imageAttributeOp->marginProp->ToString();
4267 }
4268 if (imageAttributeOp->borderRadius.has_value()) {
4269 imageStyle.borderRadius = imageAttributeOp->borderRadius->ToString();
4270 }
4271 return imageStyle;
4272 }
4273
SetSubSpansWithAIWrite(RefPtr<SpanString> & spanString,int32_t start,int32_t end)4274 void RichEditorPattern::SetSubSpansWithAIWrite(RefPtr<SpanString>& spanString, int32_t start, int32_t end)
4275 {
4276 placeholderSpansMap_.clear();
4277 CHECK_NULL_VOID(spanString);
4278 std::list<RefPtr<SpanItem>> subSpans;
4279 std::u16string text;
4280 size_t index = 0;
4281 size_t placeholderGains = 0;
4282 for (const auto& spanItem : spans_) {
4283 if (!spanItem) {
4284 continue;
4285 }
4286 auto oldEnd = spanItem->position;
4287 auto oldStart = spanItem->rangeStart;
4288 if (oldEnd <= start || oldStart >= end) {
4289 continue;
4290 }
4291 RefPtr<SpanItem> newSpanItem = MakeRefPtr<SpanItem>();
4292 auto spanStart = oldStart <= start ? 0 : oldStart - start;
4293 auto spanEnd = oldEnd < end ? oldEnd - start : end - start;
4294 spanStart += static_cast<int32_t>(placeholderGains);
4295 if (spanItem->spanItemType == SpanItemType::NORMAL) {
4296 newSpanItem = spanItem->GetSameStyleSpanItem();
4297 newSpanItem->urlAddress = spanItem->urlAddress;
4298 newSpanItem->content = spanItem->content
4299 .substr(std::max(start - oldStart, 0), std::min(end, oldEnd) - std::max(start, oldStart));
4300 } else {
4301 InitPlaceholderSpansMap(newSpanItem, spanItem, index, placeholderGains);
4302 spanEnd += static_cast<int32_t>(placeholderGains);
4303 }
4304 newSpanItem->interval = {spanStart, spanEnd};
4305 newSpanItem->position = spanEnd;
4306 newSpanItem->rangeStart = spanStart;
4307 newSpanItem->textLineStyle->ResetLeadingMargin();
4308 text.append(newSpanItem->content);
4309 subSpans.emplace_back(newSpanItem);
4310 }
4311 spanString->SetString(text);
4312 spanString->SetSpanItems(std::move(subSpans));
4313 }
4314
InitPlaceholderSpansMap(RefPtr<SpanItem> & newSpanItem,const RefPtr<SpanItem> & spanItem,size_t & index,size_t & placeholderGains)4315 void RichEditorPattern::InitPlaceholderSpansMap(
4316 RefPtr<SpanItem>& newSpanItem, const RefPtr<SpanItem>& spanItem, size_t& index, size_t& placeholderGains)
4317 {
4318 newSpanItem->content = UtfUtils::Str8ToStr16("![id" + std::to_string(index++) + "]");
4319 switch (spanItem->spanItemType) {
4320 case SpanItemType::SYMBOL: {
4321 placeholderSpansMap_[newSpanItem->content] = spanItem;
4322 placeholderGains += PLACEHOLDER_LENGTH - SYMBOL_CONTENT_LENGTH;
4323 break;
4324 }
4325 case SpanItemType::CustomSpan: {
4326 if (!isSpanStringMode_) {
4327 placeholderSpansMap_[newSpanItem->content] = spanItem;
4328 } else {
4329 auto customSpanItem = DynamicCast<CustomSpanItem>(spanItem);
4330 placeholderSpansMap_[newSpanItem->content] = customSpanItem;
4331 }
4332 placeholderGains += PLACEHOLDER_LENGTH - CUSTOM_CONTENT_LENGTH;
4333 break;
4334 }
4335 case SpanItemType::IMAGE: {
4336 placeholderSpansMap_[newSpanItem->content] = spanItem;
4337 placeholderGains += PLACEHOLDER_LENGTH - CUSTOM_CONTENT_LENGTH;
4338 break;
4339 }
4340 default:
4341 break;
4342 }
4343 }
4344
SetSubSpans(RefPtr<SpanString> & spanString,int32_t start,int32_t end)4345 void RichEditorPattern::SetSubSpans(RefPtr<SpanString>& spanString, int32_t start, int32_t end)
4346 {
4347 CHECK_NULL_VOID(spanString);
4348 std::list<RefPtr<SpanItem>> subSpans;
4349 std::u16string text;
4350 for (const auto& spanItem : spans_) {
4351 if (!spanItem || spanItem->spanItemType == SpanItemType::CustomSpan ||
4352 spanItem->spanItemType == SpanItemType::SYMBOL) {
4353 continue;
4354 }
4355 auto spanEndPos = spanItem->position;
4356 auto spanStartPos = spanItem->rangeStart;
4357 if (spanEndPos > start && spanStartPos < end) {
4358 int32_t oldStart = spanStartPos;
4359 int32_t oldEnd = spanEndPos;
4360 auto spanStart = oldStart <= start ? 0 : oldStart - start;
4361 auto spanEnd = oldEnd < end ? oldEnd - start : end - start;
4362 auto newSpanItem = GetSameSpanItem(spanItem);
4363 CHECK_NULL_CONTINUE(newSpanItem);
4364 newSpanItem->spanItemType = spanItem->spanItemType;
4365 newSpanItem->interval = {spanStart, spanEnd};
4366 newSpanItem->position = spanEnd;
4367 newSpanItem->rangeStart = spanStart;
4368 newSpanItem->content = spanItem->content
4369 .substr(std::max(start - oldStart, 0), std::min(end, oldEnd) - std::max(start, oldStart));
4370 text.append(newSpanItem->content);
4371 subSpans.emplace_back(newSpanItem);
4372 }
4373 }
4374 spanString->SetString(text);
4375 spanString->SetSpanItems(std::move(subSpans));
4376 }
4377
GetSameSpanItem(const RefPtr<SpanItem> & spanItem)4378 RefPtr<SpanItem> RichEditorPattern::GetSameSpanItem(const RefPtr<SpanItem>& spanItem)
4379 {
4380 CHECK_NULL_RETURN(spanItem, nullptr);
4381 if (spanItem->spanItemType == SpanItemType::IMAGE) {
4382 auto imageSpanItem = DynamicCast<ImageSpanItem>(spanItem);
4383 CHECK_NULL_RETURN(imageSpanItem, nullptr);
4384 auto newSpanItem = MakeRefPtr<ImageSpanItem>();
4385 auto options = imageSpanItem->options;
4386 if (!options.imagePixelMap) {
4387 auto imageNode = GetImageSpanNodeBySpanItem(imageSpanItem);
4388 CHECK_NULL_RETURN(imageNode, nullptr);
4389 auto pattern = imageNode->GetPattern<ImagePattern>();
4390 CHECK_NULL_RETURN(pattern, nullptr);
4391 auto image = pattern->GetCanvasImage();
4392 CHECK_NULL_RETURN(image, nullptr);
4393 auto pixelMap = image->GetPixelMap();
4394 if (!pixelMap) {
4395 pixelMap = imageNode->GetDragPixelMap();
4396 }
4397 options.imagePixelMap = pixelMap;
4398 }
4399 newSpanItem->SetImageSpanOptions(options);
4400 return newSpanItem;
4401 } else if (spanItem->spanItemType == SpanItemType::NORMAL) {
4402 auto newSpanItem = spanItem->GetSameStyleSpanItem();
4403 newSpanItem->urlAddress = spanItem->urlAddress;
4404 return newSpanItem;
4405 }
4406 return nullptr;
4407 }
4408
GetImageSpanNodeBySpanItem(const RefPtr<ImageSpanItem> & spanItem)4409 RefPtr<ImageSpanNode> RichEditorPattern::GetImageSpanNodeBySpanItem(const RefPtr<ImageSpanItem>& spanItem)
4410 {
4411 auto host = GetHost();
4412 CHECK_NULL_RETURN(host, nullptr);
4413 auto uiNodes = host->GetChildren();
4414 auto it = std::find_if(uiNodes.begin(), uiNodes.end(), [spanItem](const RefPtr<UINode>& uiNode) {
4415 auto imageSpanNode = DynamicCast<ImageSpanNode>(uiNode);
4416 CHECK_NULL_RETURN(imageSpanNode, false);
4417 return imageSpanNode->GetSpanItem() == spanItem;
4418 });
4419 CHECK_NULL_RETURN(it != uiNodes.end(), nullptr);
4420 return DynamicCast<ImageSpanNode>(*it);
4421 }
4422
SetSubMap(RefPtr<SpanString> & spanString)4423 void RichEditorPattern::SetSubMap(RefPtr<SpanString>& spanString)
4424 {
4425 CHECK_NULL_VOID(spanString);
4426 auto subSpans = spanString->GetSpanItems();
4427 std::unordered_map<SpanType, std::list<RefPtr<SpanBase>>> subMap;
4428 for (auto& spanItem : subSpans) {
4429 if (!spanItem) {
4430 continue;
4431 }
4432 auto start = spanItem->rangeStart;
4433 auto end = spanItem->position;
4434 std::list<RefPtr<SpanBase>> spanBases;
4435 if (spanItem->spanItemType == NG::SpanItemType::IMAGE) {
4436 spanBases = { spanString->ToImageSpan(spanItem, start, end) };
4437 } else if (spanItem->spanItemType == NG::SpanItemType::NORMAL) {
4438 spanBases = { spanString->ToFontSpan(spanItem, start, end),
4439 spanString->ToDecorationSpan(spanItem, start, end),
4440 spanString->ToBaselineOffsetSpan(spanItem, start, end),
4441 spanString->ToLetterSpacingSpan(spanItem, start, end),
4442 spanString->ToGestureSpan(spanItem, start, end),
4443 spanString->ToParagraphStyleSpan(spanItem, start, end),
4444 spanString->ToLineHeightSpan(spanItem, start, end),
4445 spanString->ToBackgroundColorSpan(spanItem, start, end),
4446 spanString->ToUrlSpan(spanItem, start, end) };
4447 }
4448 for (auto& spanBase : spanBases) {
4449 if (!spanBase) {
4450 continue;
4451 }
4452 auto it = subMap.find(spanBase->GetSpanType());
4453 if (it == subMap.end()) {
4454 subMap.insert({ spanBase->GetSpanType(), { spanBase } });
4455 } else {
4456 it->second.emplace_back(std::move(spanBase));
4457 }
4458 }
4459 }
4460 spanString->SetSpanMap(std::move(subMap));
4461 }
4462
AddSpanByPasteData(const RefPtr<SpanString> & spanString)4463 void RichEditorPattern::AddSpanByPasteData(const RefPtr<SpanString>& spanString)
4464 {
4465 CHECK_NULL_VOID(spanString);
4466 if (spanString->GetSpansMap().empty()) {
4467 CompleteStyledString(const_cast<RefPtr<SpanString>&>(spanString));
4468 }
4469 if (isSpanStringMode_) {
4470 InsertStyledStringByPaste(spanString);
4471 } else {
4472 AddSpansByPaste(spanString->GetSpanItems());
4473 }
4474
4475 if (aiWriteAdapter_->GetAIWrite()) {
4476 return;
4477 }
4478 StartTwinkling();
4479 auto host = GetHost();
4480 CHECK_NULL_VOID(host);
4481 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
4482 host->MarkModifyDone();
4483 }
4484
CompleteStyledString(RefPtr<SpanString> & spanString)4485 void RichEditorPattern::CompleteStyledString(RefPtr<SpanString>& spanString)
4486 {
4487 CHECK_NULL_VOID(spanString);
4488 std::u16string text;
4489 auto spans = spanString->GetSpanItems();
4490 std::for_each(spans.begin(), spans.end(), [&text](RefPtr<SpanItem>& item) {
4491 CHECK_NULL_VOID(item);
4492 text.append(item->content);
4493 item->position = item->interval.second;
4494 item->rangeStart = item->interval.first;
4495 });
4496 spanString->SetString(std::move(text));
4497 SetSubMap(spanString);
4498 }
4499
InsertStyledStringByPaste(const RefPtr<SpanString> & spanString)4500 void RichEditorPattern::InsertStyledStringByPaste(const RefPtr<SpanString>& spanString)
4501 {
4502 CHECK_NULL_VOID(spanString && styledString_);
4503 int32_t changeStart = caretPosition_;
4504 int32_t changeLength = 0;
4505 if (textSelector_.IsValid()) {
4506 changeStart = textSelector_.GetTextStart();
4507 changeLength = textSelector_.GetTextEnd() - textSelector_.GetTextStart();
4508 }
4509 CHECK_NULL_VOID(BeforeStyledStringChange(changeStart, changeLength, spanString));
4510 auto subSpanString = spanString;
4511 int32_t startLength = maxLength_.value_or(INT_MAX) + changeLength - GetTextContentLength();
4512 if (spanString->GetLength() >= startLength) {
4513 auto range = TextEmojiProcessor::CalSubU16stringRange(
4514 startLength, spanString->GetLength() - startLength, spanString->GetU16string(), true, true);
4515 auto subLength = range.endIndex - range.startIndex;
4516 subSpanString = subSpanString->GetSubSpanString(0, spanString->GetLength() - subLength);
4517 }
4518
4519 if (changeLength > 0 && subSpanString->GetLength() > 0) {
4520 DeleteForwardInStyledString(changeLength, false);
4521 }
4522 ResetSelection();
4523 styledString_->InsertSpanString(changeStart, subSpanString);
4524 SetCaretPosition(caretPosition_ + subSpanString->GetLength());
4525 AfterStyledStringChange(changeStart, changeLength, subSpanString->GetU16string());
4526 }
4527
HandleOnDragInsertStyledString(const RefPtr<SpanString> & spanString,bool isCopy)4528 void RichEditorPattern::HandleOnDragInsertStyledString(const RefPtr<SpanString>& spanString, bool isCopy)
4529 {
4530 CHECK_NULL_VOID(spanString);
4531 int currentCaretPosition = caretPosition_;
4532 auto strLength = spanString->GetLength();
4533 insertValueLength_ = strLength;
4534 if (isDragSponsor_ && !isCopy) {
4535 bool isInsertForward = currentCaretPosition < dragRange_.first;
4536 bool isInsertBackward = currentCaretPosition > dragRange_.second;
4537 CHECK_NULL_VOID(isInsertForward || isInsertBackward);
4538 CHECK_NULL_VOID(BeforeStyledStringChange(currentCaretPosition, 0, spanString));
4539 styledString_->InsertSpanString(currentCaretPosition, spanString);
4540 AfterStyledStringChange(currentCaretPosition, 0, spanString->GetU16string());
4541 if (isInsertForward) {
4542 SetCaretPosition(currentCaretPosition + strLength);
4543 dragRange_.first += strLength;
4544 dragRange_.second += strLength;
4545 }
4546 DeleteValueInStyledString(dragRange_.first, strLength, true, false);
4547 } else {
4548 CHECK_NULL_VOID(BeforeStyledStringChange(currentCaretPosition, 0, spanString));
4549 styledString_->InsertSpanString(currentCaretPosition, spanString);
4550 SetCaretPosition(currentCaretPosition + strLength);
4551 AfterStyledStringChange(currentCaretPosition, 0, spanString->GetU16string());
4552 }
4553 StartTwinkling();
4554 auto host = GetHost();
4555 CHECK_NULL_VOID(host);
4556 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
4557 }
4558
AddSpansByPaste(const std::list<RefPtr<NG::SpanItem>> & spans)4559 void RichEditorPattern::AddSpansByPaste(const std::list<RefPtr<NG::SpanItem>>& spans)
4560 {
4561 if (GetTextContentLength() >= maxLength_.value_or(INT_MAX)) {
4562 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "AddSpansByPaste: Reach the maxLength. maxLength=%{public}d", maxLength_.value_or(INT_MAX));
4563 return;
4564 }
4565 if (textSelector_.IsValid()) {
4566 SetCaretPosition(textSelector_.GetTextStart());
4567 DeleteForward(textSelector_.GetTextStart(), textSelector_.GetTextEnd() - textSelector_.GetTextStart());
4568 ResetSelection();
4569 }
4570 for (const auto& spanItem : spans) {
4571 if (!spanItem) {
4572 continue;
4573 }
4574 auto imageSpanItem = DynamicCast<ImageSpanItem>(spanItem);
4575 if (imageSpanItem) {
4576 auto options = imageSpanItem->options;
4577 options.offset = caretPosition_;
4578 AddImageSpan(options, true, caretPosition_, true);
4579 } else {
4580 auto options = GetTextSpanOptions(spanItem);
4581 AddTextSpan(options, true, caretPosition_);
4582 }
4583 }
4584 }
4585
CalculateTruncationLength(const std::u16string & insertValue,int32_t start)4586 int32_t RichEditorPattern::CalculateTruncationLength(const std::u16string& insertValue, int32_t start)
4587 {
4588 if (!textSelector_.SelectNothing()) {
4589 start += textSelector_.GetTextEnd() - textSelector_.GetTextStart();
4590 }
4591 auto truncationLength = static_cast<int32_t>(insertValue.length()) - start;
4592 auto range = TextEmojiProcessor::CalSubU16stringRange(start, truncationLength, insertValue, true, true);
4593 auto allowInsertLength = static_cast<int32_t>(insertValue.length()) - range.endIndex + range.startIndex;
4594 return allowInsertLength;
4595 }
4596
GetTextSpanOptions(const RefPtr<SpanItem> & spanItem)4597 TextSpanOptions RichEditorPattern::GetTextSpanOptions(const RefPtr<SpanItem>& spanItem)
4598 {
4599 CHECK_NULL_RETURN(spanItem, {});
4600 TextStyle textStyle = GetDefaultTextStyle();
4601 UseSelfStyle(spanItem->fontStyle, spanItem->textLineStyle, textStyle);
4602 textStyle.SetTextBackgroundStyle(spanItem->backgroundStyle);
4603 struct UpdateParagraphStyle paraStyle;
4604 paraStyle.textAlign = spanItem->textLineStyle->GetTextAlign();
4605 paraStyle.leadingMargin = spanItem->textLineStyle->GetLeadingMargin();
4606 paraStyle.wordBreak = spanItem->textLineStyle->GetWordBreak();
4607 paraStyle.lineBreakStrategy = spanItem->textLineStyle->GetLineBreakStrategy();
4608 paraStyle.paragraphSpacing = spanItem->textLineStyle->GetParagraphSpacing();
4609 TextSpanOptions options;
4610 options.value = spanItem->content;
4611 options.offset = caretPosition_;
4612 UserGestureOptions gestureOption;
4613 gestureOption.onClick = spanItem->onClick;
4614 gestureOption.onLongPress = spanItem->onLongPress;
4615 options.userGestureOption = gestureOption;
4616 options.style = textStyle;
4617 options.paraStyle = paraStyle;
4618 return options;
4619 }
4620
ResetDragSpanItems()4621 void RichEditorPattern::ResetDragSpanItems()
4622 {
4623 auto host = GetHost();
4624 CHECK_NULL_VOID(host);
4625 std::unordered_set<int32_t> nodeIds;
4626 std::for_each(dragSpanItems_.begin(), dragSpanItems_.end(), [&nodeIds](RefPtr<SpanItem>& item) {
4627 CHECK_NULL_VOID(item);
4628 item->EndDrag();
4629 auto imageSpanItem = DynamicCast<ImageSpanItem>(item);
4630 if (imageSpanItem) {
4631 nodeIds.emplace(imageSpanItem->imageNodeId);
4632 return;
4633 }
4634 auto placeholderSpanItem = DynamicCast<PlaceholderSpanItem>(item);
4635 if (placeholderSpanItem) {
4636 nodeIds.emplace(placeholderSpanItem->placeholderSpanNodeId);
4637 }
4638 });
4639 const auto& childrens = host->GetChildren();
4640 for (const auto& child : childrens) {
4641 auto findResult = nodeIds.find(child->GetId());
4642 CHECK_NULL_CONTINUE(findResult != nodeIds.end());
4643 auto node = DynamicCast<FrameNode>(child);
4644 CHECK_NULL_CONTINUE(node);
4645 auto renderContext = node->GetRenderContext();
4646 CHECK_NULL_CONTINUE(renderContext);
4647 renderContext->UpdateOpacity(1);
4648 node->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
4649 }
4650 dragSpanItems_.clear();
4651 }
4652
SelectOverlayIsOn()4653 bool RichEditorPattern::SelectOverlayIsOn()
4654 {
4655 return selectOverlay_->SelectOverlayIsOn();
4656 }
4657
UpdateEditingValue(const std::shared_ptr<TextEditingValue> & value,bool needFireChangeEvent)4658 void RichEditorPattern::UpdateEditingValue(const std::shared_ptr<TextEditingValue>& value, bool needFireChangeEvent)
4659 {
4660 #ifdef ENABLE_STANDARD_INPUT
4661 InsertValue(UtfUtils::Str8ToStr16(value->text), true);
4662 #else
4663 if (value->isDelete) {
4664 HandleOnDelete(true);
4665 } else {
4666 InsertValue(UtfUtils::Str8ToStr16(value->appendText));
4667 }
4668 #endif
4669 }
4670
HandleAISpanHoverEvent(const MouseInfo & info)4671 void RichEditorPattern::HandleAISpanHoverEvent(const MouseInfo& info)
4672 {
4673 if (info.GetAction() != MouseAction::MOVE || !NeedShowAIDetect()) {
4674 return;
4675 }
4676 auto scrollBar = GetScrollBar();
4677 if (scrollBar && (scrollBar->IsHover() || scrollBar->IsPressed())) {
4678 return;
4679 }
4680 if (dataDetectorAdapter_->aiSpanRects_.empty()) {
4681 for (const auto& kv : dataDetectorAdapter_->aiSpanMap_) {
4682 auto& aiSpan = kv.second;
4683 const auto& aiRects = paragraphs_.GetRects(aiSpan.start, aiSpan.end);
4684 dataDetectorAdapter_->aiSpanRects_.insert(
4685 dataDetectorAdapter_->aiSpanRects_.end(), aiRects.begin(), aiRects.end());
4686 }
4687 }
4688
4689 auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
4690 PointF textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
4691 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
4692 auto host = GetHost();
4693 CHECK_NULL_VOID(host);
4694 auto pipeline = GetContext();
4695 CHECK_NULL_VOID(pipeline);
4696 auto nodeId = host->GetId();
4697 for (auto&& rect : dataDetectorAdapter_->aiSpanRects_) {
4698 if (!rect.IsInRegion(textOffset)) {
4699 continue;
4700 }
4701 if (currentMouseStyle_ != MouseFormat::HAND_POINTING) {
4702 pipeline->ChangeMouseStyle(nodeId, MouseFormat::HAND_POINTING);
4703 currentMouseStyle_ = MouseFormat::HAND_POINTING;
4704 }
4705 return;
4706 }
4707 if (currentMouseStyle_ != MouseFormat::TEXT_CURSOR) {
4708 pipeline->ChangeMouseStyle(nodeId, MouseFormat::TEXT_CURSOR);
4709 currentMouseStyle_ = MouseFormat::TEXT_CURSOR;
4710 }
4711 }
4712
InitMouseEvent()4713 void RichEditorPattern::InitMouseEvent()
4714 {
4715 CHECK_NULL_VOID(!mouseEventInitialized_);
4716 auto host = GetHost();
4717 CHECK_NULL_VOID(host);
4718 auto eventHub = host->GetEventHub<EventHub>();
4719 CHECK_NULL_VOID(eventHub);
4720 auto inputHub = eventHub->GetOrCreateInputEventHub();
4721 CHECK_NULL_VOID(inputHub);
4722
4723 auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
4724 auto pattern = weak.Upgrade();
4725 CHECK_NULL_VOID(pattern);
4726 pattern->HandleMouseEvent(info);
4727 };
4728 auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
4729 inputHub->AddOnMouseEvent(mouseEvent);
4730 auto hoverTask = [weak = WeakClaim(this)](bool isHover) {
4731 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "on hover event isHover=%{public}d", isHover);
4732 auto pattern = weak.Upgrade();
4733 if (pattern) {
4734 pattern->OnHover(isHover);
4735 }
4736 };
4737 auto hoverEvent = MakeRefPtr<InputEvent>(std::move(hoverTask));
4738 inputHub->AddOnHoverEvent(hoverEvent);
4739 mouseEventInitialized_ = true;
4740 }
4741
OnHover(bool isHover)4742 void RichEditorPattern::OnHover(bool isHover)
4743 {
4744 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "isHover=%{public}d", isHover);
4745 if (!isHover && lastHoverSpanItem_) {
4746 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "spanItem hover false");
4747 lastHoverSpanItem_->onHover_(false, lastHoverInfo_);
4748 lastHoverSpanItem_.Reset();
4749 }
4750 auto scrollBar = GetScrollBar();
4751 if (isHover && (!scrollBar || !scrollBar->IsPressed())) {
4752 ChangeMouseStyle(MouseFormat::TEXT_CURSOR);
4753 } else {
4754 ChangeMouseStyle(MouseFormat::DEFAULT, true);
4755 HandleUrlSpanForegroundClear();
4756 }
4757 }
4758
ChangeMouseStyle(MouseFormat format,bool freeMouseHoldNode)4759 void RichEditorPattern::ChangeMouseStyle(MouseFormat format, bool freeMouseHoldNode)
4760 {
4761 auto host = GetHost();
4762 CHECK_NULL_VOID(host);
4763 auto pipeline = GetContext();
4764 CHECK_NULL_VOID(pipeline);
4765 auto nodeId = host->GetId();
4766 // Do not change mouse style to text-cursor if the right-button custom menu is showing
4767 bool shouldPreventChange = (format == MouseFormat::TEXT_CURSOR && selectOverlay_->IsRightButtonCustomMenuShow());
4768 CHECK_NULL_VOID(!shouldPreventChange);
4769 pipeline->SetMouseStyleHoldNode(nodeId);
4770 pipeline->ChangeMouseStyle(nodeId, format);
4771 currentMouseStyle_ = format;
4772 IF_TRUE(freeMouseHoldNode, pipeline->FreeMouseStyleHoldNode(nodeId));
4773 }
4774
RequestKeyboard(bool isFocusViewChanged,bool needStartTwinkling,bool needShowSoftKeyboard,SourceType sourceType)4775 bool RichEditorPattern::RequestKeyboard(bool isFocusViewChanged, bool needStartTwinkling, bool needShowSoftKeyboard,
4776 SourceType sourceType)
4777 {
4778 auto host = GetHost();
4779 CHECK_NULL_RETURN(host, false);
4780 auto context = host->GetContext();
4781 CHECK_NULL_RETURN(context, false);
4782 CHECK_NULL_RETURN(needShowSoftKeyboard, false);
4783 if (needShowSoftKeyboard && customKeyboardBuilder_) {
4784 return RequestCustomKeyboard();
4785 }
4786 #if defined(ENABLE_STANDARD_INPUT)
4787 if (!EnableStandardInput(needShowSoftKeyboard, sourceType)) {
4788 return false;
4789 }
4790 #else
4791 if (!UnableStandardInput(isFocusViewChanged)) {
4792 return false;
4793 }
4794 #endif
4795 return true;
4796 }
4797
4798 #if defined(ENABLE_STANDARD_INPUT)
4799 #ifdef WINDOW_SCENE_SUPPORTED
GetSCBSystemWindowId()4800 uint32_t RichEditorPattern::GetSCBSystemWindowId()
4801 {
4802 RefPtr<FrameNode> frameNode = GetHost();
4803 CHECK_NULL_RETURN(frameNode, {});
4804 auto focusSystemWindowId = WindowSceneHelper::GetFocusSystemWindowId(frameNode);
4805 return focusSystemWindowId;
4806 }
4807 #endif
4808
EnableStandardInput(bool needShowSoftKeyboard,SourceType sourceType)4809 bool RichEditorPattern::EnableStandardInput(bool needShowSoftKeyboard, SourceType sourceType)
4810 {
4811 auto host = GetHost();
4812 CHECK_NULL_RETURN(host, false);
4813 auto context = host->GetContext();
4814 CHECK_NULL_RETURN(context, false);
4815 if (richEditTextChangeListener_ == nullptr) {
4816 richEditTextChangeListener_ = new OnTextChangedListenerImpl(WeakClaim(this));
4817 }
4818 auto inputMethod = MiscServices::InputMethodController::GetInstance();
4819 CHECK_NULL_RETURN(inputMethod, false);
4820 auto miscTextConfig = GetMiscTextConfig();
4821 CHECK_NULL_RETURN(miscTextConfig.has_value(), false);
4822 TAG_LOGD(
4823 AceLogTag::ACE_RICH_TEXT, "RequestKeyboard set calling window id is : %{public}u", miscTextConfig->windowId);
4824 MiscServices::TextConfig textconfig = miscTextConfig.value();
4825 #ifdef WINDOW_SCENE_SUPPORTED
4826 auto systemWindowId = GetSCBSystemWindowId();
4827 if (systemWindowId) {
4828 miscTextConfig->windowId = systemWindowId;
4829 }
4830 #endif
4831 auto textFieldManager = DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
4832 if (host && textFieldManager) {
4833 textFieldManager->SetImeAttached(true);
4834 textFieldManager->SetLastRequestKeyboardId(host->GetId());
4835 }
4836 OHOS::MiscServices::AttachOptions attachOptions;
4837 attachOptions.isShowKeyboard = needShowSoftKeyboard;
4838 attachOptions.requestKeyboardReason =
4839 static_cast<OHOS::MiscServices::RequestKeyboardReason>(static_cast<int32_t>(sourceType));
4840 auto ret = inputMethod->Attach(richEditTextChangeListener_, attachOptions, textconfig);
4841 if (ret == MiscServices::ErrorCode::NO_ERROR) {
4842 textFieldManager->SetIsImeAttached(true);
4843 }
4844 UpdateCaretInfoToController();
4845 if (context) {
4846 inputMethod->SetCallingWindow(context->GetWindowId());
4847 }
4848 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
4849 imeAttached_ = true;
4850 #endif
4851 return true;
4852 }
4853
GetMiscTextConfig()4854 std::optional<MiscServices::TextConfig> RichEditorPattern::GetMiscTextConfig()
4855 {
4856 auto tmpHost = GetHost();
4857 CHECK_NULL_RETURN(tmpHost, {});
4858 auto pipeline = tmpHost->GetContextRefPtr();
4859 auto renderContext = tmpHost->GetRenderContext();
4860 CHECK_NULL_RETURN(pipeline && renderContext, {});
4861
4862 float caretHeight = 0.0f;
4863 OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight);
4864 if (NearZero(caretHeight)) {
4865 auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
4866 caretHeight = overlayModifier ? overlayModifier->GetCaretHeight() : DEFAULT_CARET_HEIGHT;
4867 }
4868 if (NearZero(caretHeight)) {
4869 auto [caretAdjustOffset, caretAdjustHeight] = CalculateCaretOffsetAndHeight();
4870 caretHeight = caretAdjustHeight;
4871 }
4872
4873 // richeditor relative to root node offset(without transform)
4874 auto parentGlobalOffset = renderContext->GetPaintRectWithoutTransform().GetOffset() -
4875 pipeline->GetRootRect().GetOffset();
4876 // caret top (without transform)
4877 auto caretTop = caretOffset.GetY() + parentGlobalOffset.GetY();
4878 double positionY = parentGlobalOffset.GetY();
4879 double height = caretTop + caretHeight + KEYBOARD_AVOID_OFFSET.ConvertToPx() - positionY;
4880
4881 if (auto manager = pipeline->GetSafeAreaManager(); manager) {
4882 auto keyboardOffset = manager->GetKeyboardOffset();
4883 positionY -= keyboardOffset;
4884 }
4885 OffsetF caretLeftTopPoint(caretOffset.GetX() + parentGlobalOffset.GetX(), caretTop);
4886 OffsetF caretRightBottomPoint(caretLeftTopPoint.GetX() + CARET_WIDTH, caretLeftTopPoint.GetY() + caretHeight);
4887 HandlePointWithTransform(caretLeftTopPoint);
4888 HandlePointWithTransform(caretRightBottomPoint);
4889 // window rect relative to screen
4890 auto windowRect = pipeline->GetCurrentWindowRect();
4891 MiscServices::CursorInfo cursorInfo { .left = caretLeftTopPoint.GetX() + windowRect.Left(),
4892 .top = caretLeftTopPoint.GetY() + windowRect.Top(),
4893 .width = std::abs(caretLeftTopPoint.GetX() - caretRightBottomPoint.GetX()),
4894 .height = std::abs(caretLeftTopPoint.GetY() - caretRightBottomPoint.GetY()) };
4895 MiscServices::InputAttribute inputAttribute = { .inputPattern = (int32_t)TextInputType::UNSPECIFIED,
4896 .enterKeyType = (int32_t)GetTextInputActionValue(GetDefaultTextInputAction()),
4897 .isTextPreviewSupported = isTextPreviewSupported_ && (!isSpanStringMode_ || isAPI18Plus),
4898 .immersiveMode = static_cast<int32_t>(keyboardAppearance_) };
4899 auto start = textSelector_.IsValid() ? textSelector_.GetStart() : caretPosition_;
4900 auto end = textSelector_.IsValid() ? textSelector_.GetEnd() : caretPosition_;
4901 MiscServices::TextConfig textConfig = { .inputAttribute = inputAttribute,
4902 .cursorInfo = cursorInfo,
4903 .range = { .start = start, .end = end },
4904 .windowId = pipeline->GetFocusWindowId(),
4905 .positionY = positionY + windowRect.Top(),
4906 .height = height };
4907 return textConfig;
4908 }
4909 #else
UnableStandardInput(bool isFocusViewChanged)4910 bool RichEditorPattern::UnableStandardInput(bool isFocusViewChanged)
4911 {
4912 auto host = GetHost();
4913 CHECK_NULL_RETURN(host, false);
4914 auto context = host->GetContext();
4915 CHECK_NULL_RETURN(context, false);
4916 if (HasConnection()) {
4917 connection_->Show(isFocusViewChanged, GetInstanceId());
4918 return true;
4919 }
4920 TextInputConfiguration config;
4921 config.type = TextInputType::UNSPECIFIED;
4922 config.action = TextInputAction::DONE;
4923 config.obscureText = false;
4924 connection_ =
4925 TextInputProxy::GetInstance().Attach(WeakClaim(this), config, context->GetTaskExecutor(), GetInstanceId());
4926 if (!HasConnection()) {
4927 return false;
4928 }
4929 TextEditingValue value;
4930 if (spans_.empty()) {
4931 value.text = UtfUtils::Str16ToStr8(textForDisplay_);
4932 } else {
4933 for (auto it = spans_.begin(); it != spans_.end(); it++) {
4934 if ((*it)->placeholderIndex < 0) {
4935 value.text.append(UtfUtils::Str16ToStr8((*it)->content));
4936 } else {
4937 value.text.append(" ");
4938 }
4939 }
4940 }
4941 value.selection.Update(caretPosition_, caretPosition_);
4942 connection_->SetEditingState(value, GetInstanceId());
4943 connection_->Show(isFocusViewChanged, GetInstanceId());
4944 return true;
4945 }
4946 #endif
4947
OnColorConfigurationUpdate()4948 void RichEditorPattern::OnColorConfigurationUpdate()
4949 {
4950 auto colorMode = GetColorMode();
4951 floatingCaretState_.UpdateOriginCaretColor(GetDisplayColorMode());
4952 if (colorMode == ColorMode::COLOR_MODE_UNDEFINED) {
4953 OnCommonColorChange();
4954 }
4955 }
4956
OnThemeScopeUpdate(int32_t themeScopeId)4957 bool RichEditorPattern::OnThemeScopeUpdate(int32_t themeScopeId)
4958 {
4959 IF_PRESENT(magnifierController_, SetColorModeChange(true));
4960 floatingCaretState_.UpdateOriginCaretColor(GetDisplayColorMode());
4961 OnCommonColorChange();
4962 return false;
4963 }
4964
OnCommonColorChange()4965 void RichEditorPattern::OnCommonColorChange()
4966 {
4967 auto host = GetHost();
4968 auto theme = GetTheme<RichEditorTheme>();
4969 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4970 CHECK_NULL_VOID(host && theme && textLayoutProperty);
4971
4972 auto displayColorMode = GetDisplayColorMode();
4973 COLOR_MODE_LOCK(displayColorMode);
4974
4975 const auto& themeTextStyle = theme->GetTextStyle();
4976 auto themeTextColor = themeTextStyle.GetTextColor();
4977 auto themeTextDecColor = themeTextStyle.GetTextDecorationColor();
4978 textLayoutProperty->UpdateTextColor(themeTextColor);
4979 textLayoutProperty->UpdateTextDecorationColor(themeTextDecColor);
4980 auto themeUrlSpanColor = GetUrlSpanColor();
4981 textLayoutProperty->UpdateUrlDefualtColor(themeUrlSpanColor);
4982 textLayoutProperty->UpdateUrlHoverColor(GetUrlHoverColor());
4983 textLayoutProperty->UpdateUrlPressedColor(GetUrlPressColor());
4984
4985 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "theme, ColorMode=%{public}d, TextColor=%{public}s, DecorationColor=%{public}s",
4986 displayColorMode, themeTextColor.ToString().c_str(), themeTextDecColor.ToString().c_str());
4987
4988 const auto& spans = host->GetChildren();
4989 for (const auto& uiNode : spans) {
4990 auto placeholderSpan = DynamicCast<PlaceholderSpanNode>(uiNode);
4991 if (placeholderSpan) {
4992 auto spanItem = placeholderSpan->GetSpanItem();
4993 CHECK_NULL_CONTINUE(spanItem);
4994 IF_PRESENT(spanItem, UpdateColorByResourceId());
4995 }
4996 auto spanNode = DynamicCast<SpanNode>(uiNode);
4997 CHECK_NULL_CONTINUE(spanNode);
4998 auto spanItem = spanNode->GetSpanItem();
4999 CHECK_NULL_CONTINUE(spanItem);
5000 auto& textColor = spanItem->urlOnRelease ? themeUrlSpanColor : themeTextColor;
5001 IF_TRUE(spanItem->useThemeFontColor, spanNode->UpdateTextColorWithoutCheck(textColor));
5002 IF_TRUE(spanItem->useThemeDecorationColor, spanNode->UpdateTextDecorationColorWithoutCheck(themeTextDecColor));
5003 spanNode->UpdateColorByResourceId();
5004 }
5005 paragraphCache_.Clear();
5006 IF_PRESENT(typingTextStyle_, UpdateColorByResourceId());
5007 IF_PRESENT(typingStyle_, UpdateColorByResourceId());
5008 IF_PRESENT(selectedBackgroundColor_, UpdateColorByResourceId());
5009
5010 IF_PRESENT(magnifierController_, SetColorModeChange(true));
5011 auto scrollBar = GetScrollBar();
5012 auto scrollbarTheme = GetTheme<ScrollBarTheme>();
5013 CHECK_NULL_VOID(scrollBar && scrollbarTheme);
5014 scrollBar->SetForegroundColor(scrollbarTheme->GetForegroundColor());
5015 scrollBar->SetBackgroundColor(scrollbarTheme->GetBackgroundColor());
5016 }
5017
UpdateCaretInfoToController()5018 void RichEditorPattern::UpdateCaretInfoToController()
5019 {
5020 CHECK_NULL_VOID(HasFocus());
5021 std::u16string text = u"";
5022 for (auto iter = spans_.begin(); iter != spans_.end(); iter++) {
5023 text += (*iter)->content;
5024 }
5025 auto start = textSelector_.IsValid() ? textSelector_.GetStart() : caretPosition_;
5026 auto end = textSelector_.IsValid() ? textSelector_.GetEnd() : caretPosition_;
5027 #if defined(ENABLE_STANDARD_INPUT)
5028 auto miscTextConfig = GetMiscTextConfig();
5029 CHECK_NULL_VOID(miscTextConfig.has_value());
5030 MiscServices::CursorInfo cursorInfo = miscTextConfig.value().cursorInfo;
5031 MiscServices::InputMethodController::GetInstance()->OnCursorUpdate(cursorInfo);
5032 MiscServices::InputMethodController::GetInstance()->OnSelectionChange(
5033 text, start, end);
5034 TAG_LOGD(AceLogTag::ACE_RICH_TEXT,
5035 "CursorInfo: pos=[%{public}.2f,%{public}.2f], size=[%{public}.2f,%{public}.2f], caret=%{public}d;"
5036 "OnSelectionChange: textLen=%{public}zu, range=[%{public}d,%{public}d]",
5037 cursorInfo.left, cursorInfo.top, cursorInfo.width, cursorInfo.height, caretPosition_,
5038 text.length(), start, end);
5039 #else
5040 if (HasConnection()) {
5041 TextEditingValue editingValue;
5042 editingValue.text = UtfUtils::Str16ToStr8(text);
5043 editingValue.hint = "";
5044 editingValue.selection.Update(start, end);
5045 connection_->SetEditingState(editingValue, GetInstanceId());
5046 }
5047 #endif
5048 }
5049
HasConnection() const5050 bool RichEditorPattern::HasConnection() const
5051 {
5052 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
5053 return imeAttached_;
5054 #else
5055 return connection_ != nullptr;
5056 #endif
5057 }
5058
SetCustomKeyboardOption(bool supportAvoidance)5059 void RichEditorPattern::SetCustomKeyboardOption(bool supportAvoidance)
5060 {
5061 keyboardAvoidance_ = supportAvoidance;
5062 }
5063
RequestCustomKeyboard()5064 bool RichEditorPattern::RequestCustomKeyboard()
5065 {
5066 #if defined(ENABLE_STANDARD_INPUT)
5067 auto inputMethod = MiscServices::InputMethodController::GetInstance();
5068 if (inputMethod) {
5069 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "RequestCKeyboard,close softkeyboard.");
5070 inputMethod->RequestHideInput();
5071 inputMethod->Close();
5072 }
5073 #else
5074 if (HasConnection()) {
5075 connection_->Close(GetInstanceId());
5076 connection_ = nullptr;
5077 }
5078 #endif
5079
5080 if (isCustomKeyboardAttached_) {
5081 return true;
5082 }
5083 CHECK_NULL_RETURN(customKeyboardBuilder_, false);
5084 auto frameNode = GetHost();
5085 CHECK_NULL_RETURN(frameNode, false);
5086 auto pipeline = frameNode->GetContext();
5087 CHECK_NULL_RETURN(pipeline, false);
5088 auto overlayManager = pipeline->GetOverlayManager();
5089 CHECK_NULL_RETURN(overlayManager, false);
5090 overlayManager->SetCustomKeyboardOption(keyboardAvoidance_);
5091 overlayManager->BindKeyboard(customKeyboardBuilder_, frameNode->GetId());
5092 isCustomKeyboardAttached_ = true;
5093 contentChange_ = false;
5094 keyboardOverlay_ = overlayManager;
5095 auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
5096 keyboardOverlay_->AvoidCustomKeyboard(frameNode->GetId(), caretHeight);
5097 return true;
5098 }
5099
CloseCustomKeyboard()5100 bool RichEditorPattern::CloseCustomKeyboard()
5101 {
5102 auto frameNode = GetHost();
5103 CHECK_NULL_RETURN(frameNode, false);
5104 CHECK_NULL_RETURN(keyboardOverlay_, false);
5105 keyboardOverlay_->CloseKeyboard(frameNode->GetId());
5106 isCustomKeyboardAttached_ = false;
5107 contentChange_ = false;
5108 return true;
5109 }
5110
CheckPreviewTextValidate(const std::u16string & previewTextValue,const PreviewRange range)5111 int32_t RichEditorPattern::CheckPreviewTextValidate(const std::u16string& previewTextValue, const PreviewRange range)
5112 {
5113 if (IsDragging()) {
5114 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "SetPreviewText is not allowed while dragging");
5115 return ERROR_BAD_PARAMETERS;
5116 }
5117 return NO_ERRORS;
5118 }
5119
CheckPreviewTextValidate(const std::string & previewTextValue,const PreviewRange range)5120 int32_t RichEditorPattern::CheckPreviewTextValidate(const std::string& previewTextValue, const PreviewRange range)
5121 {
5122 return CheckPreviewTextValidate(UtfUtils::Str8DebugToStr16(previewTextValue), range);
5123 }
5124
SetPreviewText(const std::u16string & previewTextValue,const PreviewRange range)5125 int32_t RichEditorPattern::SetPreviewText(const std::u16string& previewTextValue, const PreviewRange range)
5126 {
5127 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "SetPreviewText, range=[%{public}d,%{public}d], isSSMode=%{public}d",
5128 range.start, range.end, isSpanStringMode_);
5129 CHECK_NULL_RETURN(!isSpanStringMode_ || isAPI18Plus, ERROR_BAD_PARAMETERS);
5130 SEC_TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "previewText=%{public}s", UtfUtils::Str16ToStr8(previewTextValue).c_str());
5131 auto host = GetHost();
5132 CHECK_NULL_RETURN(host, ERROR_BAD_PARAMETERS);
5133 CHECK_NULL_RETURN(!IsDragging(), ERROR_BAD_PARAMETERS);
5134
5135 if (!IsPreviewTextInputting()) {
5136 if (!InitPreviewText(previewTextValue, range)) {
5137 return ERROR_BAD_PARAMETERS;
5138 }
5139 } else {
5140 if (!UpdatePreviewText(previewTextValue, range)) {
5141 return ERROR_BAD_PARAMETERS;
5142 }
5143 }
5144 previewTextRecord_.replacedRange.Set(previewTextRecord_.startOffset, previewTextRecord_.endOffset);
5145 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
5146 return NO_ERRORS;
5147 }
5148
InitPreviewText(const std::u16string & previewTextValue,const PreviewRange & range)5149 bool RichEditorPattern::InitPreviewText(const std::u16string& previewTextValue, const PreviewRange& range)
5150 {
5151 if (range.start != -1 || range.end != -1) {
5152 return ReplaceText(previewTextValue, range);
5153 }
5154 // interrupt touch selecting when initialize preview text
5155 ResetTouchSelectState();
5156 IF_PRESENT(magnifierController_, RemoveMagnifierFrameNode());
5157 auto& record = previewTextRecord_;
5158 record.needReplacePreviewText = true;
5159 record.previewTextHasStarted = true;
5160 record.replacedRange = range;
5161 record.startOffset = textSelector_.SelectNothing() ? caretPosition_ : textSelector_.GetTextStart();
5162 record.newPreviewContent = previewTextValue;
5163 auto length = static_cast<int32_t>(previewTextValue.length());
5164 record.endOffset = record.startOffset + length;
5165 auto spanCountBefore = spans_.size();
5166 ProcessInsertValue(previewTextValue, OperationType::IME, false);
5167 record.isSpanSplit = spans_.size() - spanCountBefore > 1;
5168 record.previewContent = record.newPreviewContent;
5169 record.newPreviewContent.clear();
5170 record.needReplacePreviewText = false;
5171 return true;
5172 }
5173
ReplaceText(const std::u16string & previewTextValue,const PreviewRange & range)5174 bool RichEditorPattern::ReplaceText(const std::u16string& previewTextValue, const PreviewRange& range)
5175 {
5176 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "ReplaceText");
5177 if (range.start < 0 || range.end < range.start || range.end > GetTextContentLength()) {
5178 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "bad PreviewRange");
5179 return false;
5180 }
5181 previewTextRecord_.replacedRange = range;
5182 previewTextRecord_.needReplaceText = true;
5183 previewTextRecord_.startOffset = range.start;
5184 previewTextRecord_.endOffset = range.end;
5185 ProcessInsertValue(previewTextValue, OperationType::IME, false);
5186 previewTextRecord_.needReplaceText = false;
5187 return true;
5188 }
5189
5190 // Used for text replacement, without notifying developer caret change
DeleteByRange(OperationRecord * const record,int32_t start,int32_t end)5191 void RichEditorPattern::DeleteByRange(OperationRecord* const record, int32_t start, int32_t end)
5192 {
5193 auto length = end - start;
5194 CHECK_NULL_VOID(length > 0);
5195 lastCaretPosition_ = caretPosition_;
5196 caretPosition_ = std::clamp(start, 0, GetTextContentLength());
5197 if (isSpanStringMode_) {
5198 DeleteValueInStyledString(start, length, true, false);
5199 return;
5200 }
5201 std::u16string deleteText = DeleteForwardOperation(length);
5202 if (record && deleteText.length() != 0) {
5203 record->deleteText = deleteText;
5204 }
5205 }
5206
UpdatePreviewText(const std::u16string & previewTextValue,const PreviewRange & range)5207 bool RichEditorPattern::UpdatePreviewText(const std::u16string& previewTextValue, const PreviewRange& range)
5208 {
5209 auto& record = previewTextRecord_;
5210 if (range.start == -1 && range.end == -1 && !record.previewContent.empty()) {
5211 record.replacedRange.Set(record.startOffset, record.endOffset);
5212 record.newPreviewContent = previewTextValue;
5213 record.needReplacePreviewText = true;
5214 record.needUpdateCaret = caretPosition_ == record.endOffset;
5215 ProcessInsertValue(previewTextValue, OperationType::IME, false);
5216 record.previewContent = record.newPreviewContent;
5217 record.newPreviewContent.clear();
5218 record.endOffset = record.startOffset + static_cast<int32_t>(previewTextValue.length());
5219 record.needReplacePreviewText = false;
5220 record.needUpdateCaret = true;
5221 } else {
5222 if (range.start < record.startOffset || range.end > record.endOffset || range.end < range.start) {
5223 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "bad PreviewRange");
5224 return false;
5225 }
5226 if (previewTextValue.empty() && range.start == range.end) {
5227 SetCaretPosition(range.end);
5228 return false;
5229 }
5230 auto replaceIndex = range.start - record.startOffset;
5231 auto replaceLength = range.end - range.start;
5232 auto oldContent = record.previewContent;
5233 auto oldPreviewLength = static_cast<int32_t>(oldContent.length());
5234 if (replaceIndex < 0 || replaceIndex + replaceLength > oldPreviewLength) {
5235 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "bad replaced range ");
5236 return false;
5237 }
5238 auto newContent = oldContent.replace(replaceIndex, replaceLength, previewTextValue);
5239 record.replacedRange = range;
5240 record.newPreviewContent = newContent;
5241 record.needReplacePreviewText = true;
5242 ProcessInsertValue(previewTextValue, OperationType::IME, false);
5243 record.previewContent = record.newPreviewContent;
5244 record.newPreviewContent.clear();
5245 record.endOffset = record.startOffset + static_cast<int32_t>(newContent.length());
5246 record.needReplacePreviewText = false;
5247 }
5248 return true;
5249 }
5250
GetPreviewTextInfo() const5251 const PreviewTextInfo RichEditorPattern::GetPreviewTextInfo() const
5252 {
5253 PreviewTextInfo info;
5254 if (!previewTextRecord_.previewContent.empty()) {
5255 info.value = previewTextRecord_.previewContent;
5256 info.offset = previewTextRecord_.startOffset;
5257 }
5258 return info;
5259 }
5260
MergeAdjacentSpans(int32_t caretPosition)5261 void RichEditorPattern::MergeAdjacentSpans(int32_t caretPosition)
5262 {
5263 auto host = GetHost();
5264 CHECK_NULL_VOID(host);
5265 auto uiNodes = host->GetChildren();
5266 auto it = std::find_if(uiNodes.begin(), uiNodes.end(), [caretPosition](const RefPtr<UINode>& uiNode) {
5267 auto spanNode = DynamicCast<SpanNode>(uiNode);
5268 CHECK_NULL_RETURN(spanNode, false);
5269 return spanNode->GetSpanItem()->position == caretPosition;
5270 });
5271 CHECK_NULL_VOID(it != uiNodes.end());
5272 auto beforeSpanNode = DynamicCast<SpanNode>(*it);
5273 ++it;
5274 CHECK_NULL_VOID(it != uiNodes.end());
5275 auto afterSpanNode = DynamicCast<SpanNode>(*it);
5276 CHECK_NULL_VOID(beforeSpanNode && afterSpanNode);
5277 CHECK_NULL_VOID(beforeSpanNode->GetTag() == V2::SPAN_ETS_TAG && afterSpanNode->GetTag() == V2::SPAN_ETS_TAG);
5278 auto beforeSpanItem = beforeSpanNode->GetSpanItem();
5279 auto afterSpanItem = afterSpanNode->GetSpanItem();
5280 CHECK_NULL_VOID(beforeSpanItem && afterSpanItem);
5281 beforeSpanNode->UpdateContent(beforeSpanItem->content + afterSpanItem->content);
5282 afterSpanItem->content.clear();
5283 RemoveEmptySpans();
5284 }
5285
FinishTextPreview()5286 void RichEditorPattern::FinishTextPreview()
5287 {
5288 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "FinishTextPreview byImf");
5289 if (previewTextRecord_.previewContent.empty()) {
5290 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "previewContent is empty");
5291 RemoveEmptySpans();
5292 IF_TRUE(previewTextRecord_.isSpanSplit, MergeAdjacentSpans(caretPosition_));
5293 previewTextRecord_.Reset();
5294 return;
5295 }
5296 auto previewContent = previewTextRecord_.previewContent;
5297 FinishTextPreviewInner();
5298 ProcessInsertValue(previewContent, OperationType::IME, false);
5299 }
5300
FinishTextPreviewInner(bool deletePreviewText)5301 void RichEditorPattern::FinishTextPreviewInner(bool deletePreviewText)
5302 {
5303 CHECK_NULL_VOID(previewTextRecord_.previewTextHasStarted);
5304 TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
5305 "FinishTextPreviewInner, deleteText=%{public}d, previewContent is empty=%{public}d", deletePreviewText,
5306 previewTextRecord_.previewContent.empty());
5307 previewTextRecord_.previewTextExiting = true;
5308 IF_TRUE(deletePreviewText && !previewTextRecord_.previewContent.empty(),
5309 DeleteByRange(nullptr, previewTextRecord_.startOffset, previewTextRecord_.endOffset));
5310 previewTextRecord_.Reset();
5311 }
5312
NotifyExitTextPreview(bool deletePreviewText)5313 void RichEditorPattern::NotifyExitTextPreview(bool deletePreviewText)
5314 {
5315 CHECK_NULL_VOID(previewTextRecord_.previewTextHasStarted);
5316 CHECK_NULL_VOID(HasFocus());
5317 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "NotifyExitTextPreview");
5318 FinishTextPreviewInner(deletePreviewText);
5319 NotifyImfFinishTextPreview();
5320 }
5321
NotifyImfFinishTextPreview()5322 void RichEditorPattern::NotifyImfFinishTextPreview()
5323 {
5324 #if defined(ENABLE_STANDARD_INPUT)
5325 MiscServices::InputMethodController::GetInstance()->OnSelectionChange(u"", caretPosition_, caretPosition_);
5326 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "notify imf that richEditor exit textPreview");
5327 #endif
5328 }
5329
GetPreviewTextRects()5330 std::vector<RectF> RichEditorPattern::GetPreviewTextRects()
5331 {
5332 auto rects = paragraphs_.GetRects(previewTextRecord_.startOffset, previewTextRecord_.endOffset,
5333 RectHeightPolicy::COVER_TEXT);
5334 auto offset = GetTextRect().GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
5335 for (RectF& rect : rects) {
5336 rect += offset;
5337 }
5338 return rects;
5339 }
5340
GetPreviewTextStyle() const5341 PreviewTextStyle RichEditorPattern::GetPreviewTextStyle() const
5342 {
5343 auto previewTextStyle = PreviewTextStyle::NORMAL;
5344 auto property = GetLayoutProperty<RichEditorLayoutProperty>();
5345 if (property && property->HasPreviewTextStyle()) {
5346 auto style = property->GetPreviewTextStyle();
5347 CHECK_NULL_RETURN(style.has_value(), previewTextStyle);
5348 if (style.value() == PREVIEW_STYLE_NORMAL) {
5349 previewTextStyle = PreviewTextStyle::NORMAL;
5350 } else if (style.value() == PREVIEW_STYLE_UNDERLINE) {
5351 previewTextStyle = PreviewTextStyle::UNDERLINE;
5352 } else {
5353 TAG_LOGW(
5354 AceLogTag::ACE_RICH_TEXT, "invalid previewTextStyle of RichEditorLayoutProperty");
5355 }
5356 }
5357 return previewTextStyle;
5358 }
5359
GetPreviewTextDecorationColor() const5360 const Color& RichEditorPattern::GetPreviewTextDecorationColor() const
5361 {
5362 auto pipeline = PipelineBase::GetCurrentContextSafelyWithCheck();
5363 CHECK_NULL_RETURN(pipeline, Color::TRANSPARENT);
5364 auto theme = pipeline->GetTheme<RichEditorTheme>();
5365 CHECK_NULL_RETURN(theme, Color::TRANSPARENT);
5366 if (GetPreviewTextStyle() == PreviewTextStyle::UNDERLINE) {
5367 return theme->GetPreviewUnderLineColor();
5368 }
5369 return Color::TRANSPARENT;
5370 }
5371
GetPreviewTextUnderlineWidth() const5372 float RichEditorPattern::GetPreviewTextUnderlineWidth() const
5373 {
5374 auto pipeline = PipelineBase::GetCurrentContextSafelyWithCheck();
5375 CHECK_NULL_RETURN(pipeline, 0.0f);
5376 auto theme = pipeline->GetTheme<RichEditorTheme>();
5377 CHECK_NULL_RETURN(theme, 0.0f);
5378 return theme->GetPreviewUnderlineWidth().ConvertToPx();
5379 }
5380
IsIMEOperation(OperationType operationType)5381 bool RichEditorPattern::IsIMEOperation(OperationType operationType)
5382 {
5383 return operationType == OperationType::IME;
5384 }
5385
InsertValue(const std::string & insertValue,bool isIME)5386 void RichEditorPattern::InsertValue(const std::string& insertValue, bool isIME)
5387 {
5388 InsertValue(UtfUtils::Str8ToStr16(insertValue), isIME);
5389 }
5390
InsertValue(const std::u16string & insertValue,bool isIME)5391 void RichEditorPattern::InsertValue(const std::u16string& insertValue, bool isIME)
5392 {
5393 InsertValueByOperationType(insertValue, isIME ? OperationType::IME : OperationType::DEFAULT);
5394 }
5395
InsertValueByOperationType(const std::u16string & insertValue,OperationType operationType)5396 void RichEditorPattern::InsertValueByOperationType(const std::u16string& insertValue, OperationType operationType)
5397 {
5398 ProcessInsertValue(insertValue, operationType, true);
5399 }
5400
ProcessTextTruncationOperation(std::u16string & text,bool calledByImf)5401 bool RichEditorPattern::ProcessTextTruncationOperation(std::u16string& text, bool calledByImf)
5402 {
5403 bool needTruncationInsertValue = calledByImf || !previewTextRecord_.needReplacePreviewText;
5404 int32_t selectLength =
5405 textSelector_.SelectNothing() ? 0 : textSelector_.GetTextEnd() - textSelector_.GetTextStart();
5406 int32_t previewContentLength = previewTextRecord_.previewContent.empty()
5407 ? previewTextRecord_.replacedRange.end - previewTextRecord_.replacedRange.start
5408 : static_cast<int32_t>(previewTextRecord_.previewContent.length());
5409
5410 if (!needTruncationInsertValue || GetTextContentLength() - previewContentLength < maxLength_.value_or(INT_MAX)) {
5411 if (needTruncationInsertValue && text.length() != 1) {
5412 auto maxLength = maxLength_.value_or(INT_MAX) - GetTextContentLength() + previewContentLength;
5413 auto allowInsertLength = CalculateTruncationLength(text, maxLength);
5414 if (allowInsertLength == 0) {
5415 TAG_LOGD(AceLogTag::ACE_RICH_TEXT,
5416 "ProcessTextTruncation: No space to insert text. maxLength=%{public}d",
5417 maxLength_.value_or(INT_MAX));
5418 return false;
5419 }
5420 text = text.substr(0, allowInsertLength);
5421 return true;
5422 }
5423 return true;
5424 }
5425
5426 if (previewTextRecord_.needReplaceText) {
5427 text = text.substr(0, maxLength_.value_or(INT_MAX) + selectLength);
5428 return true;
5429 }
5430 if (!textSelector_.SelectNothing()) {
5431 auto allowInsertLength = CalculateTruncationLength(text, 0);
5432 if (allowInsertLength == 0) {
5433 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "ProcessTextTruncation: No space to insert text. maxLength=%{public}d",
5434 maxLength_.value_or(INT_MAX));
5435 return false;
5436 }
5437 text = text.substr(0, allowInsertLength);
5438 return true;
5439 }
5440 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "ProcessTextTruncation: maxLength=%{public}d", maxLength_.value_or(INT_MAX));
5441 IF_TRUE(IsPreviewTextInputting(), FinishTextPreviewInner());
5442 return false;
5443 }
5444
ProcessInsertValueMore(const std::u16string & text,OperationRecord record,OperationType operationType,RichEditorChangeValue changeValue,PreviewTextRecord preRecord)5445 void RichEditorPattern::ProcessInsertValueMore(const std::u16string& text, OperationRecord record,
5446 OperationType operationType, RichEditorChangeValue changeValue, PreviewTextRecord preRecord)
5447 {
5448 if (preRecord.needReplacePreviewText && !previewTextRecord_.needReplacePreviewText) {
5449 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "previewText finished when ProcessInsertValue");
5450 NotifyImfFinishTextPreview();
5451 return;
5452 }
5453 ClearRedoOperationRecords();
5454 InsertValueOperation(text, &record, operationType);
5455 record.afterCaretPosition = caretPosition_;
5456 if (isDragSponsor_) {
5457 record.deleteCaretPostion = dragRange_.first;
5458 }
5459 AddOperationRecord(record);
5460 AfterContentChange(changeValue);
5461 }
5462
5463 // operationType: when type is IME, it controls whether to perform ime callbacks
5464 // calledByImf: true means real input; false means preview input
ProcessInsertValue(const std::u16string & insertValue,OperationType operationType,bool calledByImf)5465 void RichEditorPattern::ProcessInsertValue(const std::u16string& insertValue, OperationType operationType,
5466 bool calledByImf)
5467 {
5468 CONTENT_MODIFY_LOCK(this);
5469 auto text = insertValue;
5470 if (!ProcessTextTruncationOperation(text, calledByImf)) {
5471 return;
5472 }
5473 bool isIME = IsIMEOperation(operationType);
5474 TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
5475 "insertLen=%{public}zu, isIME=%{public}d, calledByImf=%{public}d, isSpanString=%{public}d",
5476 insertValue.length(), isIME, calledByImf, isSpanStringMode_);
5477 SEC_TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "insertValue=%{public}s",
5478 StringUtils::RestoreEscape(UtfUtils::Str16ToStr8(insertValue)).c_str());
5479
5480 if (isIME && calledByImf && (!isEditing_ || IsDragging())) {
5481 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "NOT allow input, isEditing=%{public}d, isDragging=%{public}d",
5482 isEditing_, IsDragging());
5483 return;
5484 }
5485 if (isSpanStringMode_) {
5486 InsertValueInStyledString(text, calledByImf);
5487 return;
5488 }
5489 OperationRecord record;
5490 record.beforeCaretPosition = caretPosition_ + moveLength_;
5491 if (textSelector_.IsValid()) {
5492 record.beforeCaretPosition = textSelector_.GetTextStart();
5493 }
5494 record.addText = text;
5495
5496 RichEditorChangeValue changeValue;
5497 PreviewTextRecord preRecord = previewTextRecord_;
5498 bool allowContentChange = BeforeChangeText(changeValue, record, RecordType::INSERT);
5499 if (calledByImf && previewTextRecord_.IsValid()) {
5500 FinishTextPreviewInner();
5501 }
5502 bool allowImeInput = isIME ? BeforeIMEInsertValue(text) : true;
5503 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "allowContentChange=%{public}d, allowImeInput=%{public}d, needReplacePreviewText=%{public}d",
5504 allowContentChange, allowImeInput, previewTextRecord_.needReplacePreviewText);
5505 bool allowPreviewText = previewTextRecord_.needReplacePreviewText;
5506 CHECK_NULL_VOID((allowContentChange && allowImeInput) || allowPreviewText);
5507 ProcessInsertValueMore(text, record, operationType, changeValue, preRecord);
5508 }
5509
InsertValueOperation(const std::u16string & insertValue,OperationRecord * const record,OperationType operationType)5510 void RichEditorPattern::InsertValueOperation(const std::u16string& insertValue, OperationRecord* const record,
5511 OperationType operationType)
5512 {
5513 bool isSelector = textSelector_.IsValid();
5514 if (isSelector) {
5515 DeleteByRange(record, textSelector_.GetTextStart(), textSelector_.GetTextEnd());
5516 ResetSelection();
5517 } else if (previewTextRecord_.needReplacePreviewText || previewTextRecord_.needReplaceText) {
5518 DeleteByRange(record, previewTextRecord_.replacedRange.start, previewTextRecord_.replacedRange.end);
5519 }
5520 bool isSingleHandleMoving = selectOverlay_->IsSingleHandleMoving();
5521 CloseSelectOverlay();
5522 TextInsertValueInfo info;
5523 CalcInsertValueObj(info);
5524 IF_TRUE((!caretVisible_ || isSingleHandleMoving) && HasFocus(), StartTwinkling());
5525 auto host = GetHost();
5526 CHECK_NULL_VOID(host);
5527 bool isIME = IsIMEOperation(operationType);
5528 RefPtr<SpanNode> spanNode = DynamicCast<SpanNode>(host->GetChildAtIndex(info.GetSpanIndex()));
5529 RefPtr<SpanNode> spanNodeBefore = DynamicCast<SpanNode>(host->GetChildAtIndex(info.GetSpanIndex() - 1));
5530 RefPtr<SpanNode> targetSpanNode = spanNode;
5531 bool needCreateNewSpan = host->GetChildren().empty();
5532 if (info.GetOffsetInSpan() == 0) {
5533 bool spanNodeBeforeCanInsert = spanNodeBefore && spanNodeBefore->GetTag() == V2::SPAN_ETS_TAG;
5534 bool spanNodeCanInsert = spanNode && spanNode->GetTag() == V2::SPAN_ETS_TAG;
5535 bool insertToBeforeNode = spanNodeBeforeCanInsert && !spanNodeCanInsert;
5536 insertToBeforeNode |= spanNodeBeforeCanInsert && spanNodeCanInsert && !IsLineSeparatorInLast(spanNodeBefore);
5537 if (insertToBeforeNode) {
5538 auto spanItem = spanNodeBefore->GetSpanItem();
5539 info.SetSpanIndex(info.GetSpanIndex() - 1);
5540 info.SetOffsetInSpan(spanItem->position - spanItem->rangeStart);
5541 targetSpanNode = spanNodeBefore;
5542 }
5543 needCreateNewSpan |= !spanNodeBeforeCanInsert && !spanNodeCanInsert;
5544 }
5545 if (needCreateNewSpan) {
5546 CreateTextSpanNode(targetSpanNode, info, insertValue, isIME);
5547 return;
5548 }
5549 if (typingStyle_.has_value() && !HasSameTypingStyle(targetSpanNode)) {
5550 InsertDiffStyleValueInSpan(targetSpanNode, info, insertValue, isIME);
5551 return;
5552 }
5553 InsertValueToSpanNode(targetSpanNode, insertValue, info);
5554 AfterInsertValue(targetSpanNode, static_cast<int32_t>(insertValue.length()), false, isIME);
5555 }
5556
DeleteSelectOperation(OperationRecord * const record)5557 void RichEditorPattern::DeleteSelectOperation(OperationRecord* const record)
5558 {
5559 std::u16string deleteText = DeleteForwardOperation(textSelector_.GetTextEnd() - textSelector_.GetTextStart());
5560 if (record && deleteText.length() != 0) {
5561 record->deleteText = deleteText;
5562 }
5563 CloseSelectOverlay();
5564 ResetSelection();
5565 }
5566
CreateTextStyleByTypingStyle()5567 TextStyle RichEditorPattern::CreateTextStyleByTypingStyle()
5568 {
5569 auto theme = GetTheme<RichEditorTheme>();
5570 auto ret = theme ? theme->GetTextStyle() : TextStyle();
5571 CHECK_NULL_RETURN(typingStyle_.has_value() && typingTextStyle_.has_value(), ret);
5572 const auto& updateSpanStyle = typingStyle_.value();
5573 const auto& textStyle = typingTextStyle_.value();
5574 IF_TRUE(updateSpanStyle.updateFontFeature, ret.SetFontFeatures(textStyle.GetFontFeatures()));
5575 IF_TRUE(updateSpanStyle.updateTextColor, ret.SetTextColor(textStyle.GetTextColor()));
5576 IF_TRUE(updateSpanStyle.updateLineHeight, ret.SetLineHeight(textStyle.GetLineHeight()));
5577 IF_TRUE(updateSpanStyle.updateLetterSpacing, ret.SetLetterSpacing(textStyle.GetLetterSpacing()));
5578 IF_TRUE(updateSpanStyle.updateFontSize, ret.SetFontSize(textStyle.GetFontSize()));
5579 IF_TRUE(updateSpanStyle.updateItalicFontStyle, ret.SetFontStyle(textStyle.GetFontStyle()));
5580 IF_TRUE(updateSpanStyle.updateFontWeight, ret.SetFontWeight(textStyle.GetFontWeight()));
5581 IF_TRUE(updateSpanStyle.updateFontFamily, ret.SetFontFamilies(textStyle.GetFontFamilies()));
5582 IF_TRUE(updateSpanStyle.updateTextShadows, ret.SetTextShadows(textStyle.GetTextShadows()));
5583 IF_TRUE(updateSpanStyle.updateHalfLeading, ret.SetHalfLeading(textStyle.GetHalfLeading()));
5584 IF_TRUE(updateSpanStyle.updateTextDecoration, ret.SetTextDecoration(textStyle.GetTextDecoration()));
5585 IF_TRUE(updateSpanStyle.updateTextDecorationColor, ret.SetTextDecorationColor(textStyle.GetTextDecorationColor()));
5586 IF_TRUE(updateSpanStyle.updateTextDecorationStyle, ret.SetTextDecorationStyle(textStyle.GetTextDecorationStyle()));
5587 IF_TRUE(updateSpanStyle.updateTextBackgroundStyle, ret.SetTextBackgroundStyle(textStyle.GetTextBackgroundStyle()));
5588 return ret;
5589 }
5590
InsertDiffStyleValueInSpan(RefPtr<SpanNode> & spanNode,const TextInsertValueInfo & info,const std::u16string & insertValue,bool isIME)5591 void RichEditorPattern::InsertDiffStyleValueInSpan(
5592 RefPtr<SpanNode>& spanNode, const TextInsertValueInfo& info, const std::u16string& insertValue, bool isIME)
5593 {
5594 auto host = GetHost();
5595 CHECK_NULL_VOID(host);
5596 TextSpanOptions options;
5597 options.value = insertValue;
5598 options.offset = caretPosition_;
5599 options.style = CreateTextStyleByTypingStyle();
5600 options.useThemeFontColor = typingStyle_->useThemeFontColor;
5601 options.useThemeDecorationColor = typingStyle_->useThemeDecorationColor;
5602 auto newSpanIndex = AddTextSpanOperation(options, false, -1, true, false);
5603 auto newSpanNode = DynamicCast<SpanNode>(host->GetChildAtIndex(newSpanIndex));
5604 CopyTextSpanLineStyle(spanNode, newSpanNode, true);
5605 AfterInsertValue(newSpanNode, static_cast<int32_t>(insertValue.length()), true, isIME);
5606 }
5607
IsLineSeparatorInLast(RefPtr<SpanNode> & spanNode)5608 bool RichEditorPattern::IsLineSeparatorInLast(RefPtr<SpanNode>& spanNode)
5609 {
5610 std::u16string content = spanNode->GetSpanItem()->content;
5611 return !content.empty() && content.back() == u'\n';
5612 }
5613
InsertValueToSpanNode(RefPtr<SpanNode> & spanNode,const std::u16string & insertValue,const TextInsertValueInfo & info)5614 void RichEditorPattern::InsertValueToSpanNode(
5615 RefPtr<SpanNode>& spanNode, const std::u16string& insertValue, const TextInsertValueInfo& info)
5616 {
5617 auto spanItem = spanNode->GetSpanItem();
5618 CHECK_NULL_VOID(spanItem);
5619 auto textTemp = spanItem->content;
5620 auto textTempSize = static_cast<int32_t>(textTemp.size());
5621 if (textTempSize < info.GetOffsetInSpan() || info.GetOffsetInSpan() < 0) {
5622 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "InsertValueToSpanNode error, spanSize=%{public}d, offsetInSpan=%{public}d",
5623 textTempSize, info.GetOffsetInSpan());
5624 RichEditorInfo errorInfo { RichEditorErrorType::INSERT_VALUE, textTempSize,
5625 static_cast<int32_t>(insertValue.length()), info.GetOffsetInSpan() };
5626 RichEditorErrorReport(errorInfo);
5627 return;
5628 }
5629 textTemp.insert(info.GetOffsetInSpan(), insertValue);
5630 spanNode->UpdateContent(textTemp);
5631 UpdateSpanPosition();
5632 SpanNodeFission(spanNode);
5633 }
5634
InsertValueToBeforeSpan(RefPtr<SpanNode> & spanNodeBefore,const std::u16string & insertValue)5635 RefPtr<SpanNode> RichEditorPattern::InsertValueToBeforeSpan(
5636 RefPtr<SpanNode>& spanNodeBefore, const std::u16string& insertValue)
5637 {
5638 auto spanItem = spanNodeBefore->GetSpanItem();
5639 CHECK_NULL_RETURN(spanItem, spanNodeBefore);
5640 auto textTemp = spanItem->content;
5641 textTemp.append(insertValue);
5642
5643 auto index = textTemp.find(LINE_SEPARATOR);
5644 if (index != std::u16string::npos) {
5645 spanNodeBefore->UpdateContent(textTemp.substr(0, index + 1));
5646 auto textAfter = textTemp.substr(index + 1);
5647 spanItem->position += static_cast<int32_t>(insertValue.length()) - static_cast<int32_t>(textAfter.length());
5648 if (!textAfter.empty()) {
5649 auto host = GetHost();
5650 CHECK_NULL_RETURN(spanItem, spanNodeBefore);
5651 TextInsertValueInfo infoAfter;
5652 infoAfter.SetSpanIndex(host->GetChildIndex(spanNodeBefore) + 1);
5653 infoAfter.SetOffsetInSpan(0);
5654 auto nodeId = ViewStackProcessor::GetInstance()->ClaimNodeId();
5655 RefPtr<SpanNode> spanNodeAfter = SpanNode::GetOrCreateSpanNode(nodeId);
5656 spanNodeAfter->MountToParent(host, infoAfter.GetSpanIndex());
5657 spanNodeAfter->UpdateContent(textAfter);
5658 CopyTextSpanStyle(spanNodeBefore, spanNodeAfter);
5659 auto spanItemAfter = spanNodeAfter->GetSpanItem();
5660 spanItemAfter->position = static_cast<int32_t>(textTemp.length());
5661 spanItemAfter->useThemeFontColor = spanItem->useThemeFontColor;
5662 spanItemAfter->useThemeDecorationColor = spanItem->useThemeDecorationColor;
5663 AddSpanItem(spanItemAfter, host->GetChildIndex(spanNodeBefore) + 1);
5664 SpanNodeFission(spanNodeAfter);
5665 return spanNodeAfter;
5666 }
5667 } else {
5668 spanNodeBefore->UpdateContent(textTemp);
5669 spanItem->position += static_cast<int32_t>(insertValue.length());
5670 }
5671 return spanNodeBefore;
5672 }
5673
CreateTextSpanNode(RefPtr<SpanNode> & spanNode,const TextInsertValueInfo & info,const std::u16string & insertValue,bool isIME)5674 void RichEditorPattern::CreateTextSpanNode(
5675 RefPtr<SpanNode>& spanNode, const TextInsertValueInfo& info, const std::u16string& insertValue, bool isIME)
5676 {
5677 auto host = GetHost();
5678 CHECK_NULL_VOID(host);
5679 auto nodeId = ViewStackProcessor::GetInstance()->ClaimNodeId();
5680 spanNode = SpanNode::GetOrCreateSpanNode(nodeId);
5681 spanNode->MountToParent(host, info.GetSpanIndex());
5682 auto spanItem = spanNode->GetSpanItem();
5683 if (typingStyle_.has_value() && typingTextStyle_.has_value()) {
5684 spanItem->useThemeFontColor = typingStyle_->useThemeFontColor;
5685 spanItem->useThemeDecorationColor = typingStyle_->useThemeDecorationColor;
5686 UpdateTextStyle(spanNode, typingStyle_.value(), typingTextStyle_.value());
5687 auto spanItem = spanNode->GetSpanItem();
5688 spanItem->SetTextStyle(typingTextStyle_);
5689 } else {
5690 spanNode->UpdateFontSize(Dimension(DEFAULT_TEXT_SIZE, DimensionUnit::FP));
5691 SetDefaultColor(spanNode);
5692 }
5693 AddSpanItem(spanItem, info.GetSpanIndex());
5694 spanNode->UpdateContent(insertValue);
5695 UpdateSpanPosition();
5696 SpanNodeFission(spanNode);
5697 AfterInsertValue(spanNode, static_cast<int32_t>(insertValue.length()), true, isIME);
5698 }
5699
SetDefaultColor(RefPtr<SpanNode> & spanNode)5700 void RichEditorPattern::SetDefaultColor(RefPtr<SpanNode>& spanNode)
5701 {
5702 auto richEditorTheme = GetTheme<RichEditorTheme>();
5703 CHECK_NULL_VOID(richEditorTheme);
5704 Color textColor = richEditorTheme->GetTextStyle().GetTextColor();
5705 spanNode->UpdateTextColorWithoutCheck(textColor);
5706 spanNode->UpdateTextDecorationColorWithoutCheck(textColor);
5707 if (auto& spanItem = spanNode->GetSpanItem(); spanItem && spanItem->urlOnRelease) {
5708 spanNode->UpdateTextColor(GetUrlSpanColor());
5709 }
5710 }
5711
BeforeIMEInsertValue(const std::u16string & insertValue)5712 bool RichEditorPattern::BeforeIMEInsertValue(const std::u16string& insertValue)
5713 {
5714 auto eventHub = GetEventHub<RichEditorEventHub>();
5715 CHECK_NULL_RETURN(eventHub, true);
5716 RichEditorInsertValue insertValueInfo;
5717 insertValueInfo.SetInsertOffset(caretPosition_);
5718 if (!previewTextRecord_.newPreviewContent.empty()) {
5719 insertValueInfo.SetPreviewText(previewTextRecord_.newPreviewContent);
5720 } else {
5721 insertValueInfo.SetInsertValue(insertValue);
5722 }
5723 return eventHub->FireAboutToIMEInput(insertValueInfo);
5724 }
5725
AfterInsertValue(const RefPtr<SpanNode> & spanNode,int32_t insertValueLength,bool isCreate,bool isIME)5726 void RichEditorPattern::AfterInsertValue(
5727 const RefPtr<SpanNode>& spanNode, int32_t insertValueLength, bool isCreate, bool isIME)
5728 {
5729 isTextChange_ = true;
5730 moveDirection_ = MoveDirection::FORWARD;
5731 moveLength_ += insertValueLength;
5732 IF_TRUE(!previewTextRecord_.needUpdateCaret, moveLength_ = 0);
5733 UpdateSpanPosition();
5734 if (isIME || aiWriteAdapter_->GetAIWrite()) {
5735 AfterIMEInsertValue(spanNode, insertValueLength, isCreate);
5736 return;
5737 }
5738 MoveCaretAfterTextChange();
5739 }
5740
AfterIMEInsertValue(const RefPtr<SpanNode> & spanNode,int32_t insertValueLength,bool isCreate)5741 bool RichEditorPattern::AfterIMEInsertValue(const RefPtr<SpanNode>& spanNode, int32_t insertValueLength, bool isCreate)
5742 {
5743 ACE_SCOPED_TRACE("RichEditorAfterIMEInsertValue");
5744 auto host = GetHost();
5745 CHECK_NULL_RETURN(host, false);
5746 auto eventHub = GetEventHub<RichEditorEventHub>();
5747 CHECK_NULL_RETURN(eventHub, false);
5748
5749 RichEditorAbstractSpanResult retInfo;
5750 retInfo.SetSpanIndex(host->GetChildIndex(spanNode));
5751 retInfo.SetEraseLength(insertValueLength);
5752 auto spanItem = spanNode->GetSpanItem();
5753 if (!previewTextRecord_.newPreviewContent.empty()) {
5754 retInfo.SetPreviewText(previewTextRecord_.newPreviewContent);
5755 } else {
5756 retInfo.SetValue(spanItem->content);
5757 }
5758 auto contentLength = static_cast<int32_t>(spanItem->content.length());
5759 retInfo.SetSpanRangeStart(spanItem->position - contentLength);
5760 retInfo.SetSpanRangeEnd(spanItem->position);
5761 retInfo.SetOffsetInSpan(caretPosition_ - retInfo.GetSpanRangeStart());
5762 retInfo.SetFontColor(spanNode->GetTextColorValue(Color::BLACK).ColorToString());
5763 retInfo.SetFontSize(spanNode->GetFontSizeValue(Dimension(DEFAULT_TEXT_SIZE, DimensionUnit::FP)).ConvertToVp());
5764 retInfo.SetFontStyle(spanNode->GetItalicFontStyleValue(OHOS::Ace::FontStyle::NORMAL));
5765 retInfo.SetFontWeight(static_cast<int32_t>(spanNode->GetFontWeightValue(FontWeight::NORMAL)));
5766 retInfo.SetTextStyle(GetTextStyleObject(spanNode));
5767 retInfo.SetUrlAddress(spanItem->urlAddress);
5768 std::string fontFamilyValue;
5769 auto fontFamily = spanNode->GetFontFamilyValue({ "HarmonyOS Sans" });
5770 for (const auto& str : fontFamily) {
5771 fontFamilyValue += str;
5772 }
5773 retInfo.SetFontFamily(fontFamilyValue);
5774 retInfo.SetTextDecoration(spanNode->GetTextDecorationValue(TextDecoration::NONE));
5775 retInfo.SetTextDecorationStyle(spanNode->GetTextDecorationStyleValue(TextDecorationStyle::SOLID));
5776 retInfo.SetFontFeature(spanNode->GetFontFeatureValue(ParseFontFeatureSettings("\"pnum\" 1")));
5777 retInfo.SetColor(spanNode->GetTextDecorationColorValue(Color::BLACK).ColorToString());
5778 TextRange onDidIMEInputRange{ caretPosition_, caretPosition_ + insertValueLength };
5779 MoveCaretAfterTextChange();
5780 eventHub->FireOnIMEInputComplete(retInfo);
5781 eventHub->FireOnDidIMEInput(onDidIMEInputRange);
5782 return true;
5783 }
5784
DoDeleteActions(int32_t currentPosition,int32_t length,RichEditorDeleteValue & info)5785 bool RichEditorPattern::DoDeleteActions(int32_t currentPosition, int32_t length, RichEditorDeleteValue& info)
5786 {
5787 auto eventHub = GetEventHub<RichEditorEventHub>();
5788 CHECK_NULL_RETURN(eventHub, false);
5789 auto allowDelete = eventHub->FireAboutToDelete(info);
5790 info.ResetRichEditorDeleteSpans();
5791 CalcDeleteValueObj(currentPosition, length, info);
5792 bool doDelete = allowDelete || IsPreviewTextInputting();
5793 if (doDelete) {
5794 bool isSingleHandleMoving = selectOverlay_->IsSingleHandleMoving();
5795 CloseSelectOverlay();
5796 ResetSelection();
5797 DeleteByDeleteValueInfo(info);
5798 IF_TRUE((!caretVisible_ || isSingleHandleMoving) && HasFocus(), StartTwinkling());
5799 eventHub->FireOnDeleteComplete();
5800 UiSessionManager::GetInstance()->ReportComponentChangeEvent("event", "RichEditor.OnDeleteComplete");
5801 }
5802 return doDelete;
5803 }
5804
IsEmojiOnCaretPosition(int32_t & emojiLength,bool isBackward,int32_t length)5805 std::pair<bool, bool> RichEditorPattern::IsEmojiOnCaretPosition(int32_t& emojiLength, bool isBackward, int32_t length)
5806 {
5807 bool isEmojiOnCaretBackward = false;
5808 bool isEmojiOnCaretForward = false;
5809 std::u16string u16;
5810 GetContentBySpans(u16);
5811 auto caretPos = std::clamp(caretPosition_, 0, static_cast<int32_t>(u16.length()));
5812 emojiLength = TextEmojiProcessor::Delete(caretPos, length, u16, isBackward);
5813 if (emojiLength > 0) {
5814 if (isBackward) {
5815 isEmojiOnCaretBackward = true;
5816 } else {
5817 isEmojiOnCaretForward = true;
5818 }
5819 }
5820 return std::make_pair(isEmojiOnCaretBackward, isEmojiOnCaretForward);
5821 }
5822
HandleOnDelete(bool backward)5823 void RichEditorPattern::HandleOnDelete(bool backward)
5824 {
5825 if (backward) {
5826 #if defined(PREVIEW)
5827 DeleteForward(1);
5828 #else
5829 DeleteBackward(1);
5830 #endif
5831 } else {
5832 #if defined(PREVIEW)
5833 DeleteBackward(1);
5834 #else
5835 DeleteForward(1);
5836 #endif
5837 }
5838 }
5839
CalculateDeleteLength(int32_t length,bool isBackward)5840 int32_t RichEditorPattern::CalculateDeleteLength(int32_t length, bool isBackward)
5841 {
5842 // handle selector
5843 if (!textSelector_.SelectNothing()) {
5844 lastCaretPosition_ = caretPosition_;
5845 caretPosition_ = isBackward ? textSelector_.GetTextEnd() : textSelector_.GetTextStart();
5846 return textSelector_.GetTextEnd() - textSelector_.GetTextStart();
5847 }
5848
5849 // handle symbol, assume caret is not within symbol
5850 auto iter = std::find_if(spans_.begin(), spans_.end(), [index = caretPosition_, isBackward]
5851 (const RefPtr<SpanItem>& spanItem) {
5852 return isBackward
5853 ? (spanItem->rangeStart < index && index <= spanItem->position)
5854 : (spanItem->rangeStart <= index && index < spanItem->position);
5855 });
5856 CHECK_NULL_RETURN(iter == spans_.end() || !(*iter) || (*iter)->unicode == 0, SYMBOL_SPAN_LENGTH);
5857
5858 // handle emoji
5859 int32_t emojiLength = 0;
5860 auto [isEmojiOnCaretBackward, isEmojiOnCaretForward] = IsEmojiOnCaretPosition(emojiLength, isBackward, length);
5861 if ((isBackward && isEmojiOnCaretBackward) || (!isBackward && isEmojiOnCaretForward)) {
5862 return emojiLength;
5863 }
5864
5865 return length;
5866 }
5867
DeleteBackward(int32_t oriLength)5868 void RichEditorPattern::DeleteBackward(int32_t oriLength)
5869 {
5870 int32_t length = isAPI14Plus ? std::clamp(oriLength, 0, caretPosition_) : oriLength;
5871 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "oriLength=%{public}d, length=%{public}d, isDragging=%{public}d",
5872 oriLength, length, IsDragging());
5873 CHECK_NULL_VOID(!IsDragging());
5874 if (isSpanStringMode_) {
5875 DeleteBackwardInStyledString(length);
5876 return;
5877 }
5878 if (IsPreviewTextInputting()) {
5879 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "do not handle DeleteBackward on previewTextInputting");
5880 return;
5881 }
5882 OperationRecord record;
5883 record.beforeCaretPosition = caretPosition_;
5884 RichEditorChangeValue changeValue;
5885 CHECK_NULL_VOID(BeforeChangeText(changeValue, record, RecordType::DEL_BACKWARD, length));
5886 std::u16string deleteText = DeleteBackwardOperation(length);
5887 if (deleteText.length() != 0) {
5888 ClearRedoOperationRecords();
5889 record.deleteText = deleteText;
5890 record.afterCaretPosition = caretPosition_;
5891 AddOperationRecord(record);
5892 AfterContentChange(changeValue);
5893 }
5894 }
5895
DeleteBackwardOperation(int32_t length)5896 std::u16string RichEditorPattern::DeleteBackwardOperation(int32_t length)
5897 {
5898 length = CalculateDeleteLength(length, true);
5899 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "delete length=%{public}d", length);
5900 std::u16string textContent;
5901 GetContentBySpans(textContent);
5902
5903 if (static_cast<int32_t>(textContent.length()) != GetTextContentLength()) {
5904 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "textContent length mismatch, %{public}d vs. %{public}d",
5905 static_cast<int32_t>(textContent.length()), GetTextContentLength());
5906 }
5907 auto start = std::clamp(caretPosition_ - length, 0, static_cast<int32_t>(textContent.length()));
5908 std::u16string deleteText =
5909 textContent.substr(static_cast<uint32_t>(start), static_cast<uint32_t>(caretPosition_ - start));
5910 RichEditorDeleteValue info;
5911 info.SetRichEditorDeleteDirection(RichEditorDeleteDirection::BACKWARD);
5912 if (caretPosition_ == 0) {
5913 info.SetLength(0);
5914 DoDeleteActions(0, 0, info);
5915 return deleteText;
5916 }
5917 info.SetOffset(caretPosition_ - length);
5918 info.SetLength(length);
5919 int32_t currentPosition = std::clamp((caretPosition_ - length), 0, static_cast<int32_t>(GetTextContentLength()));
5920 if (!spans_.empty()) {
5921 CalcDeleteValueObj(currentPosition, length, info);
5922 bool doDelete = DoDeleteActions(currentPosition, length, info);
5923 if (!doDelete) {
5924 return u"";
5925 }
5926 }
5927 auto host = GetHost();
5928 if (host && host->GetChildren().empty()) {
5929 textForDisplay_.clear();
5930 }
5931 RequestKeyboardToEdit();
5932 return deleteText;
5933 }
5934
DeleteForward(int32_t oriLength)5935 void RichEditorPattern::DeleteForward(int32_t oriLength)
5936 {
5937 int32_t length = isAPI14Plus ? std::clamp(oriLength, 0, GetTextContentLength() - caretPosition_) : oriLength;
5938 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "oriLength=%{public}d, length=%{public}d, isDragging=%{public}d",
5939 oriLength, length, IsDragging());
5940 CHECK_NULL_VOID(!IsDragging());
5941 if (isSpanStringMode_) {
5942 DeleteForwardInStyledString(length);
5943 return;
5944 }
5945 if (IsPreviewTextInputting()) {
5946 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "do not handle DeleteForward in previewTextInputting");
5947 return;
5948 }
5949 OperationRecord record;
5950 record.beforeCaretPosition = caretPosition_;
5951 RichEditorChangeValue changeValue;
5952 CHECK_NULL_VOID(BeforeChangeText(changeValue, record, RecordType::DEL_FORWARD, length));
5953 std::u16string deleteText = DeleteForwardOperation(length);
5954 if (deleteText.length() != 0) {
5955 ClearRedoOperationRecords();
5956 record.deleteText = deleteText;
5957 record.afterCaretPosition = caretPosition_;
5958 AddOperationRecord(record);
5959 AfterContentChange(changeValue);
5960 }
5961 }
5962
DeleteForwardOperation(int32_t length)5963 std::u16string RichEditorPattern::DeleteForwardOperation(int32_t length)
5964 {
5965 length = CalculateDeleteLength(length, false);
5966 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "delete length=%{public}d", length);
5967 std::u16string textContent;
5968 GetContentBySpans(textContent);
5969 if (static_cast<int32_t>(textContent.length()) != GetTextContentLength()) {
5970 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "textContent length mismatch, %{public}d vs. %{public}d",
5971 static_cast<int32_t>(textContent.length()), GetTextContentLength());
5972 }
5973 auto end = std::clamp(caretPosition_ + length, 0, static_cast<int32_t>(textContent.length()));
5974 std::u16string deleteText = textContent.substr(
5975 static_cast<uint32_t>(std::clamp(caretPosition_, 0, static_cast<int32_t>(textContent.length()))),
5976 static_cast<uint32_t>(end - caretPosition_));
5977 RichEditorDeleteValue info;
5978 info.SetOffset(caretPosition_);
5979 info.SetRichEditorDeleteDirection(RichEditorDeleteDirection::FORWARD);
5980 int32_t currentPosition = caretPosition_;
5981 if (currentPosition == GetTextContentLength()) {
5982 info.SetLength(0);
5983 DoDeleteActions(currentPosition, 0, info);
5984 return deleteText;
5985 }
5986 info.SetLength(length);
5987 if (!spans_.empty()) {
5988 CalcDeleteValueObj(currentPosition, length, info);
5989 bool doDelete = DoDeleteActions(currentPosition, length, info);
5990 if (!doDelete) {
5991 return u"";
5992 }
5993 }
5994 return deleteText;
5995 }
5996
DeleteContent(int32_t length)5997 void RichEditorPattern::DeleteContent(int32_t length)
5998 {
5999 length = CalculateDeleteLength(length, true);
6000 std::u16string textContent;
6001 GetContentBySpans(textContent);
6002
6003 auto start = std::clamp(GetTextContentLength() - length, 0, static_cast<int32_t>(textContent.length()));
6004 std::u16string deleteText =
6005 textContent.substr(static_cast<uint32_t>(start), static_cast<uint32_t>(GetTextContentLength() - start));
6006 RichEditorDeleteValue info;
6007 info.SetRichEditorDeleteDirection(RichEditorDeleteDirection::BACKWARD);
6008 if (GetTextContentLength() == 0) {
6009 info.SetLength(0);
6010 DoDeleteActions(0, 0, info);
6011 return;
6012 }
6013 info.SetOffset(GetTextContentLength() - length);
6014 info.SetLength(length);
6015 int32_t currentPosition = std::clamp((GetTextContentLength() - length), 0, static_cast<int32_t>(GetTextContentLength()));
6016 if (!spans_.empty()) {
6017 CalcDeleteValueObj(currentPosition, length, info);
6018 bool doDelete = DoDeleteActions(currentPosition, length, info);
6019 if (!doDelete) {
6020 return;
6021 }
6022 }
6023 auto host = GetHost();
6024 if (host && host->GetChildren().empty()) {
6025 textForDisplay_.clear();
6026 }
6027 RequestKeyboardToEdit();
6028 }
6029
DeleteToMaxLength(std::optional<int32_t> length)6030 void RichEditorPattern::DeleteToMaxLength(std::optional<int32_t> length)
6031 {
6032 if (length.value_or(INT_MAX) >= GetTextContentLength()) {
6033 return;
6034 }
6035 int32_t textContentLength = GetTextContentLength();
6036 if (isSpanStringMode_) {
6037 DeleteValueInStyledString(length.value_or(INT_MAX), GetTextContentLength() - length.value_or(INT_MAX));
6038 } else {
6039 while (textContentLength > length.value_or(INT_MAX)) {
6040 textContentLength -= CalculateDeleteLength(CUSTOM_CONTENT_LENGTH, true);
6041 DeleteContent(CUSTOM_CONTENT_LENGTH);
6042 }
6043 }
6044 }
6045
OnBackPressed()6046 bool RichEditorPattern::OnBackPressed()
6047 {
6048 auto tmpHost = GetHost();
6049 CHECK_NULL_RETURN(tmpHost, false);
6050 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "RichEditor %{public}d receives back press event, isStopBackPress=%{public}d",
6051 tmpHost->GetId(), isStopBackPress_);
6052 if (SelectOverlayIsOn()) {
6053 CloseSelectOverlay();
6054 textSelector_.Update(textSelector_.destinationOffset);
6055 StartTwinkling();
6056 return isStopBackPress_;
6057 }
6058 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
6059 if (!imeShown_ && !isCustomKeyboardAttached_) {
6060 #else
6061 if (!isCustomKeyboardAttached_) {
6062 #endif
6063 return false;
6064 }
6065 tmpHost->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6066 ResetSelection();
6067 CloseKeyboard(false);
6068 FocusHub::LostFocusToViewRoot();
6069 #if defined(ANDROID_PLATFORM)
6070 return false;
6071 #else
6072 return isStopBackPress_;
6073 #endif
6074 }
6075
6076 void RichEditorPattern::SetInputMethodStatus(bool keyboardShown)
6077 {
6078 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
6079 imeShown_ = keyboardShown;
6080 #endif
6081 }
6082
6083 bool RichEditorPattern::BeforeStatusCursorMove(bool isLeft)
6084 {
6085 CHECK_NULL_RETURN(textSelector_.IsValid(), true);
6086 CHECK_NULL_RETURN(!selectOverlay_->IsSingleHandleShow(), true);
6087 SetCaretPosition(isLeft ? textSelector_.GetTextStart() : textSelector_.GetTextEnd());
6088 MoveCaretToContentRect();
6089 StartTwinkling();
6090 CloseSelectOverlay();
6091 ResetSelection();
6092 return false;
6093 }
6094
6095 bool RichEditorPattern::CursorMoveLeft()
6096 {
6097 CHECK_NULL_RETURN(BeforeStatusCursorMove(true), false);
6098 CloseSelectOverlay();
6099 ResetSelection();
6100 int32_t emojiLength = 0;
6101 int32_t caretPosition = caretPosition_;
6102 constexpr int32_t DELETE_COUNT = 1;
6103 auto [isEmojiOnCaretBackward, isEmojiOnCaretForward] = IsEmojiOnCaretPosition(emojiLength, true, DELETE_COUNT);
6104 if (isEmojiOnCaretBackward) {
6105 caretPosition = std::clamp((caretPosition_ - emojiLength), 0, static_cast<int32_t>(GetTextContentLength()));
6106 } else {
6107 caretPosition = std::clamp((caretPosition_ - 1), 0, static_cast<int32_t>(GetTextContentLength()));
6108 }
6109 AdjustSelectorForSymbol(caretPosition, HandleType::SECOND, SelectorAdjustPolicy::EXCLUDE);
6110 if (caretPosition_ == caretPosition) {
6111 return false;
6112 }
6113 SetCaretPosition(caretPosition);
6114 MoveCaretToContentRect();
6115 StartTwinkling();
6116 auto host = GetHost();
6117 CHECK_NULL_RETURN(host, false);
6118 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6119 return true;
6120 }
6121
6122 bool RichEditorPattern::CursorMoveRight()
6123 {
6124 CHECK_NULL_RETURN(BeforeStatusCursorMove(false), false);
6125 CloseSelectOverlay();
6126 ResetSelection();
6127 int32_t emojiLength = 0;
6128 int32_t caretPosition = caretPosition_;
6129 constexpr int32_t DELETE_COUNT = 1;
6130 auto [isEmojiOnCaretBackward, isEmojiOnCaretForward] = IsEmojiOnCaretPosition(emojiLength, false, DELETE_COUNT);
6131 if (isEmojiOnCaretForward) {
6132 caretPosition = std::clamp((caretPosition_ + emojiLength), 0, static_cast<int32_t>(GetTextContentLength()));
6133 } else {
6134 caretPosition = std::clamp((caretPosition_ + 1), 0, static_cast<int32_t>(GetTextContentLength()));
6135 }
6136 AdjustSelectorForSymbol(caretPosition, HandleType::SECOND, SelectorAdjustPolicy::INCLUDE);
6137 if (caretPosition_ == caretPosition) {
6138 return false;
6139 }
6140 SetCaretPosition(caretPosition);
6141 MoveCaretToContentRect();
6142 StartTwinkling();
6143 auto host = GetHost();
6144 CHECK_NULL_RETURN(host, false);
6145 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6146 return true;
6147 }
6148
6149 bool RichEditorPattern::CursorMoveUp()
6150 {
6151 CloseSelectOverlay();
6152 ResetSelection();
6153 float caretHeight = 0.0f;
6154 float leadingMarginOffset = 0.0f;
6155 CaretOffsetInfo caretInfo;
6156 if (static_cast<int32_t>(GetTextContentLength()) > 1) {
6157 caretInfo = GetCaretOffsetInfoByPosition();
6158 int32_t caretPosition = CalcMoveUpPos(leadingMarginOffset);
6159 CHECK_NULL_RETURN(overlayMod_, false);
6160 auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
6161 auto currentCaretOffsetOverlay = overlayMod->GetCaretOffset();
6162 auto caretOffsetWidth = overlayMod->GetCaretWidth();
6163 auto rectLineInfo = CalcLineInfoByPosition();
6164 caretPosition = std::clamp(caretPosition, 0, static_cast<int32_t>(GetTextContentLength()));
6165 if (caretPosition_ == caretPosition) {
6166 caretPosition = 0;
6167 }
6168 // at line middle or line end
6169 bool cursorNotAtLineStart =
6170 NearEqual(currentCaretOffsetOverlay.GetX(), caretInfo.caretOffsetUp.GetX(), caretOffsetWidth);
6171 bool isEnter = NearZero(currentCaretOffsetOverlay.GetX() - richTextRect_.GetX(), rectLineInfo.GetX());
6172 SetCaretPosition(caretPosition);
6173 MoveCaretToContentRect();
6174 if (cursorNotAtLineStart && !isEnter) {
6175 OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight, false, false);
6176 SetLastClickOffset(caretOffset);
6177 caretAffinityPolicy_ = CaretAffinityPolicy::UPSTREAM_FIRST;
6178 }
6179 }
6180 StartTwinkling();
6181 auto host = GetHost();
6182 CHECK_NULL_RETURN(host, false);
6183 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6184 return true;
6185 }
6186
6187 bool RichEditorPattern::CursorMoveDown()
6188 {
6189 CloseSelectOverlay();
6190 ResetSelection();
6191 if (static_cast<int32_t>(GetTextContentLength()) > 1) {
6192 float caretHeight = 0.0f;
6193 float leadingMarginOffset = 0.0f;
6194 float caretHeightEnd = 0.0f;
6195 CaretOffsetInfo caretInfo;
6196 int32_t caretPositionEnd;
6197 caretInfo = GetCaretOffsetInfoByPosition();
6198 caretPositionEnd = CalcMoveDownPos(leadingMarginOffset);
6199 CHECK_NULL_RETURN(overlayMod_, false);
6200 auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
6201 auto caretOffsetOverlay = overlayMod->GetCaretOffset();
6202 auto caretOffsetWidth = overlayMod->GetCaretWidth();
6203 bool cursorNotAtLineStart =
6204 NearEqual(caretOffsetOverlay.GetX(), caretInfo.caretOffsetUp.GetX(), caretOffsetWidth);
6205 bool isEnter = NearZero(caretInfo.caretOffsetUp.GetX() - richTextRect_.GetX(), leadingMarginOffset);
6206 caretPositionEnd = std::clamp(caretPositionEnd, 0, static_cast<int32_t>(GetTextContentLength()));
6207 auto currentLineInfo = CalcLineInfoByPosition();
6208 if (caretPositionEnd <= caretPosition_) {
6209 OffsetF caretOffsetEnd = CalcCursorOffsetByPosition(GetTextContentLength(), caretHeightEnd);
6210 if (NearEqual(caretOffsetEnd.GetY() - GetTextRect().GetY(), currentLineInfo.GetY(), 0.5f)) {
6211 caretPositionEnd = GetTextContentLength();
6212 } else {
6213 caretPositionEnd += 1;
6214 }
6215 }
6216 SetCaretPosition(caretPositionEnd);
6217 if (cursorNotAtLineStart && caretPosition_ != 0 && !isEnter) {
6218 OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight, false, false);
6219 SetLastClickOffset(caretOffset);
6220 caretAffinityPolicy_ = CaretAffinityPolicy::UPSTREAM_FIRST;
6221 }
6222 MoveCaretToContentRect();
6223 }
6224 StartTwinkling();
6225 auto host = GetHost();
6226 CHECK_NULL_RETURN(host, false);
6227 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6228 return true;
6229 }
6230
6231 void RichEditorPattern::CursorMoveToNextWord(CaretMoveIntent direction)
6232 {
6233 CHECK_NULL_VOID(direction == CaretMoveIntent::LeftWord || direction == CaretMoveIntent::RightWord);
6234 bool isDirectionLeft = direction == CaretMoveIntent::LeftWord;
6235 auto index = caretPosition_;
6236 if (!textSelector_.SelectNothing()) {
6237 index = isDirectionLeft ? textSelector_.GetTextStart() + 1 : textSelector_.GetTextEnd() - 1;
6238 }
6239 auto newPos = isDirectionLeft ? GetLeftWordIndex(index) : GetRightWordIndex(index);
6240 CloseSelectOverlay();
6241 ResetSelection();
6242 SetCaretPosition(newPos);
6243 MoveCaretToContentRect();
6244 IF_TRUE(isEditing_, StartTwinkling());
6245 auto host = GetHost();
6246 CHECK_NULL_VOID(host);
6247 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6248 }
6249
6250 int32_t RichEditorPattern::GetLeftWordIndex(int32_t index)
6251 {
6252 AdjustIndexSkipSpace(index, MoveDirection::BACKWARD);
6253 int32_t newPos = std::max(0, index - 1);
6254 AdjustSelectorForSymbol(newPos, HandleType::FIRST, SelectorAdjustPolicy::INCLUDE);
6255 AdjustWordSelection(newPos, index);
6256 AdjustSelector(newPos, HandleType::FIRST);
6257 return newPos;
6258 }
6259
6260 int32_t RichEditorPattern::GetRightWordIndex(int32_t index)
6261 {
6262 int32_t newPos = std::min(index + 1, GetTextContentLength());
6263 AdjustWordSelection(index, newPos);
6264 AdjustSelector(newPos, HandleType::SECOND);
6265 AdjustIndexSkipSpace(newPos, MoveDirection::FORWARD);
6266 return newPos;
6267 }
6268
6269 bool RichEditorPattern::CursorMoveToParagraphBegin()
6270 {
6271 CloseSelectOverlay();
6272 ResetSelection();
6273 auto newPos = GetParagraphBeginPosition(caretPosition_);
6274 if (newPos == caretPosition_ && caretPosition_ > 0) {
6275 newPos = GetParagraphBeginPosition(caretPosition_ - 1);
6276 }
6277 if (newPos == caretPosition_) {
6278 return false;
6279 }
6280 SetCaretPosition(newPos);
6281 MoveCaretToContentRect();
6282 StartTwinkling();
6283 auto host = GetHost();
6284 CHECK_NULL_RETURN(host, false);
6285 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6286 return true;
6287 }
6288
6289 bool RichEditorPattern::CursorMoveToParagraphEnd()
6290 {
6291 CloseSelectOverlay();
6292 ResetSelection();
6293 auto newPos = GetParagraphEndPosition(caretPosition_);
6294 if (newPos == caretPosition_ && caretPosition_ < static_cast<int32_t>(GetTextContentLength())) {
6295 newPos = GetParagraphEndPosition(caretPosition_ + 1);
6296 }
6297 if (newPos == caretPosition_) {
6298 return false;
6299 }
6300 SetCaretPosition(newPos);
6301 MoveCaretToContentRect();
6302 StartTwinkling();
6303 auto host = GetHost();
6304 CHECK_NULL_RETURN(host, false);
6305 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6306 return true;
6307 }
6308
6309 bool RichEditorPattern::CursorMoveHome()
6310 {
6311 CloseSelectOverlay();
6312 ResetSelection();
6313 if (0 == caretPosition_) {
6314 return false;
6315 }
6316 SetCaretPosition(0);
6317 MoveCaretToContentRect();
6318 StartTwinkling();
6319 auto host = GetHost();
6320 CHECK_NULL_RETURN(host, false);
6321 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6322 return true;
6323 }
6324
6325 bool RichEditorPattern::CursorMoveEnd()
6326 {
6327 int32_t currentPositionIndex = 0;
6328 if (textSelector_.SelectNothing()) {
6329 currentPositionIndex = caretPosition_;
6330 } else {
6331 currentPositionIndex = textSelector_.GetTextEnd();
6332 }
6333 CloseSelectOverlay();
6334 ResetSelection();
6335 auto newPos = GetTextContentLength();
6336 if (newPos == currentPositionIndex) {
6337 StartTwinkling();
6338 return false;
6339 }
6340 SetCaretPosition(newPos);
6341 MoveCaretToContentRect();
6342 StartTwinkling();
6343 auto host = GetHost();
6344 CHECK_NULL_RETURN(host, false);
6345 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6346 return true;
6347 }
6348
6349 int32_t RichEditorPattern::GetLeftWordPosition(int32_t caretPosition)
6350 {
6351 int32_t offset = 0;
6352 bool jumpSpace = true;
6353 for (auto iter = spans_.rbegin(); iter != spans_.rend(); iter++) {
6354 auto span = *iter;
6355 auto content = span->content;
6356 if (caretPosition <= span->position - static_cast<int32_t>(content.length())) {
6357 continue;
6358 }
6359 int32_t position = span->position;
6360 for (auto iterContent = content.rbegin(); iterContent != content.rend(); iterContent++) {
6361 if (position-- > caretPosition) {
6362 continue;
6363 }
6364 if (*iterContent != u' ' || span->placeholderIndex >= 0) {
6365 jumpSpace = false;
6366 }
6367 if (position + 1 == caretPosition) {
6368 if (!(StringUtils::IsLetterOrNumberForWchar(*iterContent) ||
6369 (*iterContent == u' ' && span->placeholderIndex < 0))) {
6370 return std::clamp(caretPosition - 1, 0, static_cast<int32_t>(GetTextContentLength()));
6371 }
6372 }
6373 if (!jumpSpace) {
6374 if (!StringUtils::IsLetterOrNumberForWchar(*iterContent)) {
6375 return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
6376 }
6377 } else {
6378 if (*iterContent == u' ' && span->placeholderIndex >= 0) {
6379 return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
6380 }
6381 }
6382 offset++;
6383 }
6384 }
6385 return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
6386 }
6387
6388 int32_t RichEditorPattern::GetRightWordPosition(int32_t caretPosition)
6389 {
6390 int32_t offset = 0;
6391 bool jumpSpace = false;
6392 for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
6393 auto span = *iter;
6394 auto content = span->content;
6395 if (caretPosition > span->position) {
6396 continue;
6397 }
6398 int32_t position = span->position - static_cast<int32_t>(content.length());
6399 for (auto iterContent = content.cbegin(); iterContent != content.cend(); iterContent++) {
6400 if (position++ < caretPosition) {
6401 continue;
6402 }
6403 if (*iterContent == u' ' && span->placeholderIndex < 0) {
6404 jumpSpace = true;
6405 offset++;
6406 continue;
6407 }
6408 if (position - 1 == caretPosition) {
6409 if (!StringUtils::IsLetterOrNumberForWchar(*iterContent)) {
6410 return std::clamp(caretPosition + 1, 0, static_cast<int32_t>(GetTextContentLength()));
6411 }
6412 }
6413 if (jumpSpace) {
6414 if (*iterContent != u' ' || span->placeholderIndex >= 0) {
6415 return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
6416 }
6417 } else {
6418 if (!(StringUtils::IsLetterOrNumberForWchar(*iterContent) ||
6419 (*iterContent == u' ' && span->placeholderIndex < 0))) {
6420 return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
6421 }
6422 }
6423 offset++;
6424 }
6425 }
6426 return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
6427 }
6428
6429 int32_t RichEditorPattern::GetParagraphBeginPosition(int32_t caretPosition)
6430 {
6431 int32_t offset = 0;
6432 for (auto iter = spans_.rbegin(); iter != spans_.rend(); iter++) {
6433 auto span = *iter;
6434 auto content = span->content;
6435 if (caretPosition <= span->position - static_cast<int32_t>(content.length())) {
6436 continue;
6437 }
6438 int32_t position = span->position;
6439 for (auto iterContent = content.rbegin(); iterContent != content.rend(); iterContent++) {
6440 if (position-- > caretPosition) {
6441 continue;
6442 }
6443 if (*iterContent == u'\n') {
6444 return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
6445 }
6446 offset++;
6447 }
6448 }
6449 return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
6450 }
6451
6452 int32_t RichEditorPattern::GetParagraphEndPosition(int32_t caretPosition)
6453 {
6454 int32_t offset = 0;
6455 for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
6456 auto span = *iter;
6457 auto content = span->content;
6458 if (caretPosition > span->position) {
6459 continue;
6460 }
6461 int32_t position = span->position - static_cast<int32_t>(content.length());
6462 for (auto iterContent = content.cbegin(); iterContent != content.cend(); iterContent++) {
6463 if (position++ < caretPosition) {
6464 continue;
6465 }
6466 if (*iterContent == u'\n') {
6467 return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
6468 }
6469 offset++;
6470 }
6471 }
6472 return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
6473 }
6474
6475 void RichEditorPattern::HandleOnSelectAll()
6476 {
6477 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnSelectAll IsPreviewTextInputting:%{public}d", IsPreviewTextInputting());
6478 CHECK_NULL_VOID(!IsPreviewTextInputting());
6479 CloseSelectOverlay();
6480 auto host = GetHost();
6481 CHECK_NULL_VOID(host);
6482 textResponseType_.reset();
6483 int32_t newPos = static_cast<int32_t>(GetTextContentLength());
6484 textSelector_.Update(0, newPos);
6485 FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
6486 SetCaretPosition(newPos);
6487 MoveCaretToContentRect();
6488 IF_TRUE(IsSelected(), StopTwinkling());
6489 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6490 }
6491
6492 int32_t RichEditorPattern::CaretPositionSelectEmoji(CaretMoveIntent direction)
6493 {
6494 int32_t newPos = caretPosition_;
6495 int32_t emojiLength = 0;
6496 constexpr int32_t DELETE_COUNT = 1;
6497 if (direction == CaretMoveIntent::Left) {
6498 auto [isEmojiOnCaretBackward, isEmojiOnCaretForward] = IsEmojiOnCaretPosition(emojiLength, true, DELETE_COUNT);
6499 if (isEmojiOnCaretBackward) {
6500 newPos = caretPosition_ - emojiLength;
6501 } else {
6502 newPos = caretPosition_ - 1;
6503 }
6504 AdjustSelectorForSymbol(newPos, HandleType::FIRST, SelectorAdjustPolicy::INCLUDE);
6505 return newPos;
6506 }
6507 auto [isEmojiOnCaretBackward, isEmojiOnCaretForward] = IsEmojiOnCaretPosition(emojiLength, false, DELETE_COUNT);
6508 if (direction == CaretMoveIntent::Right) {
6509 if (isEmojiOnCaretForward) {
6510 newPos = caretPosition_ + emojiLength;
6511 } else {
6512 newPos = caretPosition_ + 1;
6513 }
6514 AdjustSelectorForSymbol(newPos, HandleType::SECOND, SelectorAdjustPolicy::INCLUDE);
6515 }
6516 return newPos;
6517 }
6518
6519 void RichEditorPattern::HandleSelect(CaretMoveIntent direction)
6520 {
6521 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "direction=%{public}d", direction);
6522 CloseSelectOverlay();
6523 auto host = GetHost();
6524 CHECK_NULL_VOID(host);
6525 int32_t newPos, fixedPos = caretPosition_;
6526 if (IsSelected()) {
6527 fixedPos = (caretPosition_ == textSelector_.GetTextStart() ? textSelector_.GetTextEnd()
6528 : textSelector_.GetTextStart());
6529 }
6530 newPos = HandleSelectWrapper(direction, fixedPos);
6531 if (newPos == -1) {
6532 return;
6533 }
6534 newPos = std::clamp(newPos, 0, static_cast<int32_t>(GetTextContentLength()));
6535 if (newPos == caretPosition_) {
6536 return;
6537 }
6538 UpdateSelector(fixedPos, newPos);
6539 FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
6540 SetCaretPosition(newPos);
6541 MoveCaretToContentRect();
6542 if (textSelector_.SelectNothing()) {
6543 StartTwinkling();
6544 } else {
6545 StopTwinkling();
6546 }
6547 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6548 }
6549
6550 void RichEditorPattern::ClearOperationRecords()
6551 {
6552 ClearRedoOperationRecords();
6553 if (operationRecords_.empty()) {
6554 return;
6555 }
6556 operationRecords_.clear();
6557 }
6558
6559 void RichEditorPattern::ClearRedoOperationRecords()
6560 {
6561 if (redoOperationRecords_.empty()) {
6562 return;
6563 }
6564 redoOperationRecords_.clear();
6565 }
6566
6567 void RichEditorPattern::AddOperationRecord(const OperationRecord& record)
6568 {
6569 if (operationRecords_.size() >= RECORD_MAX_LENGTH) {
6570 // case of max length is 0
6571 if (operationRecords_.empty()) {
6572 return;
6573 }
6574 operationRecords_.erase(operationRecords_.begin());
6575 }
6576 operationRecords_.emplace_back(record);
6577 }
6578
6579 void RichEditorPattern::UpdateShiftFlag(const KeyEvent& keyEvent)
6580 {
6581 bool hasKeyShift = keyEvent.HasKey(KeyCode::KEY_SHIFT_LEFT) || keyEvent.HasKey(KeyCode::KEY_SHIFT_RIGHT);
6582 auto action = keyEvent.action;
6583 bool isShiftPressed = hasKeyShift && (action == KeyAction::DOWN || action == KeyAction::UP);
6584 if (isShiftPressed != shiftFlag_) {
6585 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "UpdateShiftFlag:%{public}d by action:%{public}d", isShiftPressed, action);
6586 shiftFlag_ = isShiftPressed;
6587 }
6588 }
6589
6590 bool RichEditorPattern::HandleOnEscape()
6591 {
6592 CloseSelectOverlay();
6593 return false;
6594 }
6595
6596 void RichEditorPattern::HandleOnUndoAction()
6597 {
6598 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnUndoAction");
6599 if (operationRecords_.empty()) {
6600 return;
6601 }
6602 auto value = operationRecords_.back();
6603 RichEditorChangeValue changeValue;
6604 CHECK_NULL_VOID(BeforeChangeText(changeValue, value, RecordType::UNDO));
6605 operationRecords_.pop_back();
6606 if (redoOperationRecords_.size() >= RECORD_MAX_LENGTH && !(redoOperationRecords_.empty())) {
6607 redoOperationRecords_.erase(redoOperationRecords_.begin());
6608 }
6609 redoOperationRecords_.push_back(value);
6610 CloseSelectOverlay();
6611 ResetSelection();
6612 if (value.addText.has_value() && value.deleteCaretPostion != -1) {
6613 UndoDrag(value);
6614 AfterContentChange(changeValue);
6615 return;
6616 }
6617 if (value.addText.has_value() && value.deleteText.has_value()) {
6618 SetCaretPosition(value.afterCaretPosition);
6619 DeleteBackwardOperation(TextEmojiProcessor::GetCharacterNum(value.addText.value_or(u"")));
6620 InsertValueOperation(value.deleteText.value_or(u""));
6621 AfterContentChange(changeValue);
6622 return;
6623 }
6624 if (value.addText.has_value()) {
6625 SetCaretPosition(value.afterCaretPosition);
6626 DeleteBackwardOperation(TextEmojiProcessor::GetCharacterNum(value.addText.value_or(u"")));
6627 }
6628 if (value.deleteText.has_value()) {
6629 SetCaretPosition(value.afterCaretPosition);
6630 InsertValueOperation(value.deleteText.value_or(u""));
6631 }
6632 AfterContentChange(changeValue);
6633 }
6634
6635 void RichEditorPattern::HandleOnRedoAction()
6636 {
6637 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnRedoAction");
6638 if (redoOperationRecords_.empty()) {
6639 return;
6640 }
6641 auto value = redoOperationRecords_.back();
6642 RichEditorChangeValue changeValue;
6643 CHECK_NULL_VOID(BeforeChangeText(changeValue, value, RecordType::REDO));
6644 redoOperationRecords_.pop_back();
6645 if (value.addText.has_value() && value.deleteCaretPostion != -1) {
6646 RedoDrag(value);
6647 AfterContentChange(changeValue);
6648 return;
6649 }
6650 if (value.addText.has_value() && value.deleteText.has_value()) {
6651 SetCaretPosition(value.beforeCaretPosition);
6652 DeleteForwardOperation(value.deleteText.value_or(u"").length());
6653 InsertValueOperation(value.addText.value_or(u""));
6654 operationRecords_.push_back(value);
6655 AfterContentChange(changeValue);
6656 return;
6657 }
6658 if (value.deleteText.has_value()) {
6659 SetCaretPosition(value.beforeCaretPosition);
6660 if (value.beforeCaretPosition != value.afterCaretPosition) {
6661 DeleteBackwardOperation(value.deleteText.value_or(u"").length());
6662 } else {
6663 DeleteForwardOperation(value.deleteText.value_or(u"").length());
6664 }
6665 }
6666 if (value.addText.has_value()) {
6667 SetCaretPosition(value.beforeCaretPosition);
6668 InsertValueOperation(value.addText.value_or(u""));
6669 }
6670 operationRecords_.push_back(value);
6671 AfterContentChange(changeValue);
6672 }
6673
6674 void RichEditorPattern::CalcInsertValueObj(TextInsertValueInfo& info)
6675 {
6676 if (spans_.empty()) {
6677 info.SetSpanIndex(0);
6678 info.SetOffsetInSpan(0);
6679 return;
6680 }
6681 auto it = std::find_if(
6682 spans_.begin(), spans_.end(), [caretPosition = caretPosition_ + moveLength_](const RefPtr<SpanItem>& spanItem) {
6683 if (spanItem->content.empty()) {
6684 return spanItem->position == caretPosition;
6685 }
6686 return spanItem->rangeStart <= caretPosition && caretPosition < spanItem->position;
6687 });
6688 if (it != spans_.end() && (*it)->unicode != 0 && (*it)->position - caretPosition_ + moveLength_ == 1) {
6689 it++;
6690 moveLength_++;
6691 }
6692 info.SetSpanIndex(std::distance(spans_.begin(), it));
6693 if (it == spans_.end()) {
6694 info.SetOffsetInSpan(0);
6695 return;
6696 }
6697 info.SetOffsetInSpan(
6698 caretPosition_ + moveLength_ - ((*it)->position - (*it)->content.length()));
6699 }
6700
6701 void RichEditorPattern::CalcDeleteValueObj(int32_t currentPosition, int32_t length, RichEditorDeleteValue& info)
6702 {
6703 auto it =
6704 std::find_if(spans_.begin(), spans_.end(), [caretPosition = currentPosition](const RefPtr<SpanItem>& spanItem) {
6705 return (spanItem->position - static_cast<int32_t>(spanItem->content.length()) <= caretPosition) &&
6706 (caretPosition < spanItem->position);
6707 });
6708 while (it != spans_.end() && length > 0) {
6709 if ((*it)->placeholderIndex >= 0 || (*it)->unicode != 0) {
6710 RichEditorAbstractSpanResult spanResult;
6711 spanResult.SetSpanIndex(std::distance(spans_.begin(), it));
6712 int32_t eraseLength = 0;
6713 if ((*it)->unicode != 0) {
6714 eraseLength = DeleteValueSetSymbolSpan(*it, spanResult);
6715 } else if (AceType::InstanceOf<ImageSpanItem>(*it)) {
6716 eraseLength = DeleteValueSetImageSpan(*it, spanResult);
6717 } else {
6718 eraseLength = DeleteValueSetBuilderSpan(*it, spanResult);
6719 }
6720 currentPosition += eraseLength;
6721 length -= eraseLength;
6722 info.SetRichEditorDeleteSpans(spanResult);
6723 } else {
6724 RichEditorAbstractSpanResult spanResult;
6725 spanResult.SetSpanIndex(std::distance(spans_.begin(), it));
6726 auto eraseLength = DeleteValueSetTextSpan(*it, currentPosition, length, spanResult);
6727 length -= eraseLength;
6728 currentPosition += eraseLength;
6729 info.SetRichEditorDeleteSpans(spanResult);
6730 }
6731 std::advance(it, 1);
6732 }
6733 }
6734
6735 RefPtr<SpanNode> RichEditorPattern::GetSpanNodeBySpanItem(const RefPtr<SpanItem> spanItem)
6736 {
6737 RefPtr<SpanNode> spanNode;
6738 auto iter = std::find(spans_.begin(), spans_.end(), spanItem);
6739 if (iter == spans_.end()) {
6740 return spanNode;
6741 }
6742 auto spanIndex = std::distance(spans_.begin(), iter);
6743 auto host = GetHost();
6744 CHECK_NULL_RETURN(host, spanNode);
6745 auto it = host->GetChildren().begin();
6746 std::advance(it, spanIndex);
6747 spanNode = AceType::DynamicCast<SpanNode>(*it);
6748 return spanNode;
6749 }
6750
6751 int32_t RichEditorPattern::DeleteValueSetSymbolSpan(
6752 const RefPtr<SpanItem>& spanItem, RichEditorAbstractSpanResult& spanResult)
6753 {
6754 spanResult.SetSpanType(SpanResultType::SYMBOL);
6755 spanResult.SetSpanRangeEnd(spanItem->position);
6756 spanResult.SetSpanRangeStart(spanItem->position - SYMBOL_SPAN_LENGTH);
6757 spanResult.SetEraseLength(SYMBOL_SPAN_LENGTH);
6758 spanResult.SetValueString(std::to_string(spanItem->unicode));
6759 spanResult.SetValueResource(spanItem->GetResourceObject());
6760 auto spanNode = GetSpanNodeBySpanItem(spanItem);
6761 if (spanNode) {
6762 spanResult.SetSymbolSpanStyle(GetSymbolSpanStyleObject(spanNode));
6763 }
6764 return SYMBOL_SPAN_LENGTH;
6765 }
6766
6767 int32_t RichEditorPattern::DeleteValueSetImageSpan(
6768 const RefPtr<SpanItem>& spanItem, RichEditorAbstractSpanResult& spanResult)
6769 {
6770 spanResult.SetSpanType(SpanResultType::IMAGE);
6771 spanResult.SetSpanRangeEnd(spanItem->position);
6772 spanResult.SetSpanRangeStart(spanItem->position - 1);
6773 spanResult.SetEraseLength(1);
6774 auto host = GetHost();
6775 CHECK_NULL_RETURN(host, IMAGE_SPAN_LENGTH);
6776 auto uiNode = host->GetChildAtIndex(spanResult.GetSpanIndex());
6777 CHECK_NULL_RETURN(uiNode, IMAGE_SPAN_LENGTH);
6778 auto imageNode = AceType::DynamicCast<FrameNode>(uiNode);
6779 CHECK_NULL_RETURN(imageNode, IMAGE_SPAN_LENGTH);
6780 auto imageRenderCtx = imageNode->GetRenderContext();
6781 if (imageRenderCtx->GetBorderRadius()) {
6782 BorderRadiusProperty brp;
6783 auto jsonObject = JsonUtil::Create(true);
6784 auto jsonBorder = JsonUtil::Create(true);
6785 InspectorFilter filter;
6786 imageRenderCtx->GetBorderRadiusValue(brp).ToJsonValue(jsonObject, jsonBorder, filter);
6787 spanResult.SetBorderRadius(jsonObject->GetValue("borderRadius")->IsObject()
6788 ? jsonObject->GetValue("borderRadius")->ToString()
6789 : jsonObject->GetString("borderRadius"));
6790 }
6791 auto geometryNode = imageNode->GetGeometryNode();
6792 CHECK_NULL_RETURN(geometryNode, IMAGE_SPAN_LENGTH);
6793 auto imageLayoutProperty = DynamicCast<ImageLayoutProperty>(imageNode->GetLayoutProperty());
6794 CHECK_NULL_RETURN(imageLayoutProperty, IMAGE_SPAN_LENGTH);
6795 spanResult.SetSizeWidth(geometryNode->GetMarginFrameSize().Width());
6796 spanResult.SetSizeHeight(geometryNode->GetMarginFrameSize().Height());
6797 if (imageLayoutProperty->GetMarginProperty()) {
6798 spanResult.SetMargin(imageLayoutProperty->GetMarginProperty()->ToString());
6799 }
6800 if (!imageLayoutProperty->GetImageSourceInfo()->GetPixmap()) {
6801 spanResult.SetValueResourceStr(imageLayoutProperty->GetImageSourceInfo()->GetSrc());
6802 } else {
6803 spanResult.SetValuePixelMap(imageLayoutProperty->GetImageSourceInfo()->GetPixmap());
6804 }
6805 if (imageLayoutProperty->HasImageFit()) {
6806 spanResult.SetImageFit(imageLayoutProperty->GetImageFitValue());
6807 }
6808 if (imageLayoutProperty->HasVerticalAlign()) {
6809 spanResult.SetVerticalAlign(imageLayoutProperty->GetVerticalAlignValue());
6810 }
6811 return IMAGE_SPAN_LENGTH;
6812 }
6813
6814 int32_t RichEditorPattern::DeleteValueSetBuilderSpan(
6815 const RefPtr<SpanItem>& spanItem, RichEditorAbstractSpanResult& spanResult)
6816 {
6817 spanResult.SetSpanType(SpanResultType::IMAGE);
6818 spanResult.SetSpanRangeEnd(spanItem->position);
6819 spanResult.SetSpanRangeStart(spanItem->position - 1);
6820 spanResult.SetEraseLength(1);
6821 auto host = GetHost();
6822 CHECK_NULL_RETURN(host, 1);
6823 auto uiNode = host->GetChildAtIndex(spanResult.GetSpanIndex());
6824 CHECK_NULL_RETURN(uiNode, 1);
6825 auto builderNode = AceType::DynamicCast<FrameNode>(uiNode);
6826 CHECK_NULL_RETURN(builderNode, 1);
6827 auto geometryNode = builderNode->GetGeometryNode();
6828 CHECK_NULL_RETURN(geometryNode, 1);
6829 spanResult.SetSizeWidth(geometryNode->GetMarginFrameSize().Width());
6830 spanResult.SetSizeHeight(geometryNode->GetMarginFrameSize().Height());
6831 return 1;
6832 }
6833
6834 int32_t RichEditorPattern::DeleteValueSetTextSpan(
6835 const RefPtr<SpanItem>& spanItem, int32_t currentPosition, int32_t length, RichEditorAbstractSpanResult& spanResult)
6836 {
6837 spanResult.SetSpanType(SpanResultType::TEXT);
6838 auto contentStartPosition
6839 = spanItem->position - static_cast<int32_t>(spanItem->content.length());
6840 spanResult.SetSpanRangeStart(contentStartPosition);
6841 int32_t eraseLength = 0;
6842 if (spanItem->position - currentPosition >= length) {
6843 eraseLength = length;
6844 } else {
6845 eraseLength = spanItem->position - currentPosition;
6846 }
6847 spanResult.SetSpanRangeEnd(spanItem->position);
6848 if (!previewTextRecord_.previewContent.empty()) {
6849 spanResult.SetPreviewText(previewTextRecord_.previewContent);
6850 } else {
6851 spanResult.SetValue(spanItem->content);
6852 }
6853 spanResult.SetOffsetInSpan(currentPosition - contentStartPosition);
6854 spanResult.SetEraseLength(eraseLength);
6855 spanResult.SetUrlAddress(spanItem->urlAddress);
6856 if (!spanItem->GetTextStyle().has_value()) {
6857 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "SpanItem text style is empty.");
6858 return eraseLength;
6859 }
6860 spanResult.SetFontColor(spanItem->GetTextStyle()->GetTextColor().ColorToString());
6861 spanResult.SetFontSize(spanItem->GetTextStyle()->GetFontSize().Value());
6862 spanResult.SetFontStyle(spanItem->GetTextStyle()->GetFontStyle());
6863 spanResult.SetFontWeight((int32_t)(spanItem->GetTextStyle()->GetFontWeight()));
6864 if (!spanItem->GetTextStyle()->GetFontFamilies().empty()) {
6865 spanResult.SetFontFamily(spanItem->GetTextStyle()->GetFontFamilies().at(0));
6866 }
6867 spanResult.SetColor(spanItem->GetTextStyle()->GetTextDecorationColor().ColorToString());
6868 spanResult.SetTextDecoration(spanItem->GetTextStyle()->GetTextDecoration());
6869 spanResult.SetTextDecorationStyle(spanItem->GetTextStyle()->GetTextDecorationStyle());
6870 spanResult.SetFontFeature(spanItem->GetTextStyle()->GetFontFeatures());
6871 auto host = GetHost();
6872 CHECK_NULL_RETURN(host, eraseLength);
6873 auto uiNode = host->GetChildAtIndex(spanResult.GetSpanIndex());
6874 CHECK_NULL_RETURN(uiNode, eraseLength);
6875 auto spanNode = DynamicCast<SpanNode>(uiNode);
6876 CHECK_NULL_RETURN(spanNode, eraseLength);
6877 spanResult.SetTextStyle(GetTextStyleObject(spanNode));
6878 return eraseLength;
6879 }
6880
6881 void RichEditorPattern::DeleteByDeleteValueInfo(const RichEditorDeleteValue& info)
6882 {
6883 auto deleteSpans = info.GetRichEditorDeleteSpans();
6884 if (deleteSpans.empty()) {
6885 return;
6886 }
6887 auto host = GetHost();
6888 CHECK_NULL_VOID(host);
6889 ProcessDeleteNodes(deleteSpans);
6890 UpdateSpanPosition();
6891 SetCaretPosition(info.GetOffset(), false);
6892 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
6893 OnModifyDone();
6894 }
6895
6896 int32_t RichEditorPattern::ProcessDeleteNodes(std::list<RichEditorAbstractSpanResult>& deleteSpans)
6897 {
6898 auto eraseLength = 0;
6899 auto host = GetHost();
6900 CHECK_NULL_RETURN(host, eraseLength);
6901 std::set<int32_t, std::greater<int32_t>> deleteNodes;
6902 for (const auto& it : deleteSpans) {
6903 eraseLength += it.GetEraseLength();
6904 switch (it.GetType()) {
6905 case SpanResultType::TEXT: {
6906 auto ui_node = host->GetChildAtIndex(it.GetSpanIndex());
6907 CHECK_NULL_RETURN(ui_node, eraseLength);
6908 auto spanNode = DynamicCast<SpanNode>(ui_node);
6909 CHECK_NULL_RETURN(spanNode, eraseLength);
6910 auto spanItem = spanNode->GetSpanItem();
6911 CHECK_NULL_RETURN(spanItem, eraseLength);
6912 auto textTemp = spanItem->content;
6913 auto textTempSize = static_cast<int32_t>(textTemp.size());
6914 if (textTempSize < it.OffsetInSpan()) {
6915 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "ProcessDeleteNodes failed, "
6916 "contentLen=%{public}zu, spanItemSize=%{public}d, offsetInSpan=%{public}d",
6917 textTemp.length(), textTempSize, it.OffsetInSpan());
6918 RichEditorInfo errorInfo { RichEditorErrorType::DELETE_NODE, textTempSize,
6919 it.GetEraseLength(), it.OffsetInSpan() };
6920 RichEditorErrorReport(errorInfo);
6921 continue;
6922 }
6923 textTemp.erase(it.OffsetInSpan(), it.GetEraseLength());
6924 if (textTemp.size() == 0) {
6925 deleteNodes.emplace(it.GetSpanIndex());
6926 }
6927 spanNode->UpdateContent(textTemp);
6928 spanItem->position -= it.GetEraseLength();
6929 break;
6930 }
6931 case SpanResultType::IMAGE:
6932 deleteNodes.emplace(it.GetSpanIndex());
6933 break;
6934 case SpanResultType::SYMBOL:
6935 deleteNodes.emplace(it.GetSpanIndex());
6936 break;
6937 default:
6938 break;
6939 }
6940 }
6941 RemoveEmptySpan(deleteNodes);
6942 return eraseLength;
6943 }
6944
6945 void RichEditorPattern::RemoveEmptySpan(std::set<int32_t, std::greater<int32_t>>& deleteSpanIndexs)
6946 {
6947 auto host = GetHost();
6948 CHECK_NULL_VOID(host);
6949 for (auto index : deleteSpanIndexs) {
6950 host->RemoveChildAtIndex(index);
6951 auto it = spans_.begin();
6952 std::advance(it, index);
6953 if (it != spans_.end()) {
6954 spans_.erase(it);
6955 }
6956 }
6957 }
6958
6959 RefPtr<GestureEventHub> RichEditorPattern::GetGestureEventHub() {
6960 auto host = GetHost();
6961 CHECK_NULL_RETURN(host, nullptr);
6962 return host->GetOrCreateGestureEventHub();
6963 }
6964
6965 bool RichEditorPattern::OnKeyEvent(const KeyEvent& keyEvent)
6966 {
6967 return TextInputClient::HandleKeyEvent(keyEvent);
6968 }
6969
6970 void RichEditorPattern::HandleExtendAction(int32_t action)
6971 {
6972 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleExtendAction %{public}d", action);
6973 switch (action) {
6974 case ACTION_SELECT_ALL:
6975 HandleMenuCallbackOnSelectAll(false);
6976 break;
6977 case ACTION_CUT:
6978 HandleOnCut();
6979 break;
6980 case ACTION_COPY:
6981 HandleOnCopy();
6982 break;
6983 case ACTION_PASTE:
6984 HandleOnPaste();
6985 break;
6986 default:
6987 break;
6988 }
6989 }
6990
6991 void RichEditorPattern::CursorMove(CaretMoveIntent direction)
6992 {
6993 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "direction=%{public}d", direction);
6994 switch (direction) {
6995 case CaretMoveIntent::Left:
6996 CursorMoveLeft();
6997 break;
6998 case CaretMoveIntent::Right:
6999 CursorMoveRight();
7000 break;
7001 case CaretMoveIntent::Up:
7002 CursorMoveUp();
7003 break;
7004 case CaretMoveIntent::Down:
7005 CursorMoveDown();
7006 break;
7007 case CaretMoveIntent::LeftWord:
7008 CursorMoveToNextWord(direction);
7009 break;
7010 case CaretMoveIntent::RightWord:
7011 CursorMoveToNextWord(direction);
7012 break;
7013 case CaretMoveIntent::ParagraghBegin:
7014 CursorMoveToParagraphBegin();
7015 break;
7016 case CaretMoveIntent::ParagraghEnd:
7017 CursorMoveToParagraphEnd();
7018 break;
7019 case CaretMoveIntent::Home:
7020 CursorMoveHome();
7021 break;
7022 case CaretMoveIntent::End:
7023 CursorMoveEnd();
7024 break;
7025 case CaretMoveIntent::LineBegin:
7026 CursorMoveLineBegin();
7027 break;
7028 case CaretMoveIntent::LineEnd:
7029 CursorMoveLineEnd();
7030 break;
7031 default:
7032 LOGW("Unsupported cursor move operation for rich editor");
7033 }
7034 }
7035
7036 void RichEditorPattern::MoveCaretAfterTextChange()
7037 {
7038 CHECK_NULL_VOID(isTextChange_ && moveLength_ != 0);
7039 isTextChange_ = false;
7040 switch (moveDirection_) {
7041 case MoveDirection::BACKWARD:
7042 SetCaretPosition(std::clamp((caretPosition_ - moveLength_), 0, GetTextContentLength()), false);
7043 break;
7044 case MoveDirection::FORWARD:
7045 SetCaretPosition(std::clamp((caretPosition_ + moveLength_), 0, GetTextContentLength()), false);
7046 break;
7047 default:
7048 break;
7049 }
7050 moveLength_ = 0;
7051 }
7052
7053 void RichEditorPattern::InitTouchEvent()
7054 {
7055 CHECK_NULL_VOID(!touchListener_);
7056 auto gesture = GetGestureEventHub();
7057 CHECK_NULL_VOID(gesture);
7058 auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
7059 auto pattern = weak.Upgrade();
7060 CHECK_NULL_VOID(pattern);
7061 pattern->sourceType_ = info.GetSourceDevice();
7062 pattern->HandleTouchEvent(info);
7063 };
7064 touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
7065 gesture->AddTouchEvent(touchListener_);
7066 }
7067
7068 void RichEditorPattern::InitPanEvent()
7069 {
7070 CHECK_NULL_VOID(!panEvent_);
7071 auto gestureHub = GetGestureEventHub();
7072 CHECK_NULL_VOID(gestureHub);
7073 auto actionStartTask = [](const GestureEvent& info) {};
7074 auto actionUpdateTask = [](const GestureEvent& info) {};
7075 auto actionEndTask = [](const GestureEvent& info) {};
7076 GestureEventNoParameter actionCancelTask;
7077 panEvent_ = MakeRefPtr<PanEvent>(std::move(actionStartTask), std::move(actionUpdateTask),
7078 std::move(actionEndTask), std::move(actionCancelTask));
7079 PanDirection panDirection = { .type = PanDirection::ALL };
7080 gestureHub->AddPanEvent(panEvent_, panDirection, 1, DEFAULT_PAN_DISTANCE);
7081 gestureHub->SetPanEventType(GestureTypeName::PAN_GESTURE);
7082 gestureHub->SetOnGestureJudgeNativeBegin([weak = WeakClaim(this)](const RefPtr<NG::GestureInfo>& gestureInfo,
7083 const std::shared_ptr<BaseGestureEvent>& info) -> GestureJudgeResult {
7084 auto pattern = weak.Upgrade();
7085 CHECK_NULL_RETURN(pattern, GestureJudgeResult::CONTINUE);
7086 auto gestureType = gestureInfo->GetType();
7087 auto inputEventType = gestureInfo->GetInputEventType();
7088 bool isDraggingCaret = (gestureType == GestureTypeName::PAN_GESTURE)
7089 && (inputEventType == InputEventType::TOUCH_SCREEN) && pattern->moveCaretState_.isMoveCaret;
7090 bool isMouseSelecting = (gestureType == GestureTypeName::PAN_GESTURE)
7091 && (inputEventType == InputEventType::MOUSE_BUTTON) && !pattern->blockPress_;
7092 if (isDraggingCaret || isMouseSelecting) {
7093 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "prevent pan gesture draggingCaret=%{public}d mouseSelecting=%{public}d",
7094 isDraggingCaret, isMouseSelecting);
7095 return GestureJudgeResult::CONTINUE;
7096 }
7097 CHECK_NULL_RETURN(gestureInfo->GetType() != GestureTypeName::PAN_GESTURE, GestureJudgeResult::REJECT);
7098 return GestureJudgeResult::CONTINUE;
7099 });
7100 }
7101
7102 void RichEditorPattern::HandleTouchEvent(const TouchEventInfo& info)
7103 {
7104 CHECK_NULL_VOID(!selectOverlay_->IsTouchAtHandle(info));
7105 CHECK_NULL_VOID(!info.GetTouches().empty());
7106 auto acceptedTouchInfo = GetAcceptedTouchLocationInfo(info);
7107 CHECK_NULL_VOID(acceptedTouchInfo.has_value());
7108 auto touchInfo = acceptedTouchInfo.value();
7109 auto touchType = touchInfo.GetTouchType();
7110 if (touchType == TouchType::DOWN) {
7111 HandleTouchDown(touchInfo);
7112 if (hasUrlSpan_) {
7113 HandleUrlSpanShowShadow(touchInfo.GetLocalLocation(), touchInfo.GetGlobalLocation(), GetUrlPressColor());
7114 }
7115 } else if (touchType == TouchType::UP) {
7116 IF_TRUE(status_ == Status::FLOATING, status_ = Status::NONE);
7117 HandleTouchUp();
7118 if (hasUrlSpan_) {
7119 HandleUrlSpanForegroundClear();
7120 }
7121 } else if (touchType == TouchType::MOVE) {
7122 HandleTouchMove(touchInfo);
7123 } else if (touchType == TouchType::CANCEL) {
7124 IF_PRESENT(magnifierController_, RemoveMagnifierFrameNode());
7125 HandleTouchCancelAfterLongPress();
7126 ResetTouchAndMoveCaretState();
7127 }
7128 }
7129
7130 std::optional<TouchLocationInfo> RichEditorPattern::GetAcceptedTouchLocationInfo(const TouchEventInfo& info)
7131 {
7132 const auto& touchInfos = info.GetChangedTouches();
7133 CHECK_NULL_RETURN(!touchInfos.empty(), std::nullopt);
7134 CHECK_NULL_RETURN(isTouchSelecting_ || moveCaretState_.touchFingerId.has_value(), touchInfos.front());
7135 const int32_t touchFingerId = isTouchSelecting_ ? selectingFingerId_ : moveCaretState_.touchFingerId.value();
7136 for (const auto& touchInfo : touchInfos) {
7137 if (touchInfo.GetFingerId() == touchFingerId) {
7138 return touchInfo;
7139 }
7140 }
7141 return std::nullopt;
7142 }
7143
7144 void RichEditorPattern::HandleUrlSpanForegroundClear()
7145 {
7146 overlayMod_->ClearSelectedForegroundColorAndRects();
7147 MarkDirtySelf();
7148 }
7149
7150 void RichEditorPattern::HandleTouchDown(const TouchLocationInfo& info)
7151 {
7152 auto sourceTool = info.GetSourceTool();
7153 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Touch down longPressState=[%{public}d, %{public}d], source=%{public}d",
7154 previewLongPress_, editingLongPress_, sourceTool);
7155 globalOffsetOnMoveStart_ = GetPaintRectGlobalOffset();
7156 moveCaretState_.Reset();
7157 ResetTouchSelectState();
7158 CHECK_NULL_VOID(HasFocus() && sourceTool == SourceTool::FINGER);
7159 auto touchDownOffset = info.GetLocalLocation();
7160 moveCaretState_.touchDownOffset = touchDownOffset;
7161 RectF lastCaretRect = GetCaretRect();
7162 if (RepeatClickCaret(touchDownOffset, lastCaretRect)) {
7163 moveCaretState_.isTouchCaret = true;
7164 auto host = GetHost();
7165 CHECK_NULL_VOID(host);
7166 }
7167 }
7168
7169 void RichEditorPattern::HandleTouchUp()
7170 {
7171 HandleTouchUpAfterLongPress();
7172 ResetTouchAndMoveCaretState();
7173 ResetTouchSelectState();
7174 if (magnifierController_) {
7175 magnifierController_->RemoveMagnifierFrameNode();
7176 }
7177 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
7178 if (isLongPress_) {
7179 isLongPress_ = false;
7180 }
7181 #endif
7182 }
7183
7184 void RichEditorPattern::StartFloatingCaretLand()
7185 {
7186 AnimationUtils::StopAnimation(magnifierAnimation_);
7187 CHECK_NULL_VOID(floatingCaretState_.isFloatingCaretVisible);
7188 auto caretOffset = CalculateCaretOffsetAndHeight().first;
7189 auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
7190 IF_PRESENT(overlayMod, StartFloatingCaretLand(caretOffset));
7191 }
7192
7193 void RichEditorPattern::ResetTouchAndMoveCaretState()
7194 {
7195 if (moveCaretState_.isMoveCaret) {
7196 isCursorAlwaysDisplayed_ = false;
7197 IF_TRUE(isEditing_, StartTwinkling());
7198 }
7199 StopAutoScroll();
7200 CheckScrollable();
7201 UpdateScrollBarOffset();
7202 moveCaretState_.Reset();
7203 StartFloatingCaretLand();
7204 }
7205
7206 void RichEditorPattern::ResetTouchSelectState()
7207 {
7208 selectingFingerId_ = -1;
7209 isTouchSelecting_ = false;
7210 previewLongPress_ = false;
7211 editingLongPress_ = false;
7212 }
7213
7214 void RichEditorPattern::HandleTouchUpAfterLongPress()
7215 {
7216 CHECK_NULL_VOID(editingLongPress_ || previewLongPress_);
7217 auto selectStart = std::min(textSelector_.GetTextStart(), GetTextContentLength());
7218 auto selectEnd = std::min(textSelector_.GetTextEnd(), GetTextContentLength());
7219 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "after long press textSelector=[%{public}d, %{public}d] isEditing=%{public}d",
7220 selectStart, selectEnd, isEditing_);
7221 textSelector_.Update(selectStart, selectEnd);
7222 FireOnSelect(selectStart, selectEnd);
7223 SetCaretPositionWithAffinity({ selectEnd, TextAffinity::UPSTREAM });
7224 CalculateHandleOffsetAndShowOverlay();
7225 selectOverlay_->ProcessOverlay({ .animation = true });
7226 FireOnSelectionChange(selectStart, selectEnd);
7227 IF_TRUE(IsSingleHandle(), ForceTriggerAvoidOnCaretChange());
7228 }
7229
7230 void RichEditorPattern::HandleTouchCancelAfterLongPress()
7231 {
7232 CHECK_NULL_VOID(editingLongPress_ || previewLongPress_);
7233 auto selectStart = std::min(textSelector_.GetTextStart(), GetTextContentLength());
7234 auto selectEnd = std::min(textSelector_.GetTextEnd(), GetTextContentLength());
7235 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "touch canceled, textSelector=[%{public}d, %{public}d] isEditing=%{public}d",
7236 selectStart, selectEnd, isEditing_);
7237 textSelector_.Update(selectStart, selectEnd);
7238 SetCaretPositionWithAffinity({ selectEnd, TextAffinity::UPSTREAM });
7239 CalculateHandleOffsetAndShowOverlay();
7240 selectOverlay_->ProcessOverlay({ .menuIsShow = selectOverlay_->IsCurrentMenuVisibile(), .animation = true });
7241 FireOnSelectionChange(selectStart, selectEnd);
7242 }
7243
7244 void RichEditorPattern::HandleTouchMove(const TouchLocationInfo& info)
7245 {
7246 auto originalLocaloffset = info.GetLocalLocation();
7247 auto offset = AdjustLocalOffsetOnMoveEvent(originalLocaloffset);
7248 if (previewLongPress_ || editingLongPress_) {
7249 if (!isTouchSelecting_) {
7250 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Touch selecting start id= %{public}d", info.GetFingerId());
7251 showSelect_ = true;
7252 isTouchSelecting_ = true;
7253 selectingFingerId_ = info.GetFingerId();
7254 }
7255 UpdateSelectionByTouchMove(offset);
7256 return;
7257 }
7258 CHECK_NULL_VOID(moveCaretState_.isTouchCaret && caretTwinkling_);
7259 if (!moveCaretState_.isMoveCaret) {
7260 auto moveDistance = (offset - moveCaretState_.touchDownOffset).GetDistance();
7261 IF_TRUE(GreatNotEqual(moveDistance, moveCaretState_.minDistance.ConvertToPx()),
7262 OnMoveCaretStart(info.GetFingerId()));
7263 }
7264 UpdateCaretByTouchMove(offset);
7265 }
7266
7267 void RichEditorPattern::OnMoveCaretStart(int32_t fingerId)
7268 {
7269 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Move caret start id=%{public}d", fingerId);
7270 moveCaretState_.isMoveCaret = true;
7271 moveCaretState_.touchFingerId = fingerId;
7272 scrollable_ = false;
7273 SetScrollEnabled(scrollable_);
7274 UpdateScrollBarOffset();
7275 ShowCaretWithoutTwinkling();
7276 }
7277
7278 void RichEditorPattern::UpdateCaretByTouchMove(const Offset& offset)
7279 {
7280 CHECK_NULL_VOID(moveCaretState_.isMoveCaret);
7281 auto host = GetHost();
7282 CHECK_NULL_VOID(host);
7283 if (SelectOverlayIsOn()) {
7284 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Close select overlay while dragging caret");
7285 selectOverlay_->CloseOverlay(false, CloseReason::CLOSE_REASON_NORMAL);
7286 }
7287 auto touchOffset = Offset(offset.GetX(), std::max(offset.GetY(), static_cast<double>(contentRect_.GetY())));
7288 Offset textOffset = ConvertTouchOffsetToTextOffset(touchOffset);
7289 auto positionWithAffinity = paragraphs_.GetGlyphPositionAtCoordinate(textOffset);
7290 SetCaretPositionWithAffinity(positionWithAffinity);
7291 SetCaretTouchMoveOffset(offset);
7292 CalcAndRecordLastClickCaretInfo(textOffset);
7293 auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
7294 auto floatingCaretCenter =
7295 Offset(floatingCaretState_.touchMoveOffset->GetX(), caretOffset.GetY() + caretHeight / 2);
7296 SetMagnifierOffsetWithAnimation(floatingCaretCenter);
7297 AutoScrollParam param = { .autoScrollEvent = AutoScrollEvent::CARET, .showScrollbar = true };
7298 AutoScrollByEdgeDetection(param, OffsetF(offset.GetX(), offset.GetY()), EdgeDetectionStrategy::OUT_BOUNDARY);
7299 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
7300 }
7301
7302 void RichEditorPattern::SetCaretTouchMoveOffset(const Offset& localOffset)
7303 {
7304 double moveDistance = 0.0;
7305 auto positionType = GetPositionTypeFromLine();
7306 auto caretBoundaryRect = GetCaretBoundaryRect();
7307 if (positionType == PositionType::DEFAULT) {
7308 floatingCaretState_.UpdateByTouchMove(localOffset, moveDistance, caretBoundaryRect);
7309 return;
7310 }
7311 auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
7312 bool isCaretAtEmptyParagraph =
7313 positionType == PositionType::PARAGRAPH_START && caretPosition_ == GetParagraphEndPosition(caretPosition_);
7314 if (isCaretAtEmptyParagraph) {
7315 moveDistance = std::abs(localOffset.GetX() - caretOffset.GetX());
7316 } else {
7317 moveDistance = (caretAffinityPolicy_ == CaretAffinityPolicy::DOWNSTREAM_FIRST || caretPosition_ == 0)
7318 ? caretOffset.GetX() - localOffset.GetX() : localOffset.GetX() - caretOffset.GetX();
7319 }
7320 floatingCaretState_.UpdateByTouchMove(localOffset, moveDistance, caretBoundaryRect);
7321 }
7322
7323 RectF RichEditorPattern::GetCaretBoundaryRect()
7324 {
7325 auto caretBoundaryRect = contentRect_;
7326 auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
7327 CHECK_NULL_RETURN(overlayModifier, caretBoundaryRect);
7328 auto caretWidth = overlayModifier->GetCaretWidth();
7329 caretBoundaryRect.SetWidth(caretBoundaryRect.Width() - caretWidth);
7330 return caretBoundaryRect;
7331 }
7332
7333 void RichEditorPattern::SetMagnifierLocalOffset(Offset offset)
7334 {
7335 CHECK_NULL_VOID(magnifierController_);
7336 auto localOffset = OffsetF{ offset.GetX(), offset.GetY() };
7337 auto localOffsetWithTrans = localOffset;
7338 selectOverlay_->GetLocalPointWithTransform(localOffsetWithTrans);
7339 magnifierController_->SetLocalOffset(localOffsetWithTrans, localOffset);
7340 }
7341
7342 void RichEditorPattern::SetMagnifierOffsetWithAnimation(Offset offset)
7343 {
7344 CHECK_NULL_VOID(magnifierController_);
7345 auto currentLocalOffset = magnifierController_->GetLocalOffset();
7346 auto currentOffset = magnifierController_->GetLocalOffsetWithoutTrans().value_or(currentLocalOffset);
7347 if (NearEqual(currentOffset.GetY(), offset.GetY(), 0.5f) || !magnifierController_->GetShowMagnifier()) {
7348 SetMagnifierLocalOffset(offset);
7349 return;
7350 }
7351 AnimationUtils::StopAnimation(magnifierAnimation_);
7352 AnimationOption option{ MAGNIFIER_ANIMATION_CURVE, MAGNIFIER_ANIMATION_DURATION };
7353 magnifierAnimation_ = AnimationUtils::StartAnimation(option, [weak = WeakClaim(this), offset]() {
7354 auto pattern = weak.Upgrade();
7355 CHECK_NULL_VOID(pattern);
7356 pattern->SetMagnifierLocalOffset(offset);
7357 });
7358 }
7359
7360 Offset RichEditorPattern::AdjustLocalOffsetOnMoveEvent(const Offset& originalOffset)
7361 {
7362 auto deltaOffset = GetPaintRectGlobalOffset() - globalOffsetOnMoveStart_;
7363 return { originalOffset.GetX() - deltaOffset.GetX(), originalOffset.GetY() - deltaOffset.GetY() };
7364 }
7365
7366 void RichEditorPattern::StartVibratorByIndexChange(int32_t currentIndex, int32_t preIndex)
7367 {
7368 CHECK_NULL_VOID(isEnableHapticFeedback_ && (currentIndex != preIndex));
7369 VibratorUtils::StartVibraFeedback("slide");
7370 }
7371
7372 bool RichEditorPattern::IsScrollBarPressed(const MouseInfo& info)
7373 {
7374 auto scrollBar = GetScrollBar();
7375 bool isScrollBarShow = scrollBar && scrollBar->NeedPaint();
7376 CHECK_NULL_RETURN(isScrollBarShow, false);
7377 Point point(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY());
7378 return scrollBar->InBarRectRegion(point);
7379 }
7380
7381 void RichEditorPattern::HandleMouseLeftButtonMove(const MouseInfo& info)
7382 {
7383 ACE_SCOPED_TRACE("RichEditorHandleMouseLeftButtonMove");
7384 CHECK_NULL_VOID(!IsPreviewTextInputting());
7385 if (blockPress_) {
7386 ACE_SCOPED_TRACE("RichEditorUpdateDragBoxes");
7387 dragBoxes_ = GetTextBoxes();
7388 return;
7389 }
7390 CHECK_NULL_VOID(leftMousePress_);
7391
7392 auto localOffset = info.GetLocalLocation();
7393 const auto& globalOffset = info.GetGlobalLocation();
7394 auto paintOffset = GetPaintRectGlobalOffset();
7395 if (!selectOverlay_->HasRenderTransform()) {
7396 localOffset = Offset(globalOffset.GetX() - paintOffset.GetX(), globalOffset.GetY() - paintOffset.GetY());
7397 }
7398 Offset textOffset = ConvertTouchOffsetToTextOffset(localOffset);
7399 if (dataDetectorAdapter_->pressedByLeftMouse_) {
7400 dataDetectorAdapter_->pressedByLeftMouse_ = false;
7401 MoveCaretAndStartFocus(textOffset);
7402 }
7403
7404 auto focusHub = GetFocusHub();
7405 CHECK_NULL_VOID(focusHub);
7406 CHECK_NULL_VOID(focusHub->IsCurrentFocus());
7407
7408 mouseStatus_ = MouseStatus::MOVE;
7409 HandleMouseSelect(localOffset);
7410 auto host = GetHost();
7411 CHECK_NULL_VOID(host);
7412 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
7413 }
7414
7415 void RichEditorPattern::HandleMouseSelect(const Offset& localOffset)
7416 {
7417 if (!textSelector_.IsValid()) {
7418 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "prevent mouse selecting because selection has been reset");
7419 return;
7420 }
7421 Offset textOffset = ConvertTouchOffsetToTextOffset(localOffset);
7422 auto position = GetTextContentLength() == 0 ? 0 : paragraphs_.GetIndex(textOffset);
7423 UpdateSelector(textSelector_.baseOffset, position);
7424 if (!isFirstMouseSelect_) {
7425 AdjustCursorPosition(position);
7426 SetCaretPosition(position);
7427 AutoScrollParam param = {
7428 .autoScrollEvent = AutoScrollEvent::MOUSE, .showScrollbar = true, .eventOffset = localOffset
7429 };
7430 AutoScrollByEdgeDetection(param, OffsetF(localOffset.GetX(), localOffset.GetY()),
7431 EdgeDetectionStrategy::OUT_BOUNDARY);
7432 showSelect_ = true;
7433 } else {
7434 isFirstMouseSelect_ = false;
7435 }
7436 if (textSelector_.SelectNothing()) {
7437 StartTwinkling();
7438 } else {
7439 StopTwinkling();
7440 }
7441 isMouseSelect_ = true;
7442 }
7443
7444 void RichEditorPattern::HandleMouseLeftButtonPress(const MouseInfo& info)
7445 {
7446 isMousePressed_ = true;
7447 auto frameNodeRange = GetSpanRangeByLocalOffset(info.GetLocalLocation());
7448 bool frameNodeSelected = textSelector_.ContainsRange(frameNodeRange);
7449 bool pressFrameNode = !frameNodeSelected && InRangeRect(info.GetGlobalLocation(), frameNodeRange);
7450 if (IsPreviewTextInputting() || IsScrollBarPressed(info) || BetweenSelectedPosition(info.GetGlobalLocation()) ||
7451 pressFrameNode) {
7452 blockPress_ = true;
7453 return;
7454 }
7455 auto focusHub = GetFocusHub();
7456 CHECK_NULL_VOID(focusHub);
7457 if (!focusHub->IsFocusable()) {
7458 return;
7459 }
7460 auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
7461 Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
7462 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
7463 int32_t position = (GetTextContentLength() == 0) ? 0 : paragraphs_.GetIndex(textOffset);
7464 if (shiftFlag_) {
7465 HandleShiftSelect(position);
7466 } else {
7467 IF_TRUE(!textSelector_.SelectNothing(), ResetSelection());
7468 textSelector_.Update(position);
7469 }
7470 leftMousePress_ = true;
7471 globalOffsetOnMoveStart_ = GetPaintRectGlobalOffset();
7472 mouseStatus_ = MouseStatus::PRESSED;
7473 blockPress_ = false;
7474 caretUpdateType_ = CaretUpdateType::PRESSED;
7475 dataDetectorAdapter_->pressedByLeftMouse_ = false;
7476 HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY()));
7477 if (dataDetectorAdapter_->pressedByLeftMouse_) {
7478 return;
7479 }
7480 UseHostToUpdateTextFieldManager();
7481 MoveCaretAndStartFocus(textOffset);
7482 CalcCaretInfoByClick(info.GetLocalLocation());
7483 }
7484
7485 void RichEditorPattern::HandleShiftSelect(int32_t position)
7486 {
7487 CHECK_NULL_VOID(shiftFlag_);
7488 int32_t start = textSelector_.SelectNothing() ? caretPosition_ : textSelector_.baseOffset;
7489 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleShiftSelect [%{public}d-%{public}d]", start, position);
7490 UpdateSelector(start, position);
7491 SetCaretPosition(position);
7492 FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
7493 auto host = GetHost();
7494 CHECK_NULL_VOID(host);
7495 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
7496 }
7497
7498 void RichEditorPattern::HandleMouseLeftButtonRelease(const MouseInfo& info)
7499 {
7500 blockPress_ = false;
7501 leftMousePress_ = false;
7502 CHECK_NULL_VOID(!IsPreviewTextInputting());
7503 auto oldMouseStatus = mouseStatus_;
7504 mouseStatus_ = MouseStatus::RELEASED;
7505 isMouseSelect_ = false;
7506 isFirstMouseSelect_ = true;
7507 if (!showSelect_) {
7508 showSelect_ = true;
7509 ResetSelection();
7510 }
7511 if (dataDetectorAdapter_->pressedByLeftMouse_ && oldMouseStatus != MouseStatus::MOVE && !IsDragging()) {
7512 dataDetectorAdapter_->ResponseBestMatchItem(dataDetectorAdapter_->clickedAISpan_);
7513 dataDetectorAdapter_->pressedByLeftMouse_ = false;
7514 isMousePressed_ = false;
7515 return;
7516 }
7517
7518 auto selectStart = std::min(textSelector_.baseOffset, textSelector_.destinationOffset);
7519 auto selectEnd = std::max(textSelector_.baseOffset, textSelector_.destinationOffset);
7520 if (selectStart != selectEnd && isMousePressed_ && oldMouseStatus == MouseStatus::MOVE) {
7521 FireOnSelect(selectStart, selectEnd);
7522 }
7523 StopAutoScroll();
7524 if (textSelector_.IsValid() && !textSelector_.StartEqualToDest() && IsSelectedBindSelectionMenu() &&
7525 oldMouseStatus == MouseStatus::MOVE) {
7526 auto offsetX = static_cast<float>(info.GetGlobalLocation().GetX());
7527 auto offsetY = static_cast<float>(info.GetGlobalLocation().GetY());
7528 selectionMenuOffsetByMouse_ = OffsetF(offsetX, offsetY);
7529 selectionMenuOffsetClick_ = OffsetF(offsetX, offsetY);
7530 ShowSelectOverlay(RectF(), RectF(), false, TextResponseType::SELECTED_BY_MOUSE);
7531 }
7532 isMousePressed_ = false;
7533 if (HasFocus()) {
7534 HandleOnEditChanged(true);
7535 }
7536 }
7537
7538 void RichEditorPattern::HandleMouseLeftButton(const MouseInfo& info)
7539 {
7540 if (info.GetAction() == MouseAction::MOVE) {
7541 HandleMouseLeftButtonMove(info);
7542 } else if (info.GetAction() == MouseAction::PRESS) {
7543 HandleMouseLeftButtonPress(info);
7544 } else if (info.GetAction() == MouseAction::RELEASE) {
7545 HandleMouseLeftButtonRelease(info);
7546 }
7547 }
7548
7549 void RichEditorPattern::HandleMouseRightButton(const MouseInfo& info)
7550 {
7551 CHECK_NULL_VOID(!IsPreviewTextInputting());
7552 auto focusHub = GetFocusHub();
7553 CHECK_NULL_VOID(focusHub);
7554 if (!focusHub->IsFocusable()) {
7555 return;
7556 }
7557 if (info.GetAction() == MouseAction::PRESS) {
7558 isMousePressed_ = true;
7559 usingMouseRightButton_ = true;
7560 CloseSelectOverlay();
7561 } else if (info.GetAction() == MouseAction::RELEASE) {
7562 auto offsetX = static_cast<float>(info.GetGlobalLocation().GetX());
7563 auto offsetY = static_cast<float>(info.GetGlobalLocation().GetY());
7564 selectionMenuOffsetByMouse_ = OffsetF(offsetX, offsetY);
7565 selectionMenuOffsetClick_ = OffsetF(offsetX, offsetY);
7566 selectOverlay_->SetIsSingleHandle(false);
7567 if (textSelector_.IsValid() && BetweenSelection(info.GetGlobalLocation())) {
7568 ShowSelectOverlay(RectF(), RectF(), IsSelectAll(), TextResponseType::RIGHT_CLICK);
7569 isMousePressed_ = false;
7570 usingMouseRightButton_ = false;
7571 return;
7572 }
7573 auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
7574 Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
7575 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
7576 HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY()));
7577 if (dataDetectorAdapter_->hasClickedAISpan_) {
7578 dataDetectorAdapter_->hasClickedAISpan_ = false;
7579 isMousePressed_ = false;
7580 usingMouseRightButton_ = false;
7581 return;
7582 }
7583 if (textSelector_.IsValid()) {
7584 CloseSelectOverlay();
7585 ResetSelection();
7586 }
7587 MouseRightFocus(info);
7588 ShowSelectOverlay(RectF(), RectF(), IsSelectAll(), TextResponseType::RIGHT_CLICK);
7589 isMousePressed_ = false;
7590 usingMouseRightButton_ = false;
7591 }
7592 }
7593
7594 std::pair<int32_t, int32_t> RichEditorPattern::GetSpanRangeByLocalOffset(Offset localOffset)
7595 {
7596 Offset textOffset = ConvertTouchOffsetToTextOffset(localOffset);
7597 auto [pos, affinity] = paragraphs_.GetGlyphPositionAtCoordinate(textOffset);
7598 auto spanFilter = [](SpanItemType itemType) {
7599 return itemType == SpanItemType::IMAGE
7600 || itemType == SpanItemType::PLACEHOLDER
7601 || itemType == SpanItemType::CustomSpan; };
7602 auto it = std::find_if(spans_.begin(), spans_.end(),
7603 [pos = static_cast<int32_t>(pos), affinity = affinity, spanFilter](const RefPtr<SpanItem>& spanItem) {
7604 CHECK_NULL_RETURN(spanFilter(spanItem->spanItemType), false);
7605 return pos == (affinity == TextAffinity::UPSTREAM ? spanItem->position : spanItem->rangeStart);
7606 });
7607 bool isMouseOnTarget = it != spans_.end();
7608 CHECK_NULL_RETURN(isMouseOnTarget, std::make_pair(-1, -1));
7609 return affinity == TextAffinity::UPSTREAM ? std::make_pair(pos - 1, pos) : std::make_pair(pos, pos + 1);
7610 }
7611
7612 void RichEditorPattern::MouseRightFocus(const MouseInfo& info)
7613 {
7614 auto host = GetHost();
7615 CHECK_NULL_VOID(host);
7616 auto focusHub = host->GetOrCreateFocusHub();
7617 CHECK_NULL_VOID(focusHub);
7618 focusHub->RequestFocusImmediately();
7619
7620 auto selectRange = GetSpanRangeByLocalOffset(info.GetLocalLocation());
7621 if (InRangeRect(info.GetGlobalLocation(), selectRange)) {
7622 selectedType_ = TextSpanType::IMAGE;
7623 textSelector_.Update(selectRange.first, selectRange.second);
7624 FireOnSelect(selectRange.first, selectRange.second);
7625 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
7626 return;
7627 }
7628 if (textSelector_.IsValid()) {
7629 ResetSelection();
7630 }
7631 Offset textOffset = ConvertTouchOffsetToTextOffset(info.GetLocalLocation());
7632 auto position = paragraphs_.GetIndex(textOffset);
7633 SetCaretPosition(position);
7634 CalcAndRecordLastClickCaretInfo(textOffset);
7635 auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
7636 selectedType_ = TextSpanType::TEXT;
7637 CHECK_NULL_VOID(overlayMod_);
7638 DynamicCast<RichEditorOverlayModifier>(overlayMod_)->SetCaretOffsetAndHeight(caretOffset, caretHeight);
7639 StartTwinkling();
7640 }
7641
7642 void RichEditorPattern::FireOnSelect(int32_t selectStart, int32_t selectEnd)
7643 {
7644 auto eventHub = GetEventHub<RichEditorEventHub>();
7645 CHECK_NULL_VOID(eventHub);
7646 auto textSelectInfo = GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT);
7647 if (!textSelectInfo.GetSelection().resultObjects.empty()) {
7648 eventHub->FireOnSelect(&textSelectInfo);
7649 }
7650 UpdateSelectionType(textSelectInfo);
7651 }
7652
7653 void RichEditorPattern::UpdateSelectionType(const SelectionInfo& textSelectInfo)
7654 {
7655 if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
7656 TextPattern::UpdateSelectionType(GetAdjustedSelectionInfo(textSelectInfo));
7657 } else {
7658 TextPattern::UpdateSelectionType(textSelectInfo);
7659 }
7660 }
7661
7662 SelectionInfo RichEditorPattern::GetAdjustedSelectionInfo(const SelectionInfo& textSelectInfo)
7663 {
7664 auto selection = textSelectInfo.GetSelection();
7665 auto resultObjects = selection.resultObjects;
7666 std::for_each(resultObjects.begin(), resultObjects.end(), [](ResultObject& object) {
7667 if (object.type == SelectSpanType::TYPEIMAGE && object.valueString == u" " && object.valuePixelMap == nullptr) {
7668 object.type = SelectSpanType::TYPEBUILDERSPAN;
7669 }
7670 });
7671 SelectionInfo adjustedInfo;
7672 adjustedInfo.SetSelectionStart(selection.selection[RichEditorSpanRange::RANGESTART]);
7673 adjustedInfo.SetSelectionEnd(selection.selection[RichEditorSpanRange::RANGEEND]);
7674 adjustedInfo.SetResultObjectList(resultObjects);
7675 return adjustedInfo;
7676 }
7677
7678 void RichEditorPattern::HandleMouseEvent(const MouseInfo& info)
7679 {
7680 HandleImageHoverEvent(info);
7681 if (selectOverlay_->IsHandleShow() && info.GetAction() == MouseAction::PRESS) {
7682 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Close selectOverlay when handle is showing");
7683 CloseSelectOverlay();
7684 }
7685 auto scrollBar = GetScrollBar();
7686 if (scrollBar && (scrollBar->IsHover() || scrollBar->IsPressed())) {
7687 ChangeMouseStyle(MouseFormat::DEFAULT);
7688 HandleUrlSpanForegroundClear();
7689 return;
7690 }
7691
7692 if (hasUrlSpan_) {
7693 auto show = HandleUrlSpanShowShadow(info.GetLocalLocation(), info.GetGlobalLocation(), GetUrlHoverColor());
7694 if (show) {
7695 ChangeMouseStyle(MouseFormat::HAND_POINTING);
7696 } else {
7697 ChangeMouseStyle(MouseFormat::TEXT_CURSOR);
7698 }
7699 }
7700
7701 if (currentMouseStyle_ == MouseFormat::DEFAULT && !IsDragging()) {
7702 ChangeMouseStyle(MouseFormat::TEXT_CURSOR);
7703 }
7704
7705 caretUpdateType_ = CaretUpdateType::NONE;
7706 if (info.GetButton() == MouseButton::LEFT_BUTTON) {
7707 sourceType_ = info.GetSourceDevice();
7708 HandleMouseLeftButton(info);
7709 } else if (info.GetButton() == MouseButton::RIGHT_BUTTON) {
7710 sourceType_ = info.GetSourceDevice();
7711 HandleMouseRightButton(info);
7712 }
7713 }
7714
7715 Color RichEditorPattern::GetUrlHoverColor()
7716 {
7717 auto theme = GetTheme<RichEditorTheme>();
7718 CHECK_NULL_RETURN(theme, Color());
7719 return theme->GetUrlHoverColor();
7720 }
7721
7722 Color RichEditorPattern::GetUrlPressColor()
7723 {
7724 auto theme = GetTheme<RichEditorTheme>();
7725 CHECK_NULL_RETURN(theme, Color());
7726 return theme->GetUrlPressColor();
7727 }
7728
7729 Color RichEditorPattern::GetUrlSpanColor()
7730 {
7731 auto theme = GetTheme<RichEditorTheme>();
7732 CHECK_NULL_RETURN(theme, Color());
7733 return theme->GetUrlDefaultColor();
7734 }
7735
7736 void RichEditorPattern::TriggerAvoidOnCaretChange()
7737 {
7738 CHECK_NULL_VOID(HasFocus());
7739 auto host = GetHost();
7740 CHECK_NULL_VOID(host);
7741 auto pipeline = host->GetContext();
7742 CHECK_NULL_VOID(pipeline);
7743 auto textFieldManager = DynamicCast<TextFieldManagerNG>(pipeline->GetTextFieldManager());
7744 CHECK_NULL_VOID(textFieldManager);
7745 CHECK_NULL_VOID(pipeline->UsingCaretAvoidMode());
7746 auto safeAreaManager = pipeline->GetSafeAreaManager();
7747 if (!safeAreaManager || NearZero(safeAreaManager->GetKeyboardInset().Length(), 0)) {
7748 return;
7749 }
7750 textFieldManager->SetHeight(GetCaretRect().Height());
7751 auto taskExecutor = pipeline->GetTaskExecutor();
7752 CHECK_NULL_VOID(taskExecutor);
7753 taskExecutor->PostTask([manager = WeakPtr<TextFieldManagerNG>(textFieldManager)] {
7754 auto textFieldManager = manager.Upgrade();
7755 CHECK_NULL_VOID(textFieldManager);
7756 textFieldManager->TriggerAvoidOnCaretChange();
7757 }, TaskExecutor::TaskType::UI, "ArkUIRichEditorTriggerAvoidOnCaretChange", PriorityType::VIP);
7758 }
7759
7760 void RichEditorPattern::OnWindowSizeChanged(int32_t width, int32_t height, WindowSizeChangeReason type)
7761 {
7762 selectOverlay_->UpdateMenuOnWindowSizeChanged(type);
7763 CHECK_NULL_VOID(type == WindowSizeChangeReason::ROTATION);
7764 auto host = GetHost();
7765 CHECK_NULL_VOID(host);
7766 auto context = host->GetContextRefPtr();
7767 CHECK_NULL_VOID(context);
7768 auto taskExecutor = context->GetTaskExecutor();
7769 CHECK_NULL_VOID(taskExecutor);
7770 auto textFieldManager = DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
7771 CHECK_NULL_VOID(textFieldManager);
7772 textFieldManager->ResetOptionalClickPosition();
7773 taskExecutor->PostTask(
7774 [weak = WeakClaim(this), manager = WeakPtr<TextFieldManagerNG>(textFieldManager)] {
7775 auto pattern = weak.Upgrade();
7776 CHECK_NULL_VOID(pattern);
7777 pattern->UpdateParentOffsetAndOverlay();
7778 pattern->UpdateModifierCaretOffsetAndHeight();
7779 pattern->UpdateTextFieldManager(Offset(pattern->parentGlobalOffset_.GetX(),
7780 pattern->parentGlobalOffset_.GetY()), pattern->frameRect_.Height());
7781 pattern->UpdateCaretInfoToController();
7782 if (pattern->HasFocus()) {
7783 auto textFieldManager = manager.Upgrade();
7784 CHECK_NULL_VOID(textFieldManager);
7785 auto container = Container::Current();
7786 CHECK_NULL_VOID(container);
7787 auto displayInfo = container->GetDisplayInfo();
7788 if (displayInfo) {
7789 auto dmRotation = static_cast<int32_t>(displayInfo->GetRotation());
7790 textFieldManager->SetFocusFieldOrientation(dmRotation);
7791 textFieldManager->SetFocusFieldAlreadyTriggerWsCallback(true);
7792 }
7793 }
7794 },
7795 TaskExecutor::TaskType::UI, "ArkUIRichEditorOnWindowSizeChangedRotation", PriorityType::VIP);
7796 }
7797
7798 void RichEditorPattern::CopySelectionMenuParams(SelectOverlayInfo& selectInfo, TextResponseType responseType)
7799 {
7800 auto selectType = selectedType_.value_or(TextSpanType::NONE);
7801 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "textSpanType=%{public}d, responseType=%{public}d", selectType, responseType);
7802 std::shared_ptr<SelectionMenuParams> menuParams = GetMenuParams(selectType, responseType);
7803 CHECK_NULL_VOID(menuParams);
7804
7805 // long pressing on the image needs to set the position of the pop-up menu following the long pressing position
7806 if (selectType == TextSpanType::IMAGE && !selectInfo.isUsingMouse) {
7807 selectInfo.menuInfo.menuOffset = OffsetF(selectionMenuOffset_.GetX(), selectionMenuOffset_.GetY());
7808 }
7809
7810 CopyBindSelectionMenuParams(selectInfo, menuParams);
7811 }
7812
7813 void RichEditorPattern::ShowSelectOverlay(const RectF& firstHandle, const RectF& secondHandle, bool isCopyAll,
7814 TextResponseType responseType, bool handleReverse)
7815 {
7816 CHECK_NULL_VOID(!IsPreviewTextInputting());
7817 textResponseType_ = responseType;
7818 selectOverlay_->ProcessOverlay({.animation = true});
7819 }
7820
7821 void RichEditorPattern::SetIsEnableSubWindowMenu()
7822 {
7823 selectOverlay_->SetIsHostNodeEnableSubWindowMenu(true);
7824 }
7825
7826 void RichEditorPattern::CheckEditorTypeChange()
7827 {
7828 CHECK_NULL_VOID(selectOverlayProxy_);
7829 CHECK_NULL_VOID(!selectOverlayProxy_->IsClosed());
7830 if (selectOverlayProxy_->GetSelectOverlayMangerInfo().menuInfo.editorType.value_or(static_cast<int32_t>(
7831 TextSpanType::NONE)) != static_cast<int32_t>(selectedType_.value_or(TextSpanType::NONE))) {
7832 CloseSelectOverlay();
7833 }
7834 }
7835
7836 void RichEditorPattern::OnCopyOperationExt(RefPtr<PasteDataMix>& pasteData)
7837 {
7838 auto subSpanString =
7839 ToStyledString(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
7840 std::vector<uint8_t> tlvData;
7841 subSpanString->EncodeTlv(tlvData);
7842 auto text = subSpanString->GetString();
7843 clipboard_->AddSpanStringRecord(pasteData, tlvData);
7844 }
7845
7846 void RichEditorPattern::HandleOnCopyStyledString()
7847 {
7848 RefPtr<PasteDataMix> pasteData = clipboard_->CreatePasteDataMix();
7849 auto subSpanString = styledString_->GetSubSpanString(textSelector_.GetTextStart(),
7850 textSelector_.GetTextEnd() - textSelector_.GetTextStart());
7851 #ifdef PREVIEW
7852 clipboard_->SetData(subSpanString->GetString(), CopyOptions::Distributed);
7853 #else
7854 std::vector<uint8_t> tlvData;
7855 subSpanString->EncodeTlv(tlvData);
7856 clipboard_->AddSpanStringRecord(pasteData, tlvData);
7857 clipboard_->AddTextRecord(pasteData, subSpanString->GetString());
7858 clipboard_->SetData(pasteData, copyOption_);
7859 #endif
7860 }
7861
7862 void RichEditorPattern::OnCopyOperation(bool isUsingExternalKeyboard)
7863 {
7864 if (isSpanStringMode_) {
7865 HandleOnCopyStyledString();
7866 return;
7867 }
7868 RefPtr<PasteDataMix> pasteData = clipboard_->CreatePasteDataMix();
7869 auto selectStart = textSelector_.GetTextStart();
7870 auto selectEnd = textSelector_.GetTextEnd();
7871 auto textSelectInfo = GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT);
7872 auto copyResultObjects = textSelectInfo.GetSelection().resultObjects;
7873 caretUpdateType_ = CaretUpdateType::NONE;
7874 if (copyResultObjects.empty()) {
7875 return;
7876 }
7877 for (auto resultObj = copyResultObjects.rbegin(); resultObj != copyResultObjects.rend(); ++resultObj) {
7878 ProcessResultObject(pasteData, *resultObj);
7879 }
7880 clipboard_->SetData(pasteData, copyOption_);
7881 }
7882
7883 void RichEditorPattern::ProcessResultObject(RefPtr<PasteDataMix> pasteData, const ResultObject& result)
7884 {
7885 CHECK_NULL_VOID(pasteData);
7886 auto multiTypeRecordImpl = AceType::MakeRefPtr<MultiTypeRecordImpl>();
7887 if (result.type == SelectSpanType::TYPESPAN) {
7888 auto data = UtfUtils::Str16ToStr8(GetSelectedSpanText(result.valueString,
7889 result.offsetInSpan[RichEditorSpanRange::RANGESTART], result.offsetInSpan[RichEditorSpanRange::RANGEEND]));
7890 #ifdef PREVIEW
7891 clipboard_->SetData(data, CopyOptions::Distributed);
7892 #else
7893 multiTypeRecordImpl->SetPlainText(data);
7894 EncodeTlvDataByResultObject(result, multiTypeRecordImpl->GetSpanStringBuffer());
7895 clipboard_->AddMultiTypeRecord(pasteData, multiTypeRecordImpl);
7896 #endif
7897 return;
7898 }
7899 if (result.type == SelectSpanType::TYPEIMAGE) {
7900 #ifdef PREVIEW
7901 if (result.valuePixelMap) {
7902 clipboard_->AddPixelMapRecord(pasteData, result.valuePixelMap);
7903 } else {
7904 clipboard_->AddImageRecord(pasteData, UtfUtils::Str16ToStr8(result.valueString));
7905 }
7906 #else
7907 if (result.valuePixelMap) {
7908 multiTypeRecordImpl->SetPixelMap(result.valuePixelMap);
7909 } else {
7910 multiTypeRecordImpl->SetUri(UtfUtils::Str16ToStr8(result.valueString));
7911 }
7912 EncodeTlvDataByResultObject(result, multiTypeRecordImpl->GetSpanStringBuffer());
7913 clipboard_->AddMultiTypeRecord(pasteData, multiTypeRecordImpl);
7914 #endif
7915 }
7916 }
7917
7918 void RichEditorPattern::EncodeTlvDataByResultObject(const ResultObject& result, std::vector<uint8_t>& tlvData)
7919 {
7920 auto selectStart = result.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] + result.offsetInSpan[RichEditorSpanRange::RANGESTART];
7921 auto selectEnd = result.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] + result.offsetInSpan[RichEditorSpanRange::RANGEEND];
7922 auto spanString = ToStyledString(selectStart, selectEnd);
7923 spanString->EncodeTlv(tlvData);
7924 }
7925
7926 void RichEditorPattern::HandleOnCopy(bool isUsingExternalKeyboard)
7927 {
7928 CHECK_NULL_VOID(clipboard_);
7929 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "isUsingExternalKeyboard=%{public}d, copyOption=%{public}d",
7930 isUsingExternalKeyboard, copyOption_);
7931 if (copyOption_ == CopyOptions::None) {
7932 return;
7933 }
7934 auto eventHub = GetEventHub<RichEditorEventHub>();
7935 CHECK_NULL_VOID(eventHub);
7936 TextCommonEvent event;
7937 eventHub->FireOnCopy(event);
7938 IF_TRUE(!event.IsPreventDefault(), OnCopyOperation(isUsingExternalKeyboard));
7939 if (selectOverlay_->IsUsingMouse() || isUsingExternalKeyboard) {
7940 CloseSelectOverlay();
7941 } else {
7942 selectOverlay_->HideMenu();
7943 }
7944 }
7945
7946 void RichEditorPattern::ResetAfterPaste()
7947 {
7948 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "ResetAfterPaste");
7949 auto pasteStr = GetPasteStr();
7950 SetCaretSpanIndex(-1);
7951 StartTwinkling();
7952 RequestKeyboardToEdit();
7953 CloseSelectOverlay();
7954 InsertValueByPaste(pasteStr);
7955 ClearPasteStr();
7956 }
7957
7958 void RichEditorPattern::InsertValueByPaste(const std::u16string& pasteStr)
7959 {
7960 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "InsertValueByPaste");
7961 if (isSpanStringMode_) {
7962 InsertValueInStyledString(pasteStr);
7963 return;
7964 }
7965 InsertValueByOperationType(pasteStr, OperationType::DEFAULT);
7966 }
7967
7968 void RichEditorPattern::HandleOnPaste()
7969 {
7970 if (IsPreviewTextInputting()) {
7971 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Paste blocked during preview text input");
7972 return;
7973 }
7974 auto eventHub = GetEventHub<RichEditorEventHub>();
7975 CHECK_NULL_VOID(eventHub);
7976 TextCommonEvent event;
7977 eventHub->FireOnPaste(event);
7978 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleOnPaste, preventDefault=%{public}d", event.IsPreventDefault());
7979 if (event.IsPreventDefault()) {
7980 CloseSelectOverlay();
7981 ResetSelection();
7982 StartTwinkling();
7983 RequestKeyboardToEdit();
7984 return;
7985 }
7986 CHECK_NULL_VOID(clipboard_);
7987 #ifdef PREVIEW
7988 auto pasteCallback = [weak = WeakClaim(this)](const std::string& text) {
7989 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "pasteCallback callback in previewer");
7990 auto richEditor = weak.Upgrade();
7991 CHECK_NULL_VOID(richEditor);
7992 richEditor->PasteStr(text);
7993 };
7994 clipboard_->GetData(pasteCallback);
7995 #else
7996 auto isSpanStringMode = isSpanStringMode_;
7997 auto pasteCallback = [weak = WeakClaim(this), isSpanStringMode](std::vector<std::vector<uint8_t>>& arrs,
7998 const std::string& text, bool& isMulitiTypeRecord) {
7999 TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
8000 "pasteCallback callback, isMulitiTypeRecord : [%{public}d], isSpanStringMode : [%{public}d]",
8001 isMulitiTypeRecord, isSpanStringMode);
8002 auto richEditor = weak.Upgrade();
8003 CHECK_NULL_VOID(richEditor);
8004 std::list<RefPtr<SpanString>> spanStrings;
8005 for (auto arr : arrs) {
8006 spanStrings.push_back(SpanString::DecodeTlv(arr));
8007 }
8008 if (!spanStrings.empty() && !isMulitiTypeRecord) {
8009 for (auto spanString : spanStrings) {
8010 richEditor->AddSpanByPasteData(spanString);
8011 richEditor->RequestKeyboardToEdit();
8012 }
8013 return;
8014 }
8015 richEditor->PasteStr(text);
8016 };
8017 clipboard_->GetSpanStringData(pasteCallback);
8018 #endif
8019 }
8020
8021 void RichEditorPattern::PasteStr(const std::string& text)
8022 {
8023 if (text.empty()) {
8024 ResetSelection();
8025 StartTwinkling();
8026 CloseSelectOverlay();
8027 RequestKeyboardToEdit();
8028 return;
8029 }
8030 AddPasteStr(text);
8031 ResetAfterPaste();
8032 }
8033
8034 void RichEditorPattern::SetCaretSpanIndex(int32_t index)
8035 {
8036 caretSpanIndex_ = index;
8037 }
8038
8039 void RichEditorPattern::HandleOnCut()
8040 {
8041 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "copyOption=%{public}d, textSelector_.IsValid()=%{public}d",
8042 copyOption_, textSelector_.IsValid());
8043 if (copyOption_ == CopyOptions::None) {
8044 return;
8045 }
8046 if (!textSelector_.IsValid()) {
8047 return;
8048 }
8049 auto eventHub = GetEventHub<RichEditorEventHub>();
8050 CHECK_NULL_VOID(eventHub);
8051 TextCommonEvent event;
8052 eventHub->FireOnCut(event);
8053 if (event.IsPreventDefault()) {
8054 CloseSelectOverlay();
8055 ResetSelection();
8056 StartTwinkling();
8057 RequestKeyboardToEdit();
8058 return;
8059 }
8060
8061 caretUpdateType_ = CaretUpdateType::NONE;
8062 OnCopyOperation();
8063 DeleteBackward(1);
8064 }
8065
8066 void RichEditorPattern::HandleOnShare()
8067 {
8068 auto eventHub = GetEventHub<RichEditorEventHub>();
8069 CHECK_NULL_VOID(eventHub);
8070 TextCommonEvent event;
8071 eventHub->FireOnShare(event);
8072 selectOverlay_->HideMenu(true);
8073 auto value = selectOverlay_->GetSelectedText();
8074 auto shareWord = std::regex_replace(value, REMOVE_SPACE_CHARS, "");
8075 CHECK_NULL_VOID(!shareWord.empty());
8076 auto pipeline = GetContext();
8077 CHECK_NULL_VOID(pipeline);
8078 auto containerId = pipeline->GetInstanceId();
8079 auto contentRect = selectOverlay_->GetSelectArea();
8080 TextShareAdapter::StartTextShareTask(containerId, contentRect, shareWord);
8081 }
8082
8083 std::function<void(Offset)> RichEditorPattern::GetThumbnailCallback()
8084 {
8085 return [wk = WeakClaim(this)](const Offset& point) {
8086 auto pattern = wk.Upgrade();
8087 CHECK_NULL_VOID(pattern);
8088 if (!pattern->BetweenSelectedPosition(point)) {
8089 return;
8090 }
8091 auto isContentDraggable = pattern->JudgeContentDraggable();
8092 if (!isContentDraggable) {
8093 TAG_LOGE(AceLogTag::ACE_RICH_TEXT, "GetThumbnailCallback call, draggable is false");
8094 pattern->SetIsTextDraggable(false);
8095 return;
8096 }
8097 if (pattern->dragBoxes_.empty()) {
8098 pattern->dragBoxes_ = pattern->GetTextBoxes();
8099 }
8100 pattern->CreateDragNode();
8101 };
8102 }
8103
8104 void RichEditorPattern::CreateDragNode()
8105 {
8106 auto host = GetHost();
8107 CHECK_NULL_VOID(host);
8108 auto children = host->GetChildren();
8109 std::list<RefPtr<FrameNode>> imageChildren;
8110 for (const auto& child : children) {
8111 auto node = DynamicCast<FrameNode>(child);
8112 CHECK_NULL_CONTINUE(node);
8113 if (auto& tag = node->GetTag(); tag == V2::IMAGE_ETS_TAG || tag == V2::PLACEHOLDER_SPAN_ETS_TAG) {
8114 imageChildren.emplace_back(node);
8115 }
8116 }
8117 TextDragInfo info;
8118 info.maxSelectedWidth = GetMaxSelectedWidth();
8119 info.handleColor = GetCaretColor();
8120 info.selectedBackgroundColor = GetSelectedBackgroundColor();
8121 auto selectOverlayInfo = selectOverlay_->GetSelectOverlayInfo();
8122 if (selectOverlayInfo.has_value()) {
8123 if (selectOverlayInfo->firstHandle.isShow) {
8124 info.firstHandle = selectOverlayInfo->firstHandle.paintRect;
8125 }
8126 if (selectOverlayInfo->secondHandle.isShow) {
8127 info.secondHandle = selectOverlayInfo->secondHandle.paintRect;
8128 }
8129 }
8130 if (textSelector_.GetTextEnd() - textSelector_.GetTextStart() == 1) {
8131 auto spanItem = GetSpanItemByPosition(textSelector_.GetTextStart());
8132 auto placeholderSpanItem = DynamicCast<PlaceholderSpanItem>(spanItem);
8133 if (placeholderSpanItem) {
8134 info.dragBackgroundColor = placeholderSpanItem->dragBackgroundColor_;
8135 info.isDragShadowNeeded = placeholderSpanItem->isDragShadowNeeded_;
8136 }
8137 }
8138 dragNode_ = RichEditorDragPattern::CreateDragNode(host, imageChildren, info);
8139 CHECK_NULL_VOID(dragNode_);
8140 InitDragShadow(host, dragNode_, info.isDragShadowNeeded, info.dragBackgroundColor.has_value());
8141 FrameNode::ProcessOffscreenNode(dragNode_);
8142 }
8143
8144 float RichEditorPattern::GetMaxSelectedWidth()
8145 {
8146 auto boxes = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
8147 CHECK_NULL_RETURN(!boxes.empty(), 0.0f);
8148 float startX = boxes.front().Left();
8149 float endX = boxes.front().Right();
8150 for (const auto& box : boxes) {
8151 startX = std::min(startX, box.Left());
8152 endX = std::max(endX, box.Right());
8153 }
8154 startX = std::min(0.0f, startX);
8155 return std::abs(startX - endX);
8156 }
8157
8158 void RichEditorPattern::InitDragShadow(const RefPtr<FrameNode>& host, const RefPtr<FrameNode>& dragNode,
8159 bool isDragShadowNeeded, bool hasDragBackgroundColor)
8160 {
8161 CHECK_NULL_VOID(host && dragNode);
8162 auto textDragPattern = dragNode->GetPattern<TextDragPattern>();
8163 CHECK_NULL_VOID(textDragPattern);
8164 auto option = host->GetDragPreviewOption();
8165 if (isDragShadowNeeded) {
8166 option.options.shadowPath = textDragPattern->GetBackgroundPath()->ConvertToSVGString();
8167 option.options.shadow = Shadow(RICH_DEFAULT_ELEVATION, {0.0, 0.0}, Color(RICH_DEFAULT_SHADOW_COLOR),
8168 ShadowStyle::OuterFloatingSM);
8169 option.options.isFilled = !hasDragBackgroundColor;
8170 } else {
8171 option.options.shadowPath.clear();
8172 option.options.shadow.reset();
8173 option.options.isFilled = true;
8174 }
8175 host->SetDragPreviewOptions(option);
8176 }
8177
8178 void RichEditorPattern::CreateHandles()
8179 {
8180 if (IsDragging()) {
8181 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "do not show handles when dragging");
8182 return;
8183 }
8184 auto host = GetHost();
8185 CHECK_NULL_VOID(host);
8186 CalculateHandleOffsetAndShowOverlay();
8187 selectOverlay_->ProcessOverlay({ .menuIsShow = selectOverlay_->IsCurrentMenuVisibile(), .animation = true });
8188 }
8189
8190 void RichEditorPattern::ShowHandles(const bool isNeedShowHandles)
8191 {
8192 if (!IsSelected()) {
8193 showSelect_ = true;
8194 IF_TRUE(isEditing_, StartTwinkling());
8195 return;
8196 }
8197 ShowHandles();
8198 }
8199
8200 void RichEditorPattern::ShowHandles()
8201 {
8202 auto host = GetHost();
8203 CHECK_NULL_VOID(host);
8204 if (!selectOverlay_->IsBothHandlesShow() && !selectOverlay_->SelectOverlayIsCreating()) {
8205 showSelect_ = true;
8206 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
8207 CHECK_NULL_VOID(textSelector_.IsValid());
8208 CHECK_NULL_VOID(!isMouseSelect_);
8209 CalculateHandleOffsetAndShowOverlay();
8210 selectOverlay_->ProcessOverlay({.menuIsShow = false, .animation = false});
8211 }
8212 }
8213
8214 void RichEditorPattern::OnAreaChangedInner()
8215 {
8216 auto host = GetHost();
8217 CHECK_NULL_VOID(host);
8218 auto context = host->GetContext();
8219 CHECK_NULL_VOID(context);
8220 auto prevParentGlobalOffset = parentGlobalOffset_;
8221 UpdateParentOffsetAndOverlay();
8222 IF_TRUE(parentGlobalOffset_ != prevParentGlobalOffset,
8223 UpdateTextFieldManager(Offset(parentGlobalOffset_.GetX(), parentGlobalOffset_.GetY()), frameRect_.Height()));
8224 IF_TRUE(parentGlobalOffset_ != prevParentGlobalOffset, UpdateCaretInfoToController());
8225 }
8226
8227 void RichEditorPattern::UpdateParentOffsetAndOverlay()
8228 {
8229 auto parentGlobalOffset = GetPaintRectGlobalOffset(); // offset on screen(with transformation)
8230 CHECK_NULL_VOID(parentGlobalOffset != parentGlobalOffset_);
8231 parentGlobalOffset_ = parentGlobalOffset;
8232 OnParentOffsetChange();
8233 selectOverlay_->UpdateSelectOverlayOnAreaChanged();
8234 }
8235
8236
8237 void RichEditorPattern::OnParentOffsetChange()
8238 {
8239 auto context = GetContext();
8240 CHECK_NULL_VOID(context);
8241 auto overlayManager = context->GetOverlayManager();
8242 IF_PRESENT(overlayManager, CloseAIEntityMenu(frameId_));
8243 }
8244
8245 void RichEditorPattern::CloseSelectionMenu()
8246 {
8247 // used by sdk
8248 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "CloseSelectionMenu");
8249 CloseSelectOverlay();
8250 }
8251
8252 void RichEditorPattern::OnDragNodeFloating()
8253 {
8254 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "OnDragNodeFloating");
8255 status_ = Status::FLOATING;
8256 }
8257
8258 void RichEditorPattern::CloseSelectOverlay()
8259 {
8260 // used by inner
8261 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "CloseSelectOverlay");
8262 selectOverlay_->CloseOverlay(true, CloseReason::CLOSE_REASON_NORMAL);
8263 }
8264
8265 void RichEditorPattern::CloseHandleAndSelect()
8266 {
8267 selectOverlay_->CloseOverlay(false, CloseReason::CLOSE_REASON_DRAG_FLOATING);
8268 showSelect_ = false;
8269 auto host = GetHost();
8270 IF_PRESENT(host, MarkDirtyNode(PROPERTY_UPDATE_RENDER));
8271 }
8272
8273 void RichEditorPattern::CalculateHandleOffsetAndShowOverlay(bool isUsingMouse)
8274 {
8275 auto globalOffset = GetGlobalOffset();
8276 if (!selectOverlay_->GetIsHandleMoving()) {
8277 textSelector_.ReverseTextSelector();
8278 }
8279 int32_t baseOffset = std::min(textSelector_.baseOffset, GetTextContentLength());
8280 int32_t destinationOffset = std::min(textSelector_.destinationOffset, GetTextContentLength());
8281 SizeF firstHandlePaintSize;
8282 SizeF secondHandlePaintSize;
8283 OffsetF firstHandleOffset;
8284 OffsetF secondHandleOffset;
8285 auto isSingleHandle = IsSingleHandle();
8286 selectOverlay_->SetIsSingleHandle(isSingleHandle);
8287 if (isSingleHandle) {
8288 auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
8289 // only show the second handle.
8290 secondHandlePaintSize = { SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), caretHeight };
8291 secondHandleOffset = caretOffset + globalOffset;
8292 } else {
8293 float startSelectHeight = 0.0f;
8294 float endSelectHeight = 0.0f;
8295 auto startOffset = CalcCursorOffsetByPosition(baseOffset, startSelectHeight, true, false);
8296 auto endOffset = CalcCursorOffsetByPosition(destinationOffset, endSelectHeight, false, false);
8297 firstHandlePaintSize = { SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), startSelectHeight };
8298 secondHandlePaintSize = { SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), endSelectHeight };
8299 firstHandleOffset = startOffset + globalOffset;
8300 secondHandleOffset = endOffset + globalOffset;
8301 firstHandleOffset.SetX(firstHandleOffset.GetX() - firstHandlePaintSize.Width() / 2.0f);
8302 secondHandleOffset.SetX(secondHandleOffset.GetX() - secondHandlePaintSize.Width() / 2.0f);
8303 }
8304 textSelector_.selectionBaseOffset = firstHandleOffset;
8305 textSelector_.selectionDestinationOffset = secondHandleOffset;
8306 textSelector_.firstHandle = RectF{ firstHandleOffset, firstHandlePaintSize };
8307 textSelector_.secondHandle = RectF{ secondHandleOffset, secondHandlePaintSize };
8308 }
8309
8310 void RichEditorPattern::CalculateDefaultHandleHeight(float& height)
8311 {
8312 #ifdef ENABLE_ROSEN_BACKEND
8313 MeasureContext content;
8314 content.textContent = "a";
8315 content.fontSize = TEXT_DEFAULT_FONT_SIZE;
8316 auto fontweight = StringUtils::FontWeightToString(FontWeight::NORMAL);
8317 content.fontWeight = fontweight;
8318 height = std::max(static_cast<float>(RosenRenderCustomPaint::MeasureTextSizeInner(content).Height()), 0.0f);
8319 #endif
8320 }
8321
8322 OffsetF RichEditorPattern::GetGlobalOffset() const
8323 {
8324 auto host = GetHost();
8325 CHECK_NULL_RETURN(host, OffsetF());
8326 auto pipeline = host->GetContext();
8327 CHECK_NULL_RETURN(pipeline, OffsetF());
8328 auto rootOffset = pipeline->GetRootRect().GetOffset();
8329 auto richEditorPaintOffset = host->GetPaintRectOffsetNG(false, true);
8330 if (selectOverlay_->HasRenderTransform()) {
8331 richEditorPaintOffset = selectOverlay_->GetPaintOffsetWithoutTransform();
8332 }
8333 return richEditorPaintOffset - rootOffset;
8334 }
8335
8336 bool RichEditorPattern::IsSingleHandle()
8337 {
8338 CHECK_NULL_RETURN(!selectOverlay_->GetIsHandleMoving(), selectOverlay_->IsSingleHandle());
8339 return GetTextContentLength() == 0 || !IsSelected();
8340 }
8341
8342 bool RichEditorPattern::IsHandlesShow()
8343 {
8344 return selectOverlay_->IsBothHandlesShow();
8345 }
8346
8347 void RichEditorPattern::ResetSelection()
8348 {
8349 bool selectNothing = textSelector_.SelectNothing();
8350 textSelector_.Update(-1, -1);
8351 if (!selectNothing) {
8352 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "ResetSelection");
8353 auto host = GetHost();
8354 CHECK_NULL_VOID(host);
8355 auto eventHub = host->GetEventHub<RichEditorEventHub>();
8356 CHECK_NULL_VOID(eventHub);
8357 auto textSelectInfo = GetSpansInfo(-1, -1, GetSpansMethod::ONSELECT);
8358 eventHub->FireOnSelect(&textSelectInfo);
8359 UpdateSelectionType(textSelectInfo);
8360 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
8361 }
8362 }
8363
8364 bool RichEditorPattern::BetweenSelection(const Offset& globalOffset)
8365 {
8366 return InRangeRect(globalOffset, { textSelector_.GetTextStart(), textSelector_.GetTextEnd() });
8367 }
8368
8369 bool RichEditorPattern::InRangeRect(const Offset& globalOffset, const std::pair<int32_t, int32_t>& range)
8370 {
8371 auto host = GetHost();
8372 CHECK_NULL_RETURN(host, false);
8373 CHECK_NULL_RETURN(0 <= range.first && range.first < range.second, false);
8374 auto offset = host->GetPaintRectOffsetNG(false, true);
8375 auto localOffset = globalOffset - Offset(offset.GetX(), offset.GetY());
8376 if (selectOverlay_->HasRenderTransform()) {
8377 localOffset = ConvertGlobalToLocalOffset(globalOffset);
8378 }
8379 auto eventHub = host->GetEventHub<EventHub>();
8380 if (GreatNotEqual(range.second, range.first)) {
8381 // Determine if the pan location is in the selected area
8382 auto rangeRects = paragraphs_.GetRects(range.first, range.second);
8383 auto panOffset = OffsetF(localOffset.GetX(), localOffset.GetY()) - GetTextRect().GetOffset() +
8384 OffsetF(0.0, std::min(baselineOffset_, 0.0f));
8385 for (const auto& rangeRect : rangeRects) {
8386 if (rangeRect.IsInRegion(PointF(panOffset.GetX(), panOffset.GetY()))) {
8387 return true;
8388 }
8389 }
8390 }
8391 return false;
8392 }
8393
8394 bool RichEditorPattern::BetweenSelectedPosition(const Offset& globalOffset)
8395 {
8396 return copyOption_ != CopyOptions::None && BetweenSelection(globalOffset);
8397 }
8398
8399 void RichEditorPattern::HandleSurfaceChanged(
8400 int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight, WindowSizeChangeReason type)
8401 {
8402 if (newWidth != prevWidth || newHeight != prevHeight) {
8403 TextPattern::HandleSurfaceChanged(newWidth, newHeight, prevWidth, prevHeight, type);
8404 UpdateOriginIsMenuShow(false);
8405 }
8406 UpdateCaretInfoToController();
8407 if (magnifierController_) {
8408 magnifierController_->RemoveMagnifierFrameNode();
8409 }
8410 }
8411
8412 void RichEditorPattern::HandleSurfacePositionChanged(int32_t posX, int32_t posY)
8413 {
8414 UpdateCaretInfoToController();
8415 }
8416
8417 void RichEditorPattern::DumpInfo()
8418 {
8419 auto& dumpLog = DumpLog::GetInstance();
8420 if (customKeyboardBuilder_) {
8421 dumpLog.AddDesc(std::string("CustomKeyboard, Attached: ").append(std::to_string(isCustomKeyboardAttached_)));
8422 }
8423 auto host = GetHost();
8424 CHECK_NULL_VOID(host);
8425 auto richEditorTheme = GetTheme<RichEditorTheme>();
8426 CHECK_NULL_VOID(richEditorTheme);
8427 dumpLog.AddDesc(std::string("caret offset: ").append(GetCaretRect().GetOffset().ToString()));
8428 dumpLog.AddDesc(std::string("caret height: ")
8429 .append(std::to_string(NearZero(GetCaretRect().Height())
8430 ? richEditorTheme->GetDefaultCaretHeight().ConvertToPx()
8431 : GetCaretRect().Height())));
8432 dumpLog.AddDesc(std::string("text rect: ").append(richTextRect_.ToString()));
8433 dumpLog.AddDesc(std::string("content rect: ").append(contentRect_.ToString()));
8434 auto richEditorPaintOffset = host->GetPaintRectOffsetNG(false, true);
8435 bool hasRenderTransform = selectOverlay_->HasRenderTransform();
8436 if (hasRenderTransform) {
8437 richEditorPaintOffset = selectOverlay_->GetPaintOffsetWithoutTransform();
8438 }
8439 dumpLog.AddDesc(std::string("hasRenderTransform: ").append(std::to_string(hasRenderTransform)));
8440 dumpLog.AddDesc(std::string("richEditorPaintOffset: ").append(richEditorPaintOffset.ToString()));
8441 auto selectOverlayInfo = selectOverlay_->GetSelectOverlayInfo();
8442 CHECK_NULL_VOID(selectOverlayInfo);
8443 dumpLog.AddDesc(std::string("selectOverlay info: ").append(selectOverlayInfo->ToString()));
8444 dumpLog.AddDesc(std::string("IsAIWrite: ").append(std::to_string(IsShowAIWrite())));
8445 dumpLog.AddDesc(std::string("keyboardAppearance: ")
8446 .append(std::to_string(static_cast<int32_t>(keyboardAppearance_))));
8447 }
8448
8449 void RichEditorPattern::RichEditorErrorReport(RichEditorInfo& info)
8450 {
8451 auto pipeline = GetContext();
8452 CHECK_NULL_VOID(pipeline);
8453 auto taskExecutor = pipeline->GetTaskExecutor();
8454 CHECK_NULL_VOID(taskExecutor);
8455 taskExecutor->PostTask(
8456 [info] {
8457 EventReport::ReportRichEditorInfo(info);
8458 },
8459 TaskExecutor::TaskType::BACKGROUND, "ArkUIRichEditorErrorReport");
8460 }
8461
8462 bool RichEditorPattern::HasFocus() const
8463 {
8464 auto focusHub = GetFocusHub();
8465 CHECK_NULL_RETURN(focusHub, false);
8466 return focusHub->IsCurrentFocus();
8467 }
8468
8469 void RichEditorPattern::UpdateTextFieldManager(const Offset& offset, float height)
8470 {
8471 if (!HasFocus()) {
8472 return;
8473 }
8474 auto context = GetHost()->GetContext();
8475 CHECK_NULL_VOID(context);
8476 auto richEditorTheme = context->GetTheme<RichEditorTheme>();
8477 CHECK_NULL_VOID(richEditorTheme);
8478 auto textFieldManager = DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
8479 CHECK_NULL_VOID(textFieldManager);
8480 auto safeAreaManager = context->GetSafeAreaManager();
8481 CHECK_NULL_VOID(safeAreaManager);
8482 auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
8483 textFieldManager->SetClickPosition({ offset.GetX() + caretOffset.GetX(), offset.GetY() + caretOffset.GetY() });
8484 textFieldManager->SetHeight(NearZero(caretHeight)
8485 ? richEditorTheme->GetDefaultCaretHeight().ConvertToPx()
8486 : caretHeight);
8487 textFieldManager->SetClickPositionOffset(safeAreaManager->GetKeyboardOffset());
8488 textFieldManager->SetOnFocusTextField(WeakClaim(this));
8489 if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_FOURTEEN)) {
8490 textFieldManager->SetUsingCustomKeyboardAvoid(keyboardAvoidance_);
8491 }
8492 if (!isTextChange_) {
8493 return;
8494 }
8495 auto taskExecutor = context->GetTaskExecutor();
8496 CHECK_NULL_VOID(taskExecutor);
8497 taskExecutor->PostTask(
8498 [weak = WeakClaim(this)] {
8499 auto pattern = weak.Upgrade();
8500 CHECK_NULL_VOID(pattern);
8501 pattern->ScrollToSafeArea();
8502 },
8503 TaskExecutor::TaskType::UI, "ArkUIRichEditorScrollToSafeArea", PriorityType::VIP);
8504 }
8505
8506 bool RichEditorPattern::IsDisabled() const
8507 {
8508 auto eventHub = GetEventHub<RichEditorEventHub>();
8509 CHECK_NULL_RETURN(eventHub, true);
8510 return !eventHub->IsEnabled();
8511 }
8512
8513 void RichEditorPattern::MouseDoubleClickParagraphEnd(int32_t& index)
8514 {
8515 bool isMouseDoubleClick = caretUpdateType_ == CaretUpdateType::DOUBLE_CLICK && sourceType_ == SourceType::MOUSE;
8516 CHECK_NULL_VOID(isMouseDoubleClick);
8517 auto paragraphEndPos = GetParagraphEndPosition(index);
8518 auto paragraphBeginPos = GetParagraphBeginPosition(index);
8519 bool isBeginEqualEnd = paragraphBeginPos == paragraphEndPos;
8520 CHECK_NULL_VOID(!isBeginEqualEnd);
8521 if (index == paragraphEndPos) {
8522 index -= 1;
8523 }
8524 }
8525
8526 void RichEditorPattern::AdjustSelectionExcludeSymbol(int32_t& start, int32_t& end)
8527 {
8528 AdjustSelectorForSymbol(start, HandleType::FIRST, SelectorAdjustPolicy::EXCLUDE);
8529 AdjustSelectorForSymbol(end, HandleType::SECOND, SelectorAdjustPolicy::EXCLUDE);
8530 }
8531
8532 void RichEditorPattern::InitSelection(const Offset& pos)
8533 {
8534 auto [currentPosition, selectType] = JudgeSelectType(pos);
8535 switch (selectType) {
8536 case SelectType::SELECT_NOTHING:
8537 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "select nothing currentPos=%{public}d", currentPosition);
8538 textSelector_.Update(currentPosition, currentPosition);
8539 return;
8540 case SelectType::SELECT_BACKWARD:
8541 currentPosition = std::max(0, currentPosition - 1);
8542 break;
8543 case SelectType::SELECT_FORWARD:
8544 break;
8545 default:
8546 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "exception select type");
8547 }
8548 int32_t nextPosition = std::min(currentPosition + 1, GetTextContentLength());
8549 AdjustSelectionExcludeSymbol(currentPosition, nextPosition);
8550 if (!IsCustomSpanInCaretPos(currentPosition, true)) {
8551 AdjustWordSelection(currentPosition, nextPosition);
8552 }
8553 AdjustSelector(currentPosition, nextPosition);
8554 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "init select [%{public}d--%{public}d]", currentPosition, nextPosition);
8555 textSelector_.Update(currentPosition, nextPosition);
8556 if (IsSelectEmpty(currentPosition, nextPosition)) {
8557 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "select rect is empty, select nothing");
8558 textSelector_.Update(currentPosition, currentPosition);
8559 }
8560 }
8561
8562 std::pair<int32_t, SelectType> RichEditorPattern::JudgeSelectType(const Offset& pos)
8563 {
8564 auto positionWithAffinity = paragraphs_.GetGlyphPositionAtCoordinate(pos);
8565 auto currentPosition = (GetTextContentLength() == 0) ? 0 : static_cast<int32_t>(positionWithAffinity.position_);
8566 auto selectType = SelectType::SELECT_NOTHING;
8567 CHECK_NULL_RETURN(GetTextContentLength() != 0, std::make_pair(currentPosition, selectType));
8568 bool isNeedSkipLineSeparator = !editingLongPress_ && IsSelectEmpty(currentPosition, currentPosition + 1);
8569 if (isNeedSkipLineSeparator && AdjustIndexSkipLineSeparator(currentPosition)) {
8570 return std::make_pair(currentPosition, SelectType::SELECT_BACKWARD);
8571 }
8572 auto height = paragraphs_.GetHeight();
8573 if (GreatNotEqual(pos.GetY(), height)) {
8574 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "touchPosY[%{public}f] > paragraphsHeight[%{public}f]", pos.GetY(), height);
8575 IF_TRUE(!editingLongPress_, selectType = SelectType::SELECT_BACKWARD);
8576 return std::make_pair(GetTextContentLength(), selectType);
8577 }
8578 TextAffinity currentAffinity = positionWithAffinity.affinity_;
8579 bool isTouchLineEnd = currentAffinity == TextAffinity::UPSTREAM && !IsTouchBeforeCaret(currentPosition, pos);
8580 if (editingLongPress_ && isTouchLineEnd) {
8581 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "touchLineEnd select nothing currentAffinity=%{public}d", currentAffinity);
8582 return std::make_pair(currentPosition, selectType);
8583 }
8584 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "currentPosition=%{public}d, currentAffinity=%{public}d",
8585 currentPosition, currentAffinity);
8586 selectType = (currentAffinity == TextAffinity::UPSTREAM) ? SelectType::SELECT_BACKWARD : SelectType::SELECT_FORWARD;
8587 return std::make_pair(currentPosition, selectType);
8588 }
8589
8590 bool RichEditorPattern::IsSelectEmpty(int32_t start, int32_t end)
8591 {
8592 auto selectedRects = paragraphs_.GetRects(start, end);
8593 return selectedRects.empty() || (selectedRects.size() == 1 && NearZero((selectedRects[0].Width())));
8594 }
8595
8596 bool RichEditorPattern::AdjustIndexSkipLineSeparator(int32_t& currentPosition)
8597 {
8598 std::u16string contentText;
8599 for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
8600 contentText.append((*iter)->content);
8601 CHECK_NULL_BREAK(currentPosition > (*iter)->position);
8602 }
8603 auto contentLength = static_cast<int32_t>(contentText.length());
8604 if (currentPosition > contentLength) {
8605 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "currentPosition=%{public}d but contentLength=%{public}d",
8606 currentPosition, contentLength);
8607 return false;
8608 }
8609 auto index = currentPosition - 1;
8610 while (index > 0) {
8611 CHECK_NULL_BREAK(contentText[index] == u'\n');
8612 index--;
8613 }
8614 if (index != currentPosition - 1) {
8615 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "skip lineSeparator %{public}d->%{public}d", currentPosition, index + 1);
8616 currentPosition = index + 1;
8617 return true;
8618 }
8619 return false;
8620 }
8621
8622 bool RichEditorPattern::AdjustIndexSkipSpace(int32_t& currentPosition, const MoveDirection direction)
8623 {
8624 bool isBackward = (direction == MoveDirection::BACKWARD);
8625 std::u16string contentText;
8626 GetContentBySpans(contentText);
8627 auto contentLength = static_cast<int32_t>(contentText.length());
8628 bool isPositionInvalid = (isBackward && currentPosition == 0) || (!isBackward && currentPosition == contentLength);
8629 if (isPositionInvalid) {
8630 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "AdjustIndexSkipSpace position=%{public}d but contentLength=%{public}d",
8631 currentPosition, contentLength);
8632 return false;
8633 }
8634 int32_t index = isBackward ? (currentPosition - 1) : currentPosition;
8635 while (isBackward ? index >= 0 : index < contentLength) {
8636 CHECK_NULL_BREAK(contentText[index] == u' ' && GetSpanType(index) == SpanItemType::NORMAL);
8637 isBackward ? index-- : index++;
8638 }
8639 int32_t adjustedIndex = isBackward ? (index + 1) : index;
8640 if (adjustedIndex != currentPosition) {
8641 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "skip space %{public}d->%{public}d", currentPosition, adjustedIndex);
8642 currentPosition = adjustedIndex;
8643 return true;
8644 }
8645 return false;
8646 }
8647
8648 bool RichEditorPattern::ResetOnInvalidSelection(int32_t start, int32_t end)
8649 {
8650 if (start < end) {
8651 return false;
8652 }
8653 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "SetSelection failed, the selected area is empty.");
8654 CloseSelectOverlay();
8655 ResetSelection();
8656 StartTwinkling();
8657 return true;
8658 }
8659
8660 bool RichEditorPattern::IsShowHandle()
8661 {
8662 auto richEditorTheme = GetTheme<RichEditorTheme>();
8663 CHECK_NULL_RETURN(richEditorTheme, false);
8664 return !richEditorTheme->IsRichEditorShowHandle();
8665 }
8666
8667 void RichEditorPattern::UpdateSelectionInfo(int32_t start, int32_t end)
8668 {
8669 UpdateSelectionType(GetSpansInfo(start, end, GetSpansMethod::ONSELECT));
8670 auto selectOverlayInfo = selectOverlay_->GetSelectOverlayInfo();
8671 textResponseType_ = selectOverlayInfo
8672 ? static_cast<TextResponseType>(selectOverlayInfo->menuInfo.responseType.value_or(0))
8673 : TextResponseType::LONG_PRESS;
8674 if (IsShowHandle() && !IsUsingMouse()) {
8675 ResetIsMousePressed();
8676 sourceType_ = SourceType::TOUCH;
8677 }
8678 }
8679
8680 void RichEditorPattern::SetSelection(int32_t start, int32_t end, const std::optional<SelectionOptions>& options,
8681 bool isForward)
8682 {
8683 bool hasFocus = HasFocus();
8684 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "setSelection, range=[%{public}d,%{public}d], hasFocus=%{public}d, "
8685 "isEditing=%{public}d", start, end, hasFocus, isEditing_);
8686 CHECK_NULL_VOID(hasFocus);
8687 if (IsPreviewTextInputting()) {
8688 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "SetSelection failed for previewText inputting");
8689 return;
8690 }
8691 if (start == -1 && end == -1) {
8692 start = 0;
8693 end = GetTextContentLength();
8694 } else {
8695 start = std::clamp(start, 0, GetTextContentLength());
8696 end = std::clamp(end, 0, GetTextContentLength());
8697 }
8698 CHECK_NULL_VOID(!ResetOnInvalidSelection(start, end));
8699 UpdateSelector(start, end);
8700
8701 if (textSelector_.IsValid() && !textSelector_.StartEqualToDest()) {
8702 StopTwinkling();
8703 if (start != textSelector_.GetTextStart() || end != textSelector_.GetTextEnd()) {
8704 FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
8705 }
8706 }
8707 SetCaretPosition(isForward ? textSelector_.GetTextStart() : textSelector_.GetTextEnd());
8708 MoveCaretToContentRect();
8709 CalculateHandleOffsetAndShowOverlay();
8710 UpdateSelectionInfo(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
8711 ProcessOverlayOnSetSelection(options);
8712 auto host = GetHost();
8713 CHECK_NULL_VOID(host);
8714 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
8715 }
8716
8717 void RichEditorPattern::ProcessOverlayOnSetSelection(const std::optional<SelectionOptions>& options)
8718 {
8719 if (!IsShowHandle()) {
8720 CloseSelectOverlay();
8721 } else if (!options.has_value() || options.value().menuPolicy == MenuPolicy::DEFAULT) {
8722 selectOverlay_->ProcessOverlay({ .menuIsShow = selectOverlay_->IsCurrentMenuVisibile(),
8723 .animation = true, .requestCode = REQUEST_RECREATE });
8724 IF_PRESENT(magnifierController_, RemoveMagnifierFrameNode());
8725 } else if (options.value().menuPolicy == MenuPolicy::HIDE) {
8726 if (selectOverlay_->IsUsingMouse()) {
8727 CloseSelectOverlay();
8728 } else {
8729 selectOverlay_->ProcessOverlay({ .menuIsShow = false, .animation = true });
8730 }
8731 } else if (options.value().menuPolicy == MenuPolicy::SHOW) {
8732 if (selectOverlay_->IsUsingMouse() || sourceType_ == SourceType::MOUSE) {
8733 selectionMenuOffsetByMouse_ = selectionMenuOffsetClick_;
8734 }
8735 selectOverlay_->ProcessOverlay({ .animation = true, .requestCode = REQUEST_RECREATE });
8736 IF_PRESENT(magnifierController_, RemoveMagnifierFrameNode());
8737 }
8738 }
8739
8740 void RichEditorPattern::BindSelectionMenu(TextResponseType type, TextSpanType richEditorType,
8741 std::function<void()>& menuBuilder, const SelectMenuParam& menuParam)
8742 {
8743 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "BindSelectionMenu spanType = %{public}d, responseType = %{public}d",
8744 richEditorType, type);
8745 TextPattern::BindSelectionMenu(richEditorType, type, menuBuilder, menuParam);
8746 }
8747
8748 RefPtr<NodePaintMethod> RichEditorPattern::CreateNodePaintMethod()
8749 {
8750 if (!contentMod_) {
8751 contentMod_ = MakeRefPtr<RichEditorContentModifier>(textStyle_, ¶graphs_, WeakClaim(this));
8752 }
8753 if (!overlayMod_) {
8754 auto scrollBar = GetScrollBar();
8755 if (scrollBar) {
8756 auto scrollBarModifier = AceType::MakeRefPtr<ScrollBarOverlayModifier>();
8757 scrollBarModifier->SetRect(scrollBar->GetActiveRect());
8758 scrollBarModifier->SetPositionMode(scrollBar->GetPositionMode());
8759 SetScrollBarOverlayModifier(scrollBarModifier);
8760 }
8761 SetEdgeEffect(EdgeEffect::FADE, GetAlwaysEnabled());
8762 SetEdgeEffect();
8763 overlayMod_ = AceType::MakeRefPtr<RichEditorOverlayModifier>(
8764 WeakClaim(this), GetScrollBarOverlayModifier(), GetScrollEdgeEffect());
8765 }
8766
8767 if (GetIsCustomFont()) {
8768 contentMod_->SetIsCustomFont(true);
8769 }
8770 return MakeRefPtr<RichEditorPaintMethod>(WeakClaim(this), ¶graphs_, baselineOffset_, contentMod_, overlayMod_);
8771 }
8772
8773 int32_t RichEditorPattern::GetHandleIndex(const Offset& offset) const
8774 {
8775 CHECK_NULL_RETURN(!isShowPlaceholder_, 0);
8776 return paragraphs_.GetIndex(Offset(offset.GetX() + contentRect_.GetX() - richTextRect_.GetX(),
8777 offset.GetY() + contentRect_.GetY() - richTextRect_.GetY()));
8778 }
8779
8780 std::vector<RectF> RichEditorPattern::GetTextBoxes()
8781 {
8782 auto selectedRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
8783 std::vector<RectF> res;
8784 res.reserve(selectedRects.size());
8785 for (auto&& rect : selectedRects) {
8786 res.emplace_back(rect);
8787 }
8788 if (!res.empty() && paragraphs_.IsSelectLineHeadAndUseLeadingMargin(textSelector_.GetTextStart())) {
8789 // To make drag screenshot include LeadingMarginPlaceholder when not single line
8790 if (res.front().GetY() != res.back().GetY()) {
8791 res.front().SetLeft(0.0f);
8792 }
8793 }
8794 return res;
8795 }
8796
8797 float RichEditorPattern::GetLineHeight() const
8798 {
8799 auto selectedRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
8800 CHECK_NULL_RETURN(selectedRects.size(), 0.0f);
8801 return selectedRects.front().Height();
8802 }
8803
8804 size_t RichEditorPattern::GetLineCount() const
8805 {
8806 return paragraphs_.GetLineCount();
8807 }
8808
8809 TextLineMetrics RichEditorPattern::GetLineMetrics(int32_t lineNumber)
8810 {
8811 if (lineNumber < 0 || GetLineCount() == 0 || static_cast<uint32_t>(lineNumber) > GetLineCount() - 1) {
8812 TAG_LOGE(AceLogTag::ACE_RICH_TEXT,
8813 "GetLineMetrics failed, lineNumber not between 0 and max lines:%{public}d", lineNumber);
8814 return TextLineMetrics();
8815 }
8816 auto lineMetrics = paragraphs_.GetLineMetrics(lineNumber);
8817 const auto& textRect = GetTextRect();
8818 lineMetrics.x += textRect.GetX();
8819 lineMetrics.y += textRect.GetY();
8820 lineMetrics.baseline += textRect.GetY();
8821 return lineMetrics;
8822 }
8823
8824 std::vector<ParagraphManager::TextBox> RichEditorPattern::GetRectsForRange(
8825 int32_t start, int32_t end, RectHeightStyle heightStyle, RectWidthStyle widthStyle)
8826 {
8827 if (start < 0 || end < 0 || start > end) {
8828 return {};
8829 }
8830 std::vector<ParagraphManager::TextBox> textBoxes =
8831 paragraphs_.GetRectsForRange(start, end, heightStyle, widthStyle);
8832 const auto& textRect = richTextRect_;
8833 std::vector<ParagraphManager::TextBox> adjustedTextBoxes;
8834 for (auto& textBox : textBoxes) {
8835 ParagraphManager::TextBox adjustedTextBox = textBox;
8836 adjustedTextBox.rect_.SetLeft(textBox.rect_.Left() + textRect.Left());
8837 adjustedTextBox.rect_.SetTop(textBox.rect_.Top() + textRect.Top());
8838 adjustedTextBoxes.push_back(adjustedTextBox);
8839 }
8840 return adjustedTextBoxes;
8841 }
8842
8843 float RichEditorPattern::GetLetterSpacing() const
8844 {
8845 auto selectedRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
8846 CHECK_NULL_RETURN(!selectedRects.empty(), 0.0f);
8847 return selectedRects.front().Width();
8848 }
8849
8850 void RichEditorPattern::UpdateSelectMenuInfo(SelectMenuInfo& menuInfo)
8851 {
8852 bool isSupportCameraInput = false;
8853 #if defined(ENABLE_STANDARD_INPUT)
8854 auto inputMethod = MiscServices::InputMethodController::GetInstance();
8855 isSupportCameraInput =
8856 inputMethod && inputMethod->IsInputTypeSupported(MiscServices::InputType::CAMERA_INPUT);
8857 #endif
8858 menuInfo.showCameraInput = !IsSelected() && isSupportCameraInput && !customKeyboardBuilder_;
8859 if (textResponseType_.has_value()) {
8860 menuInfo.responseType = static_cast<int32_t>(textResponseType_.value());
8861 }
8862 }
8863
8864 RectF RichEditorPattern::GetCaretRect() const
8865 {
8866 RectF rect;
8867 CHECK_NULL_RETURN(overlayMod_, rect);
8868 auto richEditorOverlay = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
8869 CHECK_NULL_RETURN(richEditorOverlay, rect);
8870 rect.SetOffset(richEditorOverlay->GetCaretOffset());
8871 rect.SetHeight(richEditorOverlay->GetCaretHeight());
8872 return rect;
8873 }
8874
8875 void RichEditorPattern::ScrollToSafeArea() const
8876 {
8877 auto host = GetHost();
8878 CHECK_NULL_VOID(host);
8879 auto pipeline = host->GetContext();
8880 CHECK_NULL_VOID(pipeline);
8881 if (pipeline->UsingCaretAvoidMode()) {
8882 // using TriggerAvoidOnCaretChange instead in CaretAvoidMode
8883 return;
8884 }
8885 auto textFieldManager = DynamicCast<TextFieldManagerNG>(pipeline->GetTextFieldManager());
8886 CHECK_NULL_VOID(textFieldManager);
8887 textFieldManager->ScrollTextFieldToSafeArea();
8888 }
8889
8890 void RichEditorPattern::InitScrollablePattern()
8891 {
8892 auto layoutProperty = GetLayoutProperty<RichEditorLayoutProperty>();
8893 CHECK_NULL_VOID(layoutProperty);
8894 auto barState = layoutProperty->GetDisplayModeValue(DisplayMode::AUTO);
8895 CHECK_NULL_VOID(!barDisplayMode_.has_value() || barDisplayMode_.value() != barState);
8896 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "setBarState=%{public}d", barState);
8897 barDisplayMode_ = barState;
8898 if (!GetScrollableEvent()) {
8899 AddScrollEvent();
8900 }
8901 SetAxis(Axis::VERTICAL);
8902 if (barState != DisplayMode::AUTO) {
8903 barState = DisplayMode::ON;
8904 }
8905 SetScrollBar(barState);
8906 auto scrollBar = GetScrollBar();
8907 if (scrollBar) {
8908 auto richEditorTheme = GetTheme<RichEditorTheme>();
8909 CHECK_NULL_VOID(richEditorTheme);
8910 scrollBar->SetMinHeight(richEditorTheme->GetScrollbarMinHeight());
8911 }
8912 if (overlayMod_) {
8913 UpdateScrollBarOffset();
8914 }
8915 auto& paddingProperty = layoutProperty->GetPaddingProperty();
8916 if (paddingProperty) {
8917 auto offsetY = paddingProperty->top.has_value() ? paddingProperty->top->GetDimension().ConvertToPx() : 0.0f;
8918 auto offsetX = paddingProperty->left.has_value() ? paddingProperty->left->GetDimension().ConvertToPx() : 0.0f;
8919 richTextRect_.SetOffset(OffsetF(offsetX, offsetY));
8920 }
8921 }
8922
8923 void RichEditorPattern::ProcessInnerPadding()
8924 {
8925 auto theme = GetTheme<RichEditorTheme>();
8926 CHECK_NULL_VOID(theme);
8927 auto host = GetHost();
8928 CHECK_NULL_VOID(host);
8929 auto layoutProperty = host->GetLayoutProperty<RichEditorLayoutProperty>();
8930 CHECK_NULL_VOID(layoutProperty);
8931 auto themePadding = theme->GetPadding();
8932 auto& paddingProp = layoutProperty->GetPaddingProperty();
8933 auto left = !paddingProp ? CalcLength(themePadding.Left()).GetDimension()
8934 : paddingProp->left.value_or(CalcLength(themePadding.Left())).GetDimension();
8935 auto top = !paddingProp ? CalcLength(themePadding.Top()).GetDimension()
8936 : paddingProp->top.value_or(CalcLength(themePadding.Top())).GetDimension();
8937 auto bottom = !paddingProp ? CalcLength(themePadding.Bottom()).GetDimension()
8938 : paddingProp->bottom.value_or(CalcLength(themePadding.Bottom())).GetDimension();
8939 auto right = !paddingProp ? CalcLength(themePadding.Right()).GetDimension()
8940 : paddingProp->right.value_or(CalcLength(themePadding.Right())).GetDimension();
8941 PaddingProperty paddings;
8942 paddings.top = NG::CalcLength(top);
8943 paddings.bottom = NG::CalcLength(bottom);
8944 paddings.left = NG::CalcLength(left);
8945 paddings.right = NG::CalcLength(right);
8946 layoutProperty->UpdatePadding(paddings);
8947 }
8948
8949 void RichEditorPattern::UpdateScrollStateAfterLayout(bool shouldDisappear)
8950 {
8951 bool hasTextOffsetChanged = false;
8952 if (GreatNotEqual(richTextRect_.GetY(), contentRect_.GetY())) {
8953 auto offset = richTextRect_.GetOffset();
8954 offset.AddY(contentRect_.GetY() - richTextRect_.GetY());
8955 richTextRect_.SetOffset(offset);
8956 hasTextOffsetChanged = true;
8957 }
8958 if (GreatNotEqual(richTextRect_.Height(), contentRect_.Height()) &&
8959 LessNotEqual(richTextRect_.Bottom(), contentRect_.Bottom())) {
8960 auto offset = richTextRect_.GetOffset();
8961 offset.AddY(contentRect_.Bottom() - richTextRect_.Bottom());
8962 richTextRect_.SetOffset(offset);
8963 hasTextOffsetChanged = true;
8964 }
8965 if (LessOrEqual(richTextRect_.Height(), contentRect_.Height()) &&
8966 LessNotEqual(richTextRect_.GetY(), contentRect_.GetY())) {
8967 richTextRect_.SetOffset(contentRect_.GetOffset());
8968 hasTextOffsetChanged = true;
8969 }
8970 if (hasTextOffsetChanged) {
8971 UpdateChildrenOffset();
8972 }
8973 StopScrollable();
8974 CheckScrollable();
8975 if (overlayMod_) {
8976 UpdateScrollBarOffset();
8977 }
8978 auto scrollBar = GetScrollBar();
8979 CHECK_NULL_VOID(scrollBar);
8980
8981 if (isFirstCallOnReady_) {
8982 isFirstCallOnReady_ = false;
8983 scrollBar->ScheduleDisappearDelayTask();
8984 return;
8985 }
8986 if (shouldDisappear) {
8987 scrollBar->ScheduleDisappearDelayTask();
8988 }
8989 }
8990
8991 bool RichEditorPattern::OnScrollCallback(float offset, int32_t source)
8992 {
8993 auto scrollBar = GetScrollBar();
8994 if (source == SCROLL_FROM_START) {
8995 IF_PRESENT(scrollBar, PlayScrollBarAppearAnimation());
8996 if (SelectOverlayIsOn()) {
8997 selectOverlay_->HideMenu(true);
8998 }
8999 ScrollablePattern::RecordScrollEvent(Recorder::EventType::SCROLL_START);
9000 UIObserverHandler::GetInstance().NotifyScrollEventStateChange(
9001 AceType::WeakClaim(this), ScrollEventType::SCROLL_START);
9002 return true;
9003 }
9004 if (IsReachedBoundary(offset)) {
9005 return false;
9006 }
9007 if (scrollBar && source == SCROLL_FROM_JUMP) {
9008 scrollBar->PlayScrollBarAppearAnimation();
9009 scrollBar->ScheduleDisappearDelayTask();
9010 }
9011 auto newOffset = MoveTextRect(offset);
9012 MoveFirstHandle(newOffset);
9013 MoveSecondHandle(newOffset);
9014 dataDetectorAdapter_->aiSpanRects_.clear();
9015 return true;
9016 }
9017
9018 float RichEditorPattern::GetCrossOverHeight() const
9019 {
9020 if (!keyboardAvoidance_ || !contentChange_ || AceApplicationInfo::GetInstance().
9021 GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_FOURTEEN)) {
9022 return 0.0f;
9023 }
9024 auto host = GetHost();
9025 CHECK_NULL_RETURN(host, 0.0f);
9026 auto pipeline = host->GetContext();
9027 CHECK_NULL_RETURN(pipeline, 0.0f);
9028 auto rootHeight = pipeline->GetRootHeight();
9029 auto keyboardY = rootHeight - pipeline->GetSafeAreaManager()->GetKeyboardInset().Length();
9030 if (GreatOrEqual(keyboardY, rootHeight)) {
9031 return 0.0f;
9032 }
9033 float height = contentRect_.Bottom();
9034 float frameY = parentGlobalOffset_.GetY() + contentRect_.GetY();
9035 float bottom = frameY + height;
9036 auto crossOverHeight = bottom - keyboardY;
9037 if (LessOrEqual(crossOverHeight, 0.0f)) {
9038 return 0.0f;
9039 }
9040 return crossOverHeight;
9041 }
9042
9043 float RichEditorPattern::MoveTextRect(float offset)
9044 {
9045 auto keyboardOffset = GetCrossOverHeight();
9046 if (GreatNotEqual(richTextRect_.Height(), contentRect_.Height() - keyboardOffset)) {
9047 if (GreatNotEqual(richTextRect_.GetY() + offset, contentRect_.GetY())) {
9048 offset = contentRect_.GetY() - richTextRect_.GetY();
9049 } else if (LessNotEqual(richTextRect_.Bottom() + offset, contentRect_.Bottom() - keyboardOffset)) {
9050 offset = contentRect_.Bottom() - keyboardOffset - richTextRect_.Bottom();
9051 }
9052 } else if (!NearEqual(richTextRect_.GetY(), contentRect_.GetY())) {
9053 offset = contentRect_.GetY() - richTextRect_.GetY();
9054 } else {
9055 return 0.0f;
9056 }
9057 if (NearEqual(offset, 0.0f)) {
9058 return offset;
9059 }
9060 scrollOffset_ = richTextRect_.GetY() + offset;
9061 richTextRect_.SetOffset(OffsetF(richTextRect_.GetX(), scrollOffset_));
9062 UpdateScrollBarOffset();
9063 UpdateChildrenOffset();
9064 if (auto host = GetHost(); host) {
9065 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
9066 }
9067 return offset;
9068 }
9069
9070 void RichEditorPattern::MoveFirstHandle(float offset)
9071 {
9072 if (SelectOverlayIsOn() && !NearEqual(offset, 0.0f)) {
9073 textSelector_.selectionBaseOffset.AddY(offset);
9074 auto firstHandleOffset = textSelector_.firstHandle.GetOffset();
9075 firstHandleOffset.AddY(offset);
9076 textSelector_.firstHandle.SetOffset(firstHandleOffset);
9077 selectOverlay_->UpdateFirstHandleOffset();
9078 }
9079 }
9080
9081 void RichEditorPattern::MoveSecondHandle(float offset)
9082 {
9083 if (SelectOverlayIsOn() && !NearEqual(offset, 0.0f)) {
9084 textSelector_.selectionDestinationOffset.AddY(offset);
9085 auto secondHandleOffset = textSelector_.secondHandle.GetOffset();
9086 secondHandleOffset.AddY(offset);
9087 textSelector_.secondHandle.SetOffset(secondHandleOffset);
9088 selectOverlay_->UpdateSecondHandleOffset();
9089 }
9090 }
9091
9092 void RichEditorPattern::SetNeedMoveCaretToContentRect()
9093 {
9094 CHECK_NULL_VOID(isRichEditorInit_);
9095 needMoveCaretToContentRect_ = true;
9096 }
9097
9098 void RichEditorPattern::MoveCaretToContentRect()
9099 {
9100 auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
9101 MoveCaretToContentRect(caretOffset, caretHeight);
9102 }
9103
9104 void RichEditorPattern::MoveCaretToContentRect(const OffsetF& caretOffset, float caretHeight)
9105 {
9106 auto keyboardOffset = GetCrossOverHeight();
9107 auto contentRect = GetTextContentRect();
9108 auto textRect = GetTextRect();
9109 auto scrollBar = GetScrollBar();
9110 if (scrollBar) {
9111 scrollBar->PlayScrollBarAppearAnimation();
9112 scrollBar->ScheduleDisappearDelayTask();
9113 }
9114 if (LessOrEqual(textRect.Height(), contentRect.Height() - keyboardOffset) || isShowPlaceholder_) {
9115 return;
9116 }
9117 if (LessNotEqual(contentRect.GetSize().Height(), caretHeight) &&
9118 !NearEqual(caretOffset.GetY() + caretHeight, contentRect.Bottom() - keyboardOffset)) {
9119 OnScrollCallback(contentRect.Bottom() - keyboardOffset - caretOffset.GetY() - caretHeight, SCROLL_FROM_NONE);
9120 }
9121 if (LessNotEqual(contentRect.GetSize().Height(), caretHeight)) {
9122 return;
9123 }
9124 if (LessNotEqual(caretOffset.GetY(), contentRect.GetY())) {
9125 if (LessOrEqual(caretOffset.GetX(), GetTextRect().GetX())) {
9126 OnScrollCallback(contentRect.GetY() - caretOffset.GetY() + caretHeight, SCROLL_FROM_NONE);
9127 } else {
9128 OnScrollCallback(contentRect.GetY() - caretOffset.GetY(), SCROLL_FROM_NONE);
9129 }
9130 } else if (GreatNotEqual(caretOffset.GetY() + caretHeight, contentRect.Bottom() - keyboardOffset)) {
9131 auto distance = contentRect.Bottom() - keyboardOffset - caretOffset.GetY() - caretHeight -
9132 CARET_BOTTOM_DISTANCE.ConvertToPx();
9133 OnScrollCallback(distance, SCROLL_FROM_NONE);
9134 }
9135 }
9136
9137 void RichEditorPattern::MoveCaretToContentRect(float offset, int32_t source)
9138 {
9139 float caretHeight = 0.0f;
9140 auto caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight);
9141 auto keyboardOffset = GetCrossOverHeight();
9142 auto contentRect = GetTextContentRect();
9143 auto distance = contentRect.Bottom() - keyboardOffset - caretOffset.GetY() - caretHeight - offset;
9144 OnScrollCallback(distance, source);
9145 }
9146
9147 bool RichEditorPattern::IsCaretInContentArea()
9148 {
9149 float caretHeight = 0.0f;
9150 auto caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight);
9151 auto keyboardOffset = GetCrossOverHeight();
9152 auto contentRect = GetTextContentRect();
9153 return GreatNotEqual(caretOffset.GetY() + caretHeight, contentRect.GetY())
9154 && LessNotEqual(caretOffset.GetY(), contentRect.Bottom() - keyboardOffset);
9155 }
9156
9157 void RichEditorPattern::UpdateScrollBarOffset()
9158 {
9159 if (!GetScrollBar() && !GetScrollBarProxy()) {
9160 return;
9161 }
9162 Size size(frameRect_.Width(), frameRect_.Height());
9163 auto verticalGap = frameRect_.Height() - contentRect_.Height();
9164 UpdateScrollBarRegion(
9165 contentRect_.GetY() - richTextRect_.GetY(), richTextRect_.Height() + verticalGap, size, Offset(0.0, 0.0));
9166 auto tmpHost = GetHost();
9167 CHECK_NULL_VOID(tmpHost);
9168 tmpHost->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
9169 }
9170
9171 void RichEditorPattern::OnScrollEndCallback()
9172 {
9173 auto scrollBar = GetScrollBar();
9174 if (scrollBar) {
9175 scrollBar->ScheduleDisappearDelayTask();
9176 }
9177 CHECK_NULL_VOID(!selectOverlay_->GetIsHandleMoving());
9178 if (IsSelectAreaVisible()) {
9179 auto info = selectOverlay_->GetSelectOverlayInfo();
9180 if (info && info->menuInfo.menuBuilder) {
9181 selectOverlay_->ProcessOverlay({ .animation = true });
9182 } else {
9183 selectOverlay_->UpdateMenuOffset();
9184 selectOverlay_->ShowMenu();
9185 }
9186 }
9187 if (AnimateStoped()) {
9188 ScrollablePattern::RecordScrollEvent(Recorder::EventType::SCROLL_STOP);
9189 UIObserverHandler::GetInstance().NotifyScrollEventStateChange(
9190 AceType::WeakClaim(this), ScrollEventType::SCROLL_STOP);
9191 }
9192 }
9193
9194 bool RichEditorPattern::IsSelectAreaVisible()
9195 {
9196 auto host = GetHost();
9197 CHECK_NULL_RETURN(host, false);
9198 auto pipeline = host->GetContext();
9199 CHECK_NULL_RETURN(pipeline, false);
9200 auto safeAreaManager = pipeline->GetSafeAreaManager();
9201 CHECK_NULL_RETURN(safeAreaManager, false);
9202 auto keyboardInsert = safeAreaManager->GetKeyboardInset();
9203 auto selectArea = GetSelectArea(SelectRectsType::ALL_LINES);
9204
9205 return !selectArea.IsEmpty() && LessNotEqual(selectArea.Top(), keyboardInsert.start);
9206 }
9207
9208 bool RichEditorPattern::IsReachedBoundary(float offset)
9209 {
9210 auto keyboardOffset = GetCrossOverHeight();
9211 return (NearEqual(richTextRect_.GetY(), contentRect_.GetY()) && GreatNotEqual(offset, 0.0f)) ||
9212 (NearEqual(richTextRect_.GetY() + richTextRect_.Height(),
9213 contentRect_.GetY() + contentRect_.Height() - keyboardOffset) &&
9214 LessNotEqual(offset, 0.0f));
9215 }
9216
9217 void RichEditorPattern::CheckScrollable()
9218 {
9219 auto gestureHub = GetGestureEventHub();
9220 CHECK_NULL_VOID(gestureHub);
9221 scrollable_ = GetTextContentLength() > 0 && GreatNotEqual(richTextRect_.Height(), contentRect_.Height());
9222 SetScrollEnabled(scrollable_);
9223 }
9224
9225 void RichEditorPattern::UpdateChildrenOffset()
9226 {
9227 auto host = GetHost();
9228 CHECK_NULL_VOID(host);
9229 std::vector<int32_t> placeholderIndex;
9230 for (const auto& child : spans_) {
9231 if (!child) {
9232 continue;
9233 }
9234 if (AceType::InstanceOf<ImageSpanItem>(child) || AceType::InstanceOf<PlaceholderSpanItem>(child)) {
9235 placeholderIndex.emplace_back(child->placeholderIndex);
9236 }
9237 }
9238 if (spans_.empty() || placeholderIndex.empty()) {
9239 return;
9240 }
9241 size_t index = 0;
9242 std::vector<RectF> rectsForPlaceholders = paragraphs_.GetPlaceholderRects();
9243 auto childrenNodes = host->GetChildren();
9244 auto textOffset = GetTextRect().GetOffset();
9245 for (const auto& child : childrenNodes) {
9246 auto childNode = AceType::DynamicCast<FrameNode>(child);
9247 if (!childNode) {
9248 continue;
9249 }
9250 if (!(childNode->GetPattern<ImagePattern>() || childNode->GetPattern<PlaceholderSpanPattern>())) {
9251 continue;
9252 }
9253 if (isSpanStringMode_) {
9254 auto imageSpanNode = AceType::DynamicCast<ImageSpanNode>(child);
9255 if (imageSpanNode && imageSpanNode->GetSpanItem()) {
9256 index = static_cast<uint32_t>(imageSpanNode->GetSpanItem()->placeholderIndex);
9257 }
9258 }
9259 if (index >= rectsForPlaceholders.size()) {
9260 break;
9261 }
9262 auto rect = rectsForPlaceholders.at(index);
9263 auto geometryNode = childNode->GetGeometryNode();
9264 if (geometryNode) {
9265 geometryNode->SetMarginFrameOffset(textOffset + OffsetF(rect.Left(), rect.Top()));
9266 childNode->ForceSyncGeometryNode();
9267 childNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
9268 }
9269 ++index;
9270 }
9271 }
9272
9273 void RichEditorPattern::AutoScrollByEdgeDetection(AutoScrollParam param, OffsetF offset, EdgeDetectionStrategy strategy)
9274 {
9275 if (NearEqual(prevAutoScrollOffset_.GetY(), offset.GetY())) {
9276 return;
9277 }
9278 prevAutoScrollOffset_ = offset;
9279 auto contentRect = GetTextContentRect();
9280 auto isDragging = param.autoScrollEvent == AutoScrollEvent::DRAG;
9281 float edgeThreshold = isDragging ? AUTO_SCROLL_DRAG_EDGE_DISTANCE.ConvertToPx()
9282 : AUTO_SCROLL_EDGE_DISTANCE.ConvertToPx();
9283 auto maxHeight = isDragging ? frameRect_.Height() : contentRect.Height();
9284 if (GreatNotEqual(edgeThreshold * 2, maxHeight)) {
9285 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AutoScrollByEdgeDetection: hot area height is great than max height.");
9286 return;
9287 }
9288 float topEdgeThreshold = isDragging ? edgeThreshold : edgeThreshold + contentRect.GetY();
9289 float bottomThreshold = isDragging ? frameRect_.Height() - edgeThreshold : contentRect.Bottom() - edgeThreshold;
9290 if (param.autoScrollEvent == AutoScrollEvent::HANDLE) {
9291 auto handleTopOffset = offset;
9292 auto handleBottomOffset = OffsetF(offset.GetX(), offset.GetY() + param.handleRect.Height());
9293 if (GreatNotEqual(handleBottomOffset.GetY(), bottomThreshold)) {
9294 param.offset = bottomThreshold - handleBottomOffset.GetY();
9295 ScheduleAutoScroll(param);
9296 } else if (LessNotEqual(handleTopOffset.GetY(), topEdgeThreshold)) {
9297 param.offset = topEdgeThreshold - handleTopOffset.GetY();
9298 ScheduleAutoScroll(param);
9299 } else {
9300 StopAutoScroll();
9301 }
9302 return;
9303 }
9304 // drag and mouse
9305 if (GreatNotEqual(offset.GetY(), bottomThreshold)) {
9306 param.offset = isDragging ? -CalcDragSpeed(bottomThreshold, frameRect_.Height(), offset.GetY())
9307 : bottomThreshold - offset.GetY();
9308 ScheduleAutoScroll(param);
9309 } else if (LessNotEqual(offset.GetY(), topEdgeThreshold)) {
9310 param.offset = isDragging ? CalcDragSpeed(topEdgeThreshold, 0, offset.GetY())
9311 : topEdgeThreshold - offset.GetY();
9312 ScheduleAutoScroll(param);
9313 } else {
9314 StopAutoScroll();
9315 }
9316 }
9317
9318 float RichEditorPattern::CalcDragSpeed(float hotAreaStart, float hotAreaEnd, float point)
9319 {
9320 auto distanceRatio = (point - hotAreaStart) / (hotAreaEnd - hotAreaStart);
9321 auto speedFactor = Curves::SHARP->MoveInternal(distanceRatio);
9322 return ((MAX_DRAG_SCROLL_SPEED * speedFactor) / TIME_UNIT) * AUTO_SCROLL_INTERVAL;
9323 }
9324
9325 void RichEditorPattern::ScheduleAutoScroll(AutoScrollParam param)
9326 {
9327 if (GreatNotEqual(param.offset, 0.0f) && IsReachTop()) {
9328 return;
9329 }
9330 if (LessNotEqual(param.offset, 0.0f) && IsReachBottom()) {
9331 return;
9332 }
9333 auto host = GetHost();
9334 CHECK_NULL_VOID(host);
9335 auto context = host->GetContext();
9336 CHECK_NULL_VOID(context);
9337 auto taskExecutor = context->GetTaskExecutor();
9338 CHECK_NULL_VOID(taskExecutor);
9339 if (param.isFirstRun_) {
9340 param.isFirstRun_ = false;
9341 currentScrollParam_ = param;
9342 if (isAutoScrollRunning_) {
9343 return;
9344 }
9345 }
9346 autoScrollTask_.Reset([weak = WeakClaim(this)]() {
9347 auto client = weak.Upgrade();
9348 CHECK_NULL_VOID(client);
9349 client->OnAutoScroll(client->currentScrollParam_);
9350 if (client->IsReachTop() || client->IsReachBottom()) {
9351 client->StopAutoScroll();
9352 }
9353 });
9354 isAutoScrollRunning_ = true;
9355 taskExecutor->PostDelayedTask(autoScrollTask_, TaskExecutor::TaskType::UI, AUTO_SCROLL_INTERVAL,
9356 "ArkUIRichEditorScheduleAutoScroll");
9357 }
9358
9359 void RichEditorPattern::OnAutoScroll(AutoScrollParam param)
9360 {
9361 if (param.showScrollbar) {
9362 auto scrollBar = GetScrollBar();
9363 IF_PRESENT(scrollBar, PlayScrollBarAppearAnimation());
9364 param.showScrollbar = false;
9365 }
9366 CHECK_NULL_VOID(param.autoScrollEvent != AutoScrollEvent::NONE);
9367 auto newOffset = MoveTextRect(param.offset);
9368 switch (param.autoScrollEvent) {
9369 case AutoScrollEvent::CARET:
9370 break;
9371 case AutoScrollEvent::HANDLE: {
9372 param.isFirstHandle ? MoveSecondHandle(newOffset) : MoveFirstHandle(newOffset);
9373 selectOverlay_->OnHandleMove(param.handleRect, param.isFirstHandle);
9374 break;
9375 }
9376 case AutoScrollEvent::DRAG:
9377 break;
9378 case AutoScrollEvent::MOUSE: {
9379 auto textOffset = ConvertTouchOffsetToTextOffset(param.eventOffset);
9380 int32_t extend = (GetTextContentLength() == 0) ? 0 : paragraphs_.GetIndex(textOffset);
9381 UpdateSelector(textSelector_.baseOffset, extend);
9382 SetCaretPosition(std::max(textSelector_.baseOffset, extend));
9383 break;
9384 }
9385 default:
9386 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "Unsupported auto scroll event type");
9387 return;
9388 }
9389 CHECK_NULL_VOID(!NearEqual(newOffset, 0.0f));
9390 ScheduleAutoScroll(param);
9391 }
9392
9393 void RichEditorPattern::StopAutoScroll()
9394 {
9395 isAutoScrollRunning_ = false;
9396 autoScrollTask_.Cancel();
9397 prevAutoScrollOffset_ = OffsetF(0.0f, 0.0f);
9398 auto scrollBar = GetScrollBar();
9399 IF_PRESENT(scrollBar, ScheduleDisappearDelayTask());
9400 }
9401
9402 bool RichEditorPattern::NeedAiAnalysis(
9403 const CaretUpdateType targeType, const int32_t pos, const int32_t& spanStart, const std::string& content)
9404 {
9405 if (spanStart < 0) {
9406 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "NeedAiAnalysis -spanStart:%{public}d, return!", spanStart);
9407 return false;
9408 }
9409
9410 if (!InputAIChecker::NeedAIAnalysis(content.empty(), targeType, lastClickTimeStamp_ - lastAiPosTimeStamp_)) {
9411 return false;
9412 }
9413
9414 if (IsClickBoundary(pos) && targeType == CaretUpdateType::PRESSED) {
9415 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "NeedAiAnalysis IsClickBoundary, return!");
9416 return false;
9417 }
9418 EmojiRelation relation = GetEmojiRelation(pos);
9419 if (relation == EmojiRelation::IN_EMOJI || relation == EmojiRelation::MIDDLE_EMOJI ||
9420 relation == EmojiRelation::BEFORE_EMOJI) {
9421 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "NeedAiAnalysis emoji relation=%{public}d, return!", relation);
9422 return false;
9423 }
9424 return true;
9425 }
9426
9427 void RichEditorPattern::AdjustCursorPosition(int32_t& pos)
9428 {
9429 // the rich text has some spans, the pos is belong to the whole richtext content, should use (pos - spanStarint)
9430 int32_t spanStart = -1;
9431 // get the span text by the position, maybe text is empty
9432 std::string content = GetPositionSpansText(pos, spanStart);
9433
9434 if (NeedAiAnalysis(CaretUpdateType::PRESSED, pos, spanStart, content)) {
9435 int32_t aiPos = pos - spanStart;
9436 DataDetectorMgr::GetInstance().AdjustCursorPosition(aiPos, content, lastAiPosTimeStamp_, lastClickTimeStamp_);
9437 if (aiPos < 0) {
9438 return;
9439 }
9440 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "get ai pos:%{public}d--spanStart%{public}d", aiPos, spanStart);
9441 pos = aiPos + spanStart;
9442 }
9443 }
9444
9445 bool RichEditorPattern::AdjustWordSelection(int32_t& start, int32_t& end)
9446 {
9447 // the rich text has some spans, the pos is belong to the whole richtext content, should use (pos - spanStarint)
9448 int32_t spanStart = -1;
9449 // get the span text by the position, maybe text is empty
9450 std::string content = GetPositionSpansText(start, spanStart);
9451 if (NeedAiAnalysis(CaretUpdateType::DOUBLE_CLICK, start, spanStart, content)) {
9452 int32_t aiPosStart = start - spanStart;
9453 int32_t aiPosEnd = end - spanStart;
9454 DataDetectorMgr::GetInstance().AdjustWordSelection(aiPosStart, content, aiPosStart, aiPosEnd);
9455 if (aiPosStart < 0 || aiPosEnd < 0) {
9456 return false;
9457 }
9458
9459 start = std::min(aiPosStart + spanStart, GetTextContentLength());
9460 end = std::min(aiPosEnd + spanStart, GetTextContentLength());
9461 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "get ai selector [%{public}d--%{public}d]", start, end);
9462 return true;
9463 }
9464 return false;
9465 }
9466
9467 void RichEditorPattern::AdjustPlaceholderSelection(int32_t& start, int32_t& end, const Offset& touchPos)
9468 {
9469 CHECK_NULL_VOID(!spans_.empty());
9470 if (!IsTouchBeforeCaret(start, touchPos)) {
9471 return;
9472 }
9473 auto it = std::find_if(spans_.begin(), spans_.end(), [start](const RefPtr<SpanItem>& spanItem) {
9474 return spanItem->position == start;
9475 });
9476 if (it != spans_.end()) {
9477 // adjust selection if touch right of image or placeholder
9478 auto spanIndex = std::distance(spans_.begin(), it);
9479 auto spanNodeBefore = DynamicCast<FrameNode>(GetChildByIndex(spanIndex));
9480 if (spanNodeBefore && (spanNodeBefore->GetTag() == V2::IMAGE_ETS_TAG ||
9481 spanNodeBefore->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG)) {
9482 end = start;
9483 --start;
9484 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "get placeholder selector [%{public}d--%{public}d]", start, end);
9485 }
9486 }
9487 }
9488
9489 bool RichEditorPattern::IsTouchAtLineEnd(int32_t caretPos, const Offset& textOffset)
9490 {
9491 auto positionWithAffinity = paragraphs_.GetGlyphPositionAtCoordinate(textOffset);
9492 TextAffinity currentAffinity = positionWithAffinity.affinity_;
9493 return currentAffinity == TextAffinity::UPSTREAM && !IsTouchBeforeCaret(caretPos, textOffset);
9494 }
9495
9496 bool RichEditorPattern::IsTouchBeforeCaret(int32_t caretPos, const Offset& textOffset) {
9497 CHECK_NULL_RETURN(!spans_.empty(), false);
9498 float selectLineHeight = 0.0f;
9499 OffsetF caretOffsetUp = paragraphs_.ComputeCursorOffset(caretPos, selectLineHeight);
9500 auto needAdjustRect = RectF{ 0, caretOffsetUp.GetY(), caretOffsetUp.GetX(), selectLineHeight };
9501 return needAdjustRect.IsInRegion(PointF{ textOffset.GetX(), textOffset.GetY() });
9502 }
9503
9504 bool RichEditorPattern::IsClickBoundary(const int32_t position)
9505 {
9506 if (InputAIChecker::IsSingleClickAtBoundary(position, GetTextContentLength())) {
9507 return true;
9508 }
9509
9510 float height = 0;
9511 auto handleOffset = CalcCursorOffsetByPosition(position, height);
9512 if (InputAIChecker::IsMultiClickAtBoundary(handleOffset, TextPattern::GetTextRect())) {
9513 return true;
9514 }
9515 return false;
9516 }
9517
9518 std::string RichEditorPattern::GetPositionSpansText(int32_t position, int32_t& startSpan)
9519 {
9520 int32_t start = position - AI_TEXT_RANGE_LEFT;
9521 int32_t end = position + AI_TEXT_RANGE_RIGHT;
9522
9523 start = std::clamp(start, 0, GetTextContentLength());
9524 end = std::clamp(end, 0, GetTextContentLength());
9525 AdjustSelector(start, end);
9526 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "caret=%{public}d, range=[%{public}d,%{public}d]", position, start, end);
9527
9528 // get all the spans between start and end, then filter the valid text
9529 auto infos = GetSpansInfo(start, end, GetSpansMethod::ONSELECT);
9530 if (infos.GetSelection().resultObjects.empty()) {
9531 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "get spans text is null pos:%{public}d,return", position);
9532 return "";
9533 }
9534 auto list = infos.GetSelection().resultObjects;
9535
9536 std::stringstream sstream;
9537 for (const auto& obj : list) {
9538 if (obj.type != SelectSpanType::TYPESPAN) {
9539 if (obj.spanPosition.spanRange[0] == position) {
9540 startSpan = -1;
9541 return "";
9542 } else if (obj.spanPosition.spanRange[1] <= position) {
9543 sstream.str("");
9544 startSpan = -1;
9545 } else {
9546 break;
9547 }
9548 } else {
9549 if (startSpan < 0) {
9550 startSpan = obj.spanPosition.spanRange[0] + obj.offsetInSpan[0];
9551 }
9552 // we should use the wide string deal to avoid crash
9553 auto wideText = obj.valueString;
9554 int32_t textLen = static_cast<int32_t>(wideText.length());
9555 if (obj.offsetInSpan[0] < textLen && obj.offsetInSpan[1] <= textLen) {
9556 sstream << UtfUtils::Str16ToStr8(
9557 wideText.substr(obj.offsetInSpan[0], obj.offsetInSpan[1] - obj.offsetInSpan[0]));
9558 }
9559 }
9560 }
9561
9562 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "get spans text ret spanStart:%{public}d", startSpan);
9563 return sstream.str();
9564 }
9565
9566 bool RichEditorPattern::IsShowSelectMenuUsingMouse()
9567 {
9568 auto host = GetHost();
9569 CHECK_NULL_RETURN(host, false);
9570 auto pipeline = host->GetContext();
9571 CHECK_NULL_RETURN(pipeline, false);
9572 auto selectOverlayManager = pipeline->GetSelectOverlayManager();
9573 CHECK_NULL_RETURN(selectOverlayManager, false);
9574 return selectOverlayManager->GetSelectOverlayInfo().isUsingMouse;
9575 }
9576
9577 RefPtr<FocusHub> RichEditorPattern::GetFocusHub() const
9578 {
9579 auto host = GetHost();
9580 CHECK_NULL_RETURN(host, nullptr);
9581 auto focusHub = host->GetOrCreateFocusHub();
9582 return focusHub;
9583 }
9584
9585 void RichEditorPattern::HandleCursorOnDragMoved(const RefPtr<NotifyDragEvent>& notifyDragEvent)
9586 {
9587 auto host = GetHost();
9588 CHECK_NULL_VOID(host);
9589 if (HasFocus()) {
9590 if (!isCursorAlwaysDisplayed_) {
9591 isCursorAlwaysDisplayed_ = true;
9592 StartTwinkling();
9593 }
9594 if (SystemProperties::GetDebugEnabled()) {
9595 TAG_LOGD(AceLogTag::ACE_RICH_TEXT,
9596 "In OnDragMoved, the cursor has always Displayed in the textField, id:" SEC_PLD(%{public}d),
9597 SEC_PARAM(host->GetId()));
9598 }
9599 return;
9600 }
9601 TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
9602 "In OnDragMoved, the dragging node is moving in the richEditor, id:" SEC_PLD(%{public}d),
9603 SEC_PARAM(host->GetId()));
9604 auto focusHub = GetFocusHub();
9605 CHECK_NULL_VOID(focusHub);
9606 isOnlyRequestFocus_ = true;
9607 focusHub->RequestFocusImmediately();
9608 if (focusHub->IsCurrentFocus()) {
9609 ShowCaretWithoutTwinkling();
9610 }
9611 };
9612
9613 void RichEditorPattern::HandleCursorOnDragLeaved(const RefPtr<NotifyDragEvent>& notifyDragEvent)
9614 {
9615 auto host = GetHost();
9616 CHECK_NULL_VOID(host);
9617 TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
9618 "In OnDragLeaved, the dragging node has left from richEditor, id:" SEC_PLD(%{public}d),
9619 SEC_PARAM(host->GetId()));
9620 auto focusHub = GetFocusHub();
9621 CHECK_NULL_VOID(focusHub);
9622 focusHub->LostFocusToViewRoot();
9623 StopTwinkling();
9624 };
9625
9626 void RichEditorPattern::HandleCursorOnDragEnded(const RefPtr<NotifyDragEvent>& notifyDragEvent)
9627 {
9628 auto host = GetHost();
9629 CHECK_NULL_VOID(host);
9630 auto focusHub = GetFocusHub();
9631 CHECK_NULL_VOID(focusHub);
9632 StopAutoScroll();
9633 if (!isCursorAlwaysDisplayed_) {
9634 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "In OnDragEnded,"
9635 " the released location is not in the current richEditor, id:" SEC_PLD(%{public}d),
9636 SEC_PARAM(host->GetId()));
9637 IF_TRUE(HasFocus(), CloseKeyboard(false));
9638 focusHub->LostFocus();
9639 StopTwinkling();
9640 return;
9641 }
9642 TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
9643 "In OnDragEnded, the released location is in the current richEditor, id:" SEC_PLD(%{public}d),
9644 SEC_PARAM(host->GetId()));
9645 focusHub->LostFocusToViewRoot();
9646 isCursorAlwaysDisplayed_ = false;
9647 StopTwinkling();
9648 };
9649
9650 void RichEditorPattern::HandleOnDragStatusCallback(
9651 const DragEventType& dragEventType, const RefPtr<NotifyDragEvent>& notifyDragEvent)
9652 {
9653 ScrollablePattern::HandleOnDragStatusCallback(dragEventType, notifyDragEvent);
9654 switch (dragEventType) {
9655 case DragEventType::MOVE:
9656 HandleCursorOnDragMoved(notifyDragEvent);
9657 break;
9658 case DragEventType::LEAVE:
9659 HandleCursorOnDragLeaved(notifyDragEvent);
9660 break;
9661 case DragEventType::DROP:
9662 HandleCursorOnDragEnded(notifyDragEvent);
9663 break;
9664 default:
9665 break;
9666 }
9667 }
9668
9669 void RichEditorPattern::HandleOnCameraInput()
9670 {
9671 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnCameraInput");
9672 #if defined(ENABLE_STANDARD_INPUT)
9673 if (richEditTextChangeListener_ == nullptr) {
9674 richEditTextChangeListener_ = new OnTextChangedListenerImpl(WeakClaim(this));
9675 }
9676 auto inputMethod = MiscServices::InputMethodController::GetInstance();
9677 if (!inputMethod) {
9678 return;
9679 }
9680 StartTwinkling();
9681 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
9682 if (imeShown_) {
9683 inputMethod->StartInputType(MiscServices::InputType::CAMERA_INPUT);
9684 } else {
9685 HandleOnEditChanged(true);
9686 auto optionalTextConfig = GetMiscTextConfig();
9687 CHECK_NULL_VOID(optionalTextConfig.has_value());
9688 MiscServices::TextConfig textConfig = optionalTextConfig.value();
9689 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnCameraInput set calling window id is : %{public}u",
9690 textConfig.windowId);
9691 #ifdef WINDOW_SCENE_SUPPORTED
9692 auto systemWindowId = GetSCBSystemWindowId();
9693 if (systemWindowId) {
9694 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "windowId From %{public}u to %{public}u.", textConfig.windowId,
9695 systemWindowId);
9696 textConfig.windowId = systemWindowId;
9697 }
9698 #endif
9699 auto ret = inputMethod->Attach(richEditTextChangeListener_, false, textConfig);
9700 if (ret == MiscServices::ErrorCode::NO_ERROR) {
9701 auto pipeline = GetContext();
9702 CHECK_NULL_VOID(pipeline);
9703 auto textFieldManager = AceType::DynamicCast<TextFieldManagerNG>(pipeline->GetTextFieldManager());
9704 CHECK_NULL_VOID(textFieldManager);
9705 textFieldManager->SetIsImeAttached(true);
9706 }
9707 inputMethod->StartInputType(MiscServices::InputType::CAMERA_INPUT);
9708 inputMethod->ShowTextInput();
9709 }
9710 CloseSelectOverlay();
9711 #endif
9712 #endif
9713 }
9714
9715 bool RichEditorPattern::CanStartAITask()
9716 {
9717 return TextPattern::CanStartAITask() && !isEditing_ && !spans_.empty();
9718 }
9719
9720 bool RichEditorPattern::NeedShowAIDetect()
9721 {
9722 return TextPattern::NeedShowAIDetect() && !isEditing_ && !isShowPlaceholder_ && !spans_.empty();
9723 }
9724
9725 void RichEditorPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
9726 {
9727 /* no fixed attr below, just return */
9728 if (filter.IsFastFilter()) {
9729 return;
9730 }
9731 json->PutExtAttr("enableDataDetector", textDetectEnable_ ? "true" : "false", filter);
9732 json->PutExtAttr("dataDetectorConfig", dataDetectorAdapter_->textDetectConfigStr_.c_str(), filter);
9733 json->PutExtAttr("placeholder", GetPlaceHolderInJson().c_str(), filter);
9734 json->PutExtAttr("bindSelectionMenu", GetBindSelectionMenuInJson().c_str(), filter);
9735 json->PutExtAttr("stopBackPress", isStopBackPress_ ? "true" : "false", filter);
9736 json->PutExtAttr("keyboardAppearance", static_cast<int32_t>(keyboardAppearance_), filter);
9737 json->PutExtAttr("maxLength", maxLength_.value_or(INT_MAX), filter);
9738 json->PutExtAttr("enableHapticFeedback", isEnableHapticFeedback_ ? "true" : "false", filter);
9739 json->PutExtAttr("barState", static_cast<int32_t>(GetBarDisplayMode()), filter);
9740 json->PutExtAttr("enableKeyboardOnFocus", needToRequestKeyboardOnFocus_ ? "true" : "false", filter);
9741 }
9742
9743 void RichEditorPattern::FillPreviewMenuInJson(const std::unique_ptr<JsonValue>& jsonValue) const
9744 {
9745 IF_PRESENT(oneStepDragController_, FillJsonValue(jsonValue));
9746 }
9747
9748 std::string RichEditorPattern::GetPlaceHolderInJson() const
9749 {
9750 auto host = GetHost();
9751 CHECK_NULL_RETURN(host, "");
9752 auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
9753 bool hasPlaceHolder = layoutProperty && layoutProperty->HasPlaceholder()
9754 && !layoutProperty->GetPlaceholder().value().empty();
9755 CHECK_NULL_RETURN(hasPlaceHolder, "");
9756 auto jsonValue = JsonUtil::Create(true);
9757 jsonValue->Put("value", UtfUtils::Str16ToStr8(layoutProperty->GetPlaceholderValue(u"")).c_str());
9758 auto jsonFont = JsonUtil::Create(true);
9759 jsonFont->Put("size", GetFontSizeWithThemeInJson(layoutProperty->GetPlaceholderFontSize()).c_str());
9760 jsonFont->Put("weight", GetFontWeightInJson(layoutProperty->GetPlaceholderFontWeight()).c_str());
9761 jsonFont->Put("family", GetFontFamilyInJson(layoutProperty->GetPlaceholderFontFamily()).c_str());
9762 jsonFont->Put("style", GetFontStyleInJson(layoutProperty->GetPlaceholderItalicFontStyle()).c_str());
9763 auto jsonStyle = JsonUtil::Create(true);
9764 jsonStyle->Put("font", jsonFont->ToString().c_str());
9765 jsonStyle->Put("fontColor", GetTextColorInJson(layoutProperty->GetPlaceholderTextColor()).c_str());
9766 jsonValue->Put("style", jsonStyle->ToString().c_str());
9767 return StringUtils::RestoreBackslash(jsonValue->ToString());
9768 }
9769
9770 std::string RichEditorPattern::GetTextColorInJson(const std::optional<Color>& value) const
9771 {
9772 CHECK_NULL_RETURN(!value, value->ColorToString());
9773 auto host = GetHost();
9774 CHECK_NULL_RETURN(host, "");
9775 auto pipeline = host->GetContext();
9776 CHECK_NULL_RETURN(pipeline, "");
9777 auto richEditorTheme = pipeline->GetTheme<RichEditorTheme>();
9778 CHECK_NULL_RETURN(richEditorTheme, "");
9779 Color textColor = richEditorTheme->GetTextStyle().GetTextColor();
9780 return textColor.ColorToString();
9781 }
9782
9783 void RichEditorPattern::GetCaretMetrics(CaretMetricsF& caretCaretMetric)
9784 {
9785 float caretHeight = 0.0f;
9786 OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight);
9787 auto host = GetHost();
9788 CHECK_NULL_VOID(host);
9789 auto offset = host->GetPaintRectOffsetNG(false, true);
9790 caretOffset += offset;
9791 caretCaretMetric.offset = caretOffset;
9792 caretCaretMetric.height = caretHeight;
9793 }
9794
9795 void RichEditorPattern::OnVirtualKeyboardAreaChanged()
9796 {
9797 CHECK_NULL_VOID(SelectOverlayIsOn() && !selectOverlay_->GetIsHandleMoving() && !selectOverlay_->GetIsHandleHidden());
9798 float selectLineHeight = 0.0f;
9799 textSelector_.selectionBaseOffset.SetX(
9800 CalcCursorOffsetByPosition(textSelector_.GetStart(), selectLineHeight).GetX());
9801 textSelector_.selectionDestinationOffset.SetX(
9802 CalcCursorOffsetByPosition(textSelector_.GetEnd(), selectLineHeight).GetX());
9803 CreateHandles();
9804 }
9805
9806 void RichEditorPattern::ResetDragOption()
9807 {
9808 auto gestureEventHub = GetGestureEventHub();
9809 CHECK_NULL_VOID(gestureEventHub);
9810 if (gestureEventHub->GetIsTextDraggable()) {
9811 CloseSelectOverlay();
9812 ResetSelection();
9813 }
9814 }
9815
9816 void RichEditorPattern::AdjustSelectRects(SelectRectsType pos, std::vector<RectF>& selectRects)
9817 {
9818 if (pos == SelectRectsType::LEFT_TOP_POINT) {
9819 selectRects.erase(std::next(selectRects.begin()), selectRects.end());
9820 selectRects.front().SetSize({0, 0});
9821 } else if (pos == SelectRectsType::RIGHT_BOTTOM_POINT) {
9822 selectRects.erase(selectRects.begin(), std::prev(selectRects.end()));
9823 selectRects.front().SetRect({selectRects.front().Right(), selectRects.front().Bottom()}, {0, 0});
9824 }
9825 }
9826
9827 RectF RichEditorPattern::GetSelectArea(SelectRectsType pos)
9828 {
9829 RectF rect;
9830 auto paintOffset = selectOverlay_->GetPaintOffsetWithoutTransform();
9831 auto selectRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
9832 auto contentRect = contentRect_;
9833 contentRect.SetOffset(contentRect.GetOffset() + paintOffset);
9834 auto host = GetHost();
9835 CHECK_NULL_RETURN(host, rect);
9836 auto parent = host->GetAncestorNodeOfFrame(false);
9837 contentRect = GetVisibleContentRect(parent, contentRect);
9838 AppendSelectRect(selectRects);
9839 if (selectRects.empty()) {
9840 CHECK_NULL_RETURN(overlayMod_, rect);
9841 auto richEditorOverlay = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
9842 CHECK_NULL_RETURN(richEditorOverlay, rect);
9843 auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
9844 auto caretWidth = Dimension(1.5f, DimensionUnit::VP).ConvertToPx();
9845 auto selectRect = RectF(caretOffset + paintOffset, SizeF(caretWidth, caretHeight));
9846 return selectRect.IntersectRectT(contentRect);
9847 }
9848 AdjustSelectRects(pos, selectRects);
9849 auto frontRect = selectRects.front();
9850 auto backRect = selectRects.back();
9851 float selectAreaRight = frontRect.Right();
9852 float selectAreaLeft = frontRect.Left();
9853 if (selectRects.size() != 1) {
9854 std::unordered_map<float, RectF> selectLineRect;
9855 for (const auto& box : selectRects) {
9856 auto combineLineRect = box;
9857 auto top = box.Top();
9858 if (selectLineRect.find(top) == selectLineRect.end()) {
9859 selectLineRect.insert({ top, combineLineRect });
9860 } else {
9861 combineLineRect = combineLineRect.CombineRectT(selectLineRect[top]);
9862 selectLineRect.insert({ top, combineLineRect });
9863 }
9864 selectAreaRight = std::max(selectAreaRight, combineLineRect.Right());
9865 selectAreaLeft = std::min(selectAreaLeft, combineLineRect.Left());
9866 }
9867 }
9868 RectF res = { selectAreaLeft + richTextRect_.GetX() + paintOffset.GetX(),
9869 frontRect.GetY() + richTextRect_.GetY() + paintOffset.GetY(), selectAreaRight - selectAreaLeft,
9870 backRect.Bottom() - frontRect.Top() };
9871 return res.IntersectRectT(contentRect);
9872 }
9873
9874 void RichEditorPattern::AppendSelectRect(std::vector<RectF>& selectRects)
9875 {
9876 CHECK_NULL_VOID(!selectOverlay_->IsSingleHandle());
9877 auto startPosition = textSelector_.GetTextStart();
9878 auto endPosition = textSelector_.GetTextEnd();
9879 auto height = 0.0f;
9880 auto caretWidth = Dimension(1.5f, DimensionUnit::VP).ConvertToPx();
9881 if (paragraphs_.IsIndexAtParagraphEnd(startPosition + 1)) {
9882 auto offset = paragraphs_.ComputeCursorOffset(startPosition, height, false, false);
9883 RectF rect = RectF(offset.GetX(), offset.GetY(), caretWidth, height);
9884 selectRects.insert(selectRects.begin(), rect);
9885 }
9886 if (paragraphs_.IsIndexAtParagraphEnd(endPosition)) {
9887 auto offset = paragraphs_.ComputeCursorOffset(endPosition, height, true, false);
9888 RectF rect = RectF(offset.GetX(), offset.GetY(), caretWidth, height);
9889 selectRects.emplace_back(rect);
9890 }
9891 }
9892
9893 bool RichEditorPattern::IsTouchInFrameArea(const PointF& touchPoint)
9894 {
9895 auto host = GetHost();
9896 CHECK_NULL_RETURN(host, false);
9897 auto viewPort = RectF(parentGlobalOffset_, frameRect_.GetSize());
9898 auto parent = host->GetAncestorNodeOfFrame(false);
9899 viewPort = GetVisibleContentRect(parent, viewPort);
9900 return viewPort.IsInRegion(touchPoint);
9901 }
9902
9903 bool RichEditorPattern::SetPlaceholder(std::vector<std::list<RefPtr<SpanItem>>>& spanItemList)
9904 {
9905 if (!spans_.empty()) {
9906 isShowPlaceholder_ = false;
9907 return false;
9908 }
9909 auto host = GetHost();
9910 CHECK_NULL_RETURN(host, false);
9911 auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
9912 CHECK_NULL_RETURN(layoutProperty, false);
9913 if (!layoutProperty->HasPlaceholder() || layoutProperty->GetPlaceholder().value().empty()) {
9914 isShowPlaceholder_ = false;
9915 return false;
9916 }
9917 auto placeholderValue = layoutProperty->GetPlaceholder().value();
9918 auto* stack = ViewStackProcessor::GetInstance();
9919 CHECK_NULL_RETURN(stack, false);
9920 auto nodeId = stack->ClaimNodeId();
9921 auto placeholderNode = SpanNode::GetOrCreateSpanNode(nodeId);
9922 CHECK_NULL_RETURN(placeholderNode, false);
9923 if (layoutProperty->HasPlaceholderFontSize()) {
9924 placeholderNode->UpdateFontSize(layoutProperty->GetPlaceholderFontSize().value());
9925 }
9926 if (layoutProperty->HasPlaceholderFontWeight()) {
9927 placeholderNode->UpdateFontWeight(layoutProperty->GetPlaceholderFontWeight().value());
9928 }
9929 if (layoutProperty->HasPlaceholderFontFamily()) {
9930 placeholderNode->UpdateFontFamily(layoutProperty->GetPlaceholderFontFamily().value());
9931 }
9932 if (layoutProperty->HasPlaceholderItalicFontStyle()) {
9933 placeholderNode->UpdateItalicFontStyle(layoutProperty->GetPlaceholderItalicFontStyle().value());
9934 }
9935 if (layoutProperty->HasPlaceholderTextColor()) {
9936 placeholderNode->UpdateTextColorWithoutCheck(layoutProperty->GetPlaceholderTextColor().value());
9937 } else {
9938 auto theme = GetTheme<RichEditorTheme>();
9939 placeholderNode->UpdateTextColorWithoutCheck(theme ? theme->GetPlaceholderColor() : Color());
9940 }
9941
9942 auto spanItem = placeholderNode->GetSpanItem();
9943 CHECK_NULL_RETURN(spanItem, false);
9944 spanItem->content = placeholderValue;
9945 spanItemList.clear();
9946 spanItemList.push_back({ { {spanItem} } });
9947 isShowPlaceholder_ = true;
9948 return true;
9949 }
9950
9951 std::string RichEditorPattern::GetPlaceHolder() const
9952 {
9953 auto host = GetHost();
9954 CHECK_NULL_RETURN(host, "");
9955 auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
9956 CHECK_NULL_RETURN(layoutProperty, "");
9957 return UtfUtils::Str16ToStr8(layoutProperty->GetPlaceholderValue(u""));
9958 }
9959
9960 Color RichEditorPattern::GetCaretColor()
9961 {
9962 if (caretColor_.has_value()) {
9963 return caretColor_.value();
9964 }
9965 auto richEditorTheme = GetTheme<RichEditorTheme>();
9966 CHECK_NULL_RETURN(richEditorTheme, SYSTEM_CARET_COLOR);
9967 return richEditorTheme->GetCaretColor();
9968 }
9969
9970 Color RichEditorPattern::GetSelectedBackgroundColor()
9971 {
9972 Color selectedBackgroundColor;
9973 if (selectedBackgroundColor_.has_value()) {
9974 selectedBackgroundColor = selectedBackgroundColor_.value();
9975 } else {
9976 auto richEditorTheme = GetTheme<RichEditorTheme>();
9977 CHECK_NULL_RETURN(richEditorTheme, SYSTEM_SELECT_BACKGROUND_COLOR);
9978 selectedBackgroundColor = richEditorTheme->GetSelectedBackgroundColor();
9979 }
9980 // Alpha == 255 Means completely opaque
9981 if (selectedBackgroundColor.GetAlpha() == COLOR_OPAQUE) {
9982 selectedBackgroundColor = selectedBackgroundColor.ChangeOpacity(DEFAILT_OPACITY);
9983 }
9984 return selectedBackgroundColor;
9985 }
9986
9987 void RichEditorPattern::HandleOnDragDropStyledString(const RefPtr<OHOS::Ace::DragEvent>& event, bool isCopy)
9988 {
9989 CHECK_NULL_VOID(event);
9990 auto data = event->GetData();
9991 CHECK_NULL_VOID(data);
9992 auto arr = UdmfClient::GetInstance()->GetSpanStringRecord(data);
9993 if (!arr.empty()) {
9994 auto spanStr = SpanString::DecodeTlv(arr);
9995 if (!spanStr->GetSpanItems().empty()) {
9996 if (isSpanStringMode_) {
9997 HandleOnDragInsertStyledString(spanStr, isCopy);
9998 return;
9999 }
10000 AddSpanByPasteData(spanStr);
10001 return;
10002 }
10003 }
10004
10005 auto records = UdmfClient::GetInstance()->GetPlainTextRecords(data);
10006 if (records.empty()) {
10007 return;
10008 }
10009 std::string str;
10010 for (const auto& record : records) {
10011 str += record;
10012 }
10013 if (str.empty()) {
10014 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "text is empty.");
10015 return;
10016 }
10017 if (isSpanStringMode_) {
10018 InsertValueInStyledString(UtfUtils::Str8ToStr16(str));
10019 } else {
10020 HandleOnDragDropTextOperation(UtfUtils::Str8ToStr16(str), isDragSponsor_, isCopy);
10021 }
10022 }
10023
10024 void RichEditorPattern::HandleOnDragDrop(const RefPtr<OHOS::Ace::DragEvent>& event, bool isCopy)
10025 {
10026 auto host = GetHost();
10027 CHECK_NULL_VOID(host);
10028 auto eventHub = host->GetEventHub<RichEditorEventHub>();
10029 CHECK_NULL_VOID(eventHub);
10030 TextCommonEvent textCommonEvent;
10031 if (textCommonEvent.IsPreventDefault()) {
10032 CloseSelectOverlay();
10033 ResetSelection();
10034 StartTwinkling();
10035 return;
10036 }
10037 HandleOnDragDropStyledString(event, isCopy);
10038 if (textSelector_.IsValid()) {
10039 CloseSelectOverlay();
10040 ResetSelection();
10041 }
10042 auto focusHub = host->GetOrCreateFocusHub();
10043 CHECK_NULL_VOID(focusHub);
10044 if (focusHub->IsCurrentFocus()) {
10045 StartTwinkling();
10046 }
10047 afterDragSelect_ = isMouseOrTouchPad(sourceTool_);
10048 releaseInDrop_ = true;
10049 }
10050
10051 void RichEditorPattern::DeleteForward(int32_t currentPosition, int32_t length)
10052 {
10053 RichEditorDeleteValue info;
10054 info.SetOffset(currentPosition);
10055 info.SetRichEditorDeleteDirection(RichEditorDeleteDirection::FORWARD);
10056 info.SetLength(length);
10057 CalcDeleteValueObj(currentPosition, length, info);
10058 DeleteByDeleteValueInfo(info);
10059 }
10060
10061 int32_t RichEditorPattern::HandleOnDragDeleteForward()
10062 {
10063 int32_t allDelLength = 0;
10064 SelectionInfo textSelectInfo = GetSpansInfo(dragRange_.first, dragRange_.second, GetSpansMethod::ONSELECT);
10065 std::list<ResultObject> dragResultObjects = textSelectInfo.GetSelection().resultObjects;
10066 for (auto ri = dragResultObjects.rbegin(); ri != dragResultObjects.rend(); ++ri) {
10067 if (SelectSpanType::TYPESPAN == ri->type || (SelectSpanType::TYPEIMAGE == ri->type && ri->valueString != u" ")) {
10068 int32_t spanStart = ri->offsetInSpan[RichEditorSpanRange::RANGESTART];
10069 int32_t spanEnd = ri->offsetInSpan[RichEditorSpanRange::RANGEEND];
10070 int32_t reStart = ri->spanPosition.spanRange[RichEditorSpanRange::RANGESTART];
10071 int32_t delStart = reStart;
10072 if (spanStart > 0) {
10073 delStart += spanStart;
10074 }
10075 int32_t delLength = spanEnd - spanStart;
10076 DeleteForward(delStart, delLength);
10077 allDelLength += delLength;
10078 }
10079 }
10080 return allDelLength;
10081 }
10082
10083 void RichEditorPattern::HandleOnDragDropTextOperation(const std::u16string& insertValue, bool isDeleteSelect, bool isCopy)
10084 {
10085 insertValueLength_ = static_cast<int32_t>(insertValue.length());
10086 if (!isDeleteSelect || isCopy) {
10087 InsertValueByOperationType(insertValue, OperationType::DRAG);
10088 return;
10089 }
10090 int32_t currentPosition = caretPosition_;
10091 int32_t strLength = static_cast<int32_t>(insertValue.length());
10092 OperationRecord record;
10093 record.addText = insertValue;
10094 record.beforeCaretPosition = dragRange_.first;
10095 RichEditorChangeValue changeValue;
10096 // drag not move, do not fire contentChange
10097 if (dragRange_.first <= currentPosition && currentPosition <= dragRange_.second) {
10098 lastCaretPosition_ = dragRange_.first;
10099 return;
10100 }
10101 CHECK_NULL_VOID(BeforeChangeText(changeValue, record, RecordType::DRAG));
10102 if (currentPosition < dragRange_.first) {
10103 InsertValueByOperationType(insertValue, OperationType::DRAG);
10104 dragRange_.first += strLength;
10105 dragRange_.second += strLength;
10106 HandleOnDragDeleteForward();
10107 lastCaretPosition_ = currentPosition;
10108 } else if (currentPosition > dragRange_.second) {
10109 InsertValueByOperationType(insertValue, OperationType::DRAG);
10110 int32_t delLength = HandleOnDragDeleteForward();
10111 caretPosition_ -= delLength;
10112 lastCaretPosition_ = currentPosition - strLength;
10113 }
10114
10115 AfterContentChange(changeValue);
10116 }
10117
10118 void RichEditorPattern::UndoDrag(const OperationRecord& record)
10119 {
10120 if (!record.addText.has_value() || record.deleteCaretPostion == -1) {
10121 return;
10122 }
10123 const auto& str = record.addText.value();
10124 int32_t length = static_cast<int32_t>(str.length());
10125 DeleteForward(record.beforeCaretPosition, length);
10126
10127 lastCaretPosition_ = caretPosition_;
10128 caretPosition_ = record.deleteCaretPostion;
10129 InsertValueOperation(str, nullptr, OperationType::DEFAULT);
10130 }
10131
10132 void RichEditorPattern::RedoDrag(const OperationRecord& record)
10133 {
10134 if (!record.addText.has_value() || record.deleteCaretPostion == -1) {
10135 return;
10136 }
10137 RichEditorChangeValue changeValue;
10138 CHECK_NULL_VOID(BeforeChangeText(changeValue, record, RecordType::REDO));
10139 const auto& str = record.addText.value();
10140 int32_t length = static_cast<int32_t>(str.length());
10141 DeleteForward(record.deleteCaretPostion, length);
10142 lastCaretPosition_ = caretPosition_;
10143 caretPosition_ = record.beforeCaretPosition;
10144 InsertValueOperation(str, nullptr, OperationType::DRAG);
10145 AfterContentChange(changeValue);
10146 }
10147
10148 void RichEditorPattern::HandleOnDragInsertValueOperation(const std::u16string& insertValue)
10149 {
10150 InsertValueByOperationType(insertValue, OperationType::DRAG);
10151 }
10152
10153 void RichEditorPattern::HandleOnDragInsertValue(const std::u16string& insertValue)
10154 {
10155 OperationRecord record;
10156 record.beforeCaretPosition = caretPosition_ + moveLength_;
10157 if (textSelector_.IsValid()) {
10158 record.beforeCaretPosition = textSelector_.GetTextStart();
10159 }
10160 record.addText = insertValue;
10161 ClearRedoOperationRecords();
10162 InsertValueByOperationType(insertValue, OperationType::DRAG);
10163 int32_t length = dragRange_.second - dragRange_.first;
10164 record.afterCaretPosition = record.beforeCaretPosition + length;
10165 record.deleteCaretPostion = dragRange_.first;
10166 AddOperationRecord(record);
10167 }
10168
10169 bool RichEditorPattern::IsEditing()
10170 {
10171 return isEditing_;
10172 }
10173
10174 void RichEditorPattern::HandleOnEditChanged(bool isEditing)
10175 {
10176 CHECK_NULL_VOID(isEditing_ != isEditing);
10177 auto host = GetHost();
10178 CHECK_NULL_VOID(host);
10179 auto eventHub = host->GetEventHub<RichEditorEventHub>();
10180 CHECK_NULL_VOID(eventHub);
10181
10182 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "editState->%{public}d", isEditing);
10183 isEditing_ = isEditing;
10184 eventHub->FireOnEditingChange(isEditing);
10185
10186 if (CanStartAITask()) {
10187 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "leave edit state, start AI task");
10188 dataDetectorAdapter_->StartAITask();
10189 } else {
10190 if (isEditing) {
10191 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "enter edit state, reset previewLongPress_");
10192 previewLongPress_ = false;
10193 }
10194 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
10195 }
10196 }
10197
10198 void RichEditorPattern::ResetKeyboardIfNeed()
10199 {
10200 bool needToResetKeyboard = false;
10201 auto currentAction = GetTextInputActionValue(GetDefaultTextInputAction());
10202 // When the enter key type changes, the keyboard needs to be reset.
10203 if (action_ != TextInputAction::UNSPECIFIED) {
10204 needToResetKeyboard = action_ != currentAction;
10205 }
10206 action_ = currentAction;
10207 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
10208 if (needToResetKeyboard) {
10209 // if keyboard attached or keyboard is shown, pull up keyboard again
10210 if (imeShown_ || isCustomKeyboardAttached_) {
10211 if (HasFocus()) {
10212 RequestKeyboard(false, true, true);
10213 }
10214 return;
10215 }
10216 #if defined(ENABLE_STANDARD_INPUT)
10217 auto inputMethod = MiscServices::InputMethodController::GetInstance();
10218 CHECK_NULL_VOID(inputMethod);
10219 MiscServices::Configuration config;
10220 config.SetEnterKeyType(static_cast<MiscServices::EnterKeyType>(action_));
10221 config.SetTextInputType(static_cast<MiscServices::TextInputType>(keyboard_));
10222 inputMethod->OnConfigurationChange(config);
10223 #endif
10224 }
10225 #else
10226 if (needToResetKeyboard && HasConnection()) {
10227 CloseSelectOverlay();
10228 ResetSelection();
10229 CloseKeyboard(false);
10230 RequestKeyboard(false, true, true);
10231 }
10232 #endif
10233 }
10234
10235 void RichEditorPattern::OnTextInputActionUpdate(TextInputAction value) {}
10236
10237 void RichEditorPattern::PerformAction(TextInputAction action, bool forceCloseKeyboard)
10238 {
10239 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "PerformAction, action=%{public}d", action);
10240 auto host = GetHost();
10241 CHECK_NULL_VOID(host);
10242 // When the Enter key is triggered, perform a line feed operation.
10243 if (action == TextInputAction::NEW_LINE) {
10244 InsertValue(u"\n", true);
10245 }
10246 // Enter key type callback
10247 TextFieldCommonEvent event;
10248 auto eventHub = host->GetEventHub<RichEditorEventHub>();
10249 eventHub->FireOnSubmit(static_cast<int32_t>(action), event);
10250 // If the developer wants to keep editing, editing will not stop
10251 if (event.IsKeepEditable() || action == TextInputAction::NEW_LINE) {
10252 return;
10253 }
10254 // Exit the editing state
10255 StopEditing();
10256 }
10257
10258 void RichEditorPattern::StopEditing()
10259 {
10260 CHECK_NULL_VOID(HasFocus());
10261 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "StopEditing");
10262
10263 // The selection status disappears, the cursor is hidden, and the soft keyboard is exited
10264 HandleBlurEvent();
10265 // In order to avoid the physical keyboard being able to type, you need to make sure that you lose focus
10266 FocusHub::LostFocusToViewRoot();
10267 }
10268
10269 TextInputAction RichEditorPattern::GetDefaultTextInputAction() const
10270 {
10271 // As with TextInput, it is a line break by default
10272 return TextInputAction::NEW_LINE;
10273 }
10274
10275 void RichEditorPattern::GetChangeSpanStyle(RichEditorChangeValue& changeValue, std::optional<TextStyle>& spanTextStyle,
10276 std::optional<struct UpdateParagraphStyle>& spanParaStyle, std::optional<std::u16string>& urlAddress,
10277 const RefPtr<SpanNode>& spanNode, int32_t spanIndex)
10278 {
10279 auto originalSpans = changeValue.GetRichEditorOriginalSpans();
10280 if (spanIndex == 0 && originalSpans.size()) {
10281 const RichEditorAbstractSpanResult& firstInfo = originalSpans.front();
10282 const RichEditorAbstractSpanResult& lastInfo = originalSpans.back();
10283 int32_t firstLength = static_cast<int32_t>(firstInfo.GetValue().length());
10284 int32_t lastLength = static_cast<int32_t>(lastInfo.GetValue().length());
10285 if (firstInfo.GetEraseLength() == firstLength && lastInfo.GetEraseLength() == lastLength) {
10286 if (spans_.size() == originalSpans.size() ||
10287 static_cast<int32_t>(spans_.size()) == (lastInfo.GetSpanIndex() + 1)) {
10288 urlAddress.reset();
10289 return; // all spanNode be deleted, set default style
10290 }
10291 spanIndex = lastInfo.GetSpanIndex() + 1;
10292 } else if (firstInfo.GetEraseLength() == firstLength) {
10293 spanIndex = lastInfo.GetSpanIndex();
10294 }
10295 auto it = spans_.begin();
10296 std::advance(it, spanIndex);
10297 if ((*it)->unicode != 0 || DynamicCast<PlaceholderSpanItem>(*it)) {
10298 return; // is not a textSpan(Image/Symbol/other)
10299 }
10300 spanTextStyle = (*it)->GetTextStyle();
10301 struct UpdateParagraphStyle paraStyle;
10302 paraStyle.textAlign = (*it)->textLineStyle->GetTextAlign();
10303 paraStyle.leadingMargin = (*it)->textLineStyle->GetLeadingMargin();
10304 paraStyle.wordBreak = (*it)->textLineStyle->GetWordBreak();
10305 paraStyle.lineBreakStrategy = (*it)->textLineStyle->GetLineBreakStrategy();
10306 paraStyle.paragraphSpacing = (*it)->textLineStyle->GetParagraphSpacing();
10307 spanParaStyle = paraStyle;
10308 } else if (spanNode && spanNode->GetSpanItem()) {
10309 spanTextStyle = spanNode->GetSpanItem()->GetTextStyle();
10310 struct UpdateParagraphStyle paraStyle;
10311 paraStyle.textAlign = spanNode->GetTextAlign();
10312 paraStyle.leadingMargin = spanNode->GetLeadingMarginValue({});
10313 paraStyle.wordBreak = spanNode->GetWordBreak();
10314 paraStyle.lineBreakStrategy = spanNode->GetLineBreakStrategy();
10315 paraStyle.paragraphSpacing = spanNode->GetParagraphSpacing();
10316 spanParaStyle = paraStyle;
10317 }
10318 }
10319
10320 void RichEditorPattern::GetReplacedSpan(RichEditorChangeValue& changeValue, int32_t& innerPosition,
10321 const std::u16string& insertValue, int32_t textIndex, std::optional<TextStyle> textStyle,
10322 std::optional<struct UpdateParagraphStyle> paraStyle, std::optional<std::u16string> urlAddress,
10323 bool isCreate, bool fixDel)
10324 {
10325 std::u16string originalStr;
10326 int32_t originalPos = 0;
10327 RefPtr<SpanItem> spanItem = fixDel ? GetDelPartiallySpanItem(changeValue, originalStr, originalPos) : nullptr;
10328 TextInsertValueInfo info;
10329 CalcInsertValueObj(info, textIndex, isCreate);
10330 int32_t spanIndex = info.GetSpanIndex();
10331 int32_t offsetInSpan = info.GetOffsetInSpan();
10332 auto host = GetHost();
10333 CHECK_NULL_VOID(host);
10334 auto uiNode = host->GetChildAtIndex(spanIndex);
10335 RefPtr<SpanNode> spanNode = DynamicCast<SpanNode>(uiNode);
10336 if (!isCreate && textIndex && uiNode && uiNode->GetTag() != V2::SPAN_ETS_TAG) {
10337 spanNode = nullptr;
10338 ++spanIndex; // select/create a new span When the span is not a textSpan(Image/Symbol/other)
10339 offsetInSpan = 0;
10340 spanNode = DynamicCast<SpanNode>(host->GetChildAtIndex(spanIndex));
10341 }
10342
10343 changeValue.SetRangeAfter({ innerPosition, innerPosition + insertValue.length()});
10344 std::u16string textTemp = insertValue;
10345 if (!textStyle && !isCreate && spanNode) {
10346 if (typingStyle_ && !HasSameTypingStyle(spanNode)) {
10347 textStyle = typingTextStyle_; // create a new span When have a different typingStyle
10348 bool insertInSpan = textIndex && offsetInSpan;
10349 spanIndex = insertInSpan ? spanIndex + 1 : spanIndex;
10350 offsetInSpan = 0;
10351 } else {
10352 textTemp = spanNode->GetSpanItem()->content;
10353 textTemp.insert(offsetInSpan, insertValue);
10354 urlAddress = spanNode->GetSpanItem()->urlAddress;
10355 }
10356 }
10357
10358 auto it = textTemp.find(LINE_SEPARATOR);
10359 bool containNextLine = it != std::u16string::npos;
10360
10361 if (textStyle || containNextLine) { // SpanNode Fission
10362 GetReplacedSpanFission(changeValue, innerPosition, textTemp, spanIndex, offsetInSpan, textStyle, paraStyle,
10363 urlAddress);
10364 } else {
10365 std::optional<TextStyle> spanTextStyle = textStyle ? textStyle : typingTextStyle_;
10366 std::optional<struct UpdateParagraphStyle> spanParaStyle = paraStyle;
10367 GetChangeSpanStyle(changeValue, spanTextStyle, spanParaStyle, urlAddress, spanNode, spanIndex);
10368 CreateSpanResult(changeValue, innerPosition, spanIndex, offsetInSpan, offsetInSpan + insertValue.length(),
10369 textTemp, spanTextStyle, spanParaStyle, urlAddress);
10370 innerPosition += static_cast<int32_t>(insertValue.length());
10371 }
10372
10373 if (spanItem) {
10374 spanItem->content = originalStr;
10375 spanItem->position = originalPos;
10376 }
10377 }
10378
10379 void RichEditorPattern::GetReplacedSpanFission(RichEditorChangeValue& changeValue, int32_t& innerPosition,
10380 std::u16string& content, int32_t startSpanIndex, int32_t offsetInSpan, std::optional<TextStyle> textStyle,
10381 std::optional<struct UpdateParagraphStyle> paraStyle, const std::optional<std::u16string>& urlAddress)
10382 {
10383 int spanIndex = startSpanIndex;
10384 auto index = content.find(LINE_SEPARATOR);
10385 while (index != std::u16string::npos) {
10386 auto textAfter = content.substr(index + 1);
10387 if (textAfter.empty()) {
10388 break;
10389 }
10390 auto textBefore = content.substr(0, index + 1);
10391 if (offsetInSpan != static_cast<int32_t>(textBefore.length())) {
10392 CreateSpanResult(changeValue, innerPosition, spanIndex, offsetInSpan, textBefore.length(),
10393 textBefore, textStyle, paraStyle, urlAddress);
10394 innerPosition += textBefore.length() - offsetInSpan;
10395 }
10396 content = textAfter;
10397 index = content.find(LINE_SEPARATOR);
10398 offsetInSpan = 0;
10399 ++spanIndex;
10400 }
10401 CreateSpanResult(changeValue, innerPosition, spanIndex, offsetInSpan, content.length(),
10402 content, textStyle, paraStyle, urlAddress);
10403 innerPosition += static_cast<int32_t>(content.length());
10404 }
10405
10406 void RichEditorPattern::CreateSpanResult(RichEditorChangeValue& changeValue, int32_t& innerPosition, int32_t spanIndex,
10407 int32_t offsetInSpan, int32_t endInSpan, std::u16string content, std::optional<TextStyle> textStyle,
10408 std::optional<struct UpdateParagraphStyle> paraStyle, const std::optional<std::u16string>& urlAddress)
10409 {
10410 RichEditorAbstractSpanResult retInfo;
10411 if (textStyle) {
10412 SetTextStyleToRet(retInfo, *textStyle);
10413 } else {
10414 retInfo.SetFontColor((Color::BLACK).ColorToString());
10415 retInfo.SetFontSize(Dimension(DEFAULT_TEXT_SIZE, DimensionUnit::FP).ConvertToVp());
10416 retInfo.SetFontStyle(OHOS::Ace::FontStyle::NORMAL);
10417 retInfo.SetFontWeight(static_cast<int32_t>(FontWeight::NORMAL));
10418 retInfo.SetTextDecoration(TextDecoration::NONE);
10419 retInfo.SetColor((Color::BLACK).ColorToString());
10420 retInfo.SetFontFamily("HarmonyOS Sans");
10421 }
10422 IF_TRUE(urlAddress.has_value(), retInfo.SetUrlAddress(urlAddress.value()));
10423 retInfo.SetSpanIndex(spanIndex);
10424 if (!previewTextRecord_.newPreviewContent.empty()) {
10425 retInfo.SetPreviewText(previewTextRecord_.newPreviewContent);
10426 } else {
10427 retInfo.SetValue(content);
10428 }
10429 int32_t rangStart = std::max(0, innerPosition - offsetInSpan);
10430 retInfo.SetSpanRangeStart(rangStart);
10431 retInfo.SetSpanRangeEnd(rangStart + content.length());
10432 retInfo.SetOffsetInSpan(offsetInSpan);
10433 retInfo.SetEraseLength(endInSpan - offsetInSpan);
10434 if (paraStyle) {
10435 TextStyleResult textStyleResult = retInfo.GetTextStyle();
10436 textStyleResult.textAlign = static_cast<int32_t>(paraStyle->textAlign.value_or(TextAlign::START));
10437 if (paraStyle->leadingMargin) {
10438 textStyleResult.leadingMarginSize[0] = paraStyle->leadingMargin->size.Width().ToString();
10439 textStyleResult.leadingMarginSize[1] = paraStyle->leadingMargin->size.Height().ToString();
10440 }
10441 IF_TRUE(paraStyle->wordBreak.has_value(),
10442 textStyleResult.wordBreak = static_cast<int32_t>(paraStyle->wordBreak.value()));
10443 IF_TRUE(paraStyle->lineBreakStrategy.has_value(),
10444 textStyleResult.lineBreakStrategy = static_cast<int32_t>(paraStyle->lineBreakStrategy.value()));
10445 IF_TRUE(paraStyle->paragraphSpacing.has_value(), textStyleResult.paragraphSpacing =
10446 Dimension(paraStyle->paragraphSpacing.value().ConvertToFp(), DimensionUnit::FP));
10447 retInfo.SetTextStyle(textStyleResult);
10448 }
10449 changeValue.SetRichEditorReplacedSpans(retInfo);
10450 }
10451
10452 void RichEditorPattern::SetTextStyleToRet(RichEditorAbstractSpanResult& retInfo, const TextStyle& textStyle)
10453 {
10454 retInfo.SetTextDecoration(textStyle.GetTextDecoration());
10455 retInfo.SetFontColor(textStyle.GetTextColor().ColorToString());
10456 retInfo.SetColor(textStyle.GetTextDecorationColor().ColorToString());
10457 retInfo.SetTextDecorationStyle(textStyle.GetTextDecorationStyle());
10458 retInfo.SetFontSize(textStyle.GetFontSize().ConvertToVp());
10459 retInfo.SetFontStyle(textStyle.GetFontStyle());
10460 TextStyleResult textStyleResult;
10461 textStyleResult.lineHeight = textStyle.GetLineHeight().ConvertToVp();
10462 textStyleResult.halfLeading = textStyle.GetHalfLeading();
10463 textStyleResult.letterSpacing = textStyle.GetLetterSpacing().ConvertToVp();
10464 textStyleResult.textShadows = textStyle.GetTextShadows();
10465 textStyleResult.textBackgroundStyle = textStyle.GetTextBackgroundStyle();
10466 retInfo.SetTextStyle(textStyleResult);
10467 retInfo.SetLineHeight(textStyle.GetLineHeight().ConvertToVp());
10468 retInfo.SetHalfLeading(textStyle.GetHalfLeading());
10469 retInfo.SetLetterspacing(textStyle.GetLetterSpacing().ConvertToVp());
10470 retInfo.SetFontFeature(textStyle.GetFontFeatures());
10471 std::string fontFamilyValue;
10472 auto fontFamily = textStyle.GetFontFamilies();
10473 for (const auto& str : fontFamily) {
10474 fontFamilyValue += str;
10475 }
10476 retInfo.SetFontFamily(fontFamilyValue);
10477 retInfo.SetFontWeight((int32_t)textStyle.GetFontWeight());
10478 }
10479
10480 void RichEditorPattern::CalcInsertValueObj(TextInsertValueInfo& info, int textIndex, bool isCreate)
10481 {
10482 if (spans_.empty()) {
10483 info.SetSpanIndex(0);
10484 info.SetOffsetInSpan(0);
10485 return;
10486 }
10487 auto it = std::find_if(
10488 spans_.begin(), spans_.end(), [caretPosition = textIndex](const RefPtr<SpanItem>& spanItem) {
10489 auto spanLength = static_cast<int32_t>(spanItem->content.length());
10490 if (spanLength == 0) {
10491 return spanItem->position == caretPosition;
10492 }
10493 return (spanItem->position - spanLength <= caretPosition) && (caretPosition <= spanItem->position);
10494 });
10495 if (it == spans_.end()) {
10496 info.SetSpanIndex(static_cast<int32_t>(spans_.size()) - 1);
10497 info.SetOffsetInSpan((*spans_.rbegin())->content.length());
10498 return;
10499 }
10500 if (textIndex && isCreate) {
10501 info.SetSpanIndex(std::distance(spans_.begin(), it) + 1);
10502 info.SetOffsetInSpan(0);
10503 return;
10504 }
10505 if ((*it)->content.back() == '\n' && (*it)->position == textIndex) { // next line/span begin
10506 info.SetSpanIndex(std::distance(spans_.begin(), it) + 1);
10507 info.SetOffsetInSpan(0);
10508 } else {
10509 info.SetSpanIndex(std::distance(spans_.begin(), it));
10510 int32_t spanStart = (*it)->position - static_cast<int32_t>((*it)->content.length());
10511 info.SetOffsetInSpan(textIndex - spanStart);
10512 }
10513 }
10514
10515 void RichEditorPattern::GetDeletedSpan(RichEditorChangeValue& changeValue, int32_t& innerPosition,
10516 int32_t length, RichEditorDeleteDirection direction)
10517 {
10518 RichEditorDeleteValue info;
10519 if (!textSelector_.SelectNothing()) {
10520 length = textSelector_.GetTextEnd() - textSelector_.GetTextStart();
10521 innerPosition = std::min(textSelector_.GetStart(), textSelector_.GetEnd());
10522 } else if (!previewTextRecord_.previewContent.empty() || previewTextRecord_.needReplaceText) {
10523 length = previewTextRecord_.replacedRange.end - previewTextRecord_.replacedRange.start;
10524 innerPosition = previewTextRecord_.replacedRange.start;
10525 } else {
10526 int32_t emojiLength = CalculateDeleteLength(length, (direction == RichEditorDeleteDirection::BACKWARD));
10527 if (direction == RichEditorDeleteDirection::BACKWARD) {
10528 innerPosition -= emojiLength;
10529 }
10530 if (length < emojiLength) {
10531 length = emojiLength;
10532 }
10533 }
10534
10535 info.SetOffset(innerPosition);
10536 info.SetRichEditorDeleteDirection(direction);
10537 info.SetLength(length);
10538 if (!spans_.empty()) {
10539 CalcDeleteValueObj(innerPosition, length, info);
10540 }
10541 if (!spans_.empty() || isAPI14Plus) {
10542 changeValue.SetRangeBefore({ innerPosition, innerPosition + length });
10543 changeValue.SetRangeAfter({ innerPosition, innerPosition });
10544 }
10545 const std::list<RichEditorAbstractSpanResult>& resultList = info.GetRichEditorDeleteSpans();
10546 for (auto& it : resultList) {
10547 if (it.GetType() == SpanResultType::TEXT) {
10548 changeValue.SetRichEditorOriginalSpans(it);
10549 } else if (it.GetType() == SpanResultType::SYMBOL && textSelector_.SelectNothing() &&
10550 previewTextRecord_.previewContent.empty()) {
10551 int32_t symbolStart = it.GetSpanRangeStart();
10552 changeValue.SetRichEditorOriginalSpans(it);
10553 changeValue.SetRangeBefore({ symbolStart, symbolStart + SYMBOL_SPAN_LENGTH });
10554 changeValue.SetRangeAfter({ symbolStart, symbolStart });
10555 }
10556 }
10557 }
10558
10559 RefPtr<SpanItem> RichEditorPattern::GetDelPartiallySpanItem(
10560 RichEditorChangeValue& changeValue, std::u16string& originalStr, int32_t& originalPos)
10561 {
10562 RefPtr<SpanItem> retItem = nullptr;
10563 if (changeValue.GetRichEditorOriginalSpans().size() == 0) {
10564 return retItem;
10565 }
10566 std::u16string textTemp;
10567 auto originalSpans = changeValue.GetRichEditorOriginalSpans();
10568 const RichEditorAbstractSpanResult& firstResult = originalSpans.front();
10569 auto it = spans_.begin();
10570 std::advance(it, firstResult.GetSpanIndex());
10571 retItem = *it;
10572 originalStr = retItem->content;
10573 originalPos = retItem->position;
10574 retItem->content.erase(firstResult.OffsetInSpan(), firstResult.GetEraseLength());
10575 retItem->position -= firstResult.GetEraseLength();
10576 if (firstResult.GetEraseLength() != static_cast<int32_t>(firstResult.GetValue().length())) {
10577 return retItem;
10578 }
10579
10580 if (firstResult.GetSpanIndex() == 0) {
10581 int32_t spanIndex = 0;
10582 for (auto& orgIt : originalSpans) {
10583 spanIndex = orgIt.GetSpanIndex();
10584 if (orgIt.GetEraseLength() != static_cast<int32_t>(orgIt.GetValue().length())) {
10585 // find the deleted(Partially) spanItem
10586 auto findIt = spans_.begin();
10587 std::advance(findIt, spanIndex);
10588 textTemp = (*findIt)->content;
10589 textTemp.erase(orgIt.OffsetInSpan(), orgIt.GetEraseLength());
10590 retItem->content = textTemp;
10591 retItem->position = textTemp.length();
10592 return retItem;
10593 }
10594 }
10595 if (spans_.size() == originalSpans.size() || static_cast<int32_t>(spans_.size()) == (spanIndex + 1)) {
10596 return retItem; // all spanNode be deleted
10597 }
10598 auto nextIt = spans_.begin();
10599 std::advance(nextIt, spanIndex + 1);
10600 if ((*nextIt)->unicode != 0 || DynamicCast<PlaceholderSpanItem>(*nextIt)) {
10601 return retItem; // is not a textSpan(Image/Symbol/other)
10602 }
10603 retItem->content = (*nextIt)->content;
10604 retItem->position = static_cast<int32_t>(retItem->content.length());
10605 }
10606 return retItem;
10607 }
10608
10609 bool RichEditorPattern::BeforeChangeText(RichEditorChangeValue& changeValue, const TextSpanOptions& options)
10610 {
10611 auto eventHub = GetEventHub<RichEditorEventHub>();
10612 CHECK_NULL_RETURN(eventHub, false);
10613 if (!eventHub->HasOnWillChange() && !eventHub->HasOnDidChange()) {
10614 return true;
10615 }
10616 int32_t innerPosition = caretPosition_;
10617
10618 // AddTextSpan
10619 std::optional<TextStyle> textStyle = std::nullopt;
10620 if (options.style.has_value()) {
10621 textStyle = options.style;
10622 }
10623 if (options.offset.has_value()) {
10624 if (spans_.empty() || options.offset.value() < 0) {
10625 innerPosition = 0;
10626 } else if (options.offset.value() > GetTextContentLength()) {
10627 innerPosition = GetTextContentLength();
10628 } else {
10629 innerPosition = options.offset.value();
10630 }
10631 } else {
10632 innerPosition = GetTextContentLength();
10633 }
10634 // only add, do not delete
10635 changeValue.SetRangeBefore({ innerPosition, innerPosition });
10636 GetReplacedSpan(changeValue, innerPosition, options.value, innerPosition, textStyle, options.paraStyle,
10637 options.urlAddress, true);
10638
10639 CHECK_NULL_RETURN(eventHub->HasOnWillChange(), true);
10640 auto ret = eventHub->FireOnWillChange(changeValue);
10641 return ret;
10642 }
10643
10644 bool RichEditorPattern::BeforeAddImage(RichEditorChangeValue& changeValue,
10645 const ImageSpanOptions& options, int32_t insertIndex)
10646 {
10647 auto eventHub = GetEventHub<RichEditorEventHub>();
10648 CHECK_NULL_RETURN(eventHub, false);
10649 if (!eventHub->HasOnWillChange() && !eventHub->HasOnDidChange()) {
10650 return true;
10651 }
10652 changeValue.SetRangeBefore({ insertIndex, insertIndex });
10653 changeValue.SetRangeAfter({ insertIndex, insertIndex + 1});
10654 RichEditorAbstractSpanResult retInfo;
10655 TextInsertValueInfo info;
10656 CalcInsertValueObj(info, insertIndex, true);
10657 int32_t spanIndex = info.GetSpanIndex();
10658 retInfo.SetSpanIndex(spanIndex);
10659 if (options.image) {
10660 retInfo.SetValueResourceStr(*options.image);
10661 }
10662 if (options.imagePixelMap) {
10663 retInfo.SetValuePixelMap(*options.imagePixelMap);
10664 }
10665 if (options.imageAttribute.has_value()) {
10666 auto imgAttr = options.imageAttribute.value();
10667 if (imgAttr.size.has_value()) {
10668 retInfo.SetSizeWidth(imgAttr.size->width.value_or(CalcDimension()).ConvertToPx());
10669 retInfo.SetSizeHeight(imgAttr.size->height.value_or(CalcDimension()).ConvertToPx());
10670 }
10671 if (imgAttr.verticalAlign.has_value()) {
10672 retInfo.SetVerticalAlign(imgAttr.verticalAlign.value());
10673 }
10674 if (imgAttr.objectFit.has_value()) {
10675 retInfo.SetImageFit(imgAttr.objectFit.value());
10676 }
10677 if (imgAttr.marginProp.has_value()) {
10678 retInfo.SetMargin(imgAttr.marginProp.value().ToString());
10679 }
10680 if (imgAttr.borderRadius.has_value()) {
10681 retInfo.SetBorderRadius(imgAttr.borderRadius.value().ToString());
10682 }
10683 }
10684 retInfo.SetOffsetInSpan(0);
10685 retInfo.SetEraseLength(1);
10686 retInfo.SetSpanRangeStart(insertIndex);
10687 retInfo.SetSpanRangeEnd(insertIndex + 1);
10688 retInfo.SetSpanType(SpanResultType::IMAGE);
10689 changeValue.SetRichEditorReplacedImageSpans(retInfo);
10690 CHECK_NULL_RETURN(eventHub->HasOnWillChange(), true);
10691 auto ret = eventHub->FireOnWillChange(changeValue);
10692 return ret;
10693 }
10694
10695 void RichEditorPattern::FixMoveDownChange(RichEditorChangeValue& changeValue, int32_t delLength)
10696 {
10697 int32_t delSpanCount = 0;
10698 for (auto& it : changeValue.GetRichEditorOriginalSpans()) {
10699 if (it.GetEraseLength() == static_cast<int32_t>(it.GetValue().length())) {
10700 ++delSpanCount;
10701 }
10702 }
10703 for (auto& it : const_cast<std::vector<RichEditorAbstractSpanResult>&>(changeValue.GetRichEditorReplacedSpans())) {
10704 if (delSpanCount) {
10705 it.SetSpanIndex(it.GetSpanIndex() - delSpanCount);
10706 }
10707 }
10708 }
10709
10710 void RichEditorPattern::BeforeUndo(
10711 RichEditorChangeValue& changeValue, int32_t& innerPosition, const OperationRecord& record)
10712 {
10713 innerPosition = record.afterCaretPosition;
10714 if (record.addText.has_value() && record.deleteCaretPostion != -1) { // UndoDrag
10715 GetDeletedSpan(changeValue, innerPosition, record.addText.value_or(u"").length(),
10716 RichEditorDeleteDirection::FORWARD);
10717 innerPosition = record.deleteCaretPostion;
10718 GetReplacedSpan(changeValue, innerPosition, record.addText.value(), innerPosition, std::nullopt, std::nullopt);
10719 } else if (record.addText.has_value() && record.deleteText.has_value()) {
10720 GetDeletedSpan(changeValue, innerPosition, record.addText.value_or(u"").length(),
10721 RichEditorDeleteDirection::BACKWARD);
10722 GetReplacedSpan(
10723 changeValue, innerPosition, record.deleteText.value(), innerPosition, std::nullopt, std::nullopt);
10724 } else if (record.deleteText.has_value()) {
10725 GetReplacedSpan(
10726 changeValue, innerPosition, record.deleteText.value(), innerPosition, std::nullopt, std::nullopt);
10727 } else if (record.addText.has_value()) {
10728 GetDeletedSpan(changeValue, innerPosition, record.addText.value_or(u"").length(),
10729 RichEditorDeleteDirection::BACKWARD);
10730 }
10731 }
10732
10733 void RichEditorPattern::BeforeRedo(
10734 RichEditorChangeValue& changeValue, int32_t& innerPosition, const OperationRecord& record)
10735 {
10736 innerPosition = record.beforeCaretPosition - record.addText.value_or(u"").length();
10737 if (record.addText.has_value() && record.deleteCaretPostion != -1) { // RedoDrag
10738 innerPosition = record.deleteCaretPostion;
10739 GetDeletedSpan(changeValue, innerPosition, record.addText.value_or(u"").length(),
10740 RichEditorDeleteDirection::FORWARD);
10741 innerPosition = record.beforeCaretPosition;
10742 GetReplacedSpan(changeValue, innerPosition, record.addText.value(), innerPosition, std::nullopt, std::nullopt);
10743 } else if (record.addText.has_value() && record.deleteText.has_value()) {
10744 GetDeletedSpan(changeValue, innerPosition, record.deleteText.value_or(u"").length(),
10745 RichEditorDeleteDirection::FORWARD);
10746 GetReplacedSpan(changeValue, innerPosition, record.addText.value(), innerPosition, std::nullopt, std::nullopt);
10747 } else if (record.deleteText.has_value()) {
10748 innerPosition = record.beforeCaretPosition - record.deleteText.value_or(u"").length();
10749 GetDeletedSpan(changeValue, innerPosition, record.deleteText.value_or(u"").length(),
10750 RichEditorDeleteDirection::FORWARD);
10751 } else if (record.addText.has_value()) {
10752 innerPosition = std::min(innerPosition, record.afterCaretPosition);
10753 int32_t innerAddPosition = record.afterCaretPosition - static_cast<int32_t>(record.addText.value().length());
10754 if (changeValue.GetRichEditorOriginalSpans().empty()) {
10755 innerPosition = caretPosition_;
10756 innerAddPosition = caretPosition_;
10757 }
10758 GetReplacedSpan(changeValue, innerAddPosition, record.addText.value(), innerPosition,
10759 std::nullopt, std::nullopt);
10760 }
10761 }
10762
10763 void RichEditorPattern::BeforeDrag(
10764 RichEditorChangeValue& changeValue, int32_t& innerPosition, const OperationRecord& record)
10765 {
10766 std::u16string recordAddText = record.addText.value_or(u"");
10767 int length = recordAddText.length();
10768 int32_t nowPosition = innerPosition;
10769 std::optional<TextStyle> style = std::nullopt;
10770 if (typingStyle_.has_value() && typingTextStyle_.has_value()) {
10771 style = typingTextStyle_.value();
10772 }
10773 if (!isDragSponsor_) { // drag from outside
10774 GetReplacedSpan(
10775 changeValue, innerPosition, recordAddText, innerPosition, style, std::nullopt, std::nullopt, true, false);
10776 } else if (nowPosition < record.beforeCaretPosition + length) { // move up
10777 innerPosition = record.beforeCaretPosition;
10778 GetDeletedSpan(changeValue, innerPosition, length, RichEditorDeleteDirection::FORWARD);
10779 innerPosition = nowPosition;
10780 GetReplacedSpan(
10781 changeValue, innerPosition, recordAddText, nowPosition, style, std::nullopt, std::nullopt, true, false);
10782 } else { // move down
10783 innerPosition = record.beforeCaretPosition;
10784 GetDeletedSpan(changeValue, innerPosition, length, RichEditorDeleteDirection::FORWARD);
10785 innerPosition = nowPosition - length;
10786 GetReplacedSpan(
10787 changeValue, innerPosition, recordAddText, nowPosition, style, std::nullopt, std::nullopt, true, false);
10788 FixMoveDownChange(changeValue, length);
10789 }
10790 }
10791
10792 bool RichEditorPattern::BeforeChangeText(
10793 RichEditorChangeValue& changeValue, const OperationRecord& record, RecordType type, int32_t delLength)
10794 {
10795 int32_t innerPosition = caretPosition_;
10796 auto eventHub = GetEventHub<RichEditorEventHub>();
10797 CHECK_NULL_RETURN(eventHub, false);
10798 if (!eventHub->HasOnWillChange() && !eventHub->HasOnDidChange()) {
10799 return true;
10800 }
10801
10802 if (RecordType::INSERT == type) {
10803 if (textSelector_.IsValid()) {
10804 GetDeletedSpan(changeValue, innerPosition,
10805 static_cast<int32_t>(textSelector_.GetTextEnd() - textSelector_.GetTextStart()));
10806 } else if (!previewTextRecord_.previewContent.empty() || previewTextRecord_.needReplaceText) {
10807 GetDeletedSpan(changeValue, innerPosition,
10808 static_cast<int32_t>(previewTextRecord_.replacedRange.end - previewTextRecord_.replacedRange.start));
10809 }
10810 if (record.addText.has_value()) {
10811 GetReplacedSpan(changeValue, innerPosition, record.addText.value(), innerPosition, std::nullopt, std::nullopt);
10812 }
10813 }
10814 if (RecordType::DEL_FORWARD == type) {
10815 innerPosition = record.beforeCaretPosition;
10816 GetDeletedSpan(changeValue, innerPosition, delLength, RichEditorDeleteDirection::FORWARD);
10817 }
10818 if (RecordType::DEL_BACKWARD == type) {
10819 innerPosition = record.beforeCaretPosition;
10820 GetDeletedSpan(changeValue, innerPosition, delLength, RichEditorDeleteDirection::BACKWARD);
10821 }
10822 if (RecordType::UNDO == type) {
10823 BeforeUndo(changeValue, innerPosition, record);
10824 }
10825 if (RecordType::REDO == type) {
10826 BeforeRedo(changeValue, innerPosition, record);
10827 }
10828 if (RecordType::DRAG == type) {
10829 BeforeDrag(changeValue, innerPosition, record);
10830 }
10831 bool isDelete = RecordType::DEL_FORWARD == type || RecordType::DEL_BACKWARD == type;
10832 if (changeValue.GetRichEditorOriginalSpans().empty() && !isDelete) {
10833 // only add, do not delete
10834 changeValue.SetRangeBefore({ caretPosition_, caretPosition_ });
10835 }
10836
10837 CHECK_NULL_RETURN(eventHub->HasOnWillChange(), true);
10838 auto ret = eventHub->FireOnWillChange(changeValue);
10839 return ret;
10840 }
10841
10842 OffsetF RichEditorPattern::GetTextPaintOffset() const
10843 {
10844 if (selectOverlay_->HasRenderTransform()) {
10845 return selectOverlay_->GetPaintRectOffsetWithTransform();
10846 }
10847 return GetPaintRectGlobalOffset();
10848 }
10849
10850 OffsetF RichEditorPattern::GetPaintRectGlobalOffset() const
10851 {
10852 auto host = GetHost();
10853 CHECK_NULL_RETURN(host, OffsetF(0.0f, 0.0f));
10854 auto pipeline = host->GetContextRefPtr();
10855 CHECK_NULL_RETURN(pipeline, OffsetF(0.0f, 0.0f));
10856 auto rootOffset = pipeline->GetRootRect().GetOffset();
10857 auto textPaintOffset = host->GetPaintRectOffsetNG(false, true);
10858 return textPaintOffset - rootOffset;
10859 }
10860
10861 void RichEditorPattern::HandlePointWithTransform(OffsetF& point)
10862 {
10863 auto host = GetHost();
10864 CHECK_NULL_VOID(host);
10865 PointF convertPoint = { point.GetX(), point.GetY() };
10866 auto parent = host;
10867 while (parent && (parent->GetTag() != V2::WINDOW_SCENE_ETS_TAG)) {
10868 auto renderContext = parent->GetRenderContext();
10869 CHECK_NULL_VOID(renderContext);
10870 auto paintOffset = renderContext->GetPaintRectWithoutTransform().GetOffset();
10871 if (parent != host) {
10872 convertPoint = convertPoint + paintOffset;
10873 }
10874 renderContext->GetPointTransform(convertPoint);
10875 parent = parent->GetAncestorNodeOfFrame(true);
10876 }
10877 point = { convertPoint.GetX(), convertPoint.GetY() };
10878 }
10879
10880 CaretOffsetInfo RichEditorPattern::GetCaretOffsetInfoByPosition(int32_t position)
10881 {
10882 CaretOffsetInfo caretInfo;
10883 int32_t currrentPosition = 0;
10884 if (position == -1) {
10885 currrentPosition = caretPosition_;
10886 } else {
10887 currrentPosition = position;
10888 }
10889 caretInfo.caretOffsetUp = CalcCursorOffsetByPosition(currrentPosition, caretInfo.caretHeightUp, false, false);
10890 caretInfo.caretOffsetDown = CalcCursorOffsetByPosition(currrentPosition, caretInfo.caretHeightDown, true, false);
10891 caretInfo.caretOffsetLine = CalcCursorOffsetByPosition(currrentPosition, caretInfo.caretHeightLine);
10892 return caretInfo;
10893 }
10894
10895 void RichEditorPattern::CalcLineSidesIndexByPosition(int32_t& startIndex, int32_t& endIndex)
10896 {
10897 Offset textStartOffset;
10898 Offset textEndOffset;
10899
10900 CHECK_NULL_VOID(overlayMod_);
10901 auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
10902 auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize());
10903 float textOffsetY = richTextRect_.GetY() - (minDet / 2.0);
10904 auto currentCaretOffsetOverlay = overlayMod->GetCaretOffset();
10905 textStartOffset = Offset(0, currentCaretOffsetOverlay.GetY() - textOffsetY);
10906 textEndOffset = Offset(richTextRect_.Width(), currentCaretOffsetOverlay.GetY() - textOffsetY);
10907 startIndex = paragraphs_.GetIndex(textStartOffset);
10908 endIndex = paragraphs_.GetIndex(textEndOffset);
10909 }
10910
10911 RectF RichEditorPattern::CalcLineInfoByPosition()
10912 {
10913 int32_t startIndex = 0;
10914 int32_t endIndex = 0;
10915
10916 CalcLineSidesIndexByPosition(startIndex, endIndex);
10917 if (startIndex == endIndex) {
10918 endIndex += 1;
10919 }
10920 auto selectedRects = paragraphs_.GetRects(startIndex, endIndex);
10921 CHECK_NULL_RETURN(selectedRects.size(), {});
10922 return selectedRects.front();
10923 }
10924
10925 int32_t RichEditorPattern::CalcMoveUpPos(float& leadingMarginOffset)
10926 {
10927 int32_t caretPosition;
10928 CaretOffsetInfo caretInfo;
10929 float textOffsetDownY = 0.0f;
10930 int32_t startIndex = 0;
10931 int32_t endIndex = 0;
10932 Offset textOffset;
10933
10934 caretInfo = GetCaretOffsetInfoByPosition();
10935 auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize());
10936 CHECK_NULL_RETURN(overlayMod_, 0);
10937 auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
10938 auto caretOffsetOverlay = overlayMod->GetCaretOffset();
10939 auto caretOffsetWidth = overlayMod->GetCaretWidth();
10940 bool cursorNotAtLineStart = NearEqual(caretOffsetOverlay.GetX(), caretInfo.caretOffsetUp.GetX(), caretOffsetWidth);
10941 float textOffsetY = richTextRect_.GetY() + (minDet / 2.0); // 2.0 Cursor one half at the center position
10942 CalcLineSidesIndexByPosition(startIndex, endIndex);
10943 auto rectLineInfo = CalcLineInfoByPosition();
10944 leadingMarginOffset = rectLineInfo.GetX();
10945 if (cursorNotAtLineStart) {
10946 textOffsetDownY = caretInfo.caretOffsetLine.GetY() - textOffsetY;
10947 // lm mean leadingMargin abbr
10948 auto lmSizeOffset = (endIndex - startIndex <= 1 && NearEqual(rectLineInfo.Width(), richTextRect_.Width()))
10949 ? rectLineInfo.GetX()
10950 : 0;
10951 textOffset = Offset(caretInfo.caretOffsetLine.GetX() - richTextRect_.GetX() + lmSizeOffset, textOffsetDownY);
10952 } else {
10953 textOffsetDownY = caretInfo.caretOffsetLine.GetY() + caretInfo.caretHeightLine - textOffsetY;
10954 textOffset = Offset(caretOffsetOverlay.GetX() - richTextRect_.GetX(), textOffsetDownY);
10955 }
10956 caretPosition = paragraphs_.GetIndex(textOffset);
10957 return caretPosition;
10958 }
10959
10960 int32_t RichEditorPattern::CalcMoveDownPos(float& leadingMarginOffset)
10961 {
10962 CaretOffsetInfo caretInfo;
10963 float textOffsetDownY = 0.0f;
10964 Offset textOffset;
10965 int32_t caretPositionEnd;
10966
10967 caretInfo = GetCaretOffsetInfoByPosition();
10968 auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize());
10969 CHECK_NULL_RETURN(overlayMod_, 0);
10970 auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
10971 auto caretOffsetOverlay = overlayMod->GetCaretOffset();
10972 auto caretOffsetWidth = overlayMod->GetCaretWidth();
10973 float textOffsetX = richTextRect_.GetX();
10974 float textOffsetY = richTextRect_.GetY() - (minDet / 2.0);
10975 bool cursorNotAtLineStart = NearEqual(caretOffsetOverlay.GetX(), caretInfo.caretOffsetUp.GetX(), caretOffsetWidth);
10976 // midle or enter
10977 auto rectLineInfo = CalcLineInfoByPosition();
10978 leadingMarginOffset = rectLineInfo.GetX();
10979 auto lineHeightDis = rectLineInfo.Height();
10980 // midle or end, first line start position,end line end position
10981 textOffsetDownY = caretInfo.caretOffsetLine.GetY() + caretInfo.caretHeightLine - textOffsetY;
10982 float lastLineTop = 0.0f;
10983 float paragraphSpacing = 0.0f;
10984 HandleCurrentPositionParagraphInfo(lastLineTop, paragraphSpacing);
10985 if (cursorNotAtLineStart || caretPosition_ == 0) {
10986 IF_TRUE(NearEqual(std::floor(caretInfo.caretOffsetLine.GetY()), std::floor(lastLineTop)),
10987 textOffsetDownY += paragraphSpacing);
10988 textOffset = Offset(caretInfo.caretOffsetLine.GetX() - textOffsetX, textOffsetDownY);
10989 } else {
10990 IF_TRUE(NearEqual(std::floor(caretInfo.caretOffsetLine.GetY() + lineHeightDis), std::floor(lastLineTop)),
10991 textOffsetDownY += paragraphSpacing);
10992 textOffsetDownY += lineHeightDis;
10993 textOffset = Offset(caretOffsetOverlay.GetX() - textOffsetX, textOffsetDownY);
10994 }
10995 caretPositionEnd = paragraphs_.GetIndex(textOffset);
10996 return caretPositionEnd;
10997 }
10998
10999 int32_t RichEditorPattern::CalcLineBeginPosition()
11000 {
11001 float caretHeight = 0.0f;
11002 OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight, false, false);
11003 auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
11004 auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize());
11005 auto textOffsetY = caretOffset.GetY() - textPaintOffset.GetY() + (minDet / 2.0);
11006 Offset textOffset = { 0, textOffsetY };
11007 auto newPos = paragraphs_.GetIndex(textOffset);
11008 return newPos;
11009 }
11010
11011 float RichEditorPattern::GetTextThemeFontSize()
11012 {
11013 auto theme = GetTheme<TextTheme>();
11014 CHECK_NULL_RETURN(theme, 0.0f);
11015 auto textStyle = theme->GetTextStyle();
11016 return textStyle.GetFontSize().ConvertToPx();
11017 }
11018
11019 int32_t RichEditorPattern::CalcLineEndPosition(int32_t index)
11020 {
11021 CaretOffsetInfo caretInfo;
11022 int32_t realCaretOffsetY = 0;
11023 int32_t realLastClickOffsetY = 0;
11024
11025 caretInfo = GetCaretOffsetInfoByPosition(index);
11026 if (NearEqual(richTextRect_.GetY(), contentRect_.GetY())) {
11027 realLastClickOffsetY = lastClickOffset_.GetY();
11028 realCaretOffsetY = caretInfo.caretOffsetDown.GetY();
11029 } else {
11030 auto scrollOffset =
11031 caretInfo.caretOffsetDown.GetY() - caretInfo.caretOffsetUp.GetY() + caretInfo.caretOffsetLine.GetY();
11032 realLastClickOffsetY = lastClickOffset_.GetY() + std::abs(richTextRect_.GetY()) + contentRect_.GetY();
11033 realCaretOffsetY = scrollOffset + std::abs(richTextRect_.GetY()) + contentRect_.GetY();
11034 }
11035 auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
11036 auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize());
11037 Offset textOffset;
11038 auto rectLineInfo = CalcLineInfoByPosition();
11039 float textWidth = richTextRect_.Width() + rectLineInfo.GetX();
11040 float textPaintOffsetY = textPaintOffset.GetY() - (minDet / 2.0);
11041 float textOffsetClickY = realLastClickOffsetY - textPaintOffsetY;
11042 float textOffsetDownY = realCaretOffsetY - textPaintOffsetY;
11043 if (lastClickOffset_.NonNegative()) {
11044 textOffset = { textWidth, textOffsetClickY };
11045 } else {
11046 textOffset = { textWidth, textOffsetDownY };
11047 }
11048 auto position = paragraphs_.GetIndex(textOffset);
11049 return position;
11050 }
11051
11052 int32_t RichEditorPattern::CalcSingleLineBeginPosition(int32_t fixedPos)
11053 {
11054 float caretHeightDown = 0.0f;
11055 OffsetF caretOffsetDown = CalcCursorOffsetByPosition(fixedPos, caretHeightDown, true, false);
11056 float caretHeightUp = 0.0f;
11057 OffsetF caretOffsetUp = CalcCursorOffsetByPosition(fixedPos, caretHeightUp, false, false);
11058 bool isCaretPosInLineEnd = !NearEqual(caretOffsetDown.GetX(), caretOffsetUp.GetX(), 0.5f);
11059
11060 auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
11061 CHECK_NULL_RETURN(overlayMod_, false);
11062 auto caretOffsetOverlay = overlayMod->GetCaretOffset();
11063 bool cursorNotAtLineStart = NearEqual(caretOffsetOverlay.GetX(), caretOffsetUp.GetX(), 0.5f);
11064
11065 Offset textOffset;
11066 if (!cursorNotAtLineStart && !lastClickOffset_.IsNegative()) {
11067 return fixedPos;
11068 } else if (isCaretPosInLineEnd && lastClickOffset_.IsNegative()) {
11069 return lastSelectionRange_.start_;
11070 } else {
11071 float caretHeight = 0.0f;
11072 OffsetF caretOffsetFixed = CalcCursorOffsetByPosition(fixedPos, caretHeight, false, false);
11073 textOffset = { 0, caretOffsetFixed.GetY() };
11074 }
11075 auto position = paragraphs_.GetIndex(textOffset);
11076 return position;
11077 }
11078
11079 int32_t RichEditorPattern::CalcSingleLineEndPosition(int32_t fixedPos)
11080 {
11081 auto rectLineInfo = CalcLineInfoByPosition();
11082 float textWidth = richTextRect_.Width() + rectLineInfo.GetX();
11083
11084 float caretHeightDown = 0.0f;
11085 OffsetF caretOffsetDown = CalcCursorOffsetByPosition(fixedPos, caretHeightDown, true, false);
11086 float caretHeightUp = 0.0f;
11087 OffsetF caretOffsetUp = CalcCursorOffsetByPosition(fixedPos, caretHeightUp, false, false);
11088 bool isCaretPosInLineEnd = !NearEqual(caretOffsetDown.GetX(), caretOffsetUp.GetX(), 0.5f);
11089
11090 auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
11091 CHECK_NULL_RETURN(overlayMod_, false);
11092 auto caretOffsetOverlay = overlayMod->GetCaretOffset();
11093 bool cursorNotAtLineStart = NearEqual(caretOffsetOverlay.GetX(), caretOffsetUp.GetX(), 0.5f);
11094
11095 Offset textOffset;
11096 if (isCaretPosInLineEnd && lastClickOffset_.IsNegative() && lastSelectionRange_.end_ > fixedPos) {
11097 return lastSelectionRange_.end_;
11098 } else if (!cursorNotAtLineStart && isCaretPosInLineEnd && fixedPos == caretPosition_) {
11099 textOffset = { textWidth, caretOffsetOverlay.GetY() };
11100 } else if (isCaretPosInLineEnd && lastSelectionRange_.end_ <= fixedPos) {
11101 CursorMoveLineEnd();
11102 return fixedPos;
11103 } else if (cursorNotAtLineStart && isCaretPosInLineEnd) {
11104 return fixedPos;
11105 } else {
11106 float caretHeight = 0.0f;
11107 OffsetF caretOffsetFixed = CalcCursorOffsetByPosition(fixedPos, caretHeight, false, false);
11108 textOffset = { textWidth, caretOffsetFixed.GetY() };
11109 }
11110 auto position = paragraphs_.GetIndex(textOffset);
11111 return position;
11112 }
11113
11114 bool RichEditorPattern::CursorMoveLineBegin()
11115 {
11116 int32_t currentPositionIndex = 0;
11117 if (textSelector_.SelectNothing()) {
11118 currentPositionIndex = caretPosition_;
11119 } else {
11120 currentPositionIndex = textSelector_.GetTextStart();
11121 }
11122 CloseSelectOverlay();
11123 ResetSelection();
11124 float caretHeightDown = 0.0f;
11125 Offset textOffset;
11126
11127 if (0 == currentPositionIndex) {
11128 SetCaretPosition(currentPositionIndex);
11129 StartTwinkling();
11130 return false;
11131 }
11132 OffsetF caretOffsetDown = CalcCursorOffsetByPosition(currentPositionIndex, caretHeightDown, true, false);
11133 auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
11134 auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize());
11135 float textPaintOffsetY = textPaintOffset.GetY() - (minDet / 2.0);
11136 if (lastClickOffset_.NonNegative()) {
11137 textOffset = { 0, lastClickOffset_.GetY() - textPaintOffsetY };
11138 } else {
11139 textOffset = { 0, caretOffsetDown.GetY() - textPaintOffsetY };
11140 }
11141 auto position = paragraphs_.GetIndex(textOffset);
11142 AdjustCursorPosition(position);
11143 SetCaretPosition(position);
11144 MoveCaretToContentRect();
11145 StartTwinkling();
11146 auto host = GetHost();
11147 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
11148 return true;
11149 }
11150
11151 bool RichEditorPattern::CursorMoveLineEnd()
11152 {
11153 int32_t position = 0;
11154 if (!textSelector_.SelectNothing()) {
11155 position = textSelector_.GetTextEnd();
11156 CaretOffsetInfo caretInfo = GetCaretOffsetInfoByPosition(position);
11157 bool cursorAtLineEnd = !NearEqual(caretInfo.caretOffsetUp.GetX(), caretInfo.caretOffsetDown.GetX(), 0.5f);
11158 bool cursorAfterNewLine = (position - 1) == GetParagraphEndPosition(position - 1);
11159 if (cursorAfterNewLine) {
11160 --position;
11161 } else if (!cursorAtLineEnd) {
11162 position = CalcLineEndPosition(textSelector_.GetTextEnd());
11163 }
11164 } else {
11165 position = CalcLineEndPosition();
11166 }
11167 CloseSelectOverlay();
11168 ResetSelection();
11169 float caretHeight = 0.0f;
11170 CHECK_NULL_RETURN(overlayMod_, false);
11171 auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
11172 SetCaretPosition(position);
11173 StartTwinkling();
11174 OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight, false, false);
11175 overlayMod->SetCaretOffsetAndHeight(caretOffset, caretHeight);
11176 SetLastClickOffset(caretOffset);
11177 caretAffinityPolicy_ = CaretAffinityPolicy::UPSTREAM_FIRST;
11178 MoveCaretToContentRect(caretOffset, caretHeight);
11179 auto host = GetHost();
11180 CHECK_NULL_RETURN(host, false);
11181 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
11182 return true;
11183 }
11184
11185 void RichEditorPattern::HandleSelectFontStyle(KeyCode code)
11186 {
11187 if (textSelector_.SelectNothing() || isSpanStringMode_) {
11188 return;
11189 }
11190 auto host = GetHost();
11191 CHECK_NULL_VOID(host);
11192 UpdateSelectSpanStyle(textSelector_.GetTextStart(), textSelector_.GetTextEnd(), code);
11193 StopTwinkling();
11194 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
11195 }
11196
11197 void RichEditorPattern::HandleOnShowMenu()
11198 {
11199 auto isSelectAreaVisible = IsSelectAreaVisible();
11200 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnShowMenu, selectAreaVisible=%{public}d,"
11201 "previewTextInputting=%{public}d,isDragging=%{public}d,isMoveCaret=%{public}d,isHandleMoving=%{public}d,"
11202 "isTouchSelecting=%{public}d,mouseStatus=%{public}d",
11203 isSelectAreaVisible, IsPreviewTextInputting(), IsDragging(), moveCaretState_.isMoveCaret,
11204 selectOverlay_->GetIsHandleMoving(), isTouchSelecting_, mouseStatus_);
11205 CHECK_NULL_VOID(isSelectAreaVisible && !IsPreviewTextInputting() && !IsDragging());
11206 CHECK_NULL_VOID(!moveCaretState_.isMoveCaret && !selectOverlay_->GetIsHandleMoving());
11207 CHECK_NULL_VOID(!isTouchSelecting_ && mouseStatus_ != MouseStatus::MOVE);
11208
11209 if (sourceType_ == SourceType::MOUSE) {
11210 if (!IsSelected()) {
11211 selectedType_ = TextSpanType::TEXT;
11212 }
11213 textResponseType_ = TextResponseType::RIGHT_CLICK;
11214 selectionMenuOffsetByMouse_ = GetCaretRect().GetOffset() + GetParentGlobalOffset();
11215 selectOverlay_->ProcessOverlay({ .animation = true });
11216 return;
11217 }
11218 if (!IsSelected()) {
11219 CreateAndShowSingleHandle();
11220 return;
11221 }
11222 if (SelectOverlayIsOn()) {
11223 selectOverlay_->SwitchToOverlayMode();
11224 if (selectOverlay_->NeedRefreshMenu()) {
11225 selectOverlay_->ProcessOverlay({ .animation = true, .requestCode = REQUEST_RECREATE });
11226 return;
11227 }
11228 selectOverlay_->UpdateMenuOffset();
11229 selectOverlay_->ShowMenu();
11230 return;
11231 }
11232 CalculateHandleOffsetAndShowOverlay();
11233 selectOverlay_->ProcessOverlay({ .animation = true });
11234 }
11235
11236 PositionType RichEditorPattern::GetPositionTypeFromLine()
11237 {
11238 auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
11239 CHECK_NULL_RETURN(overlayMod, PositionType::DEFAULT);
11240 auto currentCaretOffsetOverlay = overlayMod->GetCaretOffset();
11241 auto caretOffsetWidth = overlayMod->GetCaretWidth();
11242 int32_t currentParagraphStart = GetParagraphBeginPosition(caretPosition_);
11243 bool isParagraphStart = caretPosition_ == currentParagraphStart;
11244 CHECK_NULL_RETURN(!isParagraphStart, PositionType::PARAGRAPH_START);
11245 int32_t currentParagraphEnd = GetParagraphEndPosition(caretPosition_);
11246 bool isParagraphEnd = caretPosition_ == currentParagraphEnd;
11247 CHECK_NULL_RETURN(!isParagraphEnd, PositionType::PARAGRAPH_END);
11248 CaretOffsetInfo caretInfo = GetCaretOffsetInfoByPosition();
11249 bool isCaretAtLineMiddle = NearEqual(caretInfo.caretOffsetDown.GetX(), caretInfo.caretOffsetUp.GetX(), 0.5f);
11250 CHECK_NULL_RETURN(!isCaretAtLineMiddle, PositionType::DEFAULT);
11251 bool isCaretAtLineEnd =
11252 NearEqual(currentCaretOffsetOverlay.GetX(), caretInfo.caretOffsetUp.GetX(), caretOffsetWidth);
11253 CHECK_NULL_RETURN(!isCaretAtLineEnd, PositionType::LINE_END);
11254 return PositionType::LINE_START;
11255 }
11256
11257 int32_t RichEditorPattern::HandleSelectWrapper(CaretMoveIntent direction, int32_t fixedPos)
11258 {
11259 int32_t index = GetCaretPosition();
11260 switch (direction) {
11261 case CaretMoveIntent::Left:
11262 return CaretPositionSelectEmoji(CaretMoveIntent::Left);
11263 case CaretMoveIntent::Right:
11264 return CaretPositionSelectEmoji(CaretMoveIntent::Right);
11265 case CaretMoveIntent::Up:
11266 return HandleKbVerticalSelection(true);
11267 case CaretMoveIntent::Down:
11268 return HandleKbVerticalSelection(false);
11269 case CaretMoveIntent::LeftWord: {
11270 return GetLeftWordIndex(index);
11271 }
11272 case CaretMoveIntent::RightWord: {
11273 return GetRightWordIndex(index);
11274 }
11275 case CaretMoveIntent::ParagraghBegin:
11276 return HandleSelectParagraghPos(true);
11277 case CaretMoveIntent::ParagraghEnd:
11278 return HandleSelectParagraghPos(false);
11279 case CaretMoveIntent::LineBegin:
11280 return CalcSingleLineBeginPosition(fixedPos);
11281 case CaretMoveIntent::LineEnd:
11282 return CalcSingleLineEndPosition(fixedPos);
11283 default:
11284 return NONE_SELECT_TYPE;
11285 }
11286 }
11287
11288 int32_t RichEditorPattern::HandleKbVerticalSelection(bool isUp)
11289 {
11290 float caretHeight = 0.0f;
11291 float newCaretHeight = 0.0f;
11292 float careOffsetY = 0.0f;
11293 int32_t newPos;
11294 Offset textOffset;
11295 OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight);
11296 auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize()) / 2.0;
11297 auto positionType = GetPositionTypeFromLine();
11298 if (isUp) {
11299 float selectStartHeight = 0.0f;
11300 OffsetF selectStartOffset = CalcCursorOffsetByPosition(textSelector_.GetTextStart(), selectStartHeight);
11301 careOffsetY = caretOffset.GetY() - GetTextRect().GetY() - minDet;
11302 textOffset = Offset(caretOffset.GetX() - GetTextRect().GetX(), careOffsetY);
11303 CHECK_NULL_RETURN(GreatNotEqual(textOffset.GetY(), 0), 0);
11304 newPos = paragraphs_.GetIndex(textOffset, true);
11305 OffsetF newCaretOffset = CalcCursorOffsetByPosition(newPos, newCaretHeight);
11306 CHECK_EQUAL_RETURN(!textSelector_.SelectNothing() && textSelector_.GetTextEnd() == caretPosition_ &&
11307 selectStartOffset.GetY() == newCaretOffset.GetY(), true, textSelector_.GetTextStart());
11308 } else {
11309 float selectEndHeight = 0.0f;
11310 OffsetF selectEndOffset = CalcCursorOffsetByPosition(textSelector_.GetEnd(), selectEndHeight);
11311 careOffsetY = caretOffset.GetY() - GetTextRect().GetY() + caretHeight + (minDet / 2.0);
11312 float lastLineTop = 0.0f;
11313 float paragraphSpacing = 0.0f;
11314 HandleCurrentPositionParagraphInfo(lastLineTop, paragraphSpacing);
11315 if (positionType == PositionType::LINE_START) {
11316 auto overlayMod = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
11317 CHECK_NULL_RETURN(overlayMod, 0);
11318 auto caretOffsetOverlay = overlayMod->GetCaretOffset();
11319 auto rectLineInfo = CalcLineInfoByPosition();
11320 careOffsetY += rectLineInfo.Height();
11321 IF_TRUE(NearEqual(std::floor(caretOffset.GetY() + rectLineInfo.Height()), std::floor(lastLineTop)),
11322 careOffsetY += paragraphSpacing);
11323 textOffset = Offset(caretOffsetOverlay.GetX() - GetTextRect().GetX(), careOffsetY);
11324 } else {
11325 IF_TRUE(NearEqual(std::floor(caretOffset.GetY()), std::floor(lastLineTop)),
11326 careOffsetY += paragraphSpacing);
11327 textOffset = Offset(caretOffset.GetX() - GetTextRect().GetX(), careOffsetY);
11328 }
11329 auto height = paragraphs_.GetHeight();
11330 CHECK_NULL_RETURN(LessNotEqual(textOffset.GetY(), height), GetTextContentLength());
11331 newPos = paragraphs_.GetIndex(textOffset, true);
11332 OffsetF newCaretOffset = CalcCursorOffsetByPosition(newPos, newCaretHeight);
11333 CHECK_EQUAL_RETURN(!textSelector_.SelectNothing() && textSelector_.GetTextStart() == caretPosition_ &&
11334 selectEndOffset.GetY() == newCaretOffset.GetY(), true, textSelector_.GetTextEnd());
11335 }
11336 return newPos;
11337 }
11338
11339 int32_t RichEditorPattern::HandleSelectParagraghPos(bool direction)
11340 {
11341 int32_t newPos = 0;
11342 CloseSelectOverlay();
11343 ResetSelection();
11344 if (direction) {
11345 newPos = GetParagraphBeginPosition(caretPosition_);
11346 if (newPos == caretPosition_ && caretPosition_ > 0) {
11347 newPos = GetParagraphBeginPosition(caretPosition_ - 1);
11348 }
11349 } else {
11350 newPos = GetParagraphEndPosition(caretPosition_);
11351 if (newPos == caretPosition_ && caretPosition_ < static_cast<int32_t>(GetTextContentLength())) {
11352 newPos = GetParagraphEndPosition(caretPosition_ + 1);
11353 }
11354 }
11355 return newPos;
11356 }
11357
11358 void RichEditorPattern::HandleSelectFontStyleWrapper(KeyCode code, TextStyle& spanStyle)
11359 {
11360 switch (code) {
11361 case KeyCode::KEY_B:
11362 if (spanStyle.GetFontWeight() == Ace::FontWeight::BOLD) {
11363 spanStyle.SetFontWeight(Ace::FontWeight::NORMAL);
11364 } else {
11365 spanStyle.SetFontWeight(Ace::FontWeight::BOLD);
11366 }
11367 break;
11368 case KeyCode::KEY_I:
11369 if (spanStyle.GetFontStyle() == OHOS::Ace::FontStyle::ITALIC) {
11370 spanStyle.SetFontStyle(OHOS::Ace::FontStyle::NORMAL);
11371 } else {
11372 spanStyle.SetFontStyle(OHOS::Ace::FontStyle::ITALIC);
11373 }
11374 break;
11375 case KeyCode::KEY_U:
11376 if (spanStyle.GetTextDecoration() == TextDecoration::UNDERLINE) {
11377 spanStyle.SetTextDecoration(TextDecoration::NONE);
11378 } else {
11379 spanStyle.SetTextDecoration(TextDecoration::UNDERLINE);
11380 }
11381 break;
11382 default:
11383 LOGW("Unsupported select operation for HandleSelectFrontStyle");
11384 return;
11385 }
11386 }
11387
11388 void RichEditorPattern::AIDeleteComb(int32_t start, int32_t end, int32_t& aiPosition, bool direction)
11389 {
11390 std::u16string selectTextContent;
11391 GetContentBySpans(selectTextContent);
11392 // get select content
11393 std::u16string selectData16 = selectTextContent.substr(static_cast<int32_t>(start), static_cast<int32_t>(end - start));
11394 std::string selectData = StringUtils::Str16ToStr8(selectData16);
11395 int32_t aiPosStart;
11396 int32_t aiPosEnd;
11397 int32_t caretPosition;
11398 int32_t size = 1;
11399
11400 if (direction) {
11401 caretPosition = end - start - size;
11402 DataDetectorMgr::GetInstance().AdjustWordSelection(caretPosition, selectData, aiPosStart, aiPosEnd);
11403 aiPosition = aiPosStart + start;
11404 } else {
11405 caretPosition = 0;
11406 DataDetectorMgr::GetInstance().AdjustWordSelection(caretPosition, selectData, aiPosStart, aiPosEnd);
11407 aiPosition = aiPosEnd + start;
11408 }
11409 if (aiPosStart < 0 || aiPosEnd < 0) {
11410 aiPosition = GetCaretPosition();
11411 }
11412 }
11413
11414 bool RichEditorPattern::HandleOnDeleteComb(bool backward)
11415 {
11416 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnDeleteComb backward=%{public}d", backward);
11417 if (textSelector_.IsValid()) {
11418 CloseSelectOverlay();
11419 SetCaretPosition(textSelector_.GetTextStart());
11420 ResetSelection();
11421 }
11422 if (backward) {
11423 DeleteBackwardWord();
11424 } else {
11425 DeleteForwardWord();
11426 }
11427 MoveCaretToContentRect();
11428 StartTwinkling();
11429 auto host = GetHost();
11430 CHECK_NULL_RETURN(host, false);
11431 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
11432 return true;
11433 }
11434
11435 void RichEditorPattern::DeleteBackwardWord()
11436 {
11437 CHECK_NULL_VOID(caretPosition_ != 0);
11438 int32_t startIndex = caretPosition_;
11439 int32_t spaceEndIndex = startIndex;
11440 AdjustIndexSkipSpace(spaceEndIndex, MoveDirection::BACKWARD);
11441 int32_t wordEndIndex = std::max(0, spaceEndIndex - 1);
11442 AdjustSelectorForSymbol(wordEndIndex, HandleType::FIRST, SelectorAdjustPolicy::INCLUDE);
11443 AdjustWordSelection(wordEndIndex, spaceEndIndex);
11444 DeleteBackward(startIndex - wordEndIndex);
11445 }
11446
11447 void RichEditorPattern::DeleteForwardWord()
11448 {
11449 CHECK_NULL_VOID(caretPosition_ != GetTextContentLength());
11450 int32_t startIndex = caretPosition_;
11451 int32_t spaceEndIndex = startIndex;
11452 AdjustIndexSkipSpace(spaceEndIndex, MoveDirection::FORWARD);
11453 int32_t wordEndIndex = std::min(spaceEndIndex + 1, GetTextContentLength());
11454 AdjustWordSelection(spaceEndIndex, wordEndIndex);
11455 DeleteForward(wordEndIndex - startIndex);
11456 }
11457
11458 const std::list<RefPtr<UINode>>& RichEditorPattern::GetAllChildren() const
11459 {
11460 childNodes_.clear();
11461 auto host = GetHost();
11462 CHECK_NULL_RETURN(host, childNodes_);
11463 auto children = host->GetChildren();
11464 for (const auto& child: children) {
11465 childNodes_.push_back(child);
11466 }
11467 return childNodes_;
11468 }
11469
11470 void RichEditorPattern::HandleTripleClickEvent(OHOS::Ace::GestureEvent& info)
11471 {
11472 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "HandleTripleClickEvent");
11473 CHECK_EQUAL_VOID(IsPreviewTextInputting(), true);
11474 CHECK_EQUAL_VOID(IsDragging(), true);
11475 bool isMouseClickWithShift = shiftFlag_ && info.GetSourceDevice() == SourceType::MOUSE;
11476 CHECK_EQUAL_VOID(isMouseClickWithShift, true);
11477 auto focusHub = GetFocusHub();
11478 CHECK_NULL_VOID(focusHub);
11479 CHECK_EQUAL_VOID(focusHub->IsFocusable(), false);
11480 auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
11481 Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
11482 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
11483 int32_t pos = paragraphs_.GetIndex(textOffset);
11484
11485 int32_t start = 0;
11486 int32_t end = 0;
11487 auto& paragraphInfoList = paragraphs_.GetParagraphs();
11488 if (!paragraphInfoList.empty() && pos == paragraphInfoList.back().end) {
11489 start = paragraphInfoList.back().start;
11490 end = paragraphInfoList.back().end;
11491 } else {
11492 for (const auto& paragraph : paragraphInfoList) {
11493 if (pos >= paragraph.start && pos < paragraph.end) {
11494 start = paragraph.start;
11495 end = paragraph.end;
11496 break;
11497 }
11498 }
11499 }
11500 if (!paragraphInfoList.empty() && paragraphInfoList.back().end != end) {
11501 --end;
11502 }
11503 end = std::min(GetTextContentLength(), end);
11504 start = std::min(GetTextContentLength(), start);
11505 CHECK_EQUAL_VOID(start > end, true);
11506 TripleClickSection(info, start, end, pos);
11507 }
11508
11509 void RichEditorPattern::UpdateSelectionByTouchMove(const Offset& touchOffset)
11510 {
11511 // While previewing + long press and move, then shall select content.
11512 auto host = GetHost();
11513 CHECK_NULL_VOID(host);
11514
11515 Offset textOffset = ConvertTouchOffsetToTextOffset(touchOffset);
11516 auto positionWithAffinity = paragraphs_.GetGlyphPositionAtCoordinate(textOffset);
11517 SetCaretPositionWithAffinity(positionWithAffinity);
11518 MoveCaretToContentRect();
11519 int32_t currentPosition = GreatNotEqual(textOffset.GetY(), paragraphs_.GetHeight())
11520 ? GetTextContentLength()
11521 : caretPosition_;
11522 IF_TRUE(GetTextContentLength() > 0, SetMagnifierLocalOffset(touchOffset));
11523 auto [initSelectStart, initSelectEnd] = initSelector_;
11524 int32_t start = std::min(initSelectStart, currentPosition);
11525 int32_t end = std::max(initSelectEnd, currentPosition);
11526 if (start == textSelector_.GetTextStart()) {
11527 StartVibratorByIndexChange(end, textSelector_.GetTextEnd());
11528 } else {
11529 StartVibratorByIndexChange(start, textSelector_.GetTextStart());
11530 }
11531 HandleSelectionChange(start, end);
11532 TriggerAvoidOnCaretChange();
11533 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
11534 }
11535
11536 void RichEditorPattern::HideMenu()
11537 {
11538 selectOverlay_->HideMenu();
11539 }
11540
11541 void RichEditorPattern::OnSelectionMenuOptionsUpdate(
11542 const NG::OnCreateMenuCallback&& onCreateMenuCallback, const NG::OnMenuItemClickCallback&& onMenuItemClick)
11543 {
11544 selectOverlay_->OnSelectionMenuOptionsUpdate(std::move(onCreateMenuCallback), std::move(onMenuItemClick));
11545 }
11546
11547 bool RichEditorPattern::CheckTripClickEvent(GestureEvent& info)
11548 {
11549 clickInfo_.push_back(info.GetTimeStamp());
11550 if (clickInfo_.size() > MAX_CLICK) {
11551 clickInfo_.erase(clickInfo_.begin());
11552 }
11553 if (clickInfo_.size() == MAX_CLICK) {
11554 std::chrono::duration<float, std::ratio<1, InputAIChecker::SECONDS_TO_MILLISECONDS>>
11555 clickTimeIntervalOne = clickInfo_[1] - clickInfo_[0];
11556 std::chrono::duration<float, std::ratio<1, InputAIChecker::SECONDS_TO_MILLISECONDS>>
11557 clickTimeIntervalTwo = clickInfo_[2] - clickInfo_[1];
11558 if (clickTimeIntervalOne.count() < DOUBLE_CLICK_INTERVAL_MS
11559 && clickTimeIntervalTwo.count() < DOUBLE_CLICK_INTERVAL_MS) {
11560 return true;
11561 }
11562 }
11563 return false;
11564 }
11565
11566 void RichEditorPattern::PreferredParagraph()
11567 {
11568 CHECK_NULL_VOID(typingTextStyle_.has_value());
11569 if (presetParagraph_) {
11570 presetParagraph_->Reset();
11571 presetParagraph_ = nullptr;
11572 }
11573 std::string textContent;
11574 textContent = "a";
11575 TextStyle textStyle;
11576 textStyle = typingTextStyle_.value();
11577 ParagraphStyle paraStyle {
11578 .align = textStyle.GetTextAlign(),
11579 .maxLines = textStyle.GetMaxLines(),
11580 .fontLocale = Localization::GetInstance()->GetFontLocale(),
11581 .wordBreak = textStyle.GetWordBreak(),
11582 .lineBreakStrategy = textStyle.GetLineBreakStrategy(),
11583 .textOverflow = textStyle.GetTextOverflow(),
11584 .fontSize = textStyle.GetFontSize().ConvertToPx(),
11585 .halfLeading = textStyle.GetHalfLeading(),
11586 .paragraphSpacing = textStyle.GetParagraphSpacing() };
11587 presetParagraph_ = Paragraph::Create(paraStyle, FontCollection::Current());
11588 CHECK_NULL_VOID(presetParagraph_);
11589 presetParagraph_->PushStyle(textStyle);
11590 presetParagraph_->AddText(StringUtils::Str8ToStr16(textContent));
11591 presetParagraph_->Build();
11592 presetParagraph_->Layout(std::numeric_limits<double>::infinity());
11593 }
11594
11595 void RichEditorPattern::TripleClickSection(GestureEvent& info, int32_t start, int32_t end, int32_t pos)
11596 {
11597 auto host = GetHost();
11598 CHECK_NULL_VOID(host);
11599 textSelector_.Update(start, end);
11600 if (IsShowHandle()) {
11601 SetCaretPositionWithAffinity({ end, TextAffinity::UPSTREAM });
11602 MoveCaretToContentRect();
11603 }
11604 UpdateSelectionType(GetSpansInfo(start, end, GetSpansMethod::ONSELECT));
11605 if (info.GetSourceDevice() == SourceType::TOUCH) {
11606 showSelect_ = true;
11607 RequestKeyboard(false, true, true);
11608 HandleOnEditChanged(true);
11609 CalculateHandleOffsetAndShowOverlay();
11610 selectOverlay_->ProcessOverlay({ .menuIsShow = !selectOverlay_->GetIsHandleMoving(), .animation = true });
11611 }
11612 if (info.GetSourceDevice() == SourceType::TOUCH && start == end) {
11613 selectOverlay_->SetIsSingleHandle(true);
11614 }
11615 if (textSelector_.SelectNothing()) {
11616 textSelector_.Update(pos, pos);
11617 } else {
11618 StopTwinkling();
11619 }
11620 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
11621 }
11622
11623 void RichEditorPattern::RequestKeyboardToEdit()
11624 {
11625 CHECK_NULL_VOID(!isEditing_ && HasFocus());
11626 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "request keyboard and enter edit");
11627 RequestKeyboard(false, true, true);
11628 HandleOnEditChanged(true);
11629 }
11630
11631 bool RichEditorPattern::IsResponseRegionExpandingNeededForStylus(const TouchEvent& touchEvent) const
11632 {
11633 if (touchEvent.sourceTool != SourceTool::PEN || touchEvent.type != TouchType::DOWN) {
11634 return false;
11635 }
11636 auto host = GetHost();
11637 CHECK_NULL_RETURN(host, false);
11638 auto focusHub = host->GetFocusHub();
11639 CHECK_NULL_RETURN(focusHub, false);
11640 if (!focusHub->IsFocusable() || !host->IsVisible()) {
11641 return false;
11642 }
11643 auto renderContext = host->GetRenderContext();
11644 CHECK_NULL_RETURN(renderContext, false);
11645 auto opacity = renderContext->GetOpacity();
11646 // if opacity is 0.0f, no need to hit frameNode.
11647 if (NearZero(opacity.value_or(1.0f))) {
11648 return false;
11649 }
11650 return true;
11651 }
11652
11653 RectF RichEditorPattern::ExpandDefaultResponseRegion(RectF& rect)
11654 {
11655 return rect + NG::SizeF(0, OHOS::Ace::HOT_AREA_ADJUST_SIZE.ConvertToPx() * OHOS::Ace::HOT_AREA_EXPAND_TIME) +
11656 NG::OffsetF(0, -OHOS::Ace::HOT_AREA_ADJUST_SIZE.ConvertToPx());
11657 }
11658
11659 bool RichEditorPattern::InsertOrDeleteSpace(int32_t index)
11660 {
11661 // delete or insert space
11662 if (index < 0 || index >= GetTextContentLength()) {
11663 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "index is invalid, index=%{public}d", index);
11664 return false;
11665 }
11666 bool success = SetCaretPosition(index);
11667 CHECK_NULL_RETURN(success, false);
11668 CloseSelectOverlay();
11669 ResetSelection();
11670
11671 auto curIt = GetSpanIter(index);
11672 if (curIt != spans_.end()) {
11673 std::u16string curText = (*curIt)->content;
11674 if ((*curIt)->spanItemType == SpanItemType::NORMAL
11675 && index >= (*curIt)->rangeStart && curText[index - (*curIt)->rangeStart] == u' ') {
11676 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "delete forward");
11677 DeleteForward(1);
11678 return true;
11679 }
11680 }
11681
11682 auto preIt = GetSpanIter(index - 1);
11683 if (preIt != spans_.end()) {
11684 std::u16string preText = (*preIt)->content;
11685 if ((*preIt)->spanItemType == SpanItemType::NORMAL
11686 && index - 1 >= (*preIt)->rangeStart && preText[index - 1 - (*preIt)->rangeStart] == u' ') {
11687 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "delete backward");
11688 DeleteBackward(1);
11689 return true;
11690 }
11691 }
11692
11693 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "insert value");
11694 InsertValue(u" ", true);
11695 return true;
11696 }
11697
11698 void RichEditorPattern::DeleteRange(int32_t start, int32_t end, bool isIME)
11699 {
11700 if (start > end) {
11701 std::swap(start, end);
11702 }
11703 start = std::max(0, start);
11704 end = std::min(GetTextContentLength(), end);
11705 if (start > GetTextContentLength() || end < 0 || start == end) {
11706 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "start=%{public}d, end=%{public}d, not in the range", start, end);
11707 return;
11708 }
11709 CHECK_NULL_VOID(!IsPreviewTextInputting());
11710 SetCaretPosition(start);
11711 auto length = end - start;
11712 if (isSpanStringMode_) {
11713 DeleteValueInStyledString(start, length, true, false);
11714 return;
11715 }
11716 OperationRecord record;
11717 record.beforeCaretPosition = caretPosition_;
11718 RichEditorChangeValue changeValue;
11719 CHECK_NULL_VOID(BeforeChangeText(changeValue, record, RecordType::DEL_FORWARD, length));
11720 std::u16string textContent;
11721 GetContentBySpans(textContent);
11722 auto realEnd = std::clamp(caretPosition_ + length, 0, static_cast<int32_t>(textContent.length()));
11723 std::u16string deleteText = textContent.substr(
11724 static_cast<uint32_t>(std::clamp(caretPosition_, 0, static_cast<int32_t>(textContent.length()))),
11725 static_cast<uint32_t>(realEnd - caretPosition_));
11726 if (caretPosition_ != GetTextContentLength()) {
11727 RichEditorDeleteValue info;
11728 info.SetOffset(caretPosition_);
11729 info.SetRichEditorDeleteDirection(RichEditorDeleteDirection::FORWARD);
11730 info.SetLength(length);
11731 int32_t currentPosition = caretPosition_;
11732 if (!spans_.empty()) {
11733 CalcDeleteValueObj(currentPosition, length, info);
11734 bool doDelete = DoDeleteActions(currentPosition, length, info);
11735 CHECK_NULL_VOID(doDelete);
11736 }
11737 }
11738 CHECK_NULL_VOID(deleteText.length() != 0);
11739 ClearRedoOperationRecords();
11740 record.deleteText = deleteText;
11741 record.afterCaretPosition = caretPosition_;
11742 AddOperationRecord(record);
11743 AfterContentChange(changeValue);
11744 }
11745
11746 void RichEditorPattern::HandleOnPageUp()
11747 {
11748 HandlePageScroll(true);
11749 }
11750
11751 void RichEditorPattern::HandleOnPageDown()
11752 {
11753 HandlePageScroll(false);
11754 }
11755
11756 void RichEditorPattern::HandlePageScroll(bool isPageUp)
11757 {
11758 auto visibleRect = selectOverlay_->GetVisibleRect();
11759 float distance = isPageUp ? visibleRect.Height() : -visibleRect.Height();
11760 RectF curCaretRect = GetCaretRect();
11761 auto height = paragraphs_.GetHeight();
11762 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "PageScroll isPageUp:%{public}d distance:%{public}f paragraphsHeight:%{public}f",
11763 isPageUp, distance, height);
11764 CloseSelectOverlay();
11765 ResetSelection();
11766 OnScrollCallback(distance, SCROLL_FROM_JUMP);
11767 auto paintOffset = selectOverlay_->GetPaintOffsetWithoutTransform();
11768 float offsetY = isPageUp ? visibleRect.Top() : visibleRect.Bottom();
11769 auto localOffset = Offset(curCaretRect.GetX(), offsetY - paintOffset.GetY());
11770 auto textOffset = ConvertTouchOffsetToTextOffset(localOffset);
11771 auto positionWithAffinity = paragraphs_.GetGlyphPositionAtCoordinate(textOffset);
11772 // If scrolling to the first or last line, move the cursor to the beginning or end of the line
11773 if (isPageUp && LessOrEqual(textOffset.GetY(), 0)) {
11774 positionWithAffinity = PositionWithAffinity(0, TextAffinity::DOWNSTREAM);
11775 } else if (!isPageUp && GreatOrEqual(textOffset.GetY(), height)) {
11776 positionWithAffinity = PositionWithAffinity(GetTextContentLength(), TextAffinity::UPSTREAM);
11777 }
11778 SetCaretPositionWithAffinity(positionWithAffinity);
11779 IF_TRUE(isEditing_, StartTwinkling());
11780 }
11781
11782 TextStyle RichEditorPattern::GetDefaultTextStyle()
11783 {
11784 auto theme = GetTheme<RichEditorTheme>();
11785 TextStyle style = theme ? theme->GetTextStyle() : TextStyle();
11786 style.SetFontSize(Dimension(DEFAULT_TEXT_SIZE, DimensionUnit::FP));
11787 style.SetFontFeatures(ParseFontFeatureSettings("\"pnum\" 1"));
11788 style.SetFontFamilies({ "HarmonyOS Sans" });
11789 return style;
11790 }
11791
11792 bool RichEditorPattern::IsShowTranslate()
11793 {
11794 auto richEditorTheme = GetTheme<RichEditorTheme>();
11795 CHECK_NULL_RETURN(richEditorTheme, false);
11796 return richEditorTheme->GetTranslateIsSupport();
11797 }
11798
11799 bool RichEditorPattern::IsShowSearch()
11800 {
11801 auto richEditorTheme = GetTheme<RichEditorTheme>();
11802 CHECK_NULL_RETURN(richEditorTheme, false);
11803 return richEditorTheme->GetSearchIsSupport();
11804 }
11805
11806 bool RichEditorPattern::IsShowAIWrite()
11807 {
11808 CHECK_NULL_RETURN(!textSelector_.SelectNothing(), false);
11809 auto container = Container::Current();
11810 if (container && container->IsScenceBoardWindow()) {
11811 return false;
11812 }
11813
11814 if (copyOption_ == CopyOptions::None) {
11815 return false;
11816 }
11817 auto theme = GetTheme<RichEditorTheme>();
11818 CHECK_NULL_RETURN(theme, false);
11819 auto bundleName = theme->GetAIWriteBundleName();
11820 auto abilityName = theme->GetAIWriteAbilityName();
11821 if (bundleName.empty() || abilityName.empty()) {
11822 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "Failed to obtain AI write package name!");
11823 return false;
11824 }
11825 aiWriteAdapter_->SetBundleName(bundleName);
11826 aiWriteAdapter_->SetAbilityName(abilityName);
11827
11828 auto isAISupport = false;
11829 if (theme->GetAIWriteIsSupport() == "true") {
11830 isAISupport = true;
11831 }
11832
11833 auto host = GetHost();
11834 CHECK_NULL_RETURN(host, false);
11835 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Whether the device supports AI write: %{public}d, nodeId: %{public}d",
11836 isAISupport, host->GetId());
11837 return isAISupport;
11838 }
11839
11840 void RichEditorPattern::GetAIWriteInfo(AIWriteInfo& info)
11841 {
11842 CHECK_NULL_VOID(!textSelector_.SelectNothing());
11843 info.firstHandle = textSelector_.firstHandle.ToString();
11844 info.secondHandle = textSelector_.secondHandle.ToString();
11845 info.selectStart = textSelector_.GetTextStart();
11846 info.selectEnd = textSelector_.GetTextEnd();
11847 auto host = GetHost();
11848 CHECK_NULL_VOID(host);
11849 info.componentType = host->GetTag();
11850
11851 // serialize the sentenced-level text
11852 auto textSize = static_cast<int32_t>(GetTextForDisplay().length()) + placeholderCount_;
11853 RefPtr<SpanString> spanString = ToStyledString(0, textSize);
11854 auto contentAll = spanString->GetU16string();
11855 auto sentenceStart = 0;
11856 auto sentenceEnd = textSize;
11857 for (int32_t i = info.selectStart; i >= 0; --i) {
11858 if (aiWriteAdapter_->IsSentenceBoundary(contentAll[i])) {
11859 sentenceStart = i + 1;
11860 break;
11861 }
11862 }
11863 for (int32_t i = info.selectEnd; i < textSize; i++) {
11864 if (aiWriteAdapter_->IsSentenceBoundary(contentAll[i])) {
11865 sentenceEnd = i;
11866 break;
11867 }
11868 }
11869 info.start = info.selectStart - sentenceStart;
11870 info.end = info.selectEnd - sentenceStart;
11871 spanString = ToStyledString(sentenceStart, sentenceEnd);
11872 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "Sentence range=[%{public}d-%{public}d], content=" SEC_PLD(%{public}s),
11873 sentenceStart, sentenceEnd, SEC_PARAM(spanString->GetString().c_str()));
11874 spanString->EncodeTlv(info.sentenceBuffer);
11875
11876 // serialize the selected text
11877 spanString = ToStyledString(info.selectStart, info.selectEnd);
11878 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "Selected range=[%{public}d-%{public}d], content=" SEC_PLD(%{public}s),
11879 info.selectStart, info.selectEnd, SEC_PARAM(spanString->GetString().c_str()));
11880 spanString->EncodeTlv(info.selectBuffer);
11881 info.selectLength = static_cast<int32_t>(aiWriteAdapter_->GetSelectLengthOnlyText(spanString->GetU16string()));
11882 }
11883
11884 void RichEditorPattern::HandleOnAIWrite()
11885 {
11886 aiWriteAdapter_->SetAIWrite(true);
11887 AIWriteInfo info;
11888 GetAIWriteInfo(info);
11889 CloseSelectOverlay();
11890 ResetSelection();
11891 CloseKeyboard(false);
11892
11893 auto callback = [weak = WeakClaim(this), info](std::vector<uint8_t>& buffer) {
11894 auto pattern = weak.Upgrade();
11895 CHECK_NULL_VOID(pattern);
11896 pattern->HandleAIWriteResult(info.selectStart, info.selectEnd, buffer);
11897 auto aiWriteAdapter = pattern->aiWriteAdapter_;
11898 CHECK_NULL_VOID(aiWriteAdapter);
11899 aiWriteAdapter->CloseModalUIExtension();
11900 };
11901 auto host = GetHost();
11902 CHECK_NULL_VOID(host);
11903 auto pipeline = host->GetContext();
11904 CHECK_NULL_VOID(pipeline);
11905 aiWriteAdapter_->SetPipelineContext(WeakClaim(pipeline));
11906 aiWriteAdapter_->ShowModalUIExtension(info, callback);
11907 }
11908
11909 SymbolSpanOptions RichEditorPattern::GetSymbolSpanOptions(const RefPtr<SpanItem>& spanItem)
11910 {
11911 CHECK_NULL_RETURN(spanItem, {});
11912 TextStyle textStyle = GetDefaultTextStyle();
11913 UseSelfStyle(spanItem->fontStyle, spanItem->textLineStyle, textStyle);
11914 SymbolSpanOptions options;
11915 options.style = textStyle;
11916 options.offset = caretPosition_;
11917 options.resourceObject = spanItem->GetResourceObject();
11918 options.symbolId = spanItem->GetSymbolId();
11919 return options;
11920 }
11921
11922 void RichEditorPattern::ReplacePlaceholderWithCustomSpan(
11923 const RefPtr<SpanItem>& spanItem, size_t& index, size_t& textIndex)
11924 {
11925 if (isSpanStringMode_) {
11926 auto customSpanItem = DynamicCast<CustomSpanItem>(spanItem);
11927 auto customSpan = MakeRefPtr<CustomSpan>();
11928 if (customSpanItem->onMeasure.has_value()) {
11929 customSpan->SetOnMeasure(customSpanItem->onMeasure.value());
11930 }
11931 if (customSpanItem->onDraw.has_value()) {
11932 customSpan->SetOnDraw(customSpanItem->onDraw.value());
11933 }
11934 auto spanString = MakeRefPtr<MutableSpanString>(customSpan);
11935 InsertStyledStringByPaste(spanString);
11936 } else {
11937 auto customSpanItem = DynamicCast<PlaceholderSpanItem>(spanItem);
11938 CHECK_NULL_VOID(customSpanItem);
11939 auto customNode = customSpanItem->GetCustomNode();
11940 SpanOptionBase options;
11941 options.offset = caretPosition_;
11942 AddPlaceholderSpan(customNode, options);
11943 }
11944 textIndex = index + PLACEHOLDER_LENGTH;
11945 }
11946
11947 void RichEditorPattern::ReplacePlaceholderWithSymbolSpan(
11948 const RefPtr<SpanItem>& spanItem, size_t& index, size_t& textIndex)
11949 {
11950 auto options = GetSymbolSpanOptions(spanItem);
11951 options.offset = caretPosition_;
11952 AddSymbolSpan(options, false, caretPosition_);
11953 textIndex = index + PLACEHOLDER_LENGTH;
11954 }
11955
11956 void RichEditorPattern::ReplacePlaceholderWithImageSpan(
11957 const RefPtr<SpanItem>& spanItem, size_t& index, size_t& textIndex)
11958 {
11959 auto imageSpanItem = DynamicCast<ImageSpanItem>(spanItem);
11960 CHECK_NULL_VOID(imageSpanItem);
11961 auto options = imageSpanItem->options;
11962 options.offset = caretPosition_;
11963 if (isSpanStringMode_) {
11964 auto spanString = MakeRefPtr<SpanString>(options);
11965 InsertStyledStringByPaste(spanString);
11966 } else {
11967 AddImageSpan(options, true, caretPosition_, true);
11968 }
11969 textIndex = index + PLACEHOLDER_LENGTH;
11970 }
11971
11972 void RichEditorPattern::ReplacePlaceholderWithRawSpans(
11973 const RefPtr<SpanItem>& spanItem, size_t& index, size_t& textIndex)
11974 {
11975 switch (spanItem->spanItemType) {
11976 case SpanItemType::SYMBOL:
11977 ReplacePlaceholderWithSymbolSpan(spanItem, index, textIndex);
11978 return;
11979 case SpanItemType::CustomSpan:
11980 ReplacePlaceholderWithCustomSpan(spanItem, index, textIndex);
11981 return;
11982 case SpanItemType::IMAGE:
11983 ReplacePlaceholderWithImageSpan(spanItem, index, textIndex);
11984 return;
11985 default:
11986 return;
11987 }
11988 }
11989
11990 void RichEditorPattern::AddSpansAndReplacePlaceholder(RefPtr<SpanString>& spanString)
11991 {
11992 auto content = spanString->GetU16string();
11993 size_t textIndex = 0;
11994 size_t index = content.find(PLACEHOLDER_MARK);
11995
11996 while (index != std::u16string::npos) {
11997 if (textIndex < index) {
11998 auto subSpan = spanString->GetSubSpanString(textIndex, index - textIndex);
11999 AddSpanByPasteData(subSpan);
12000 }
12001 auto key = content.substr(index, PLACEHOLDER_LENGTH);
12002 if (placeholderSpansMap_.find(key) == placeholderSpansMap_.end()) {
12003 index = content.find(PLACEHOLDER_MARK, index + 1);
12004 continue;
12005 }
12006 auto spanItem = placeholderSpansMap_[key];
12007 if (!spanItem) {
12008 index = content.find(PLACEHOLDER_MARK, index + 1);
12009 continue;
12010 }
12011 ReplacePlaceholderWithRawSpans(spanItem, index, textIndex);
12012 index = content.find(PLACEHOLDER_MARK, index + 1);
12013 }
12014 if (textIndex < content.length()) {
12015 auto subSpan = spanString->GetSubSpanString(textIndex, content.length() - textIndex);
12016 AddSpanByPasteData(subSpan);
12017 }
12018 }
12019
12020 void RichEditorPattern::InsertSpanByBackData(RefPtr<SpanString>& spanString)
12021 {
12022 CHECK_NULL_VOID(spanString);
12023 if (textSelector_.IsValid()) {
12024 SetCaretPosition(textSelector_.GetTextStart());
12025 DeleteForward(textSelector_.GetTextStart(), textSelector_.GetTextEnd() - textSelector_.GetTextStart());
12026 ResetSelection();
12027 }
12028 if (placeholderSpansMap_.empty()) {
12029 AddSpanByPasteData(spanString);
12030 } else {
12031 AddSpansAndReplacePlaceholder(spanString);
12032 }
12033 StartTwinkling();
12034 auto host = GetHost();
12035 CHECK_NULL_VOID(host);
12036 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
12037 host->MarkModifyDone();
12038 }
12039
12040 void RichEditorPattern::HandleAIWriteResult(int32_t start, int32_t end, std::vector<uint8_t>& buffer)
12041 {
12042 RefPtr<SpanString> spanString = SpanString::DecodeTlv(buffer);
12043 if (spanString->GetSpanItems().empty()) {
12044 return;
12045 }
12046 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "Backfilling results range=[%{public}d--%{public}d], content=" SEC_PLD(%{public}s),
12047 start, end, SEC_PARAM(spanString->GetString().c_str()));
12048
12049 textSelector_.Update(start, end);
12050 auto length = end - start;
12051 CHECK_NULL_VOID(length > 0);
12052 DeleteBackward(length);
12053 InsertSpanByBackData(spanString);
12054 BeforeIMEInsertValue(UtfUtils::Str8ToStr16(spanString->GetString()));
12055 InsertValue(u"");
12056 }
12057
12058 bool RichEditorPattern::IsTextEditableForStylus() const
12059 {
12060 CHECK_NULL_RETURN(!customKeyboardBuilder_, false);
12061 auto host = GetHost();
12062 CHECK_NULL_RETURN(host, false);
12063 auto focusHub = host->GetFocusHub();
12064 CHECK_NULL_RETURN(focusHub, false);
12065 if (!focusHub->IsFocusable() || !host->IsVisible()) {
12066 return false;
12067 }
12068 auto renderContext = host->GetRenderContext();
12069 CHECK_NULL_RETURN(renderContext, false);
12070 auto opacity = renderContext->GetOpacity();
12071 // if opacity is 0.0f, no need to hit frameNode.
12072 if (NearZero(opacity.value_or(1.0f))) {
12073 return false;
12074 }
12075 return true;
12076 }
12077
12078 void RichEditorPattern::DumpInfo(std::unique_ptr<JsonValue>& json)
12079 {
12080 if (customKeyboardBuilder_) {
12081 json->Put("CustomKeyboard, Attached", std::to_string(isCustomKeyboardAttached_).c_str());
12082 }
12083 auto host = GetHost();
12084 CHECK_NULL_VOID(host);
12085 auto richEditorTheme = GetTheme<RichEditorTheme>();
12086 CHECK_NULL_VOID(richEditorTheme);
12087 json->Put("caret offset", GetCaretRect().GetOffset().ToString().c_str());
12088 json->Put("caret height",
12089 std::to_string(NearZero(GetCaretRect().Height()) ? richEditorTheme->GetDefaultCaretHeight().ConvertToPx()
12090 : GetCaretRect().Height())
12091 .c_str());
12092 json->Put("text rect", richTextRect_.ToString().c_str());
12093 json->Put("content rect", contentRect_.ToString().c_str());
12094 auto richEditorPaintOffset = host->GetPaintRectOffsetNG(false, true);
12095 bool hasRenderTransform = selectOverlay_->HasRenderTransform();
12096 if (hasRenderTransform) {
12097 richEditorPaintOffset = selectOverlay_->GetPaintOffsetWithoutTransform();
12098 }
12099 json->Put("hasRenderTransform", std::to_string(hasRenderTransform).c_str());
12100 json->Put("richEditorPaintOffset", richEditorPaintOffset.ToString().c_str());
12101 auto selectOverlayInfo = selectOverlay_->GetSelectOverlayInfo();
12102 CHECK_NULL_VOID(selectOverlayInfo);
12103 json->Put("selectOverlay info", selectOverlayInfo->ToString().c_str());
12104 }
12105
12106 RectF RichEditorPattern::GetCaretRelativeRect()
12107 {
12108 CHECK_NULL_RETURN(caretTwinkling_, RectF(-1, -1, -1, -1));
12109 auto [caretOffset, caretHeight] = CalculateCaretOffsetAndHeight();
12110 CHECK_NULL_RETURN(overlayMod_, RectF(0, 0, 0, 0));
12111 auto caretWidth = DynamicCast<RichEditorOverlayModifier>(overlayMod_)->GetCaretWidth();
12112 return RectF(caretOffset.GetX(), caretOffset.GetY(), caretWidth, caretHeight);
12113 }
12114 } // namespace OHOS::Ace::NG
12115