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