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