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 <algorithm>
18 #include <chrono>
19 #include <cstddef>
20 #include <cstdint>
21 #include <functional>
22 #include <iterator>
23 #include <sstream>
24 #include <string>
25 #include <utility>
26
27 #include "base/geometry/dimension.h"
28 #include "base/geometry/ng/offset_t.h"
29 #include "base/geometry/ng/rect_t.h"
30 #include "base/geometry/offset.h"
31 #include "base/log/dump_log.h"
32 #include "base/log/log_wrapper.h"
33 #include "base/memory/ace_type.h"
34 #include "base/utils/string_utils.h"
35 #include "base/utils/utils.h"
36 #include "core/common/ai/data_detector_mgr.h"
37 #include "core/common/clipboard/paste_data.h"
38 #include "core/common/container.h"
39 #include "core/common/container_scope.h"
40 #include "core/common/ime/text_input_client.h"
41 #include "core/components/common/layout/constants.h"
42 #include "core/components_ng/base/view_stack_processor.h"
43 #include "core/components_ng/event/event_hub.h"
44 #include "core/components_ng/event/gesture_event_hub.h"
45 #include "core/components_ng/event/long_press_event.h"
46 #include "core/components_ng/pattern/image/image_pattern.h"
47 #include "core/components_ng/pattern/rich_editor/rich_editor_event_hub.h"
48 #include "core/components_ng/pattern/rich_editor/rich_editor_layout_property.h"
49 #include "core/components_ng/pattern/rich_editor/rich_editor_model.h"
50 #include "core/components_ng/pattern/rich_editor/rich_editor_overlay_modifier.h"
51 #include "core/components_ng/pattern/rich_editor/rich_editor_theme.h"
52 #include "core/components_ng/pattern/rich_editor/selection_info.h"
53 #include "core/components_ng/pattern/rich_editor_drag/rich_editor_drag_pattern.h"
54 #include "core/components_ng/pattern/text/span_node.h"
55 #include "core/components_ng/pattern/text/text_base.h"
56 #include "core/components_ng/pattern/text/typed_text.h"
57 #include "core/components_ng/pattern/text_field/text_field_manager.h"
58 #include "core/components_ng/pattern/text_field/text_input_ai_checker.h"
59 #include "core/components_ng/property/property.h"
60 #include "core/components_v2/inspector/inspector_constants.h"
61 #include "core/gestures/gesture_info.h"
62 #include "core/pipeline/base/element_register.h"
63
64 #if not defined(ACE_UNITTEST)
65 #if defined(ENABLE_STANDARD_INPUT)
66 #include "commonlibrary/c_utils/base/include/refbase.h"
67
68 #include "core/components_ng/pattern/text_field/on_text_changed_listener_impl.h"
69 #endif
70 #endif
71
72 #include "core/common/ace_engine_ext.h"
73 #include "core/common/udmf/udmf_client.h"
74
75 #ifdef WINDOW_SCENE_SUPPORTED
76 #include "core/components_ng/pattern/window_scene/helper/window_scene_helper.h"
77 #endif
78
79 namespace OHOS::Ace::NG {
80 namespace {
81 #if defined(ENABLE_STANDARD_INPUT)
82 // should be moved to theme
83 constexpr float CARET_WIDTH = 1.5f;
84 constexpr float DEFAULT_CARET_HEIGHT = 18.5f;
85 constexpr Dimension KEYBOARD_AVOID_OFFSET = 24.0_vp;
86 #endif
87 constexpr int32_t IMAGE_SPAN_LENGTH = 1;
88 constexpr int32_t SYMBOL_SPAN_LENGTH = 2;
89 constexpr int32_t RICH_EDITOR_TWINKLING_INTERVAL_MS = 500;
90 constexpr float DEFAULT_TEXT_SIZE = 16.0f;
91 constexpr int32_t AUTO_SCROLL_INTERVAL = 15;
92 constexpr Dimension AUTO_SCROLL_EDGE_DISTANCE = 15.0_vp;
93 constexpr Dimension AUTO_SCROLL_DRAG_EDGE_DISTANCE = 58.0_vp;
94 constexpr float MAX_DRAG_SCROLL_SPEED = 2400.0f;
95 constexpr float TIME_UNIT = 1000.0f;
96 constexpr float DOUBLE_CLICK_INTERVAL_MS = 300.0f;
97 constexpr float BOX_EPSILON = 0.5f;
98 constexpr uint32_t RECORD_MAX_LENGTH = 20;
99
100 const std::wstring lineSeparator = L"\n";
101 // hen do ai anaylsis, we should limit the left an right limit of the string
102 constexpr static int32_t AI_TEXT_RANGE_LEFT = 50;
103 constexpr static int32_t AI_TEXT_RANGE_RIGHT = 50;
104 } // namespace
RichEditorPattern()105 RichEditorPattern::RichEditorPattern() {}
106
~RichEditorPattern()107 RichEditorPattern::~RichEditorPattern()
108 {
109 if (isCustomKeyboardAttached_) {
110 CloseCustomKeyboard();
111 }
112 }
113
OnModifyDone()114 void RichEditorPattern::OnModifyDone()
115 {
116 auto host = GetHost();
117 CHECK_NULL_VOID(host);
118 auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
119 copyOption_ = layoutProperty->GetCopyOption().value_or(CopyOptions::Distributed);
120 auto context = PipelineContext::GetCurrentContext();
121 CHECK_NULL_VOID(context);
122 context->AddOnAreaChangeNode(host->GetId());
123 if (!clipboard_ && context) {
124 clipboard_ = ClipboardProxy::GetInstance()->GetClipboard(context->GetTaskExecutor());
125 }
126 instanceId_ = context->GetInstanceId();
127 InitMouseEvent();
128 auto focusHub = host->GetOrCreateFocusHub();
129 CHECK_NULL_VOID(focusHub);
130 InitFocusEvent(focusHub);
131 auto gestureEventHub = host->GetOrCreateGestureEventHub();
132 InitClickEvent(gestureEventHub);
133 InitLongPressEvent(gestureEventHub);
134 InitTouchEvent();
135 HandleEnabled();
136 ProcessInnerPadding();
137 InitScrollablePattern();
138 if (CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
139 dataDetectorAdapter_->StartAITask();
140 }
141 if (host->IsDraggable() && copyOption_ != CopyOptions::None) {
142 InitDragDropEvent();
143 AddDragFrameNodeToManager(host);
144 } else {
145 ClearDragDropEvent();
146 RemoveDragFrameNodeFromManager(host);
147 }
148 Register2DragDropManager();
149 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
150
151 auto eventHub = host->GetEventHub<EventHub>();
152 CHECK_NULL_VOID(eventHub);
153 bool enabledCache = eventHub->IsEnabled();
154 if (textDetectEnable_ && enabledCache != enabled_) {
155 enabled_ = enabledCache;
156 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
157 }
158 }
159
HandleEnabled()160 void RichEditorPattern::HandleEnabled()
161 {
162 auto host = GetHost();
163 CHECK_NULL_VOID(host);
164 auto renderContext = host->GetRenderContext();
165 CHECK_NULL_VOID(renderContext);
166 if (IsDisabled()) {
167 auto pipeline = PipelineContext::GetCurrentContext();
168 CHECK_NULL_VOID(pipeline);
169 auto richEditorTheme = pipeline->GetTheme<RichEditorTheme>();
170 CHECK_NULL_VOID(richEditorTheme);
171 auto disabledAlpha = richEditorTheme->GetDisabledAlpha();
172 renderContext->OnOpacityUpdate(disabledAlpha);
173 } else {
174 auto opacity = renderContext->GetOpacity().value_or(1.0);
175 renderContext->OnOpacityUpdate(opacity);
176 }
177 }
178
BeforeCreateLayoutWrapper()179 void RichEditorPattern::BeforeCreateLayoutWrapper()
180 {
181 TextPattern::PreCreateLayoutWrapper();
182 }
183
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)184 bool RichEditorPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
185 {
186 if (config.skipMeasure || dirty->SkipMeasureContent()) {
187 return false;
188 }
189 frameRect_ = dirty->GetGeometryNode()->GetFrameRect();
190 auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
191 CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
192 auto richEditorLayoutAlgorithm =
193 DynamicCast<RichEditorLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
194 CHECK_NULL_RETURN(richEditorLayoutAlgorithm, false);
195 parentGlobalOffset_ = richEditorLayoutAlgorithm->GetParentGlobalOffset();
196 richTextRect_ = richEditorLayoutAlgorithm->GetTextRect();
197 UpdateTextFieldManager(Offset(parentGlobalOffset_.GetX(), parentGlobalOffset_.GetY()), frameRect_.Height());
198 auto restoreSelectOverlayProxy = selectOverlayProxy_;
199 selectOverlayProxy_.Reset(); // skip show selectoverlay in the TextPattern.
200 bool ret = TextPattern::OnDirtyLayoutWrapperSwap(dirty, config);
201 selectOverlayProxy_ = restoreSelectOverlayProxy;
202 UpdateScrollStateAfterLayout(config.frameSizeChange);
203 if (!isRichEditorInit_) {
204 auto eventHub = GetEventHub<RichEditorEventHub>();
205 CHECK_NULL_RETURN(eventHub, ret);
206 eventHub->FireOnReady();
207 ClearOperationRecords();
208 isFirstCallOnReady_ = true;
209 isRichEditorInit_ = true;
210 }
211 MoveCaretOnLayoutSwap();
212 if (textSelector_.IsValid() && SelectOverlayIsOn() && isShowMenu_) {
213 CalculateHandleOffsetAndShowOverlay();
214 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle);
215 }
216 isShowMenu_ = true;
217 UpdateCaretInfoToController();
218 auto host = GetHost();
219 CHECK_NULL_RETURN(host, ret);
220 auto context = host->GetRenderContext();
221 CHECK_NULL_RETURN(context, ret);
222 if (context->GetClipEdge().has_value()) {
223 auto geometryNode = host->GetGeometryNode();
224 auto frameOffset = geometryNode->GetFrameOffset();
225 auto frameSize = geometryNode->GetFrameSize();
226 auto height = static_cast<float>(paragraphs_.GetHeight() + std::fabs(baselineOffset_));
227 if (!context->GetClipEdge().value() && LessNotEqual(frameSize.Height(), height)) {
228 RectF boundsRect(frameOffset.GetX(), frameOffset.GetY(), frameSize.Width(), height);
229 CHECK_NULL_RETURN(overlayMod_, ret);
230 overlayMod_->SetBoundsRect(boundsRect);
231 }
232 }
233 caretUpdateType_ = CaretUpdateType::NONE;
234 return ret;
235 }
236
MoveCaretOnLayoutSwap()237 void RichEditorPattern::MoveCaretOnLayoutSwap()
238 {
239 MoveCaretAfterTextChange();
240 if (HasFocus()) {
241 MoveCaretToContentRect();
242 }
243 }
244
CreateImageSourceInfo(const ImageSpanOptions & options)245 std::function<ImageSourceInfo()> RichEditorPattern::CreateImageSourceInfo(const ImageSpanOptions& options)
246 {
247 std::string src;
248 RefPtr<PixelMap> pixMap = nullptr;
249 std::string bundleName;
250 std::string moduleName;
251 if (options.image.has_value()) {
252 src = options.image.value();
253 }
254 if (options.imagePixelMap.has_value()) {
255 pixMap = options.imagePixelMap.value();
256 }
257 if (options.bundleName.has_value()) {
258 bundleName = options.bundleName.value();
259 }
260 if (options.moduleName.has_value()) {
261 moduleName = options.moduleName.value();
262 }
263 auto createSourceInfoFunc = [src, noPixMap = !options.imagePixelMap.has_value(), pixMap, bundleName,
264 moduleName]() -> ImageSourceInfo {
265 #if defined(PIXEL_MAP_SUPPORTED)
266 if (noPixMap) {
267 return { src, bundleName, moduleName };
268 }
269 return ImageSourceInfo(pixMap);
270 #else
271 return { src, bundleName, moduleName };
272 #endif
273 };
274 return std::move(createSourceInfoFunc);
275 }
276
GetTextContentLength()277 int32_t RichEditorPattern::GetTextContentLength()
278 {
279 if (!spans_.empty()) {
280 auto it = spans_.rbegin();
281 return (*it)->position;
282 }
283 return 0;
284 }
AddImageSpan(const ImageSpanOptions & options,bool isPaste,int32_t index)285 int32_t RichEditorPattern::AddImageSpan(const ImageSpanOptions& options, bool isPaste, int32_t index)
286 {
287 auto host = GetHost();
288 CHECK_NULL_RETURN(host, -1);
289
290 auto imageNode = ImageSpanNode::GetOrCreateSpanNode(V2::IMAGE_ETS_TAG,
291 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<ImagePattern>(); });
292 auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
293
294 // Disable the image itself event
295 imageNode->SetDraggable(false);
296 auto gesture = imageNode->GetOrCreateGestureEventHub();
297 CHECK_NULL_RETURN(gesture, -1);
298
299 OperationRecord record;
300 record.beforeCaretPosition = options.offset.value_or(static_cast<int32_t>(GetTextContentLength()));
301 record.addText = " ";
302 ClearRedoOperationRecords();
303 record.afterCaretPosition = record.beforeCaretPosition + 1;
304 AddOperationRecord(record);
305
306 // Masked the default drag behavior of node image
307 gesture->SetDragEvent(nullptr, { PanDirection::DOWN }, 0, Dimension(0));
308
309 int32_t spanIndex = 0;
310 int32_t offset = -1;
311 if (options.offset.has_value()) {
312 offset = TextSpanSplit(options.offset.value());
313 if (offset == -1) {
314 spanIndex = static_cast<int32_t>(host->GetChildren().size());
315 } else {
316 spanIndex = offset;
317 }
318 imageNode->MountToParent(host, offset);
319 } else if (index != -1) {
320 imageNode->MountToParent(host, index);
321 spanIndex = index;
322 } else {
323 spanIndex = static_cast<int32_t>(host->GetChildren().size());
324 imageNode->MountToParent(host);
325 }
326 std::function<ImageSourceInfo()> createSourceInfoFunc = CreateImageSourceInfo(options);
327 imageLayoutProperty->UpdateImageSourceInfo(createSourceInfoFunc());
328 if (options.imageAttribute.has_value()) {
329 auto imgAttr = options.imageAttribute.value();
330 if (imgAttr.size.has_value()) {
331 imageLayoutProperty->UpdateUserDefinedIdealSize(
332 CalcSize(CalcLength(imgAttr.size.value().width), CalcLength(imgAttr.size.value().height)));
333 }
334 if (imgAttr.verticalAlign.has_value()) {
335 imageLayoutProperty->UpdateVerticalAlign(imgAttr.verticalAlign.value());
336 }
337 if (imgAttr.objectFit.has_value()) {
338 imageLayoutProperty->UpdateImageFit(imgAttr.objectFit.value());
339 }
340 if (imgAttr.marginProp.has_value()) {
341 imageLayoutProperty->UpdateMargin(imgAttr.marginProp.value());
342 }
343 if (imgAttr.borderRadius.has_value()) {
344 auto imageRenderCtx = imageNode->GetRenderContext();
345 imageRenderCtx->UpdateBorderRadius(imgAttr.borderRadius.value());
346 imageRenderCtx->SetClipToBounds(true);
347 }
348 }
349 if (isPaste) {
350 isTextChange_ = true;
351 moveDirection_ = MoveDirection::FORWARD;
352 moveLength_ += 1;
353 MoveCaretAfterTextChange();
354 }
355 imageNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
356 imageNode->MarkModifyDone();
357 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
358 host->MarkModifyDone();
359 auto spanItem = imageNode->GetSpanItem();
360 // The length of the imageSpan defaults to the length of a character to calculate the position
361 spanItem->content = " ";
362 AddSpanItem(spanItem, offset);
363 if (options.userGestureOption.onClick) {
364 auto tmpClickFunc = options.userGestureOption.onClick;
365 spanItem->SetOnClickEvent(std::move(tmpClickFunc));
366 }
367 if (options.userGestureOption.onLongPress) {
368 auto tmpLongPressFunc = options.userGestureOption.onLongPress;
369 spanItem->SetLongPressEvent(std::move(tmpLongPressFunc));
370 }
371 if (options.offset.has_value() && options.offset.value() <= GetCaretPosition()) {
372 SetCaretPosition(options.offset.value() + 1 + moveLength_);
373 moveLength_ = 0;
374 } else {
375 placeholderCount_++;
376 SetCaretPosition(GetTextContentLength());
377 }
378 if (!isPaste && textSelector_.IsValid()) {
379 CloseSelectOverlay();
380 ResetSelection();
381 }
382 return spanIndex;
383 }
384
AddSpanItem(const RefPtr<SpanItem> & item,int32_t offset)385 void RichEditorPattern::AddSpanItem(const RefPtr<SpanItem>& item, int32_t offset)
386 {
387 auto host = GetHost();
388 CHECK_NULL_VOID(host);
389 if (offset == -1) {
390 offset = host->GetChildren().size();
391 }
392 offset = std::clamp(offset, 0, static_cast<int32_t>(host->GetChildren().size()) - 1);
393 auto it = spans_.begin();
394 std::advance(it, offset);
395 spans_.insert(it, item);
396 UpdateSpanPosition();
397 }
398
AddPlaceholderSpan(const RefPtr<UINode> & customNode,const SpanOptionBase & options)399 int32_t RichEditorPattern::AddPlaceholderSpan(const RefPtr<UINode>& customNode, const SpanOptionBase& options)
400 {
401 CHECK_NULL_RETURN(customNode, 0);
402 auto host = GetHost();
403 CHECK_NULL_RETURN(host, 0);
404 auto placeholderSpanNode = PlaceholderSpanNode::GetOrCreateSpanNode(V2::PLACEHOLDER_SPAN_ETS_TAG,
405 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<PlaceholderSpanPattern>(); });
406 CHECK_NULL_RETURN(placeholderSpanNode, 0);
407 customNode->MountToParent(placeholderSpanNode);
408 SetSelfAndChildDraggableFalse(customNode);
409 auto focusHub = placeholderSpanNode->GetOrCreateFocusHub();
410 focusHub->SetFocusable(false);
411 int32_t spanIndex = 0;
412 int32_t offset = -1;
413 auto optionalPosition = options.offset.value_or(-1);
414 if (optionalPosition >= 0) {
415 offset = TextSpanSplit(options.offset.value());
416 if (offset == -1) {
417 spanIndex = static_cast<int32_t>(host->GetChildren().size());
418 } else {
419 spanIndex = offset;
420 }
421 placeholderSpanNode->MountToParent(host, offset);
422 } else {
423 spanIndex = static_cast<int32_t>(host->GetChildren().size());
424 placeholderSpanNode->MountToParent(host);
425 }
426 auto spanItem = placeholderSpanNode->GetSpanItem();
427 spanItem->content = " ";
428 AddSpanItem(spanItem, offset);
429 if (options.offset.has_value() && options.offset.value() <= GetCaretPosition()) {
430 SetCaretPosition(options.offset.value() + 1 + moveLength_);
431 moveLength_ = 0;
432 } else {
433 placeholderCount_++;
434 SetCaretPosition(GetTextContentLength());
435 }
436 if (textSelector_.IsValid()) {
437 CloseSelectOverlay();
438 ResetSelection();
439 }
440 placeholderSpanNode->MarkModifyDone();
441 placeholderSpanNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
442 host->MarkModifyDone();
443 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
444 return spanIndex;
445 }
446
SetSelfAndChildDraggableFalse(const RefPtr<UINode> & customNode)447 void RichEditorPattern::SetSelfAndChildDraggableFalse(const RefPtr<UINode>& customNode)
448 {
449 CHECK_NULL_VOID(customNode);
450 auto frameNode = DynamicCast<FrameNode>(customNode);
451 if (frameNode) {
452 frameNode->SetDraggable(false);
453 }
454 for (const auto& child : customNode->GetChildren()) {
455 SetSelfAndChildDraggableFalse(child);
456 }
457 }
458
AddTextSpan(const TextSpanOptions & options,bool isPaste,int32_t index)459 int32_t RichEditorPattern::AddTextSpan(const TextSpanOptions& options, bool isPaste, int32_t index)
460 {
461 OperationRecord record;
462 record.beforeCaretPosition = options.offset.value_or(static_cast<int32_t>(GetTextContentLength()));
463 record.addText = options.value;
464 ClearRedoOperationRecords();
465 record.afterCaretPosition =
466 record.beforeCaretPosition + static_cast<int32_t>(StringUtils::ToWstring(options.value).length());
467 AddOperationRecord(record);
468 return AddTextSpanOperation(options, isPaste, index, false, false);
469 }
470
AddTextSpanOperation(const TextSpanOptions & options,bool isPaste,int32_t index,bool needLeadingMargin,bool updateCaretOPosition)471 int32_t RichEditorPattern::AddTextSpanOperation(
472 const TextSpanOptions& options, bool isPaste, int32_t index, bool needLeadingMargin, bool updateCaretOPosition)
473 {
474 auto host = GetHost();
475 CHECK_NULL_RETURN(host, -1);
476
477 auto* stack = ViewStackProcessor::GetInstance();
478 auto nodeId = stack->ClaimNodeId();
479 auto spanNode = SpanNode::GetOrCreateSpanNode(nodeId);
480
481 int32_t spanIndex = 0;
482 int32_t offset = -1;
483 if (options.offset.has_value()) {
484 offset = TextSpanSplit(options.offset.value(), needLeadingMargin);
485 if (offset == -1) {
486 spanIndex = static_cast<int32_t>(host->GetChildren().size());
487 } else {
488 spanIndex = offset;
489 }
490 spanNode->MountToParent(host, offset);
491 } else if (index != -1) {
492 spanNode->MountToParent(host, index);
493 spanIndex = index;
494 } else {
495 spanIndex = static_cast<int32_t>(host->GetChildren().size());
496 spanNode->MountToParent(host);
497 }
498 spanNode->UpdateContent(options.value);
499 spanNode->AddPropertyInfo(PropertyInfo::NONE);
500 if (options.style.has_value()) {
501 spanNode->UpdateTextColor(options.style.value().GetTextColor());
502 spanNode->AddPropertyInfo(PropertyInfo::FONTCOLOR);
503 spanNode->UpdateFontSize(options.style.value().GetFontSize());
504 spanNode->AddPropertyInfo(PropertyInfo::FONTSIZE);
505 spanNode->UpdateItalicFontStyle(options.style.value().GetFontStyle());
506 spanNode->AddPropertyInfo(PropertyInfo::FONTSTYLE);
507 spanNode->UpdateFontWeight(options.style.value().GetFontWeight());
508 spanNode->AddPropertyInfo(PropertyInfo::FONTWEIGHT);
509 spanNode->UpdateFontFamily(options.style.value().GetFontFamilies());
510 spanNode->AddPropertyInfo(PropertyInfo::FONTFAMILY);
511 spanNode->UpdateTextDecoration(options.style.value().GetTextDecoration());
512 spanNode->AddPropertyInfo(PropertyInfo::TEXTDECORATION);
513 spanNode->UpdateTextDecorationColor(options.style.value().GetTextDecorationColor());
514 spanNode->AddPropertyInfo(PropertyInfo::NONE);
515 spanNode->UpdateTextShadow(options.style.value().GetTextShadows());
516 spanNode->AddPropertyInfo(PropertyInfo::TEXTSHADOW);
517 }
518 auto spanItem = spanNode->GetSpanItem();
519 spanItem->content = options.value;
520 spanItem->SetTextStyle(options.style);
521 spanItem->hasResourceFontColor = options.hasResourceFontColor;
522 spanItem->hasResourceDecorationColor = options.hasResourceDecorationColor;
523 AddSpanItem(spanItem, offset);
524 if (options.paraStyle) {
525 int32_t start = 0;
526 int32_t end = 0;
527 spanItem->GetIndex(start, end);
528 UpdateParagraphStyle(start, end, *options.paraStyle);
529 }
530 if (options.userGestureOption.onClick) {
531 auto tmpClickFunc = options.userGestureOption.onClick;
532 spanItem->SetOnClickEvent(std::move(tmpClickFunc));
533 }
534 if (options.userGestureOption.onLongPress) {
535 auto tmpLongPressFunc = options.userGestureOption.onLongPress;
536 spanItem->SetLongPressEvent(std::move(tmpLongPressFunc));
537 }
538 if (updateCaretOPosition) {
539 if (options.offset.has_value() && options.offset.value() <= GetCaretPosition()) {
540 SetCaretPosition(options.offset.value() + 1 + moveLength_);
541 moveLength_ = 0;
542 } else {
543 SetCaretPosition(GetTextContentLength());
544 }
545 }
546 if (!isPaste && textSelector_.IsValid()) {
547 CloseSelectOverlay();
548 ResetSelection();
549 }
550 SpanNodeFission(spanNode);
551 return spanIndex;
552 }
553
AddSymbolSpan(const SymbolSpanOptions & options,bool isPaste,int32_t index)554 int32_t RichEditorPattern::AddSymbolSpan(const SymbolSpanOptions& options, bool isPaste, int32_t index)
555 {
556 OperationRecord record;
557 record.beforeCaretPosition = options.offset.value_or(static_cast<int32_t>(GetTextContentLength()));
558 record.addText = " ";
559 ClearRedoOperationRecords();
560 record.afterCaretPosition = record.beforeCaretPosition + 1;
561 AddOperationRecord(record);
562 return AddSymbolSpanOperation(options, isPaste, index);
563 }
564
AddSymbolSpanOperation(const SymbolSpanOptions & options,bool isPaste,int32_t index)565 int32_t RichEditorPattern::AddSymbolSpanOperation(const SymbolSpanOptions& options, bool isPaste, int32_t index)
566 {
567 auto host = GetHost();
568 CHECK_NULL_RETURN(host, -1);
569
570 auto* stack = ViewStackProcessor::GetInstance();
571 auto nodeId = stack->ClaimNodeId();
572 auto spanNode = SpanNode::GetOrCreateSpanNode(V2::SYMBOL_SPAN_ETS_TAG, nodeId);
573
574 int32_t spanIndex = 0;
575 int32_t offset = -1;
576 if (options.offset.has_value()) {
577 offset = TextSpanSplit(options.offset.value());
578 if (offset == -1) {
579 spanIndex = static_cast<int32_t>(host->GetChildren().size());
580 } else {
581 spanIndex = offset;
582 }
583 spanNode->MountToParent(host, offset);
584 } else if (index != -1) {
585 spanNode->MountToParent(host, index);
586 spanIndex = index;
587 } else {
588 spanIndex = static_cast<int32_t>(host->GetChildren().size());
589 spanNode->MountToParent(host);
590 }
591 spanNode->UpdateContent(options.symbolId);
592 spanNode->AddPropertyInfo(PropertyInfo::NONE);
593 if (options.style.has_value()) {
594 spanNode->UpdateFontSize(options.style.value().GetFontSize());
595 spanNode->AddPropertyInfo(PropertyInfo::FONTSIZE);
596 spanNode->UpdateFontWeight(options.style.value().GetFontWeight());
597 spanNode->AddPropertyInfo(PropertyInfo::FONTWEIGHT);
598 spanNode->UpdateSymbolColorList(options.style.value().GetSymbolColorList());
599 spanNode->AddPropertyInfo(PropertyInfo::SYMBOL_COLOR);
600 spanNode->UpdateSymbolRenderingStrategy(options.style.value().GetRenderStrategy());
601 spanNode->AddPropertyInfo(PropertyInfo::SYMBOL_RENDERING_STRATEGY);
602 spanNode->UpdateSymbolEffectStrategy(options.style.value().GetEffectStrategy());
603 spanNode->AddPropertyInfo(PropertyInfo::SYMBOL_EFFECT_STRATEGY);
604 }
605 auto spanItem = spanNode->GetSpanItem();
606 spanItem->content = " ";
607 spanItem->SetTextStyle(options.style);
608 spanItem->SetResourceObject(options.resourceObject);
609 AddSpanItem(spanItem, offset);
610 if (options.offset.has_value() && options.offset.value() <= GetCaretPosition()) {
611 SetCaretPosition(options.offset.value() + SYMBOL_SPAN_LENGTH + moveLength_);
612 moveLength_ = 0;
613 } else {
614 SetCaretPosition(GetTextContentLength());
615 }
616 SpanNodeFission(spanNode);
617 return spanIndex;
618 }
619
SpanNodeFission(RefPtr<SpanNode> & spanNode)620 void RichEditorPattern::SpanNodeFission(RefPtr<SpanNode>& spanNode)
621 {
622 auto spanItem = spanNode->GetSpanItem();
623 auto content = StringUtils::ToWstring(spanItem->content);
624 auto contentLen = content.length();
625 auto spanStart = spanItem->position - contentLen;
626 for (size_t i = 0; i < content.length(); i++) {
627 auto character = content[i];
628 if (character == '\n') {
629 auto charPosition = spanStart + i;
630 TextSpanSplit(static_cast<int32_t>(charPosition + 1));
631 }
632 }
633 }
634
DeleteSpans(const RangeOptions & options)635 void RichEditorPattern::DeleteSpans(const RangeOptions& options)
636 {
637 int32_t start = 0;
638 int32_t end = 0;
639 auto length = GetTextContentLength();
640 start = (!options.start.has_value()) ? 0 : options.start.value();
641 end = (!options.end.has_value()) ? length : options.end.value();
642 if (start > end) {
643 auto value = start;
644 start = end;
645 end = value;
646 }
647 start = std::max(0, start);
648 end = std::min(length, end);
649 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "index range=[%{public}d, %{public}d]", start, end);
650 if (start > length || end < 0 || start == end) {
651 return;
652 }
653
654 OperationRecord record;
655 record.beforeCaretPosition = start;
656 std::wstringstream wss;
657 for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
658 wss << StringUtils::ToWstring((*iter)->content);
659 }
660 std::wstring deleteText = wss.str().substr(start, end - start);
661 record.deleteText = StringUtils::ToString(deleteText);
662 ClearRedoOperationRecords();
663 record.afterCaretPosition = start;
664 AddOperationRecord(record);
665
666 auto startInfo = GetSpanPositionInfo(start);
667 auto endInfo = GetSpanPositionInfo(end - 1);
668 if (startInfo.spanIndex_ == endInfo.spanIndex_) {
669 DeleteSpanByRange(start, end, startInfo);
670 } else {
671 DeleteSpansByRange(start, end, startInfo, endInfo);
672 }
673 if (textSelector_.IsValid()) {
674 SetCaretPosition(textSelector_.GetTextStart());
675 CloseSelectOverlay();
676 ResetSelection();
677 }
678 SetCaretOffset(start);
679 auto host = GetHost();
680 CHECK_NULL_VOID(host);
681 auto childrens = host->GetChildren();
682 if (childrens.empty() || GetTextContentLength() == 0) {
683 SetCaretPosition(0);
684 }
685 UpdateSpanPosition();
686 }
687
DeleteSpanByRange(int32_t start,int32_t end,SpanPositionInfo info)688 void RichEditorPattern::DeleteSpanByRange(int32_t start, int32_t end, SpanPositionInfo info)
689 {
690 auto host = GetHost();
691 CHECK_NULL_VOID(host);
692 auto childrens = host->GetChildren();
693 auto it = childrens.begin();
694 std::advance(it, info.spanIndex_);
695 if (start == info.spanStart_ && end == info.spanEnd_) {
696 ClearContent(*it);
697 host->RemoveChild(*it);
698 } else {
699 auto spanNode = DynamicCast<SpanNode>(*it);
700 CHECK_NULL_VOID(spanNode);
701 auto spanItem = spanNode->GetSpanItem();
702 auto beforStr = StringUtils::ToWstring(spanItem->content).substr(0, start - info.spanStart_);
703 auto endStr = StringUtils::ToWstring(spanItem->content).substr(end - info.spanStart_);
704 std::wstring result = beforStr + endStr;
705 auto str = StringUtils::ToString(result);
706 spanNode->UpdateContent(str);
707 }
708 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
709 host->MarkModifyDone();
710 }
711
DeleteSpansByRange(int32_t start,int32_t end,SpanPositionInfo startInfo,SpanPositionInfo endInfo)712 void RichEditorPattern::DeleteSpansByRange(
713 int32_t start, int32_t end, SpanPositionInfo startInfo, SpanPositionInfo endInfo)
714 {
715 auto host = GetHost();
716 CHECK_NULL_VOID(host);
717 auto childrens = host->GetChildren();
718 if (childrens.empty()) {
719 return;
720 }
721
722 auto itStart = childrens.begin();
723 if (startInfo.spanIndex_ >= static_cast<int32_t>(childrens.size())) {
724 std::advance(itStart, childrens.size() - 1);
725 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "startInfo.spanIndex_ is larger than childrens size");
726 } else {
727 std::advance(itStart, startInfo.spanIndex_);
728 }
729 auto saveStartSpan = (start == startInfo.spanStart_) ? 0 : 1;
730 if (saveStartSpan) {
731 auto spanNodeStart = DynamicCast<SpanNode>(*itStart);
732 CHECK_NULL_VOID(spanNodeStart);
733 auto spanItemStart = spanNodeStart->GetSpanItem();
734 auto beforStr = StringUtils::ToWstring(spanItemStart->content).substr(0, start - startInfo.spanStart_);
735 auto strStart = StringUtils::ToString(beforStr);
736 spanNodeStart->UpdateContent(strStart);
737 }
738 auto itEnd = childrens.begin();
739 std::advance(itEnd, endInfo.spanIndex_);
740 auto delEndSpan = (end == endInfo.spanEnd_) ? 1 : 0;
741 if (!delEndSpan) {
742 auto spanNodeEnd = DynamicCast<SpanNode>(*itEnd);
743 CHECK_NULL_VOID(spanNodeEnd);
744 auto spanItemEnd = spanNodeEnd->GetSpanItem();
745 auto endStr =
746 StringUtils::ToWstring(spanItemEnd->content).substr(end - endInfo.spanStart_, endInfo.spanEnd_ - end);
747 auto strEnd = StringUtils::ToString(endStr);
748 spanNodeEnd->UpdateContent(strEnd);
749 }
750 auto startIter = childrens.begin();
751 std::advance(startIter, startInfo.spanIndex_ + saveStartSpan);
752 auto endIter = childrens.begin();
753 std::advance(endIter, endInfo.spanIndex_);
754 for (auto iter = startIter; iter != endIter; ++iter) {
755 ClearContent(*iter);
756 host->RemoveChild(*iter);
757 }
758 if (endIter != childrens.end() && delEndSpan) {
759 ClearContent(*endIter);
760 host->RemoveChild(*endIter);
761 }
762 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
763 host->MarkModifyDone();
764 }
765
GetLeftTextOfCursor(int32_t number)766 std::u16string RichEditorPattern::GetLeftTextOfCursor(int32_t number)
767 {
768 if (number > caretPosition_) {
769 number = caretPosition_;
770 }
771 auto start = caretPosition_;
772 if (IsSelected()) {
773 start = std::min(textSelector_.GetStart(), textSelector_.GetEnd());
774 }
775 auto stringText = GetSelectedText(start - number, start);
776 return StringUtils::Str8ToStr16(stringText);
777 }
778
GetRightTextOfCursor(int32_t number)779 std::u16string RichEditorPattern::GetRightTextOfCursor(int32_t number)
780 {
781 auto end = caretPosition_;
782 if (IsSelected()) {
783 end = std::max(textSelector_.GetStart(), textSelector_.GetEnd());
784 }
785 auto stringText = GetSelectedText(end, end + number);
786 return StringUtils::Str8ToStr16(stringText);
787 }
788
GetTextIndexAtCursor()789 int32_t RichEditorPattern::GetTextIndexAtCursor()
790 {
791 return caretPosition_;
792 }
793
ClearContent(const RefPtr<UINode> & child)794 void RichEditorPattern::ClearContent(const RefPtr<UINode>& child)
795 {
796 CHECK_NULL_VOID(child);
797 if (child->GetTag() == V2::SPAN_ETS_TAG) {
798 auto spanNode = DynamicCast<SpanNode>(child);
799 if (spanNode) {
800 spanNode->UpdateContent("");
801 spanNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
802 }
803 }
804 }
805
GetSpanPositionInfo(int32_t position)806 SpanPositionInfo RichEditorPattern::GetSpanPositionInfo(int32_t position)
807 {
808 SpanPositionInfo spanPositionInfo(-1, -1, -1, -1);
809 CHECK_NULL_RETURN(!spans_.empty(), spanPositionInfo);
810 position = std::clamp(position, 0, GetTextContentLength());
811 // find the spanItem where the position is
812 auto it = std::find_if(spans_.begin(), spans_.end(), [position](const RefPtr<SpanItem>& spanItem) {
813 return (spanItem->position - static_cast<int32_t>(StringUtils::ToWstring(spanItem->content).length()) <=
814 position) &&
815 (position < spanItem->position);
816 });
817 if (it != spans_.end() && (*it)->unicode != 0 && (*it)->position - caretPosition_ + moveLength_ == 1) {
818 it++;
819 moveLength_++;
820 position++;
821 }
822
823 // the position is at the end
824 if (it == spans_.end()) {
825 return spanPositionInfo;
826 }
827
828 spanPositionInfo.spanIndex_ = std::distance(spans_.begin(), it);
829 auto contentLen = StringUtils::ToWstring((*it)->content).length();
830 spanPositionInfo.spanStart_ = (*it)->position - contentLen;
831 spanPositionInfo.spanEnd_ = (*it)->position;
832 spanPositionInfo.spanOffset_ = position - spanPositionInfo.spanStart_;
833 return spanPositionInfo;
834 }
835
CopyTextSpanStyle(RefPtr<SpanNode> & source,RefPtr<SpanNode> & target,bool needLeadingMargin)836 void RichEditorPattern::CopyTextSpanStyle(RefPtr<SpanNode>& source, RefPtr<SpanNode>& target, bool needLeadingMargin)
837 {
838 CHECK_NULL_VOID(source);
839 CHECK_NULL_VOID(target);
840
841 CopyTextSpanFontStyle(source, target);
842 CopyTextSpanLineStyle(source, target, needLeadingMargin);
843 }
844
CopyTextSpanFontStyle(RefPtr<SpanNode> & source,RefPtr<SpanNode> & target)845 void RichEditorPattern::CopyTextSpanFontStyle(RefPtr<SpanNode>& source, RefPtr<SpanNode>& target)
846 {
847 if (source->HasFontSize()) {
848 target->UpdateFontSize(source->GetFontSizeValue(Dimension()));
849 target->AddPropertyInfo(PropertyInfo::FONTSIZE);
850 }
851
852 if (source->HasTextColor()) {
853 target->UpdateTextColor(source->GetTextColorValue(Color::BLACK));
854 target->AddPropertyInfo(PropertyInfo::FONTCOLOR);
855 }
856
857 if (source->HasItalicFontStyle()) {
858 target->UpdateItalicFontStyle(source->GetItalicFontStyleValue(OHOS::Ace::FontStyle::NORMAL));
859 target->AddPropertyInfo(PropertyInfo::FONTSTYLE);
860 }
861
862 if (source->HasFontWeight()) {
863 target->UpdateFontWeight(source->GetFontWeightValue(FontWeight::NORMAL));
864 target->AddPropertyInfo(PropertyInfo::FONTWEIGHT);
865 }
866
867 if (source->HasFontFamily()) {
868 target->UpdateFontFamily(source->GetFontFamilyValue({ "HarmonyOS Sans" }));
869 target->AddPropertyInfo(PropertyInfo::FONTFAMILY);
870 }
871
872 if (source->HasTextDecoration()) {
873 target->UpdateTextDecoration(source->GetTextDecorationValue(TextDecoration::NONE));
874 target->AddPropertyInfo(PropertyInfo::TEXTDECORATION);
875 }
876
877 if (source->HasTextDecorationColor()) {
878 target->UpdateTextDecorationColor(source->GetTextDecorationColorValue(Color::BLACK));
879 target->AddPropertyInfo(PropertyInfo::NONE);
880 }
881
882 if (source->HasTextCase()) {
883 target->UpdateTextCase(source->GetTextCaseValue(TextCase::NORMAL));
884 target->AddPropertyInfo(PropertyInfo::TEXTCASE);
885 }
886
887 if (source->HasLetterSpacing()) {
888 target->UpdateLetterSpacing(source->GetLetterSpacingValue(Dimension()));
889 target->AddPropertyInfo(PropertyInfo::LETTERSPACE);
890 }
891 }
892
CopyTextSpanLineStyle(RefPtr<SpanNode> & source,RefPtr<SpanNode> & target,bool needLeadingMargin)893 void RichEditorPattern::CopyTextSpanLineStyle(
894 RefPtr<SpanNode>& source, RefPtr<SpanNode>& target, bool needLeadingMargin)
895 {
896 if (source->HasLineHeight()) {
897 target->UpdateLineHeight(source->GetLineHeightValue(Dimension()));
898 target->AddPropertyInfo(PropertyInfo::LINEHEIGHT);
899 }
900
901 if (source->HasTextShadow()) {
902 target->UpdateTextShadow(source->GetTextShadowValue({Shadow()}));
903 target->AddPropertyInfo(PropertyInfo::TEXTSHADOW);
904 }
905
906 if (needLeadingMargin && source->HasLeadingMargin()) {
907 target->UpdateLeadingMargin(source->GetLeadingMarginValue({}));
908 target->AddPropertyInfo(PropertyInfo::LEADING_MARGIN);
909 }
910
911 if (source->HasTextAlign()) {
912 target->UpdateTextAlign(source->GetTextAlignValue(TextAlign::LEFT));
913 target->AddPropertyInfo(PropertyInfo::TEXT_ALIGN);
914 }
915 }
916
TextSpanSplit(int32_t position,bool needLeadingMargin)917 int32_t RichEditorPattern::TextSpanSplit(int32_t position, bool needLeadingMargin)
918 {
919 if (spans_.empty()) {
920 return -1;
921 }
922
923 auto positionInfo = GetSpanPositionInfo(position);
924 int32_t spanIndex = positionInfo.spanIndex_;
925 int32_t spanStart = positionInfo.spanStart_;
926 int32_t offsetInSpan = positionInfo.spanOffset_;
927 TAG_LOGD(AceLogTag::ACE_RICH_TEXT,
928 "position=%{public}d, spanIndex=%{public}d, spanStart=%{public}d, offsetInSpan=%{public}d",
929 position, spanIndex, spanStart, offsetInSpan);
930
931 if (offsetInSpan <= 0) {
932 return spanIndex;
933 }
934
935 auto host = GetHost();
936 CHECK_NULL_RETURN(host, -1);
937 auto it = host->GetChildren().begin();
938 std::advance(it, spanIndex);
939
940 auto spanNode = DynamicCast<SpanNode>(*it);
941 CHECK_NULL_RETURN(spanNode, -1);
942 auto spanItem = spanNode->GetSpanItem();
943 auto spanItemContent = StringUtils::ToWstring(spanItem->content);
944 if (offsetInSpan > static_cast<int32_t>(spanItemContent.length())) {
945 offsetInSpan = static_cast<int32_t>(spanItemContent.length());
946 }
947 auto newContent = spanItemContent.substr(offsetInSpan);
948 auto deleteContent = spanItemContent.substr(0, offsetInSpan);
949
950 auto* stack = ViewStackProcessor::GetInstance();
951 CHECK_NULL_RETURN(stack, -1);
952 auto nodeId = stack->ClaimNodeId();
953 auto newSpanNode = SpanNode::GetOrCreateSpanNode(nodeId);
954 CHECK_NULL_RETURN(newSpanNode, -1);
955
956 auto newSpanItem = newSpanNode->GetSpanItem();
957 newSpanItem->position = spanStart + offsetInSpan;
958 auto spanIter = spans_.begin();
959 std::advance(spanIter, spanIndex);
960 spans_.insert(spanIter, newSpanItem);
961
962 spanNode->UpdateContent(StringUtils::ToString(newContent));
963 newSpanNode->UpdateContent(StringUtils::ToString(deleteContent));
964
965 CopyTextSpanStyle(spanNode, newSpanNode, needLeadingMargin);
966 newSpanNode->MountToParent(host, spanIndex);
967
968 return spanIndex + 1;
969 }
970
GetCaretPosition()971 int32_t RichEditorPattern::GetCaretPosition()
972 {
973 return caretPosition_;
974 }
975
SetCaretOffset(int32_t caretPosition)976 bool RichEditorPattern::SetCaretOffset(int32_t caretPosition)
977 {
978 bool success = false;
979 success = SetCaretPosition(caretPosition);
980 auto host = GetHost();
981 CHECK_NULL_RETURN(host, false);
982 auto focusHub = host->GetOrCreateFocusHub();
983 CHECK_NULL_RETURN(focusHub, false);
984 if (focusHub->IsCurrentFocus()) {
985 StartTwinkling();
986 }
987 CloseSelectOverlay();
988 ResetSelection();
989 return success;
990 }
991
CalcCursorOffsetByPosition(int32_t position,float & selectLineHeight,bool downStreamFirst,bool needLineHighest)992 OffsetF RichEditorPattern::CalcCursorOffsetByPosition(
993 int32_t position, float& selectLineHeight, bool downStreamFirst, bool needLineHighest)
994 {
995 selectLineHeight = 0.0f;
996 auto host = GetHost();
997 CHECK_NULL_RETURN(host, OffsetF(0, 0));
998 auto pipeline = PipelineContext::GetCurrentContext();
999 CHECK_NULL_RETURN(pipeline, OffsetF(0, 0));
1000 auto rootOffset = pipeline->GetRootRect().GetOffset();
1001 auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
1002 auto startOffset = paragraphs_.ComputeCursorOffset(position, selectLineHeight, downStreamFirst, needLineHighest);
1003 auto children = host->GetChildren();
1004 if (NearZero(selectLineHeight)) {
1005 if (children.empty() || GetTextContentLength() == 0) {
1006 return textPaintOffset - rootOffset;
1007 }
1008 if (std::all_of(children.begin(), children.end(), [](RefPtr<UINode>& node) {
1009 CHECK_NULL_RETURN(node, false);
1010 return (node->GetTag() == V2::IMAGE_ETS_TAG || node->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG);
1011 })) {
1012 bool isTail = false;
1013 auto it = children.begin();
1014 if (position >= static_cast<int32_t>(children.size())) {
1015 std::advance(it, (static_cast<int32_t>(children.size()) - 1));
1016 isTail = true;
1017 } else {
1018 std::advance(it, position);
1019 }
1020 if (it == children.end()) {
1021 return startOffset;
1022 }
1023 auto imageNode = DynamicCast<FrameNode>(*it);
1024 if (imageNode) {
1025 auto geometryNode = imageNode->GetGeometryNode();
1026 CHECK_NULL_RETURN(geometryNode, OffsetF(0.0f, 0.0f));
1027 startOffset = geometryNode->GetMarginFrameOffset();
1028 selectLineHeight = geometryNode->GetMarginFrameSize().Height();
1029 startOffset += isTail ? OffsetF(geometryNode->GetMarginFrameSize().Width(), 0.0f) : OffsetF(0.0f, 0.0f);
1030 }
1031 return startOffset;
1032 }
1033 }
1034 auto caretOffset = startOffset + textPaintOffset + rootOffset;
1035 auto geometryNode = host->GetGeometryNode();
1036 CHECK_NULL_RETURN(geometryNode, caretOffset);
1037 auto frameSize = geometryNode->GetFrameRect().GetSize();
1038 CHECK_NULL_RETURN(overlayMod_, caretOffset);
1039 float caretWidth = DynamicCast<RichEditorOverlayModifier>(overlayMod_)->GetCaretWidth();
1040 caretOffset.SetX(std::clamp(caretOffset.GetX(), 0.0f, static_cast<float>(frameSize.Width()) - caretWidth));
1041 return caretOffset;
1042 }
1043
SetCaretPosition(int32_t pos)1044 bool RichEditorPattern::SetCaretPosition(int32_t pos)
1045 {
1046 auto correctPos = std::clamp(pos, 0, GetTextContentLength());
1047 ResetLastClickOffset();
1048 UpdateCaretInfoToController();
1049 if (pos == correctPos) {
1050 FireOnSelectionChange(correctPos);
1051 caretPosition_ = correctPos;
1052 return true;
1053 }
1054 return false;
1055 }
1056
FireOnSelectionChange(const int32_t caretPosition)1057 void RichEditorPattern::FireOnSelectionChange(const int32_t caretPosition)
1058 {
1059 if (!textSelector_.SelectNothing() || caretPosition == caretPosition_) {
1060 return;
1061 }
1062 FireOnSelectionChange(caretPosition, caretPosition);
1063 }
1064
FireOnSelectionChange(const TextSelector & selector)1065 void RichEditorPattern::FireOnSelectionChange(const TextSelector& selector)
1066 {
1067 if (selector.SelectNothing()) {
1068 return;
1069 }
1070 FireOnSelectionChange(selector.GetStart(), selector.GetEnd());
1071 }
1072
FireOnSelectionChange(int32_t start,int32_t end)1073 void RichEditorPattern::FireOnSelectionChange(int32_t start, int32_t end)
1074 {
1075 auto host = GetHost();
1076 CHECK_NULL_VOID(host);
1077 auto eventHub = host->GetEventHub<RichEditorEventHub>();
1078 CHECK_NULL_VOID(eventHub);
1079 CHECK_NULL_VOID(HasFocus());
1080 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "range=[%{public}d,%{public}d], caretTwinkling_=%{public}d",
1081 start, end, caretTwinkling_);
1082 if (start < 0 || end < 0) {
1083 return;
1084 }
1085 if (start == end && !caretTwinkling_) {
1086 return;
1087 }
1088 if (start > end) {
1089 std::swap(start, end);
1090 }
1091 auto rangeInfo = SelectionRangeInfo(start, end);
1092 eventHub->FireOnSelectionChange(&rangeInfo);
1093 }
1094
GetCaretVisible() const1095 bool RichEditorPattern::GetCaretVisible() const
1096 {
1097 return caretVisible_;
1098 }
1099
SetUpdateSpanStyle(struct UpdateSpanStyle updateSpanStyle)1100 void RichEditorPattern::SetUpdateSpanStyle(struct UpdateSpanStyle updateSpanStyle)
1101 {
1102 updateSpanStyle_ = updateSpanStyle;
1103 }
1104
SetTypingStyle(struct UpdateSpanStyle typingStyle,TextStyle textStyle)1105 void RichEditorPattern::SetTypingStyle(struct UpdateSpanStyle typingStyle, TextStyle textStyle)
1106 {
1107 typingStyle_ = typingStyle;
1108 typingTextStyle_ = textStyle;
1109 }
1110
UpdateTextStyle(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle updateSpanStyle,TextStyle textStyle)1111 void RichEditorPattern::UpdateTextStyle(
1112 RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle updateSpanStyle, TextStyle textStyle)
1113 {
1114 if (spanNode->GetTag() != V2::SPAN_ETS_TAG || updateSpanStyle_.isSymbolStyle) {
1115 return;
1116 }
1117 auto host = GetHost();
1118 CHECK_NULL_VOID(host);
1119 if (updateSpanStyle.updateTextColor.has_value()) {
1120 spanNode->UpdateTextColor(textStyle.GetTextColor());
1121 spanNode->AddPropertyInfo(PropertyInfo::FONTCOLOR);
1122 }
1123 if (updateSpanStyle.updateFontSize.has_value()) {
1124 spanNode->UpdateFontSize(textStyle.GetFontSize());
1125 spanNode->AddPropertyInfo(PropertyInfo::FONTSIZE);
1126 }
1127 if (updateSpanStyle.updateItalicFontStyle.has_value()) {
1128 spanNode->UpdateItalicFontStyle(textStyle.GetFontStyle());
1129 spanNode->AddPropertyInfo(PropertyInfo::FONTSTYLE);
1130 }
1131 if (updateSpanStyle.updateFontWeight.has_value()) {
1132 spanNode->UpdateFontWeight(textStyle.GetFontWeight());
1133 spanNode->AddPropertyInfo(PropertyInfo::FONTWEIGHT);
1134 }
1135 if (updateSpanStyle.updateFontFamily.has_value()) {
1136 spanNode->UpdateFontFamily(textStyle.GetFontFamilies());
1137 spanNode->AddPropertyInfo(PropertyInfo::FONTFAMILY);
1138 }
1139 if (updateSpanStyle.updateTextDecoration.has_value()) {
1140 spanNode->UpdateTextDecoration(textStyle.GetTextDecoration());
1141 spanNode->AddPropertyInfo(PropertyInfo::TEXTDECORATION);
1142 }
1143 if (updateSpanStyle.updateTextDecorationColor.has_value()) {
1144 spanNode->UpdateTextDecorationColor(textStyle.GetTextDecorationColor());
1145 spanNode->AddPropertyInfo(PropertyInfo::NONE);
1146 }
1147 if (updateSpanStyle.updateTextShadows.has_value()) {
1148 spanNode->UpdateTextShadow(textStyle.GetTextShadows());
1149 spanNode->AddPropertyInfo(PropertyInfo::TEXTSHADOW);
1150 }
1151 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1152 host->MarkModifyDone();
1153 }
1154
UpdateSymbolStyle(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle updateSpanStyle,TextStyle textStyle)1155 void RichEditorPattern::UpdateSymbolStyle(
1156 RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle updateSpanStyle, TextStyle textStyle)
1157 {
1158 if (spanNode->GetTag() != V2::SYMBOL_SPAN_ETS_TAG || !updateSpanStyle_.isSymbolStyle) {
1159 return;
1160 }
1161 auto host = GetHost();
1162 CHECK_NULL_VOID(host);
1163 if (updateSpanStyle.updateFontSize.has_value()) {
1164 spanNode->UpdateFontSize(textStyle.GetFontSize());
1165 spanNode->AddPropertyInfo(PropertyInfo::FONTSIZE);
1166 }
1167 if (updateSpanStyle.updateFontWeight.has_value()) {
1168 spanNode->UpdateFontWeight(textStyle.GetFontWeight());
1169 spanNode->AddPropertyInfo(PropertyInfo::FONTWEIGHT);
1170 }
1171 if (updateSpanStyle.updateSymbolColor.has_value()) {
1172 spanNode->UpdateSymbolColorList(textStyle.GetSymbolColorList());
1173 spanNode->AddPropertyInfo(PropertyInfo::SYMBOL_COLOR);
1174 }
1175 if (updateSpanStyle.updateSymbolRenderingStrategy.has_value()) {
1176 spanNode->UpdateSymbolRenderingStrategy(textStyle.GetRenderStrategy());
1177 spanNode->AddPropertyInfo(PropertyInfo::SYMBOL_RENDERING_STRATEGY);
1178 }
1179 if (updateSpanStyle.updateSymbolEffectStrategy.has_value()) {
1180 spanNode->UpdateSymbolEffectStrategy(textStyle.GetEffectStrategy());
1181 spanNode->AddPropertyInfo(PropertyInfo::SYMBOL_EFFECT_STRATEGY);
1182 }
1183 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1184 host->MarkModifyDone();
1185 }
1186
HasSameTypingStyle(const RefPtr<SpanNode> & spanNode)1187 bool RichEditorPattern::HasSameTypingStyle(const RefPtr<SpanNode>& spanNode)
1188 {
1189 auto spanItem = spanNode->GetSpanItem();
1190 CHECK_NULL_RETURN(spanItem, false);
1191 auto spanTextStyle = spanItem->GetTextStyle();
1192 if (spanTextStyle.has_value() && typingTextStyle_.has_value()) {
1193 return spanTextStyle.value() == typingTextStyle_.value();
1194 } else {
1195 return !(spanTextStyle.has_value() || typingTextStyle_.has_value());
1196 }
1197 }
1198
UpdateImageStyle(RefPtr<FrameNode> & imageNode,const ImageSpanAttribute & imageStyle)1199 void RichEditorPattern::UpdateImageStyle(RefPtr<FrameNode>& imageNode, const ImageSpanAttribute& imageStyle)
1200 {
1201 auto host = GetHost();
1202 CHECK_NULL_VOID(host);
1203 auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
1204 if (updateSpanStyle_.updateImageWidth.has_value() || updateSpanStyle_.updateImageHeight.has_value()) {
1205 imageLayoutProperty->UpdateUserDefinedIdealSize(
1206 CalcSize(CalcLength(imageStyle.size.value().width), CalcLength(imageStyle.size.value().height)));
1207 }
1208 if (updateSpanStyle_.updateImageFit.has_value()) {
1209 imageLayoutProperty->UpdateImageFit(imageStyle.objectFit.value());
1210 }
1211 if (updateSpanStyle_.updateImageVerticalAlign.has_value()) {
1212 imageLayoutProperty->UpdateVerticalAlign(imageStyle.verticalAlign.value());
1213 }
1214 if (updateSpanStyle_.borderRadius.has_value()) {
1215 auto imageRenderCtx = imageNode->GetRenderContext();
1216 imageRenderCtx->UpdateBorderRadius(imageStyle.borderRadius.value());
1217 imageRenderCtx->SetClipToBounds(true);
1218 }
1219 if (updateSpanStyle_.marginProp.has_value()) {
1220 imageLayoutProperty->UpdateMargin(imageStyle.marginProp.value());
1221 }
1222
1223 imageNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1224 imageNode->MarkModifyDone();
1225 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1226 host->MarkModifyDone();
1227 }
1228
SymbolSpanUpdateStyle(RefPtr<SpanNode> & spanNode,struct UpdateSpanStyle updateSpanStyle,TextStyle textStyle)1229 bool RichEditorPattern::SymbolSpanUpdateStyle(
1230 RefPtr<SpanNode>& spanNode, struct UpdateSpanStyle updateSpanStyle, TextStyle textStyle)
1231 {
1232 if (spanNode->GetTag() == V2::SYMBOL_SPAN_ETS_TAG) {
1233 UpdateSymbolStyle(spanNode, updateSpanStyle_, textStyle);
1234 return true;
1235 }
1236 return false;
1237 }
1238
UpdateSpanStyle(int32_t start,int32_t end,const TextStyle & textStyle,const ImageSpanAttribute & imageStyle)1239 void RichEditorPattern::UpdateSpanStyle(
1240 int32_t start, int32_t end, const TextStyle& textStyle, const ImageSpanAttribute& imageStyle)
1241 {
1242 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "range=[%{public}d, %{public}d]", start, end);
1243 auto host = GetHost();
1244 CHECK_NULL_VOID(host);
1245 int32_t spanStart = 0;
1246 int32_t spanEnd = 0;
1247 for (auto it = host->GetChildren().begin(); it != host->GetChildren().end(); ++it) {
1248 auto spanNode = DynamicCast<SpanNode>(*it);
1249 auto imageNode = DynamicCast<FrameNode>(*it);
1250 if (!spanNode) {
1251 if (spanEnd != 0) {
1252 spanStart = spanEnd;
1253 }
1254 spanEnd = spanStart + 1;
1255 } else {
1256 spanNode->GetSpanItem()->GetIndex(spanStart, spanEnd);
1257 }
1258 if (spanEnd < start) {
1259 continue;
1260 }
1261
1262 if (spanStart >= start && spanEnd <= end) {
1263 if (spanNode) {
1264 UpdateSymbolStyle(spanNode, updateSpanStyle_, textStyle);
1265 UpdateTextStyle(spanNode, updateSpanStyle_, textStyle);
1266 } else {
1267 UpdateImageStyle(imageNode, imageStyle);
1268 }
1269 if (spanEnd == end) {
1270 break;
1271 }
1272 } else if (spanStart < start && start < spanEnd) {
1273 if (SymbolSpanUpdateStyle(spanNode, updateSpanStyle_, textStyle)) {
1274 continue;
1275 }
1276 TextSpanSplit(start, true);
1277 --it;
1278 } else if (spanStart < end && end < spanEnd) {
1279 if (SymbolSpanUpdateStyle(spanNode, updateSpanStyle_, textStyle)) {
1280 continue;
1281 }
1282 TextSpanSplit(end, true);
1283 --(--it);
1284 } else if (spanStart >= end) {
1285 break;
1286 }
1287 }
1288
1289 // Custom menus do not actively close.
1290 if (!SelectOverlayIsOn() || selectOverlayProxy_->GetSelectOverlayMangerInfo().menuInfo.menuBuilder == nullptr) {
1291 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "CloseSelectOverlay");
1292 CloseSelectOverlay();
1293 }
1294 }
1295
GetParagraphInfo(int32_t start,int32_t end)1296 std::vector<ParagraphInfo> RichEditorPattern::GetParagraphInfo(int32_t start, int32_t end)
1297 {
1298 std::vector<ParagraphInfo> res;
1299 auto spanNodes = GetParagraphNodes(start, end);
1300 CHECK_NULL_RETURN(!spanNodes.empty(), {});
1301
1302 auto&& firstSpan = spanNodes.front()->GetSpanItem();
1303 auto paraStart = firstSpan->position - StringUtils::ToWstring(firstSpan->content).length();
1304
1305 for (auto it = spanNodes.begin(); it != spanNodes.end(); ++it) {
1306 if (it == std::prev(spanNodes.end()) || StringUtils::ToWstring((*it)->GetSpanItem()->content).back() == L'\n') {
1307 ParagraphInfo info;
1308 auto lm = (*it)->GetLeadingMarginValue({});
1309
1310 res.emplace_back(ParagraphInfo {
1311 .leadingMarginPixmap = lm.pixmap,
1312 .leadingMarginSize = { Dimension(lm.size.Width()).ConvertToVp(),
1313 Dimension(lm.size.Height()).ConvertToVp() },
1314 .textAlign = static_cast<int32_t>((*it)->GetTextAlignValue(TextAlign::START)),
1315 .range = { paraStart, (*it)->GetSpanItem()->position },
1316 });
1317 paraStart = (*it)->GetSpanItem()->position;
1318 }
1319 }
1320
1321 return res;
1322 }
1323
GetParagraphLength(const std::list<RefPtr<UINode>> & spans) const1324 int32_t RichEditorPattern::GetParagraphLength(const std::list<RefPtr<UINode>>& spans) const
1325 {
1326 if (spans.empty()) {
1327 return 0;
1328 }
1329 int32_t imageSpanCnt = 0;
1330 for (auto it = spans.rbegin(); it != spans.rend(); ++it) {
1331 auto spanNode = DynamicCast<SpanNode>(*it);
1332 if (spanNode) {
1333 return spanNode->GetSpanItem()->position + imageSpanCnt;
1334 }
1335 ++imageSpanCnt;
1336 }
1337 return imageSpanCnt;
1338 }
1339
GetParagraphNodes(int32_t start,int32_t end) const1340 std::vector<RefPtr<SpanNode>> RichEditorPattern::GetParagraphNodes(int32_t start, int32_t end) const
1341 {
1342 CHECK_NULL_RETURN(start != end, {});
1343 auto host = GetHost();
1344 CHECK_NULL_RETURN(host, {});
1345 CHECK_NULL_RETURN(!host->GetChildren().empty(), {});
1346
1347 const auto& spans = host->GetChildren();
1348 int32_t length = GetParagraphLength(spans);
1349 std::vector<RefPtr<SpanNode>> res;
1350
1351 if (start >= length) {
1352 return res;
1353 }
1354
1355 auto headIt = spans.begin();
1356 auto flagNode = headIt;
1357 bool isEnd = false;
1358 int32_t spanEnd = -1;
1359 while (flagNode != spans.end()) {
1360 auto spanNode = DynamicCast<SpanNode>(*flagNode);
1361 if (spanNode) {
1362 auto&& info = spanNode->GetSpanItem();
1363 spanEnd = info->position;
1364 isEnd = StringUtils::ToWstring(info->content).back() == '\n';
1365 } else {
1366 ++spanEnd;
1367 isEnd = false;
1368 }
1369 flagNode++;
1370 if (spanEnd > start) {
1371 break;
1372 }
1373 if (isEnd) {
1374 headIt = flagNode;
1375 }
1376 }
1377 while (headIt != flagNode) {
1378 auto spanNode = DynamicCast<SpanNode>(*headIt);
1379 if (spanNode) {
1380 res.emplace_back(spanNode);
1381 }
1382 headIt++;
1383 }
1384 while (flagNode != spans.end() && (spanEnd < end || !isEnd)) {
1385 auto spanNode = DynamicCast<SpanNode>(*flagNode);
1386 if (spanNode) {
1387 res.emplace_back(spanNode);
1388 auto&& info = spanNode->GetSpanItem();
1389 spanEnd = info->position;
1390 isEnd = StringUtils::ToWstring(info->content).back() == '\n';
1391 } else {
1392 ++spanEnd;
1393 isEnd = false;
1394 }
1395 flagNode++;
1396 }
1397
1398 return res;
1399 }
1400
UpdateParagraphStyle(int32_t start,int32_t end,const struct UpdateParagraphStyle & style)1401 void RichEditorPattern::UpdateParagraphStyle(int32_t start, int32_t end, const struct UpdateParagraphStyle& style)
1402 {
1403 auto spanNodes = GetParagraphNodes(start, end);
1404 for (const auto& spanNode : spanNodes) {
1405 if (style.textAlign.has_value()) {
1406 spanNode->UpdateTextAlign(*style.textAlign);
1407 } else {
1408 spanNode->UpdateTextAlign(TextAlign::START);
1409 }
1410 if (style.leadingMargin.has_value()) {
1411 spanNode->GetSpanItem()->leadingMargin = *style.leadingMargin;
1412 spanNode->UpdateLeadingMargin(*style.leadingMargin);
1413 }
1414 }
1415 }
1416
ScheduleCaretTwinkling()1417 void RichEditorPattern::ScheduleCaretTwinkling()
1418 {
1419 auto context = PipelineContext::GetCurrentContext();
1420 CHECK_NULL_VOID(context);
1421
1422 if (!context->GetTaskExecutor()) {
1423 return;
1424 }
1425
1426 if (isCursorAlwaysDisplayed_) {
1427 return;
1428 }
1429
1430 auto weak = WeakClaim(this);
1431 caretTwinklingTask_.Reset([weak] {
1432 auto client = weak.Upgrade();
1433 CHECK_NULL_VOID(client);
1434 client->OnCaretTwinkling();
1435 });
1436 auto taskExecutor = context->GetTaskExecutor();
1437 CHECK_NULL_VOID(taskExecutor);
1438 taskExecutor->PostDelayedTask(caretTwinklingTask_, TaskExecutor::TaskType::UI, RICH_EDITOR_TWINKLING_INTERVAL_MS);
1439 }
1440
StartTwinkling()1441 void RichEditorPattern::StartTwinkling()
1442 {
1443 caretTwinklingTask_.Cancel();
1444 // Fire on selecion change when caret invisible -> visible
1445 if (!caretTwinkling_) {
1446 caretTwinkling_ = true;
1447 FireOnSelectionChange(caretPosition_, caretPosition_);
1448 }
1449 caretVisible_ = true;
1450 auto tmpHost = GetHost();
1451 CHECK_NULL_VOID(tmpHost);
1452 tmpHost->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1453 ScheduleCaretTwinkling();
1454 }
1455
OnCaretTwinkling()1456 void RichEditorPattern::OnCaretTwinkling()
1457 {
1458 caretTwinklingTask_.Cancel();
1459 caretVisible_ = !caretVisible_;
1460 GetHost()->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1461 ScheduleCaretTwinkling();
1462 }
1463
StopTwinkling()1464 void RichEditorPattern::StopTwinkling()
1465 {
1466 caretTwinkling_ = false;
1467 caretTwinklingTask_.Cancel();
1468 if (caretVisible_) {
1469 caretVisible_ = false;
1470 GetHost()->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1471 }
1472 }
1473
HandleClickEvent(GestureEvent & info)1474 void RichEditorPattern::HandleClickEvent(GestureEvent& info)
1475 {
1476 if (dataDetectorAdapter_->hasClickedAISpan_) {
1477 dataDetectorAdapter_->hasClickedAISpan_ = false;
1478 } else if (hasClicked_) {
1479 hasClicked_ = false;
1480 TimeStamp clickTimeStamp = info.GetTimeStamp();
1481 std::chrono::duration<float, std::ratio<1, InputAIChecker::SECONDS_TO_MILLISECONDS>> timeout =
1482 clickTimeStamp - lastClickTimeStamp_;
1483 lastClickTimeStamp_ = info.GetTimeStamp();
1484 if (timeout.count() < DOUBLE_CLICK_INTERVAL_MS) {
1485 HandleDoubleClickEvent(info);
1486 return;
1487 }
1488 }
1489 HandleSingleClickEvent(info);
1490 }
1491
HandleSingleClickEvent(OHOS::Ace::GestureEvent & info)1492 void RichEditorPattern::HandleSingleClickEvent(OHOS::Ace::GestureEvent& info)
1493 {
1494 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "in handleSingleClickEvent");
1495 hasClicked_ = true;
1496 lastClickTimeStamp_ = info.GetTimeStamp();
1497 if (info.GetSourceDevice() != SourceType::MOUSE && SelectOverlayIsOn() &&
1498 BetweenSelectedPosition(info.GetGlobalLocation())) {
1499 selectOverlayProxy_->ShowOrHiddenMenu(false);
1500 return;
1501 }
1502
1503 auto textRect = GetTextRect();
1504 textRect.SetTop(textRect.GetY() - std::min(baselineOffset_, 0.0f));
1505 textRect.SetHeight(textRect.Height() - std::max(baselineOffset_, 0.0f));
1506 Offset textOffset = { info.GetLocalLocation().GetX() - textRect.GetX(),
1507 info.GetLocalLocation().GetY() - textRect.GetY() };
1508 HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY()));
1509 if (dataDetectorAdapter_->hasClickedAISpan_) {
1510 if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
1511 selectOverlayProxy_->DisableMenu(true);
1512 }
1513 return;
1514 }
1515
1516 HandleUserClickEvent(info);
1517 if (textSelector_.IsValid() && !isMouseSelect_) {
1518 CloseSelectOverlay();
1519 ResetSelection();
1520 }
1521
1522 caretUpdateType_ = CaretUpdateType::PRESSED;
1523 UseHostToUpdateTextFieldManager();
1524
1525 auto position = paragraphs_.GetIndex(textOffset);
1526 AdjustCursorPosition(position);
1527
1528 auto focusHub = GetHost()->GetOrCreateFocusHub();
1529 if (focusHub) {
1530 SetCaretPosition(position);
1531 if (!dataDetectorAdapter_->hasClickedAISpan_ && focusHub->RequestFocusImmediately()) {
1532 float caretHeight = 0.0f;
1533 OffsetF caretOffset = CalcCursorOffsetByPosition(GetCaretPosition(), caretHeight, false, false);
1534 CHECK_NULL_VOID(overlayMod_);
1535 DynamicCast<RichEditorOverlayModifier>(overlayMod_)->SetCaretOffsetAndHeight(caretOffset, caretHeight);
1536 StartTwinkling();
1537 if (overlayMod_) {
1538 RequestKeyboard(false, true, true);
1539 }
1540 }
1541 }
1542 UseHostToUpdateTextFieldManager();
1543 CalcCaretInfoByClick(info);
1544 }
1545
HandleDoubleClickEvent(OHOS::Ace::GestureEvent & info)1546 void RichEditorPattern::HandleDoubleClickEvent(OHOS::Ace::GestureEvent& info)
1547 {
1548 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "in double HandleDoubleClickEvent,use mouse:%{public}d", info.GetSourceDevice());
1549 caretUpdateType_ = CaretUpdateType::DOUBLE_CLICK;
1550 HandleDoubleClickOrLongPress(info);
1551 caretUpdateType_ = CaretUpdateType::NONE;
1552 }
1553
HandleUserGestureEvent(GestureEvent & info,std::function<bool (RefPtr<SpanItem> item,GestureEvent & info)> && gestureFunc)1554 bool RichEditorPattern::HandleUserGestureEvent(
1555 GestureEvent& info, std::function<bool(RefPtr<SpanItem> item, GestureEvent& info)>&& gestureFunc)
1556 {
1557 RectF textContentRect = contentRect_;
1558 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
1559 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
1560 if (!textContentRect.IsInRegion(PointF(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY())) ||
1561 spans_.empty()) {
1562 return false;
1563 }
1564 PointF textOffset = { info.GetLocalLocation().GetX() - GetTextRect().GetX(),
1565 info.GetLocalLocation().GetY() - GetTextRect().GetY() };
1566 int32_t start = 0;
1567 bool isParagraphHead = true;
1568 Offset paragraphOffset(0, 0);
1569 for (const auto& item : spans_) {
1570 if (!item) {
1571 continue;
1572 }
1573 std::vector<RectF> selectedRects = paragraphs_.GetRects(start, item->position);
1574 start = item->position;
1575 if (isParagraphHead && !selectedRects.empty()) {
1576 if (item->leadingMargin.has_value()) {
1577 auto addWidth = item->leadingMargin.value().size.Width();
1578 selectedRects[0].SetLeft(selectedRects[0].GetX() - addWidth);
1579 selectedRects[0].SetWidth(selectedRects[0].GetSize().Width() + addWidth);
1580 }
1581 paragraphOffset.SetX(selectedRects[0].GetOffset().GetX());
1582 paragraphOffset.SetY(selectedRects[0].GetOffset().GetY());
1583 isParagraphHead = false;
1584 }
1585 if (!isParagraphHead && item->content.back() == '\n') {
1586 isParagraphHead = true;
1587 }
1588 for (auto&& rect : selectedRects) {
1589 if (!rect.IsInRegion(textOffset)) {
1590 continue;
1591 }
1592 info = info.SetScreenLocation(
1593 Offset(textOffset.GetX() - paragraphOffset.GetX(), textOffset.GetY() - paragraphOffset.GetY()));
1594 return gestureFunc(item, info);
1595 }
1596 }
1597 return false;
1598 }
1599
ClickAISpan(const PointF & textOffset,const AISpan & aiSpan)1600 bool RichEditorPattern::ClickAISpan(const PointF& textOffset, const AISpan& aiSpan)
1601 {
1602 auto calculateHandleFunc = [weak = WeakClaim(this)]() {
1603 auto pattern = weak.Upgrade();
1604 CHECK_NULL_VOID(pattern);
1605 pattern->CalculateHandleOffsetAndShowOverlay();
1606 };
1607 auto showSelectOverlayFunc = [weak = WeakClaim(this)](const RectF& firstHandle, const RectF& secondHandle) {
1608 auto pattern = weak.Upgrade();
1609 CHECK_NULL_VOID(pattern);
1610 pattern->ShowSelectOverlay(firstHandle, secondHandle);
1611 };
1612
1613 std::vector<RectF> aiRects = paragraphs_.GetRects(aiSpan.start, aiSpan.end);
1614 for (auto&& rect : aiRects) {
1615 if (rect.IsInRegion(textOffset)) {
1616 dataDetectorAdapter_->hasClickedAISpan_ = true;
1617 ShowUIExtensionMenu(aiSpan, calculateHandleFunc, showSelectOverlayFunc);
1618 return true;
1619 }
1620 }
1621 return false;
1622 }
1623
HandleUserClickEvent(GestureEvent & info)1624 bool RichEditorPattern::HandleUserClickEvent(GestureEvent& info)
1625 {
1626 auto clickFunc = [](RefPtr<SpanItem> item, GestureEvent& info) -> bool {
1627 if (item && item->onClick) {
1628 item->onClick(info);
1629 return true;
1630 }
1631 return false;
1632 };
1633 return HandleUserGestureEvent(info, std::move(clickFunc));
1634 }
1635
CalcCaretInfoByClick(GestureEvent & info)1636 void RichEditorPattern::CalcCaretInfoByClick(GestureEvent& info)
1637 {
1638 auto textRect = GetTextRect();
1639 auto touchOffset = info.GetLocalLocation();
1640 textRect.SetTop(textRect.GetY() - std::min(baselineOffset_, 0.0f));
1641 textRect.SetHeight(textRect.Height() - std::max(baselineOffset_, 0.0f));
1642 Offset textOffset = { touchOffset.GetX() - textRect.GetX(), touchOffset.GetY() - textRect.GetY() };
1643 // get the caret position
1644 auto position = paragraphs_.GetIndex(textOffset);
1645 // get the caret offset when click
1646 float selectLineHeight = 0.0f;
1647 auto lastClickOffset = paragraphs_.ComputeCursorInfoByClick(position, selectLineHeight,
1648 OffsetF(static_cast<float>(textOffset.GetX()), static_cast<float>(textOffset.GetY())));
1649
1650 lastClickOffset.AddX(textRect.GetX());
1651 lastClickOffset.AddY(textRect.GetY());
1652
1653 CHECK_NULL_VOID(overlayMod_);
1654 DynamicCast<RichEditorOverlayModifier>(overlayMod_)->SetCaretOffsetAndHeight(lastClickOffset, selectLineHeight);
1655 SetLastClickOffset(lastClickOffset);
1656
1657 MoveCaretToContentRect(lastClickOffset, selectLineHeight);
1658 }
1659
InitClickEvent(const RefPtr<GestureEventHub> & gestureHub)1660 void RichEditorPattern::InitClickEvent(const RefPtr<GestureEventHub>& gestureHub)
1661 {
1662 CHECK_NULL_VOID(!clickEventInitialized_);
1663 auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
1664 auto pattern = weak.Upgrade();
1665 CHECK_NULL_VOID(pattern);
1666 pattern->HandleClickEvent(info);
1667 };
1668 auto clickListener = MakeRefPtr<ClickEvent>(std::move(clickCallback));
1669 gestureHub->AddClickEvent(clickListener);
1670 clickEventInitialized_ = true;
1671 }
1672
InitFocusEvent(const RefPtr<FocusHub> & focusHub)1673 void RichEditorPattern::InitFocusEvent(const RefPtr<FocusHub>& focusHub)
1674 {
1675 CHECK_NULL_VOID(!focusEventInitialized_);
1676 auto focusTask = [weak = WeakClaim(this)]() {
1677 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "rich editor in focus");
1678 auto pattern = weak.Upgrade();
1679 CHECK_NULL_VOID(pattern);
1680 pattern->HandleFocusEvent();
1681 };
1682 focusHub->SetOnFocusInternal(focusTask);
1683 auto blurTask = [weak = WeakClaim(this)]() {
1684 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "rich editor in blur");
1685 auto pattern = weak.Upgrade();
1686 CHECK_NULL_VOID(pattern);
1687 pattern->HandleBlurEvent();
1688 };
1689 focusHub->SetOnBlurInternal(blurTask);
1690 focusEventInitialized_ = true;
1691 auto keyTask = [weak = WeakClaim(this)](const KeyEvent& keyEvent) -> bool {
1692 auto pattern = weak.Upgrade();
1693 CHECK_NULL_RETURN(pattern, false);
1694 return pattern->OnKeyEvent(keyEvent);
1695 };
1696 focusHub->SetOnKeyEventInternal(std::move(keyTask));
1697 }
1698
CheckBlurReason()1699 bool RichEditorPattern::CheckBlurReason()
1700 {
1701 auto host = GetHost();
1702 CHECK_NULL_RETURN(host, false);
1703 auto curFocusHub = host->GetFocusHub();
1704 CHECK_NULL_RETURN(curFocusHub, false);
1705 auto curBlurReason = curFocusHub->GetBlurReason();
1706 if (curBlurReason == BlurReason::FRAME_DESTROY) {
1707 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "TextFieldPattern CheckBlurReason, Close Keyboard.");
1708 return true;
1709 }
1710 return false;
1711 }
1712
HandleBlurEvent()1713 void RichEditorPattern::HandleBlurEvent()
1714 {
1715 if (textDetectEnable_) {
1716 isLongPress_ = false;
1717 if (CanStartAITask()) {
1718 dataDetectorAdapter_->StartAITask();
1719 }
1720 }
1721 StopTwinkling();
1722 // The pattern handles blurevent, Need to close the softkeyboard first.
1723 if ((customKeyboardBuilder_ && isCustomKeyboardAttached_) || CheckBlurReason()) {
1724 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "RichEditor Blur, Close Keyboard.");
1725 CloseKeyboard(true);
1726 }
1727
1728 if (textSelector_.IsValid()) {
1729 CloseSelectOverlay();
1730 ResetSelection();
1731 }
1732 }
1733
HandleFocusEvent()1734 void RichEditorPattern::HandleFocusEvent()
1735 {
1736 auto host = GetHost();
1737 if (host && textDetectEnable_ && !dataDetectorAdapter_->aiSpanMap_.empty()) {
1738 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1739 }
1740 dataDetectorAdapter_->CancelAITask();
1741 UseHostToUpdateTextFieldManager();
1742 StartTwinkling();
1743 if (!usingMouseRightButton_ && !isLongPress_ && !isDragging_) {
1744 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Handle Focus Event, Request keyboard.");
1745 RequestKeyboard(false, true, true);
1746 }
1747 }
1748
UseHostToUpdateTextFieldManager()1749 void RichEditorPattern::UseHostToUpdateTextFieldManager()
1750 {
1751 auto host = GetHost();
1752 CHECK_NULL_VOID(host);
1753 auto context = PipelineContext::GetCurrentContext();
1754 CHECK_NULL_VOID(context);
1755 auto globalOffset = host->GetPaintRectOffset() - context->GetRootRect().GetOffset();
1756 UpdateTextFieldManager(Offset(globalOffset.GetX(), globalOffset.GetY()), frameRect_.Height());
1757 }
1758
OnVisibleChange(bool isVisible)1759 void RichEditorPattern::OnVisibleChange(bool isVisible)
1760 {
1761 TextPattern::OnVisibleChange(isVisible);
1762 StopTwinkling();
1763 CloseKeyboard(true);
1764 }
1765
CloseKeyboard(bool forceClose)1766 bool RichEditorPattern::CloseKeyboard(bool forceClose)
1767 {
1768 CloseSelectOverlay();
1769 ResetSelection();
1770 if (forceClose) {
1771 if (customKeyboardBuilder_ && isCustomKeyboardAttached_) {
1772 return CloseCustomKeyboard();
1773 }
1774 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Request close soft keyboard.");
1775 #if defined(ENABLE_STANDARD_INPUT)
1776 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
1777 if (!imeAttached_) {
1778 return false;
1779 }
1780 #endif
1781 auto inputMethod = MiscServices::InputMethodController::GetInstance();
1782 CHECK_NULL_RETURN(inputMethod, false);
1783 inputMethod->HideTextInput();
1784 inputMethod->Close();
1785 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
1786 imeAttached_ = false;
1787 #endif
1788 #else
1789 if (HasConnection()) {
1790 connection_->Close(GetInstanceId());
1791 connection_ = nullptr;
1792 }
1793 #endif
1794 return true;
1795 }
1796 return false;
1797 }
1798
JudgeDraggable(GestureEvent & info)1799 bool RichEditorPattern::JudgeDraggable(GestureEvent& info)
1800 {
1801 auto host = GetHost();
1802 CHECK_NULL_RETURN(host, false);
1803 if (copyOption_ == CopyOptions::None) {
1804 return false;
1805 }
1806 auto hub = host->GetEventHub<EventHub>();
1807 CHECK_NULL_RETURN(hub, false);
1808 auto gestureHub = hub->GetOrCreateGestureEventHub();
1809 if (BetweenSelectedPosition(info.GetGlobalLocation())) {
1810 dragBoxes_ = GetTextBoxes();
1811 // prevent long press event from being triggered when dragging
1812 auto selectStart = textSelector_.GetTextStart();
1813 auto selectEnd = textSelector_.GetTextEnd();
1814 auto textSelectInfo = GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT);
1815 auto resultObjects = textSelectInfo.GetSelection().resultObjects;
1816 auto iter =
1817 std::find_if(resultObjects.begin(), resultObjects.end(), [](ResultObject& obj) { return obj.isDraggable; });
1818 gestureHub->SetIsTextDraggable(iter != resultObjects.end());
1819 return true;
1820 }
1821 gestureHub->SetIsTextDraggable(false);
1822 return false;
1823 }
1824
HandleLongPress(GestureEvent & info)1825 void RichEditorPattern::HandleLongPress(GestureEvent& info)
1826 {
1827 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "handle long press!");
1828 caretUpdateType_ = CaretUpdateType::LONG_PRESSED;
1829 HandleDoubleClickOrLongPress(info);
1830 caretUpdateType_ = CaretUpdateType::NONE;
1831 }
1832
HandleDoubleClickOrLongPress(GestureEvent & info)1833 void RichEditorPattern::HandleDoubleClickOrLongPress(GestureEvent& info)
1834 {
1835 if (caretUpdateType_ == CaretUpdateType::LONG_PRESSED) {
1836 HandleUserLongPressEvent(info);
1837 }
1838 if (JudgeDraggable(info) || isMousePressed_) {
1839 return;
1840 }
1841 auto host = GetHost();
1842 CHECK_NULL_VOID(host);
1843 auto focusHub = host->GetOrCreateFocusHub();
1844 CHECK_NULL_VOID(focusHub);
1845 isLongPress_ = true;
1846 auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
1847 Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
1848 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
1849 InitSelection(textOffset);
1850 auto selectEnd = std::max(textSelector_.baseOffset, textSelector_.destinationOffset);
1851 auto selectStart = std::min(textSelector_.baseOffset, textSelector_.destinationOffset);
1852
1853 auto textSelectInfo = GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT);
1854 UpdateSelectionType(textSelectInfo);
1855 CalculateHandleOffsetAndShowOverlay();
1856 if (IsShowSelectMenuUsingMouse()) {
1857 CloseSelectOverlay();
1858 }
1859 selectionMenuOffset_ = info.GetGlobalLocation();
1860 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1861 auto eventHub = host->GetEventHub<RichEditorEventHub>();
1862 CHECK_NULL_VOID(eventHub);
1863 if (!textSelectInfo.GetSelection().resultObjects.empty()) {
1864 eventHub->FireOnSelect(&textSelectInfo);
1865 }
1866 SetCaretPosition(std::min(selectEnd, GetTextContentLength()));
1867 focusHub->RequestFocusImmediately();
1868 if (overlayMod_) {
1869 RequestKeyboard(false, true, true);
1870 }
1871 if (info.GetSourceDevice() != SourceType::MOUSE || caretUpdateType_ != CaretUpdateType::DOUBLE_CLICK) {
1872 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle);
1873 StopTwinkling();
1874 } else if (selectStart == selectEnd) {
1875 StartTwinkling();
1876 } else {
1877 StopTwinkling();
1878 }
1879 }
1880
HandleUserLongPressEvent(GestureEvent & info)1881 bool RichEditorPattern::HandleUserLongPressEvent(GestureEvent& info)
1882 {
1883 auto longPressFunc = [](RefPtr<SpanItem> item, GestureEvent& info) -> bool {
1884 if (item && item->onLongPress) {
1885 item->onLongPress(info);
1886 return true;
1887 }
1888 return false;
1889 };
1890 return HandleUserGestureEvent(info, std::move(longPressFunc));
1891 }
1892
HandleMenuCallbackOnSelectAll()1893 void RichEditorPattern::HandleMenuCallbackOnSelectAll()
1894 {
1895 auto textSize = static_cast<int32_t>(GetWideText().length()) + placeholderCount_;
1896 textSelector_.Update(0, textSize);
1897 CalculateHandleOffsetAndShowOverlay();
1898 if (IsShowSelectMenuUsingMouse()) {
1899 CloseSelectOverlay();
1900 }
1901 auto responseType = selectOverlayProxy_
1902 ? static_cast<TextResponseType>(
1903 selectOverlayProxy_->GetSelectOverlayMangerInfo().menuInfo.responseType.value_or(0))
1904 : TextResponseType::LONG_PRESS;
1905 selectMenuInfo_.showCopyAll = false;
1906 selectOverlayProxy_->UpdateSelectMenuInfo(selectMenuInfo_);
1907 auto host = GetHost();
1908 CHECK_NULL_VOID(host);
1909 FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
1910 selectOverlayProxy_.Reset();
1911 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true, responseType);
1912 SetCaretPosition(textSize);
1913 MoveCaretToContentRect();
1914 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1915 }
1916
InitLongPressEvent(const RefPtr<GestureEventHub> & gestureHub)1917 void RichEditorPattern::InitLongPressEvent(const RefPtr<GestureEventHub>& gestureHub)
1918 {
1919 CHECK_NULL_VOID(!longPressEvent_);
1920 auto longPressCallback = [weak = WeakClaim(this)](GestureEvent& info) {
1921 auto pattern = weak.Upgrade();
1922 CHECK_NULL_VOID(pattern);
1923 pattern->HandleLongPress(info);
1924 };
1925 longPressEvent_ = MakeRefPtr<LongPressEvent>(std::move(longPressCallback));
1926 gestureHub->SetLongPressEvent(longPressEvent_);
1927
1928 auto onTextSelectorChange = [weak = WeakClaim(this), &selector = textSelector_]() {
1929 auto pattern = weak.Upgrade();
1930 CHECK_NULL_VOID(pattern);
1931 auto frameNode = pattern->GetHost();
1932 CHECK_NULL_VOID(frameNode);
1933 frameNode->OnAccessibilityEvent(AccessibilityEventType::TEXT_SELECTION_UPDATE);
1934 pattern->FireOnSelectionChange(selector);
1935 };
1936 textSelector_.SetOnAccessibility(std::move(onTextSelectorChange));
1937 }
1938
InitDragDropEvent()1939 void RichEditorPattern::InitDragDropEvent()
1940 {
1941 auto host = GetHost();
1942 CHECK_NULL_VOID(host);
1943 auto gestureHub = host->GetOrCreateGestureEventHub();
1944 CHECK_NULL_VOID(gestureHub);
1945 gestureHub->InitDragDropEvent();
1946 gestureHub->SetThumbnailCallback(GetThumbnailCallback());
1947 auto eventHub = host->GetEventHub<EventHub>();
1948 CHECK_NULL_VOID(eventHub);
1949 auto onDragStart = [weakPtr = WeakClaim(this)](const RefPtr<OHOS::Ace::DragEvent>& event,
1950 const std::string& extraParams) -> NG::DragDropInfo {
1951 NG::DragDropInfo itemInfo;
1952 auto pattern = weakPtr.Upgrade();
1953 CHECK_NULL_RETURN(pattern, itemInfo);
1954 pattern->timestamp_ = std::chrono::system_clock::now().time_since_epoch().count();
1955 auto eventHub = pattern->GetEventHub<RichEditorEventHub>();
1956 eventHub->SetTimestamp(pattern->GetTimestamp());
1957 CHECK_NULL_RETURN(eventHub, itemInfo);
1958 pattern->showSelect_ = false;
1959 return pattern->OnDragStart(event, extraParams);
1960 };
1961 eventHub->SetOnDragStart(std::move(onDragStart));
1962 auto onDragMove = [weakPtr = WeakClaim(this)](
1963 const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& extraParams) {
1964 auto pattern = weakPtr.Upgrade();
1965 CHECK_NULL_VOID(pattern);
1966 pattern->showSelect_ = false;
1967 pattern->OnDragMove(event);
1968 };
1969 eventHub->SetOnDragMove(std::move(onDragMove));
1970 auto onDragEnd = [weakPtr = WeakClaim(this), scopeId = Container::CurrentId()](
1971 const RefPtr<OHOS::Ace::DragEvent>& event) {
1972 ContainerScope scope(scopeId);
1973 auto pattern = weakPtr.Upgrade();
1974 CHECK_NULL_VOID(pattern);
1975 pattern->showSelect_ = true;
1976 pattern->StopAutoScroll();
1977 pattern->ClearRedoOperationRecords();
1978 pattern->OnDragEnd(event);
1979 };
1980 eventHub->SetOnDragEnd(std::move(onDragEnd));
1981 onDragDropAndLeave();
1982 }
1983
onDragDropAndLeave()1984 void RichEditorPattern::onDragDropAndLeave()
1985 {
1986 auto host = GetHost();
1987 CHECK_NULL_VOID(host);
1988 auto eventHub = host->GetEventHub<EventHub>();
1989 CHECK_NULL_VOID(eventHub);
1990 auto onDragDrop = [weakPtr = WeakClaim(this), scopeId = Container::CurrentId()](
1991 const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& value) {};
1992 eventHub->SetOnDrop(std::move(onDragDrop));
1993 auto onDragDragLeave = [weakPtr = WeakClaim(this), scopeId = Container::CurrentId()](
1994 const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& value) {
1995 ContainerScope scope(scopeId);
1996 auto pattern = weakPtr.Upgrade();
1997 CHECK_NULL_VOID(pattern);
1998 pattern->StopAutoScroll();
1999 };
2000 eventHub->SetOnDragLeave(onDragDragLeave);
2001 }
2002
ClearDragDropEvent()2003 void RichEditorPattern::ClearDragDropEvent()
2004 {
2005 auto host = GetHost();
2006 CHECK_NULL_VOID(host);
2007 auto gestureHub = host->GetOrCreateGestureEventHub();
2008 CHECK_NULL_VOID(gestureHub);
2009 gestureHub->SetIsTextDraggable(false);
2010 auto eventHub = host->GetEventHub<EventHub>();
2011 CHECK_NULL_VOID(eventHub);
2012 eventHub->SetOnDragStart(nullptr);
2013 eventHub->SetOnDragEnter(nullptr);
2014 eventHub->SetOnDragMove(nullptr);
2015 eventHub->SetOnDragLeave(nullptr);
2016 eventHub->SetOnDragEnd(nullptr);
2017 eventHub->SetOnDrop(nullptr);
2018 }
2019
OnDragMove(const RefPtr<OHOS::Ace::DragEvent> & event)2020 void RichEditorPattern::OnDragMove(const RefPtr<OHOS::Ace::DragEvent>& event)
2021 {
2022 auto pipeline = PipelineBase::GetCurrentContext();
2023 CHECK_NULL_VOID(pipeline);
2024 auto theme = pipeline->GetTheme<RichEditorTheme>();
2025 CHECK_NULL_VOID(theme);
2026 auto touchX = event->GetX();
2027 auto touchY = event->GetY();
2028 auto textRect = GetTextRect();
2029 textRect.SetTop(textRect.GetY() - std::min(baselineOffset_, 0.0f));
2030 Offset textOffset = { touchX - textRect.GetX() - GetParentGlobalOffset().GetX(),
2031 touchY - textRect.GetY() - GetParentGlobalOffset().GetY() - theme->GetInsertCursorOffset().ConvertToPx() };
2032 auto position = paragraphs_.GetIndex(textOffset);
2033 float caretHeight = 0.0f;
2034 SetCaretPosition(position);
2035 OffsetF caretOffset = CalcCursorOffsetByPosition(GetCaretPosition(), caretHeight);
2036 CHECK_NULL_VOID(overlayMod_);
2037 DynamicCast<RichEditorOverlayModifier>(overlayMod_)->SetCaretOffsetAndHeight(caretOffset, caretHeight);
2038
2039 AutoScrollParam param = { .autoScrollEvent = AutoScrollEvent::DRAG, .showScrollbar = true };
2040 auto localOffset = OffsetF(touchX, touchY) - parentGlobalOffset_;
2041 AutoScrollByEdgeDetection(param, localOffset, EdgeDetectionStrategy::IN_BOUNDARY);
2042 }
2043
OnDragEnd(const RefPtr<Ace::DragEvent> & event)2044 void RichEditorPattern::OnDragEnd(const RefPtr<Ace::DragEvent>& event)
2045 {
2046 ResetDragRecordSize(-1);
2047 auto host = GetHost();
2048 CHECK_NULL_VOID(host);
2049 if (status_ == Status::DRAGGING) {
2050 status_ = Status::NONE;
2051 }
2052 if (recoverDragResultObjects_.empty()) {
2053 return;
2054 }
2055 UpdateSpanItemDragStatus(recoverDragResultObjects_, false);
2056 recoverDragResultObjects_.clear();
2057 if (event && event->GetResult() != DragRet::DRAG_SUCCESS) {
2058 HandleSelectionChange(recoverStart_, recoverEnd_);
2059 showSelect_ = true;
2060 isShowMenu_ = false;
2061 CalculateHandleOffsetAndShowOverlay();
2062 if (!IsUsingMouse()) {
2063 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle);
2064 }
2065 }
2066 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
2067 }
2068
SelectOverlayIsOn()2069 bool RichEditorPattern::SelectOverlayIsOn()
2070 {
2071 auto pipeline = PipelineContext::GetCurrentContext();
2072 CHECK_NULL_RETURN(pipeline, false);
2073 CHECK_NULL_RETURN(selectOverlayProxy_, false);
2074 auto overlayId = selectOverlayProxy_->GetSelectOverlayId();
2075 return pipeline->GetSelectOverlayManager()->HasSelectOverlay(overlayId);
2076 }
2077
UpdateEditingValue(const std::shared_ptr<TextEditingValue> & value,bool needFireChangeEvent)2078 void RichEditorPattern::UpdateEditingValue(const std::shared_ptr<TextEditingValue>& value, bool needFireChangeEvent)
2079 {
2080 InsertValue(value->text);
2081 }
2082
PerformAction(TextInputAction action,bool forceCloseKeyboard)2083 void RichEditorPattern::PerformAction(TextInputAction action, bool forceCloseKeyboard)
2084 {
2085 InsertValue("\n");
2086 }
2087
InitMouseEvent()2088 void RichEditorPattern::InitMouseEvent()
2089 {
2090 CHECK_NULL_VOID(!mouseEventInitialized_);
2091 auto host = GetHost();
2092 CHECK_NULL_VOID(host);
2093 auto eventHub = host->GetEventHub<EventHub>();
2094 CHECK_NULL_VOID(eventHub);
2095 auto inputHub = eventHub->GetOrCreateInputEventHub();
2096 CHECK_NULL_VOID(inputHub);
2097
2098 auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
2099 auto pattern = weak.Upgrade();
2100 CHECK_NULL_VOID(pattern);
2101 pattern->HandleMouseEvent(info);
2102 };
2103 auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
2104 inputHub->AddOnMouseEvent(mouseEvent);
2105 auto hoverTask = [weak = WeakClaim(this)](bool isHover) {
2106 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "on hover event isHover=%{public}d", isHover);
2107 auto pattern = weak.Upgrade();
2108 if (pattern) {
2109 pattern->OnHover(isHover);
2110 }
2111 };
2112 auto hoverEvent = MakeRefPtr<InputEvent>(std::move(hoverTask));
2113 inputHub->AddOnHoverEvent(hoverEvent);
2114 mouseEventInitialized_ = true;
2115 }
2116
OnHover(bool isHover)2117 void RichEditorPattern::OnHover(bool isHover)
2118 {
2119 auto frame = GetHost();
2120 CHECK_NULL_VOID(frame);
2121 auto frameId = frame->GetId();
2122 auto pipeline = PipelineContext::GetCurrentContext();
2123 CHECK_NULL_VOID(pipeline);
2124 auto scrollBar = GetScrollBar();
2125 if (isHover && !scrollBar->IsPressed()) {
2126 pipeline->SetMouseStyleHoldNode(frameId);
2127 pipeline->ChangeMouseStyle(frameId, MouseFormat::TEXT_CURSOR);
2128 } else {
2129 pipeline->ChangeMouseStyle(frameId, MouseFormat::DEFAULT);
2130 pipeline->FreeMouseStyleHoldNode(frameId);
2131 }
2132 }
2133
RequestKeyboard(bool isFocusViewChanged,bool needStartTwinkling,bool needShowSoftKeyboard)2134 bool RichEditorPattern::RequestKeyboard(bool isFocusViewChanged, bool needStartTwinkling, bool needShowSoftKeyboard)
2135 {
2136 auto context = PipelineContext::GetCurrentContext();
2137 CHECK_NULL_RETURN(context, false);
2138 CHECK_NULL_RETURN(needShowSoftKeyboard, false);
2139 if (needShowSoftKeyboard && customKeyboardBuilder_) {
2140 return RequestCustomKeyboard();
2141 }
2142 #if defined(ENABLE_STANDARD_INPUT)
2143 if (!EnableStandardInput(needShowSoftKeyboard)) {
2144 return false;
2145 }
2146 #else
2147 if (!UnableStandardInput(isFocusViewChanged)) {
2148 return false;
2149 }
2150 #endif
2151 return true;
2152 }
2153
2154 #if defined(ENABLE_STANDARD_INPUT)
2155 #ifdef WINDOW_SCENE_SUPPORTED
GetSCBSystemWindowId()2156 uint32_t RichEditorPattern::GetSCBSystemWindowId()
2157 {
2158 RefPtr<FrameNode> frameNode = GetHost();
2159 CHECK_NULL_RETURN(frameNode, {});
2160 auto focusSystemWindowId = WindowSceneHelper::GetFocusSystemWindowId(frameNode);
2161 return focusSystemWindowId;
2162 }
2163 #endif
2164
EnableStandardInput(bool needShowSoftKeyboard)2165 bool RichEditorPattern::EnableStandardInput(bool needShowSoftKeyboard)
2166 {
2167 auto context = PipelineContext::GetCurrentContext();
2168 CHECK_NULL_RETURN(context, false);
2169 MiscServices::Configuration configuration;
2170 configuration.SetEnterKeyType(static_cast<MiscServices::EnterKeyType>(static_cast<int32_t>(TextInputAction::DONE)));
2171 configuration.SetTextInputType(
2172 static_cast<MiscServices::TextInputType>(static_cast<int32_t>(TextInputType::UNSPECIFIED)));
2173 MiscServices::InputMethodController::GetInstance()->OnConfigurationChange(configuration);
2174 if (richEditTextChangeListener_ == nullptr) {
2175 richEditTextChangeListener_ = new OnTextChangedListenerImpl(WeakClaim(this));
2176 }
2177 auto inputMethod = MiscServices::InputMethodController::GetInstance();
2178 CHECK_NULL_RETURN(inputMethod, false);
2179 auto miscTextConfig = GetMiscTextConfig();
2180 CHECK_NULL_RETURN(miscTextConfig.has_value(), false);
2181 TAG_LOGI(
2182 AceLogTag::ACE_RICH_TEXT, "RequestKeyboard set calling window id is : %{public}u", miscTextConfig->windowId);
2183 MiscServices::TextConfig textconfig = miscTextConfig.value();
2184 #ifdef WINDOW_SCENE_SUPPORTED
2185 auto systemWindowId = GetSCBSystemWindowId();
2186 if (systemWindowId) {
2187 miscTextConfig->windowId = systemWindowId;
2188 }
2189 #endif
2190 inputMethod->Attach(richEditTextChangeListener_, needShowSoftKeyboard, textconfig);
2191 UpdateKeyboardOffset(textconfig.positionY, textconfig.height);
2192 if (context) {
2193 inputMethod->SetCallingWindow(context->GetWindowId());
2194 }
2195 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
2196 imeAttached_ = true;
2197 #endif
2198 return true;
2199 }
2200
GetMiscTextConfig()2201 std::optional<MiscServices::TextConfig> RichEditorPattern::GetMiscTextConfig()
2202 {
2203 auto tmpHost = GetHost();
2204 CHECK_NULL_RETURN(tmpHost, {});
2205 auto pipeline = tmpHost->GetContext();
2206 CHECK_NULL_RETURN(pipeline, {});
2207 auto windowRect = pipeline->GetCurrentWindowRect();
2208 double positionY = (tmpHost->GetPaintRectOffset() - pipeline->GetRootRect().GetOffset()).GetY() + windowRect.Top();
2209 double height = frameRect_.Height();
2210
2211 float caretHeight = 0.0f;
2212 OffsetF caretOffset = CalcCursorOffsetByPosition(GetCaretPosition(), caretHeight);
2213 if (NearZero(caretHeight)) {
2214 auto overlayModifier = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
2215 caretHeight = overlayModifier ? overlayModifier->GetCaretHeight() : DEFAULT_CARET_HEIGHT;
2216 }
2217 auto offset = KEYBOARD_AVOID_OFFSET.ConvertToPx();
2218 auto caretTop = caretOffset.GetY() + windowRect.Top() + parentGlobalOffset_.GetY();
2219 height = caretTop + caretHeight + offset - positionY;
2220
2221 MiscServices::CursorInfo cursorInfo { .left = caretOffset.GetX() + windowRect.Left() + parentGlobalOffset_.GetX(),
2222 .top = caretTop,
2223 .width = CARET_WIDTH,
2224 .height = caretHeight };
2225 MiscServices::InputAttribute inputAttribute = { .inputPattern = (int32_t)TextInputType::UNSPECIFIED,
2226 .enterKeyType = (int32_t)TextInputAction::DONE };
2227 MiscServices::TextConfig textConfig = { .inputAttribute = inputAttribute,
2228 .cursorInfo = cursorInfo,
2229 .range = { .start = textSelector_.GetStart(), .end = textSelector_.GetEnd() },
2230 .windowId = pipeline->GetFocusWindowId(),
2231 .positionY = positionY,
2232 .height = height };
2233 return textConfig;
2234 }
2235 #else
UnableStandardInput(bool isFocusViewChanged)2236 bool RichEditorPattern::UnableStandardInput(bool isFocusViewChanged)
2237 {
2238 auto context = PipelineContext::GetCurrentContext();
2239 CHECK_NULL_RETURN(context, false);
2240 if (HasConnection()) {
2241 connection_->Show(isFocusViewChanged, GetInstanceId());
2242 return true;
2243 }
2244 TextInputConfiguration config;
2245 config.type = TextInputType::UNSPECIFIED;
2246 config.action = TextInputAction::DONE;
2247 config.obscureText = false;
2248 connection_ =
2249 TextInputProxy::GetInstance().Attach(WeakClaim(this), config, context->GetTaskExecutor(), GetInstanceId());
2250 if (!HasConnection()) {
2251 return false;
2252 }
2253 TextEditingValue value;
2254 if (spans_.empty()) {
2255 value.text = textForDisplay_;
2256 } else {
2257 for (auto it = spans_.begin(); it != spans_.end(); it++) {
2258 if ((*it)->placeholderIndex < 0) {
2259 value.text.append((*it)->content);
2260 } else {
2261 value.text.append(" ");
2262 }
2263 }
2264 }
2265 value.selection.Update(caretPosition_, caretPosition_);
2266 connection_->SetEditingState(value, GetInstanceId());
2267 connection_->Show(isFocusViewChanged, GetInstanceId());
2268 return true;
2269 }
2270 #endif
2271
OnColorConfigurationUpdate()2272 void RichEditorPattern::OnColorConfigurationUpdate()
2273 {
2274 auto host = GetHost();
2275 CHECK_NULL_VOID(host);
2276 const auto& spans = host->GetChildren();
2277 auto context = PipelineContext::GetCurrentContext();
2278 CHECK_NULL_VOID(context);
2279 auto theme = context->GetTheme<TextTheme>();
2280 CHECK_NULL_VOID(theme);
2281 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
2282 CHECK_NULL_VOID(textLayoutProperty);
2283 textLayoutProperty->UpdateTextColor(theme->GetTextStyle().GetTextColor());
2284 textLayoutProperty->UpdateTextDecorationColor(theme->GetTextStyle().GetTextColor());
2285 for (auto span : spans) {
2286 auto spanNode = DynamicCast<SpanNode>(span);
2287 if (!spanNode) {
2288 continue;
2289 }
2290 auto spanItem = spanNode->GetSpanItem();
2291 if (!spanItem) {
2292 continue;
2293 }
2294 if (spanItem->hasResourceFontColor) {
2295 spanNode->UpdateTextColor(theme->GetTextStyle().GetTextColor());
2296 }
2297 if (spanItem->hasResourceDecorationColor) {
2298 spanNode->UpdateTextDecorationColor(theme->GetTextStyle().GetTextColor());
2299 }
2300 }
2301 }
2302
UpdateCaretInfoToController()2303 void RichEditorPattern::UpdateCaretInfoToController()
2304 {
2305 CHECK_NULL_VOID(HasFocus());
2306 auto selectionResult = GetSpansInfo(0, GetTextContentLength(), GetSpansMethod::ONSELECT);
2307 auto resultObjects = selectionResult.GetSelection().resultObjects;
2308 std::string text = "";
2309 if (!resultObjects.empty()) {
2310 for (const auto& resultObj : resultObjects) {
2311 if (resultObj.type == SelectSpanType::TYPESPAN) {
2312 text += resultObj.valueString;
2313 }
2314 }
2315 }
2316 #if defined(ENABLE_STANDARD_INPUT)
2317 auto miscTextConfig = GetMiscTextConfig();
2318 CHECK_NULL_VOID(miscTextConfig.has_value());
2319 MiscServices::CursorInfo cursorInfo = miscTextConfig.value().cursorInfo;
2320 MiscServices::InputMethodController::GetInstance()->OnCursorUpdate(cursorInfo);
2321 MiscServices::InputMethodController::GetInstance()->OnSelectionChange(
2322 StringUtils::Str8ToStr16(text), textSelector_.GetStart(), textSelector_.GetEnd());
2323 TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
2324 "Caret position update, left %{public}f, top %{public}f, width %{public}f, height %{public}f; textSelector_ "
2325 "Start "
2326 "%{public}d, end %{public}d",
2327 cursorInfo.left, cursorInfo.top, cursorInfo.width, cursorInfo.height, textSelector_.GetStart(),
2328 textSelector_.GetEnd());
2329 #else
2330 if (HasConnection()) {
2331 TextEditingValue editingValue;
2332 editingValue.text = text;
2333 editingValue.hint = "";
2334 editingValue.selection.Update(textSelector_.baseOffset, textSelector_.destinationOffset);
2335 connection_->SetEditingState(editingValue, GetInstanceId());
2336 }
2337 #endif
2338 }
2339
HasConnection() const2340 bool RichEditorPattern::HasConnection() const
2341 {
2342 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
2343 return imeAttached_;
2344 #else
2345 return connection_ != nullptr;
2346 #endif
2347 }
2348
RequestCustomKeyboard()2349 bool RichEditorPattern::RequestCustomKeyboard()
2350 {
2351 #if defined(ENABLE_STANDARD_INPUT)
2352 auto inputMethod = MiscServices::InputMethodController::GetInstance();
2353 if (inputMethod) {
2354 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "RequestCKeyboard,close softkeyboard.");
2355 inputMethod->RequestHideInput();
2356 inputMethod->Close();
2357 }
2358 #else
2359 if (HasConnection()) {
2360 connection_->Close(GetInstanceId());
2361 connection_ = nullptr;
2362 }
2363 #endif
2364
2365 if (isCustomKeyboardAttached_) {
2366 return true;
2367 }
2368 CHECK_NULL_RETURN(customKeyboardBuilder_, false);
2369 auto frameNode = GetHost();
2370 CHECK_NULL_RETURN(frameNode, false);
2371 auto pipeline = PipelineContext::GetCurrentContext();
2372 CHECK_NULL_RETURN(pipeline, false);
2373 auto overlayManager = pipeline->GetOverlayManager();
2374 CHECK_NULL_RETURN(overlayManager, false);
2375 overlayManager->BindKeyboard(customKeyboardBuilder_, frameNode->GetId());
2376 isCustomKeyboardAttached_ = true;
2377 keyboardOverlay_ = overlayManager;
2378 return true;
2379 }
2380
CloseCustomKeyboard()2381 bool RichEditorPattern::CloseCustomKeyboard()
2382 {
2383 auto frameNode = GetHost();
2384 CHECK_NULL_RETURN(frameNode, false);
2385 CHECK_NULL_RETURN(keyboardOverlay_, false);
2386 keyboardOverlay_->CloseKeyboard(frameNode->GetId());
2387 isCustomKeyboardAttached_ = false;
2388 return true;
2389 }
2390
InsertValue(const std::string & insertValue)2391 void RichEditorPattern::InsertValue(const std::string& insertValue)
2392 {
2393 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "insertValue=[%{public}s]", StringUtils::RestoreEscape(insertValue).c_str());
2394 OperationRecord record;
2395 record.beforeCaretPosition = caretPosition_ + moveLength_;
2396 if (textSelector_.IsValid()) {
2397 record.beforeCaretPosition = textSelector_.GetTextStart();
2398 }
2399 record.addText = insertValue;
2400 ClearRedoOperationRecords();
2401 InsertValueOperation(insertValue, &record);
2402 record.afterCaretPosition = caretPosition_ + moveLength_;
2403 AddOperationRecord(record);
2404 }
2405
InsertValueOperation(const std::string & insertValue,OperationRecord * const record)2406 void RichEditorPattern::InsertValueOperation(const std::string& insertValue, OperationRecord* const record)
2407 {
2408 bool isSelector = textSelector_.IsValid();
2409 if (isSelector) {
2410 SetCaretPosition(textSelector_.GetTextStart());
2411 }
2412
2413 std::string insertValueTemp = insertValue;
2414 bool isLineSeparator = insertValueTemp == std::string("\n");
2415
2416 auto isInsert = BeforeIMEInsertValue(insertValueTemp);
2417 CHECK_NULL_VOID(isInsert);
2418 TextInsertValueInfo info;
2419 CalcInsertValueObj(info);
2420 if (isSelector) {
2421 std::wstring deleteText = DeleteForwardOperation(textSelector_.GetTextEnd() - textSelector_.GetTextStart());
2422 if (record && deleteText.length() != 0) {
2423 record->deleteText = StringUtils::ToString(deleteText);
2424 }
2425 CloseSelectOverlay();
2426 ResetSelection();
2427 }
2428 if (!caretVisible_) {
2429 StartTwinkling();
2430 }
2431 auto host = GetHost();
2432 CHECK_NULL_VOID(host);
2433 RefPtr<SpanNode> spanNode = DynamicCast<SpanNode>(host->GetChildAtIndex(info.GetSpanIndex()));
2434 if (spanNode == nullptr || spanNode->GetSpanItem()->unicode != 0) {
2435 InsertValueWithoutSpan(spanNode, info, insertValueTemp);
2436 return;
2437 }
2438 if (info.GetOffsetInSpan() == 0) {
2439 auto spanNodeBefore = DynamicCast<SpanNode>(host->GetChildAtIndex(info.GetSpanIndex() - 1));
2440 if (spanNodeBefore != nullptr && !IsLineSeparatorInLast(spanNodeBefore) &&
2441 spanNodeBefore->GetTag() == V2::SPAN_ETS_TAG) {
2442 InsertValueAfterBeforeSpan(spanNodeBefore, spanNode, info, insertValueTemp);
2443 return;
2444 }
2445 }
2446 if (typingStyle_.has_value() && !HasSameTypingStyle(spanNode)) {
2447 InsertDiffStyleValueInSpan(spanNode, info, insertValueTemp);
2448 return;
2449 }
2450 if (!isLineSeparator) {
2451 InsertValueToSpanNode(spanNode, insertValueTemp, info);
2452 } else {
2453 SpanNodeFission(spanNode, insertValueTemp, info);
2454 }
2455 AfterIMEInsertValue(spanNode, static_cast<int32_t>(StringUtils::ToWstring(insertValueTemp).length()), false);
2456 }
2457
InsertValueWithoutSpan(RefPtr<SpanNode> & spanNode,const TextInsertValueInfo & info,const std::string & insertValue)2458 void RichEditorPattern::InsertValueWithoutSpan(
2459 RefPtr<SpanNode>& spanNode, const TextInsertValueInfo& info, const std::string& insertValue)
2460 {
2461 auto host = GetHost();
2462 CHECK_NULL_VOID(host);
2463 if (info.GetSpanIndex() == 0) {
2464 CreateTextSpanNode(spanNode, info, insertValue);
2465 return;
2466 }
2467 auto spanNodeBefore = DynamicCast<SpanNode>(host->GetChildAtIndex(info.GetSpanIndex() - 1));
2468 if (spanNodeBefore == nullptr || spanNodeBefore->GetSpanItem()->unicode != 0) {
2469 CreateTextSpanNode(spanNode, info, insertValue);
2470 return;
2471 }
2472 InsertValueAfterBeforeSpan(spanNodeBefore, spanNode, info, insertValue);
2473 }
2474
InsertValueAfterBeforeSpan(RefPtr<SpanNode> & spanNodeBefore,RefPtr<SpanNode> & spanNode,const TextInsertValueInfo & info,const std::string & insertValue)2475 void RichEditorPattern::InsertValueAfterBeforeSpan(RefPtr<SpanNode>& spanNodeBefore, RefPtr<SpanNode>& spanNode,
2476 const TextInsertValueInfo& info, const std::string& insertValue)
2477 {
2478 if (typingStyle_.has_value() && !HasSameTypingStyle(spanNodeBefore)) {
2479 CreateTextSpanNode(spanNode, info, insertValue);
2480 CopyTextSpanLineStyle(spanNodeBefore, spanNode, true);
2481 return;
2482 }
2483 auto spanNodeGet = InsertValueToBeforeSpan(spanNodeBefore, insertValue);
2484 bool isCreate = spanNodeBefore->GetId() != spanNodeGet->GetId();
2485 AfterIMEInsertValue(
2486 spanNodeGet, static_cast<int32_t>(StringUtils::ToWstring(insertValue).length()), isCreate);
2487 }
2488
InsertDiffStyleValueInSpan(RefPtr<SpanNode> & spanNode,const TextInsertValueInfo & info,const std::string & insertValue)2489 void RichEditorPattern::InsertDiffStyleValueInSpan(
2490 RefPtr<SpanNode>& spanNode, const TextInsertValueInfo& info, const std::string& insertValue)
2491 {
2492 auto host = GetHost();
2493 CHECK_NULL_VOID(host);
2494 TextSpanOptions options;
2495 options.value = insertValue;
2496 options.offset = caretPosition_;
2497 options.style = typingTextStyle_;
2498 auto newSpanIndex = AddTextSpanOperation(options, false, -1, true);
2499 auto newSpanNode = DynamicCast<SpanNode>(host->GetChildAtIndex(newSpanIndex));
2500 CopyTextSpanLineStyle(spanNode, newSpanNode, true);
2501 AfterIMEInsertValue(spanNode, static_cast<int32_t>(StringUtils::ToWstring(insertValue).length()), true);
2502 }
2503
IsLineSeparatorInLast(RefPtr<SpanNode> & spanNode)2504 bool RichEditorPattern::IsLineSeparatorInLast(RefPtr<SpanNode>& spanNode)
2505 {
2506 auto spanItem = spanNode->GetSpanItem();
2507 auto text = spanItem->content;
2508 std::wstring textTemp = StringUtils::ToWstring(text);
2509 auto index = textTemp.find(lineSeparator);
2510 if (index != std::wstring::npos) {
2511 auto textBefore = textTemp.substr(0, index + 1);
2512 auto textAfter = textTemp.substr(index + 1);
2513 if (textAfter.empty()) {
2514 return true;
2515 }
2516 }
2517 return false;
2518 }
2519
InsertValueToSpanNode(RefPtr<SpanNode> & spanNode,const std::string & insertValue,const TextInsertValueInfo & info)2520 void RichEditorPattern::InsertValueToSpanNode(
2521 RefPtr<SpanNode>& spanNode, const std::string& insertValue, const TextInsertValueInfo& info)
2522 {
2523 auto spanItem = spanNode->GetSpanItem();
2524 CHECK_NULL_VOID(spanItem);
2525 auto text = spanItem->content;
2526 std::wstring textTemp = StringUtils::ToWstring(text);
2527 auto textTempSize = static_cast<int32_t>(textTemp.size());
2528 if (textTempSize < info.GetOffsetInSpan()) {
2529 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "InsertValue error, offsetInSpan is greater than the size of spanItem, "
2530 "spanItemSize = %{public}d, offsetInSpan = %{public}d", textTempSize, info.GetOffsetInSpan());
2531 return;
2532 }
2533 std::wstring insertValueTemp = StringUtils::ToWstring(insertValue);
2534 textTemp.insert(info.GetOffsetInSpan(), insertValueTemp);
2535 text = StringUtils::ToString(textTemp);
2536 spanNode->UpdateContent(text);
2537 spanItem->position += static_cast<int32_t>(StringUtils::ToWstring(insertValue).length());
2538 }
2539
SpanNodeFission(RefPtr<SpanNode> & spanNode,const std::string & insertValue,const TextInsertValueInfo & info)2540 void RichEditorPattern::SpanNodeFission(
2541 RefPtr<SpanNode>& spanNode, const std::string& insertValue, const TextInsertValueInfo& info)
2542 {
2543 auto spanItem = spanNode->GetSpanItem();
2544 CHECK_NULL_VOID(spanItem);
2545 auto text = spanItem->content;
2546 std::wstring textTemp = StringUtils::ToWstring(text);
2547 std::wstring insertValueTemp = StringUtils::ToWstring(insertValue);
2548 InsertValueInSpanOffset(info, textTemp, insertValueTemp);
2549 auto index = textTemp.find(lineSeparator);
2550 if (index != std::wstring::npos) {
2551 auto textBefore = textTemp.substr(0, index + 1);
2552 auto textAfter = textTemp.substr(index + 1);
2553 text = StringUtils::ToString(textBefore);
2554 spanNode->UpdateContent(text);
2555 spanItem->position += 1 - static_cast<int32_t>(textAfter.length());
2556 if (!textAfter.empty()) {
2557 TextInsertValueInfo infoAfter;
2558 infoAfter.SetSpanIndex(info.GetSpanIndex() + 1);
2559 infoAfter.SetOffsetInSpan(0);
2560 auto host = GetHost();
2561 CHECK_NULL_VOID(host);
2562 auto nodeId = ViewStackProcessor::GetInstance()->ClaimNodeId();
2563 RefPtr<SpanNode> spanNodeAfter = SpanNode::GetOrCreateSpanNode(nodeId);
2564 spanNodeAfter->MountToParent(host, infoAfter.GetSpanIndex());
2565 spanNodeAfter->UpdateContent(StringUtils::ToString(textAfter));
2566 CopyTextSpanStyle(spanNode, spanNodeAfter);
2567 }
2568 } else {
2569 text = StringUtils::ToString(textTemp);
2570 spanNode->UpdateContent(text);
2571 spanItem->position += static_cast<int32_t>(StringUtils::ToWstring(insertValue).length());
2572 }
2573 }
2574
InsertValueInSpanOffset(const TextInsertValueInfo & info,std::wstring & text,const std::wstring & insertValue)2575 void RichEditorPattern::InsertValueInSpanOffset(
2576 const TextInsertValueInfo& info, std::wstring& text, const std::wstring& insertValue)
2577 {
2578 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "insert value info: %{public}s", info.ToString().c_str());
2579 auto offsetInSpan = std::clamp(info.GetOffsetInSpan(), 0, static_cast<int32_t>(text.length()));
2580 text.insert(offsetInSpan, insertValue);
2581 }
2582
InsertValueToBeforeSpan(RefPtr<SpanNode> & spanNodeBefore,const std::string & insertValue)2583 RefPtr<SpanNode> RichEditorPattern::InsertValueToBeforeSpan(
2584 RefPtr<SpanNode>& spanNodeBefore, const std::string& insertValue)
2585 {
2586 auto spanItem = spanNodeBefore->GetSpanItem();
2587 CHECK_NULL_RETURN(spanItem, spanNodeBefore);
2588 auto text = spanItem->content;
2589 std::wstring textTemp = StringUtils::ToWstring(text);
2590 std::wstring insertValueTemp = StringUtils::ToWstring(insertValue);
2591 textTemp.append(insertValueTemp);
2592
2593 auto index = textTemp.find(lineSeparator);
2594 if (index != std::wstring::npos) {
2595 auto textBefore = textTemp.substr(0, index + 1);
2596 auto textAfter = textTemp.substr(index + 1);
2597 text = StringUtils::ToString(textBefore);
2598 spanNodeBefore->UpdateContent(text);
2599 spanItem->position += static_cast<int32_t>(insertValueTemp.length()) - static_cast<int32_t>(textAfter.length());
2600 if (!textAfter.empty()) {
2601 auto host = GetHost();
2602 CHECK_NULL_RETURN(spanItem, spanNodeBefore);
2603 TextInsertValueInfo infoAfter;
2604 infoAfter.SetSpanIndex(host->GetChildIndex(spanNodeBefore) + 1);
2605 infoAfter.SetOffsetInSpan(0);
2606 auto nodeId = ViewStackProcessor::GetInstance()->ClaimNodeId();
2607 RefPtr<SpanNode> spanNodeAfter = SpanNode::GetOrCreateSpanNode(nodeId);
2608 spanNodeAfter->MountToParent(host, infoAfter.GetSpanIndex());
2609 spanNodeAfter->UpdateContent(StringUtils::ToString(textAfter));
2610 CopyTextSpanStyle(spanNodeBefore, spanNodeAfter);
2611 auto spanItemAfter = spanNodeAfter->GetSpanItem();
2612 spanItemAfter->position = static_cast<int32_t>(textTemp.length());
2613 spanItemAfter->hasResourceFontColor = spanItem->hasResourceFontColor;
2614 spanItemAfter->hasResourceDecorationColor = spanItem->hasResourceDecorationColor;
2615 AddSpanItem(spanItemAfter, host->GetChildIndex(spanNodeBefore) + 1);
2616 SpanNodeFission(spanNodeAfter);
2617 return spanNodeAfter;
2618 }
2619 } else {
2620 text = StringUtils::ToString(textTemp);
2621 spanNodeBefore->UpdateContent(text);
2622 spanItem->position += static_cast<int32_t>(StringUtils::ToWstring(insertValue).length());
2623 }
2624 return spanNodeBefore;
2625 }
2626
CreateTextSpanNode(RefPtr<SpanNode> & spanNode,const TextInsertValueInfo & info,const std::string & insertValue,bool isIME)2627 void RichEditorPattern::CreateTextSpanNode(
2628 RefPtr<SpanNode>& spanNode, const TextInsertValueInfo& info, const std::string& insertValue, bool isIME)
2629 {
2630 auto host = GetHost();
2631 CHECK_NULL_VOID(host);
2632 auto nodeId = ViewStackProcessor::GetInstance()->ClaimNodeId();
2633 spanNode = SpanNode::GetOrCreateSpanNode(nodeId);
2634 spanNode->MountToParent(host, info.GetSpanIndex());
2635 auto spanItem = spanNode->GetSpanItem();
2636 spanItem->hasResourceFontColor = true;
2637 spanItem->hasResourceDecorationColor = true;
2638 spanNode->UpdateContent(insertValue);
2639 AddSpanItem(spanItem, info.GetSpanIndex());
2640 if (typingStyle_.has_value() && typingTextStyle_.has_value()) {
2641 UpdateTextStyle(spanNode, typingStyle_.value(), typingTextStyle_.value());
2642 auto spanItem = spanNode->GetSpanItem();
2643 spanItem->SetTextStyle(typingTextStyle_);
2644 } else {
2645 spanNode->UpdateFontSize(Dimension(DEFAULT_TEXT_SIZE, DimensionUnit::FP));
2646 spanNode->AddPropertyInfo(PropertyInfo::FONTSIZE);
2647 }
2648 if (isIME) {
2649 AfterIMEInsertValue(spanNode, static_cast<int32_t>(StringUtils::ToWstring(insertValue).length()), true);
2650 }
2651 }
2652
BeforeIMEInsertValue(const std::string & insertValue)2653 bool RichEditorPattern::BeforeIMEInsertValue(const std::string& insertValue)
2654 {
2655 auto eventHub = GetEventHub<RichEditorEventHub>();
2656 CHECK_NULL_RETURN(eventHub, true);
2657 RichEditorInsertValue insertValueInfo;
2658 insertValueInfo.SetInsertOffset(caretPosition_);
2659 insertValueInfo.SetInsertValue(insertValue);
2660 return eventHub->FireAboutToIMEInput(insertValueInfo);
2661 }
2662
AfterIMEInsertValue(const RefPtr<SpanNode> & spanNode,int32_t insertValueLength,bool isCreate)2663 void RichEditorPattern::AfterIMEInsertValue(const RefPtr<SpanNode>& spanNode, int32_t insertValueLength, bool isCreate)
2664 {
2665 RichEditorAbstractSpanResult retInfo;
2666 isTextChange_ = true;
2667 moveDirection_ = MoveDirection::FORWARD;
2668 moveLength_ += insertValueLength;
2669 auto eventHub = GetEventHub<RichEditorEventHub>();
2670 CHECK_NULL_VOID(eventHub);
2671 auto host = GetHost();
2672 CHECK_NULL_VOID(host);
2673 retInfo.SetSpanIndex(host->GetChildIndex(spanNode));
2674 retInfo.SetEraseLength(insertValueLength);
2675 retInfo.SetValue(spanNode->GetSpanItem()->content);
2676 auto contentLength = StringUtils::ToWstring(spanNode->GetSpanItem()->content).length();
2677 if (isCreate) {
2678 auto spanStart = 0;
2679 auto spanEnd = static_cast<int32_t>(contentLength);
2680 retInfo.SetSpanRangeStart(spanStart);
2681 retInfo.SetSpanRangeEnd(spanEnd);
2682 retInfo.SetOffsetInSpan(0);
2683 } else {
2684 auto spanEnd = spanNode->GetSpanItem()->position;
2685 auto spanStart = spanEnd - static_cast<int32_t>(contentLength);
2686 retInfo.SetSpanRangeStart(spanStart);
2687 retInfo.SetSpanRangeEnd(spanEnd);
2688 retInfo.SetOffsetInSpan(GetCaretPosition() - retInfo.GetSpanRangeStart());
2689 }
2690 retInfo.SetFontColor(spanNode->GetTextColorValue(Color::BLACK).ColorToString());
2691 retInfo.SetFontSize(spanNode->GetFontSizeValue(Dimension(16.0f, DimensionUnit::VP)).ConvertToVp());
2692 retInfo.SetFontStyle(spanNode->GetItalicFontStyleValue(OHOS::Ace::FontStyle::NORMAL));
2693 retInfo.SetFontWeight(static_cast<int32_t>(spanNode->GetFontWeightValue(FontWeight::NORMAL)));
2694 std::string fontFamilyValue;
2695 auto fontFamily = spanNode->GetFontFamilyValue({ "HarmonyOS Sans" });
2696 for (const auto& str : fontFamily) {
2697 fontFamilyValue += str;
2698 }
2699 retInfo.SetFontFamily(fontFamilyValue);
2700 retInfo.SetTextDecoration(spanNode->GetTextDecorationValue(TextDecoration::NONE));
2701 retInfo.SetColor(spanNode->GetTextDecorationColorValue(Color::BLACK).ColorToString());
2702 UpdateSpanPosition();
2703 MoveCaretAfterTextChange();
2704 eventHub->FireOnIMEInputComplete(retInfo);
2705 }
2706
ResetFirstNodeStyle()2707 void RichEditorPattern::ResetFirstNodeStyle()
2708 {
2709 auto tmpHost = GetHost();
2710 CHECK_NULL_VOID(tmpHost);
2711 auto spans = tmpHost->GetChildren();
2712 if (!spans.empty()) {
2713 auto&& firstNode = DynamicCast<SpanNode>(*(spans.begin()));
2714 if (firstNode) {
2715 firstNode->ResetTextAlign();
2716 firstNode->ResetLeadingMargin();
2717 tmpHost->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2718 }
2719 }
2720 }
2721
FireOnDeleteComplete(const RichEditorDeleteValue & info)2722 void RichEditorPattern::FireOnDeleteComplete(const RichEditorDeleteValue& info)
2723 {
2724 auto eventHub = GetEventHub<RichEditorEventHub>();
2725 CHECK_NULL_VOID(eventHub);
2726 auto isDelete = eventHub->FireAboutToDelete(info);
2727 if (isDelete) {
2728 DeleteByDeleteValueInfo(info);
2729 eventHub->FireOnDeleteComplete();
2730 }
2731 }
2732
EraseEmoji(const RefPtr<SpanItem> & spanItem)2733 bool RichEditorPattern::EraseEmoji(const RefPtr<SpanItem>& spanItem)
2734 {
2735 CHECK_NULL_RETURN(spanItem, false);
2736 auto& content = spanItem->content;
2737 bool eraseSuccess = false;
2738 uint32_t start = 0;
2739 uint32_t end = 0;
2740 while (start < content.length()) {
2741 auto unicode = TypedText::GetUTF8Next(content.c_str(), start, end);
2742 if (TypedText::IsEmoji(unicode)) {
2743 content.erase(start, end - start);
2744 eraseSuccess = true;
2745 continue;
2746 }
2747 start = end;
2748 }
2749 return eraseSuccess;
2750 }
2751
EraseEmoji()2752 bool RichEditorPattern::EraseEmoji()
2753 {
2754 auto host = GetHost();
2755 CHECK_NULL_RETURN(host, false);
2756 std::set<int32_t, std::greater<int32_t>> deleteNodes;
2757 bool eraseSuccess = false;
2758 for (auto i = 0; i < static_cast<int32_t>(host->GetChildren().size()); ++i) {
2759 RefPtr<UINode> uiNode = host->GetChildAtIndex(i);
2760 auto spanNode = DynamicCast<SpanNode>(uiNode);
2761 if (!spanNode || spanNode->GetTag() != V2::SPAN_ETS_TAG) {
2762 continue;
2763 }
2764 const auto& spanItem = spanNode->GetSpanItem();
2765 eraseSuccess |= EraseEmoji(spanItem);
2766 if (spanItem && spanItem->content.empty()) {
2767 deleteNodes.emplace(i);
2768 }
2769 }
2770 for (auto index : deleteNodes) {
2771 host->RemoveChildAtIndex(index);
2772 }
2773 if (eraseSuccess) {
2774 UpdateSpanPosition();
2775 SetCaretPosition(std::clamp(caretPosition_, 0, static_cast<int32_t>(GetTextContentLength())));
2776 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2777 OnModifyDone();
2778 }
2779 return eraseSuccess;
2780 }
2781
HandleOnDelete(bool backward)2782 void RichEditorPattern::HandleOnDelete(bool backward)
2783 {
2784 backward ? DeleteBackward(1) : DeleteForward(1);
2785 }
2786
DeleteBackward(int32_t length)2787 void RichEditorPattern::DeleteBackward(int32_t length)
2788 {
2789 if (EraseEmoji()) {
2790 return;
2791 }
2792 OperationRecord record;
2793 record.beforeCaretPosition = caretPosition_;
2794 std::wstring deleteText = DeleteBackwardOperation(length);
2795 if (deleteText.length() != 0) {
2796 ClearRedoOperationRecords();
2797 record.deleteText = StringUtils::ToString(deleteText);
2798 record.afterCaretPosition = caretPosition_;
2799 AddOperationRecord(record);
2800 }
2801 }
2802
DeleteBackwardOperation(int32_t length)2803 std::wstring RichEditorPattern::DeleteBackwardOperation(int32_t length)
2804 {
2805 if (textSelector_.IsValid()) {
2806 length = textSelector_.GetTextEnd() - textSelector_.GetTextStart();
2807 SetCaretPosition(textSelector_.GetTextEnd());
2808 CloseSelectOverlay();
2809 ResetSelection();
2810 }
2811 std::wstringstream wss;
2812 for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
2813 wss << StringUtils::ToWstring((*iter)->content);
2814 }
2815 auto textContent = wss.str();
2816 if (static_cast<int32_t>(textContent.length()) != GetTextContentLength()) {
2817 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "textContent length mismatch, %{public}d vs. %{public}d",
2818 static_cast<int32_t>(textContent.length()), GetTextContentLength());
2819 }
2820 auto start = std::clamp(caretPosition_ - length, 0, static_cast<int32_t>(textContent.length()));
2821 std::wstring deleteText =
2822 textContent.substr(static_cast<uint32_t>(start), static_cast<uint32_t>(caretPosition_ - start));
2823 RichEditorDeleteValue info;
2824 info.SetRichEditorDeleteDirection(RichEditorDeleteDirection::BACKWARD);
2825 if (caretPosition_ == 0) {
2826 info.SetLength(0);
2827 ResetFirstNodeStyle();
2828 FireOnDeleteComplete(info);
2829 return deleteText;
2830 }
2831 if (length == spans_.back()->position) {
2832 ResetFirstNodeStyle();
2833 textForDisplay_.clear();
2834 }
2835 info.SetOffset(caretPosition_ - 1);
2836 info.SetLength(length);
2837 int32_t currentPosition = std::clamp((caretPosition_ - length), 0, static_cast<int32_t>(GetTextContentLength()));
2838 if (!spans_.empty()) {
2839 CalcDeleteValueObj(currentPosition, length, info);
2840 FireOnDeleteComplete(info);
2841 }
2842 if (!caretVisible_) {
2843 StartTwinkling();
2844 }
2845 return deleteText;
2846 }
2847
DeleteForward(int32_t length)2848 void RichEditorPattern::DeleteForward(int32_t length)
2849 {
2850 if (EraseEmoji()) {
2851 return;
2852 }
2853 OperationRecord record;
2854 record.beforeCaretPosition = caretPosition_;
2855 std::wstring deleteText = DeleteForwardOperation(length);
2856 if (deleteText.length() != 0) {
2857 ClearRedoOperationRecords();
2858 record.deleteText = StringUtils::ToString(deleteText);
2859 record.afterCaretPosition = caretPosition_;
2860 AddOperationRecord(record);
2861 }
2862 }
2863
DeleteForwardOperation(int32_t length)2864 std::wstring RichEditorPattern::DeleteForwardOperation(int32_t length)
2865 {
2866 if (textSelector_.IsValid()) {
2867 length = textSelector_.GetTextEnd() - textSelector_.GetTextStart();
2868 SetCaretPosition(textSelector_.GetTextStart());
2869 CloseSelectOverlay();
2870 ResetSelection();
2871 }
2872 std::wstringstream wss;
2873 for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
2874 wss << StringUtils::ToWstring((*iter)->content);
2875 }
2876 auto textContent = wss.str();
2877 if (static_cast<int32_t>(textContent.length()) != GetTextContentLength()) {
2878 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "textContent length mismatch, %{public}d vs. %{public}d",
2879 static_cast<int32_t>(textContent.length()), GetTextContentLength());
2880 }
2881 auto end = std::clamp(caretPosition_ + length, 0, static_cast<int32_t>(textContent.length()));
2882 std::wstring deleteText = textContent.substr(
2883 static_cast<uint32_t>(std::clamp(caretPosition_, 0, static_cast<int32_t>(textContent.length()))),
2884 static_cast<uint32_t>(end - caretPosition_));
2885 if (caretPosition_ == GetTextContentLength()) {
2886 return deleteText;
2887 }
2888 RichEditorDeleteValue info;
2889 info.SetOffset(caretPosition_);
2890 info.SetRichEditorDeleteDirection(RichEditorDeleteDirection::FORWARD);
2891 info.SetLength(length);
2892 int32_t currentPosition = caretPosition_;
2893 if (!spans_.empty()) {
2894 CalcDeleteValueObj(currentPosition, length, info);
2895 auto eventHub = GetEventHub<RichEditorEventHub>();
2896 CHECK_NULL_RETURN(eventHub, deleteText);
2897 auto isDelete = eventHub->FireAboutToDelete(info);
2898 if (isDelete) {
2899 DeleteByDeleteValueInfo(info);
2900 eventHub->FireOnDeleteComplete();
2901 }
2902 }
2903 if (!caretVisible_) {
2904 StartTwinkling();
2905 }
2906 return deleteText;
2907 }
2908
OnBackPressed()2909 bool RichEditorPattern::OnBackPressed()
2910 {
2911 auto tmpHost = GetHost();
2912 CHECK_NULL_RETURN(tmpHost, false);
2913 TAG_LOGI(AceLogTag::ACE_TEXT_FIELD, "RichEditor %{public}d receives back press event", tmpHost->GetId());
2914 if (SelectOverlayIsOn()) {
2915 CloseSelectOverlay();
2916 textSelector_.Update(textSelector_.destinationOffset);
2917 StartTwinkling();
2918 return true;
2919 }
2920 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
2921 if (!imeShown_ && !isCustomKeyboardAttached_) {
2922 #else
2923 if (!isCustomKeyboardAttached_) {
2924 #endif
2925 return false;
2926 }
2927 tmpHost->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2928 CloseKeyboard(true);
2929 FocusHub::LostFocusToViewRoot();
2930 #if defined(ANDROID_PLATFORM)
2931 return false;
2932 #else
2933 return true;
2934 #endif
2935 }
2936
2937 void RichEditorPattern::SetInputMethodStatus(bool keyboardShown)
2938 {
2939 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
2940 imeShown_ = keyboardShown;
2941 #endif
2942 }
2943
2944 bool RichEditorPattern::CursorMoveLeft()
2945 {
2946 CloseSelectOverlay();
2947 ResetSelection();
2948 auto caretPosition = std::clamp((caretPosition_ - 1), 0, static_cast<int32_t>(GetTextContentLength()));
2949 if (caretPosition_ == caretPosition) {
2950 return false;
2951 }
2952 SetCaretPosition(caretPosition);
2953 MoveCaretToContentRect();
2954 StartTwinkling();
2955 auto host = GetHost();
2956 CHECK_NULL_RETURN(host, false);
2957 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2958 return true;
2959 }
2960
2961 bool RichEditorPattern::CursorMoveRight()
2962 {
2963 CloseSelectOverlay();
2964 ResetSelection();
2965 auto caretPosition = std::clamp((caretPosition_ + 1), 0, static_cast<int32_t>(GetTextContentLength()));
2966 if (caretPosition_ == caretPosition) {
2967 return false;
2968 }
2969 SetCaretPosition(caretPosition);
2970 MoveCaretToContentRect();
2971 StartTwinkling();
2972 auto host = GetHost();
2973 CHECK_NULL_RETURN(host, false);
2974 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2975 return true;
2976 }
2977
2978 bool RichEditorPattern::CursorMoveUp()
2979 {
2980 CloseSelectOverlay();
2981 ResetSelection();
2982 if (static_cast<int32_t>(GetTextContentLength()) > 1) {
2983 float caretHeight = 0.0f;
2984 OffsetF caretOffset = CalcCursorOffsetByPosition(GetCaretPosition(), caretHeight);
2985 auto minDet = paragraphs_.minParagraphFontSize.value_or(0.0f) / 2.0;
2986 int32_t caretPosition = paragraphs_.GetIndex(
2987 Offset(caretOffset.GetX() - GetTextRect().GetX(), caretOffset.GetY() - GetTextRect().GetY() - minDet));
2988 caretPosition = std::clamp(caretPosition, 0, static_cast<int32_t>(GetTextContentLength()));
2989 if (caretPosition_ == caretPosition) {
2990 return false;
2991 }
2992 SetCaretPosition(caretPosition);
2993 MoveCaretToContentRect();
2994 }
2995 StartTwinkling();
2996 auto host = GetHost();
2997 CHECK_NULL_RETURN(host, false);
2998 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2999 return true;
3000 }
3001
3002 bool RichEditorPattern::CursorMoveDown()
3003 {
3004 CloseSelectOverlay();
3005 ResetSelection();
3006 if (static_cast<int32_t>(GetTextContentLength()) > 1) {
3007 float caretHeight = 0.0f;
3008 OffsetF caretOffset = CalcCursorOffsetByPosition(GetCaretPosition(), caretHeight);
3009 auto minDet = paragraphs_.minParagraphFontSize.value() / 2.0;
3010 int32_t caretPosition = paragraphs_.GetIndex(Offset(caretOffset.GetX() - GetTextRect().GetX(),
3011 caretOffset.GetY() - GetTextRect().GetY() + caretHeight + minDet / 2.0));
3012 caretPosition = std::clamp(caretPosition, 0, static_cast<int32_t>(GetTextContentLength()));
3013 if (caretPosition_ == caretPosition) {
3014 return false;
3015 }
3016 SetCaretPosition(caretPosition);
3017 MoveCaretToContentRect();
3018 }
3019 StartTwinkling();
3020 auto host = GetHost();
3021 CHECK_NULL_RETURN(host, false);
3022 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3023 return true;
3024 }
3025
3026 bool RichEditorPattern::CursorMoveLeftWord()
3027 {
3028 CloseSelectOverlay();
3029 ResetSelection();
3030 auto newPos = GetLeftWordPosition(caretPosition_);
3031 if (newPos == caretPosition_) {
3032 return false;
3033 }
3034 SetCaretPosition(newPos);
3035 MoveCaretToContentRect();
3036 StartTwinkling();
3037 auto host = GetHost();
3038 CHECK_NULL_RETURN(host, false);
3039 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3040 return true;
3041 }
3042
3043 bool RichEditorPattern::CursorMoveRightWord()
3044 {
3045 CloseSelectOverlay();
3046 ResetSelection();
3047 auto newPos = GetRightWordPosition(caretPosition_);
3048 if (newPos == caretPosition_) {
3049 return false;
3050 }
3051 SetCaretPosition(newPos);
3052 MoveCaretToContentRect();
3053 StartTwinkling();
3054 auto host = GetHost();
3055 CHECK_NULL_RETURN(host, false);
3056 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3057 return true;
3058 }
3059
3060 bool RichEditorPattern::CursorMoveToParagraphBegin()
3061 {
3062 CloseSelectOverlay();
3063 ResetSelection();
3064 auto newPos = GetParagraphBeginPosition(caretPosition_);
3065 if (newPos == caretPosition_ && caretPosition_ > 0) {
3066 newPos = GetParagraphBeginPosition(caretPosition_ - 1);
3067 }
3068 if (newPos == caretPosition_) {
3069 return false;
3070 }
3071 SetCaretPosition(newPos);
3072 MoveCaretToContentRect();
3073 StartTwinkling();
3074 auto host = GetHost();
3075 CHECK_NULL_RETURN(host, false);
3076 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3077 return true;
3078 }
3079
3080 bool RichEditorPattern::CursorMoveToParagraphEnd()
3081 {
3082 CloseSelectOverlay();
3083 ResetSelection();
3084 auto newPos = GetParagraphEndPosition(caretPosition_);
3085 if (newPos == caretPosition_ && caretPosition_ < static_cast<int32_t>(GetTextContentLength())) {
3086 newPos = GetParagraphEndPosition(caretPosition_ + 1);
3087 }
3088 if (newPos == caretPosition_) {
3089 return false;
3090 }
3091 SetCaretPosition(newPos);
3092 MoveCaretToContentRect();
3093 StartTwinkling();
3094 auto host = GetHost();
3095 CHECK_NULL_RETURN(host, false);
3096 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3097 return true;
3098 }
3099
3100 bool RichEditorPattern::CursorMoveHome()
3101 {
3102 CloseSelectOverlay();
3103 ResetSelection();
3104 if (0 == caretPosition_) {
3105 return false;
3106 }
3107 SetCaretPosition(0);
3108 MoveCaretToContentRect();
3109 StartTwinkling();
3110 auto host = GetHost();
3111 CHECK_NULL_RETURN(host, false);
3112 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3113 return true;
3114 }
3115
3116 bool RichEditorPattern::CursorMoveEnd()
3117 {
3118 CloseSelectOverlay();
3119 ResetSelection();
3120 auto newPos = GetTextContentLength();
3121 if (newPos == caretPosition_) {
3122 return false;
3123 }
3124 SetCaretPosition(newPos);
3125 MoveCaretToContentRect();
3126 StartTwinkling();
3127 auto host = GetHost();
3128 CHECK_NULL_RETURN(host, false);
3129 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3130 return true;
3131 }
3132
3133 int32_t RichEditorPattern::GetLeftWordPosition(int32_t caretPosition)
3134 {
3135 int32_t offset = 0;
3136 bool jumpSpace = true;
3137 for (auto iter = spans_.rbegin(); iter != spans_.rend(); iter++) {
3138 auto span = *iter;
3139 auto content = StringUtils::ToWstring(span->content);
3140 if (caretPosition <= span->position - static_cast<int32_t>(content.length())) {
3141 continue;
3142 }
3143 int32_t position = span->position;
3144 for (auto iterContent = content.rbegin(); iterContent != content.rend(); iterContent++) {
3145 if (position-- > caretPosition) {
3146 continue;
3147 }
3148 if (*iterContent != L' ' || span->placeholderIndex >= 0) {
3149 jumpSpace = false;
3150 }
3151 if (position + 1 == caretPosition) {
3152 if (!(StringUtils::IsLetterOrNumberForWchar(*iterContent) ||
3153 (*iterContent == L' ' && span->placeholderIndex < 0))) {
3154 return std::clamp(caretPosition - 1, 0, static_cast<int32_t>(GetTextContentLength()));
3155 }
3156 }
3157 if (!jumpSpace) {
3158 if (!StringUtils::IsLetterOrNumberForWchar(*iterContent)) {
3159 return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
3160 }
3161 } else {
3162 if (*iterContent == L' ' && span->placeholderIndex >= 0) {
3163 return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
3164 }
3165 }
3166 offset++;
3167 }
3168 }
3169 return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
3170 }
3171
3172 int32_t RichEditorPattern::GetRightWordPosition(int32_t caretPosition)
3173 {
3174 int32_t offset = 0;
3175 bool jumpSpace = false;
3176 for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
3177 auto span = *iter;
3178 auto content = StringUtils::ToWstring(span->content);
3179 if (caretPosition > span->position) {
3180 continue;
3181 }
3182 int32_t position = span->position - static_cast<int32_t>(content.length());
3183 for (auto iterContent = content.cbegin(); iterContent != content.cend(); iterContent++) {
3184 if (position++ < caretPosition) {
3185 continue;
3186 }
3187 if (*iterContent == L' ' && span->placeholderIndex < 0) {
3188 jumpSpace = true;
3189 offset++;
3190 continue;
3191 }
3192 if (position - 1 == caretPosition) {
3193 if (!StringUtils::IsLetterOrNumberForWchar(*iterContent)) {
3194 return std::clamp(caretPosition + 1, 0, static_cast<int32_t>(GetTextContentLength()));
3195 }
3196 }
3197 if (jumpSpace) {
3198 if (*iterContent != L' ' || span->placeholderIndex >= 0) {
3199 return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
3200 }
3201 } else {
3202 if (!(StringUtils::IsLetterOrNumberForWchar(*iterContent) ||
3203 (*iterContent == L' ' && span->placeholderIndex < 0))) {
3204 return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
3205 }
3206 }
3207 offset++;
3208 }
3209 }
3210 return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
3211 }
3212
3213 int32_t RichEditorPattern::GetParagraphBeginPosition(int32_t caretPosition)
3214 {
3215 int32_t offset = 0;
3216 for (auto iter = spans_.rbegin(); iter != spans_.rend(); iter++) {
3217 auto span = *iter;
3218 auto content = StringUtils::ToWstring(span->content);
3219 if (caretPosition <= span->position - static_cast<int32_t>(content.length())) {
3220 continue;
3221 }
3222 int32_t position = span->position;
3223 for (auto iterContent = content.rbegin(); iterContent != content.rend(); iterContent++) {
3224 if (position-- > caretPosition) {
3225 continue;
3226 }
3227 if (*iterContent == L'\n') {
3228 return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
3229 }
3230 offset++;
3231 }
3232 }
3233 return std::clamp(caretPosition - offset, 0, static_cast<int32_t>(GetTextContentLength()));
3234 }
3235
3236 int32_t RichEditorPattern::GetParagraphEndPosition(int32_t caretPosition)
3237 {
3238 int32_t offset = 0;
3239 for (auto iter = spans_.cbegin(); iter != spans_.cend(); iter++) {
3240 auto span = *iter;
3241 auto content = StringUtils::ToWstring(span->content);
3242 if (caretPosition > span->position) {
3243 continue;
3244 }
3245 int32_t position = span->position - static_cast<int32_t>(content.length());
3246 for (auto iterContent = content.cbegin(); iterContent != content.cend(); iterContent++) {
3247 if (position++ < caretPosition) {
3248 continue;
3249 }
3250 if (*iterContent == L'\n') {
3251 return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
3252 }
3253 offset++;
3254 }
3255 }
3256 return std::clamp(caretPosition + offset, 0, static_cast<int32_t>(GetTextContentLength()));
3257 }
3258
3259 void RichEditorPattern::HandleOnSelectAll()
3260 {
3261 CloseSelectOverlay();
3262 auto host = GetHost();
3263 CHECK_NULL_VOID(host);
3264 int32_t newPos = static_cast<int32_t>(GetTextContentLength());
3265 textSelector_.Update(0, newPos);
3266 FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
3267 SetCaretPosition(newPos);
3268 MoveCaretToContentRect();
3269 StartTwinkling();
3270 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3271 }
3272
3273 void RichEditorPattern::HandleSelect(CaretMoveIntent direction)
3274 {
3275 CloseSelectOverlay();
3276 auto host = GetHost();
3277 CHECK_NULL_VOID(host);
3278 int32_t newPos, fixedPos = caretPosition_;
3279 if (IsSelected()) {
3280 fixedPos = (caretPosition_ == textSelector_.GetTextStart() ? textSelector_.GetTextEnd()
3281 : textSelector_.GetTextStart());
3282 }
3283 switch (direction) {
3284 case CaretMoveIntent::Left:
3285 newPos = caretPosition_ - 1;
3286 break;
3287 case CaretMoveIntent::Right:
3288 newPos = caretPosition_ + 1;
3289 break;
3290 case CaretMoveIntent::Up: {
3291 float caretHeight = 0.0f;
3292 OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight);
3293 auto minDet = paragraphs_.minParagraphFontSize.value() / 2.0;
3294 newPos = paragraphs_.GetIndex(Offset(caretOffset.GetX() - GetTextRect().GetX(),
3295 caretOffset.GetY() - GetTextRect().GetY() - minDet), true);
3296 break;
3297 }
3298 case CaretMoveIntent::Down: {
3299 float caretHeight = 0.0f;
3300 OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight);
3301 auto minDet = paragraphs_.minParagraphFontSize.value() / 2.0;
3302 newPos = paragraphs_.GetIndex(Offset(caretOffset.GetX() - GetTextRect().GetX(),
3303 caretOffset.GetY() - GetTextRect().GetY() + caretHeight + minDet / 2.0), true);
3304 break;
3305 }
3306 default:
3307 LOGW("Unsupported select operation for rich editor");
3308 return;
3309 }
3310 newPos = std::clamp(newPos, 0, static_cast<int32_t>(GetTextContentLength()));
3311 if (newPos == caretPosition_) {
3312 return;
3313 }
3314 textSelector_.Update(newPos, fixedPos);
3315 FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
3316 SetCaretPosition(newPos);
3317 MoveCaretToContentRect();
3318 StartTwinkling();
3319 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3320 }
3321
3322 void RichEditorPattern::ClearOperationRecords()
3323 {
3324 ClearRedoOperationRecords();
3325 if (operationRecords_.empty()) {
3326 return;
3327 }
3328 operationRecords_.clear();
3329 }
3330
3331 void RichEditorPattern::ClearRedoOperationRecords()
3332 {
3333 if (redoOperationRecords_.empty()) {
3334 return;
3335 }
3336 redoOperationRecords_.clear();
3337 }
3338
3339 void RichEditorPattern::AddOperationRecord(const OperationRecord& record)
3340 {
3341 if (operationRecords_.size() >= RECORD_MAX_LENGTH) {
3342 // case of max length is 0
3343 if (operationRecords_.empty()) {
3344 return;
3345 }
3346 operationRecords_.erase(operationRecords_.begin());
3347 }
3348 operationRecords_.emplace_back(record);
3349 }
3350
3351 bool RichEditorPattern::HandleOnEscape()
3352 {
3353 CloseSelectOverlay();
3354 return false;
3355 }
3356
3357 void RichEditorPattern::HandleOnUndoAction()
3358 {
3359 if (operationRecords_.empty()) {
3360 return;
3361 }
3362 auto value = operationRecords_.back();
3363 operationRecords_.pop_back();
3364 if (redoOperationRecords_.size() >= RECORD_MAX_LENGTH && !(redoOperationRecords_.empty())) {
3365 redoOperationRecords_.erase(redoOperationRecords_.begin());
3366 }
3367 redoOperationRecords_.push_back(value);
3368 CloseSelectOverlay();
3369 ResetSelection();
3370 if (value.addText.has_value() && value.deleteText.has_value()) {
3371 SetCaretPosition(value.afterCaretPosition);
3372 DeleteBackwardOperation(StringUtils::ToWstring(value.addText.value_or("")).length());
3373 InsertValueOperation(value.deleteText.value_or(""));
3374 return;
3375 }
3376 if (value.addText.has_value()) {
3377 SetCaretPosition(value.afterCaretPosition);
3378 DeleteBackwardOperation(StringUtils::ToWstring(value.addText.value_or("")).length());
3379 }
3380 if (value.deleteText.has_value()) {
3381 SetCaretPosition(value.afterCaretPosition);
3382 InsertValueOperation(value.deleteText.value_or(""));
3383 }
3384 }
3385
3386 void RichEditorPattern::HandleOnRedoAction()
3387 {
3388 if (redoOperationRecords_.empty()) {
3389 return;
3390 }
3391 auto value = redoOperationRecords_.back();
3392 redoOperationRecords_.pop_back();
3393 if (value.addText.has_value() && value.deleteText.has_value()) {
3394 SetCaretPosition(value.beforeCaretPosition);
3395 DeleteForwardOperation(StringUtils::ToWstring(value.deleteText.value_or("")).length());
3396 InsertValueOperation(value.addText.value_or(""));
3397 operationRecords_.push_back(value);
3398 return;
3399 }
3400 if (value.deleteText.has_value()) {
3401 SetCaretPosition(value.beforeCaretPosition);
3402 if (value.beforeCaretPosition != value.afterCaretPosition) {
3403 DeleteBackwardOperation(StringUtils::ToWstring(value.deleteText.value_or("")).length());
3404 } else {
3405 DeleteForwardOperation(StringUtils::ToWstring(value.deleteText.value_or("")).length());
3406 }
3407 }
3408 if (value.addText.has_value()) {
3409 SetCaretPosition(value.beforeCaretPosition);
3410 InsertValueOperation(value.addText.value_or(""));
3411 }
3412 operationRecords_.push_back(value);
3413 }
3414
3415 void RichEditorPattern::CalcInsertValueObj(TextInsertValueInfo& info)
3416 {
3417 if (spans_.empty()) {
3418 info.SetSpanIndex(0);
3419 info.SetOffsetInSpan(0);
3420 return;
3421 }
3422 auto it = std::find_if(
3423 spans_.begin(), spans_.end(), [caretPosition = caretPosition_ + moveLength_](const RefPtr<SpanItem>& spanItem) {
3424 auto spanLength = static_cast<int32_t>(StringUtils::ToWstring(spanItem->content).length());
3425 if (spanLength == 0) {
3426 return spanItem->position == caretPosition;
3427 }
3428 return (spanItem->position - spanLength <= caretPosition) && (caretPosition < spanItem->position);
3429 });
3430 if (it != spans_.end() && (*it)->unicode != 0 && (*it)->position - caretPosition_ + moveLength_ == 1) {
3431 it++;
3432 moveLength_++;
3433 }
3434 info.SetSpanIndex(std::distance(spans_.begin(), it));
3435 if (it == spans_.end()) {
3436 info.SetOffsetInSpan(0);
3437 return;
3438 }
3439 info.SetOffsetInSpan(
3440 caretPosition_ + moveLength_ - ((*it)->position - StringUtils::ToWstring((*it)->content).length()));
3441 }
3442
3443 void RichEditorPattern::CalcDeleteValueObj(int32_t currentPosition, int32_t length, RichEditorDeleteValue& info)
3444 {
3445 auto it =
3446 std::find_if(spans_.begin(), spans_.end(), [caretPosition = currentPosition](const RefPtr<SpanItem>& spanItem) {
3447 return (spanItem->position - static_cast<int32_t>(StringUtils::ToWstring(spanItem->content).length()) <=
3448 caretPosition) &&
3449 (caretPosition < spanItem->position);
3450 });
3451 while (it != spans_.end() && length > 0) {
3452 if ((*it)->placeholderIndex >= 0 || (*it)->unicode != 0) {
3453 RichEditorAbstractSpanResult spanResult;
3454 spanResult.SetSpanIndex(std::distance(spans_.begin(), it));
3455 int32_t eraseLength = 0;
3456 if ((*it)->unicode != 0) {
3457 eraseLength = DeleteValueSetSymbolSpan(*it, spanResult);
3458 } else if (AceType::InstanceOf<ImageSpanItem>(*it)) {
3459 eraseLength = DeleteValueSetImageSpan(*it, spanResult);
3460 } else {
3461 eraseLength = DeleteValueSetBuilderSpan(*it, spanResult);
3462 }
3463 currentPosition += eraseLength;
3464 length -= eraseLength;
3465 info.SetRichEditorDeleteSpans(spanResult);
3466 } else {
3467 RichEditorAbstractSpanResult spanResult;
3468 spanResult.SetSpanIndex(std::distance(spans_.begin(), it));
3469 auto eraseLength = DeleteValueSetTextSpan(*it, currentPosition, length, spanResult);
3470 length -= eraseLength;
3471 currentPosition += eraseLength;
3472 info.SetRichEditorDeleteSpans(spanResult);
3473 }
3474 std::advance(it, 1);
3475 }
3476 }
3477
3478 int32_t RichEditorPattern::DeleteValueSetSymbolSpan(
3479 const RefPtr<SpanItem>& spanItem, RichEditorAbstractSpanResult& spanResult)
3480 {
3481 spanResult.SetSpanType(SpanResultType::SYMBOL);
3482 spanResult.SetSpanRangeEnd(spanItem->position);
3483 spanResult.SetSpanRangeStart(spanItem->position - SYMBOL_SPAN_LENGTH);
3484 if (GetCaretPosition() < spanItem->position) {
3485 spanResult.SetEraseLength(1);
3486 } else {
3487 spanResult.SetEraseLength(SYMBOL_SPAN_LENGTH);
3488 }
3489 return SYMBOL_SPAN_LENGTH;
3490 }
3491
3492 int32_t RichEditorPattern::DeleteValueSetImageSpan(
3493 const RefPtr<SpanItem>& spanItem, RichEditorAbstractSpanResult& spanResult)
3494 {
3495 spanResult.SetSpanType(SpanResultType::IMAGE);
3496 spanResult.SetSpanRangeEnd(spanItem->position);
3497 spanResult.SetSpanRangeStart(spanItem->position - 1);
3498 spanResult.SetEraseLength(1);
3499 auto host = GetHost();
3500 CHECK_NULL_RETURN(host, IMAGE_SPAN_LENGTH);
3501 auto uiNode = host->GetChildAtIndex(spanResult.GetSpanIndex());
3502 CHECK_NULL_RETURN(uiNode, IMAGE_SPAN_LENGTH);
3503 auto imageNode = AceType::DynamicCast<FrameNode>(uiNode);
3504 CHECK_NULL_RETURN(imageNode, IMAGE_SPAN_LENGTH);
3505 auto geometryNode = imageNode->GetGeometryNode();
3506 CHECK_NULL_RETURN(geometryNode, IMAGE_SPAN_LENGTH);
3507 auto imageLayoutProperty = DynamicCast<ImageLayoutProperty>(imageNode->GetLayoutProperty());
3508 CHECK_NULL_RETURN(imageLayoutProperty, IMAGE_SPAN_LENGTH);
3509 spanResult.SetSizeWidth(geometryNode->GetMarginFrameSize().Width());
3510 spanResult.SetSizeHeight(geometryNode->GetMarginFrameSize().Height());
3511 if (!imageLayoutProperty->GetImageSourceInfo()->GetPixmap()) {
3512 spanResult.SetValueResourceStr(imageLayoutProperty->GetImageSourceInfo()->GetSrc());
3513 } else {
3514 spanResult.SetValuePixelMap(imageLayoutProperty->GetImageSourceInfo()->GetPixmap());
3515 }
3516 if (imageLayoutProperty->HasImageFit()) {
3517 spanResult.SetImageFit(imageLayoutProperty->GetImageFitValue());
3518 }
3519 if (imageLayoutProperty->HasVerticalAlign()) {
3520 spanResult.SetVerticalAlign(imageLayoutProperty->GetVerticalAlignValue());
3521 }
3522 return IMAGE_SPAN_LENGTH;
3523 }
3524
3525 int32_t RichEditorPattern::DeleteValueSetBuilderSpan(
3526 const RefPtr<SpanItem>& spanItem, RichEditorAbstractSpanResult& spanResult)
3527 {
3528 spanResult.SetSpanType(SpanResultType::IMAGE);
3529 spanResult.SetSpanRangeEnd(spanItem->position);
3530 spanResult.SetSpanRangeStart(spanItem->position - 1);
3531 spanResult.SetEraseLength(1);
3532 auto host = GetHost();
3533 CHECK_NULL_RETURN(host, 1);
3534 auto uiNode = host->GetChildAtIndex(spanResult.GetSpanIndex());
3535 CHECK_NULL_RETURN(uiNode, 1);
3536 auto builderNode = AceType::DynamicCast<FrameNode>(uiNode);
3537 CHECK_NULL_RETURN(builderNode, 1);
3538 auto geometryNode = builderNode->GetGeometryNode();
3539 CHECK_NULL_RETURN(geometryNode, 1);
3540 spanResult.SetSizeWidth(geometryNode->GetMarginFrameSize().Width());
3541 spanResult.SetSizeHeight(geometryNode->GetMarginFrameSize().Height());
3542 return 1;
3543 }
3544
3545 int32_t RichEditorPattern::DeleteValueSetTextSpan(
3546 const RefPtr<SpanItem>& spanItem, int32_t currentPosition, int32_t length, RichEditorAbstractSpanResult& spanResult)
3547 {
3548 spanResult.SetSpanType(SpanResultType::TEXT);
3549 auto contentStartPosition = spanItem->position - StringUtils::ToWstring(spanItem->content).length();
3550 spanResult.SetSpanRangeStart(contentStartPosition);
3551 int32_t eraseLength = 0;
3552 if (spanItem->position - currentPosition >= length) {
3553 eraseLength = length;
3554 } else {
3555 eraseLength = spanItem->position - currentPosition;
3556 }
3557 spanResult.SetSpanRangeEnd(spanItem->position);
3558 spanResult.SetValue(spanItem->content);
3559 spanResult.SetOffsetInSpan(currentPosition - contentStartPosition);
3560 spanResult.SetEraseLength(eraseLength);
3561 spanResult.SetFontColor(spanItem->GetTextStyle()->GetTextColor().ColorToString());
3562 spanResult.SetFontSize(spanItem->GetTextStyle()->GetFontSize().Value());
3563 spanResult.SetFontStyle(spanItem->GetTextStyle()->GetFontStyle());
3564 spanResult.SetFontWeight((int32_t)(spanItem->GetTextStyle()->GetFontWeight()));
3565 if (!spanItem->GetTextStyle()->GetFontFamilies().empty()) {
3566 spanResult.SetFontFamily(spanItem->GetTextStyle()->GetFontFamilies().at(0));
3567 }
3568 spanResult.SetColor(spanItem->GetTextStyle()->GetTextDecorationColor().ColorToString());
3569 spanResult.SetTextDecoration(spanItem->GetTextStyle()->GetTextDecoration());
3570 return eraseLength;
3571 }
3572
3573 void RichEditorPattern::DeleteByDeleteValueInfo(const RichEditorDeleteValue& info)
3574 {
3575 auto deleteSpans = info.GetRichEditorDeleteSpans();
3576 auto host = GetHost();
3577 CHECK_NULL_VOID(host);
3578 std::list<RefPtr<UINode>> deleteNode;
3579 std::set<int32_t, std::greater<int32_t>> deleteNodes;
3580 auto caretMoveLength = 0;
3581 for (const auto& it : deleteSpans) {
3582 caretMoveLength += it.GetEraseLength();
3583 switch (it.GetType()) {
3584 case SpanResultType::TEXT: {
3585 auto ui_node = host->GetChildAtIndex(it.GetSpanIndex());
3586 CHECK_NULL_VOID(ui_node);
3587 auto spanNode = DynamicCast<SpanNode>(ui_node);
3588 CHECK_NULL_VOID(spanNode);
3589 auto spanItem = spanNode->GetSpanItem();
3590 CHECK_NULL_VOID(spanItem);
3591 auto text = spanItem->content;
3592 std::wstring textTemp = StringUtils::ToWstring(text);
3593 textTemp.erase(it.OffsetInSpan(), it.GetEraseLength());
3594 if (textTemp.size() == 0) {
3595 deleteNodes.emplace(it.GetSpanIndex());
3596 }
3597 text = StringUtils::ToString(textTemp);
3598 spanNode->UpdateContent(text);
3599 spanItem->position -= it.GetEraseLength();
3600 break;
3601 }
3602 case SpanResultType::IMAGE:
3603 deleteNodes.emplace(it.GetSpanIndex());
3604 break;
3605 case SpanResultType::SYMBOL:
3606 deleteNodes.emplace(it.GetSpanIndex());
3607 break;
3608 default:
3609 break;
3610 }
3611 }
3612 for (auto index : deleteNodes) {
3613 host->RemoveChildAtIndex(index);
3614 }
3615 if (info.GetRichEditorDeleteDirection() == RichEditorDeleteDirection::BACKWARD) {
3616 SetCaretPosition(std::clamp(caretPosition_ - caretMoveLength, 0, static_cast<int32_t>(GetTextContentLength())));
3617 }
3618 UpdateSpanPosition();
3619 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
3620 OnModifyDone();
3621 }
3622
3623 bool RichEditorPattern::OnKeyEvent(const KeyEvent& keyEvent)
3624 {
3625 return TextInputClient::HandleKeyEvent(keyEvent);
3626 }
3627
3628 void RichEditorPattern::CursorMove(CaretMoveIntent direction)
3629 {
3630 switch (direction) {
3631 case CaretMoveIntent::Left:
3632 CursorMoveLeft();
3633 break;
3634 case CaretMoveIntent::Right:
3635 CursorMoveRight();
3636 break;
3637 case CaretMoveIntent::Up:
3638 CursorMoveUp();
3639 break;
3640 case CaretMoveIntent::Down:
3641 CursorMoveDown();
3642 break;
3643 case CaretMoveIntent::LeftWord:
3644 CursorMoveLeftWord();
3645 break;
3646 case CaretMoveIntent::RightWord:
3647 CursorMoveRightWord();
3648 break;
3649 case CaretMoveIntent::ParagraghBegin:
3650 CursorMoveToParagraphBegin();
3651 break;
3652 case CaretMoveIntent::ParagraghEnd:
3653 CursorMoveToParagraphEnd();
3654 break;
3655 case CaretMoveIntent::Home:
3656 CursorMoveHome();
3657 break;
3658 case CaretMoveIntent::End:
3659 CursorMoveEnd();
3660 break;
3661 default:
3662 LOGW("Unsupported cursor move operation for rich editor");
3663 }
3664 }
3665
3666 void RichEditorPattern::MoveCaretAfterTextChange()
3667 {
3668 CHECK_NULL_VOID(isTextChange_);
3669 isTextChange_ = false;
3670 switch (moveDirection_) {
3671 case MoveDirection::BACKWARD:
3672 SetCaretPosition(
3673 std::clamp((caretPosition_ - moveLength_), 0, static_cast<int32_t>(GetTextContentLength())));
3674 break;
3675 case MoveDirection::FORWARD:
3676 SetCaretPosition(
3677 std::clamp((caretPosition_ + moveLength_), 0, static_cast<int32_t>(GetTextContentLength())));
3678 break;
3679 default:
3680 break;
3681 }
3682 moveLength_ = 0;
3683 }
3684
3685 void RichEditorPattern::InitTouchEvent()
3686 {
3687 CHECK_NULL_VOID(!touchListener_);
3688 auto host = GetHost();
3689 CHECK_NULL_VOID(host);
3690
3691 auto gesture = host->GetOrCreateGestureEventHub();
3692 CHECK_NULL_VOID(gesture);
3693 auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
3694 auto pattern = weak.Upgrade();
3695 CHECK_NULL_VOID(pattern);
3696 pattern->HandleTouchEvent(info);
3697 };
3698 touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
3699 gesture->AddTouchEvent(touchListener_);
3700 }
3701
3702 void RichEditorPattern::HandleTouchEvent(const TouchEventInfo& info)
3703 {
3704 if (SelectOverlayIsOn()) {
3705 return;
3706 }
3707 auto touchType = info.GetTouches().front().GetTouchType();
3708 if (touchType == TouchType::DOWN) {
3709 } else if (touchType == TouchType::UP) {
3710 isMousePressed_ = false;
3711 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
3712 if (isLongPress_) {
3713 isLongPress_ = false;
3714 }
3715 #endif
3716 }
3717 }
3718
3719 bool RichEditorPattern::IsScrollBarPressed(const MouseInfo& info)
3720 {
3721 auto scrollBar = GetScrollBar();
3722 Point point(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY());
3723 return scrollBar->InBarTouchRegion(point);
3724 }
3725
3726 void RichEditorPattern::HandleMouseLeftButtonMove(const MouseInfo& info)
3727 {
3728 if (blockPress_ || !leftMousePress_) {
3729 return;
3730 }
3731 auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
3732 Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
3733 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
3734
3735 mouseStatus_ = MouseStatus::MOVE;
3736 if (isFirstMouseSelect_) {
3737 int32_t extend = paragraphs_.GetIndex(textOffset);
3738 textSelector_.Update(textSelector_.baseOffset, extend);
3739 isFirstMouseSelect_ = false;
3740 } else {
3741 int32_t extend = paragraphs_.GetIndex(textOffset);
3742 textSelector_.Update(textSelector_.baseOffset, extend);
3743 auto position = paragraphs_.GetIndex(textOffset);
3744 AdjustCursorPosition(position);
3745 SetCaretPosition(position);
3746 AutoScrollParam param = {
3747 .autoScrollEvent = AutoScrollEvent::MOUSE, .showScrollbar = true, .eventOffset = info.GetLocalLocation()
3748 };
3749 AutoScrollByEdgeDetection(param, OffsetF(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY()),
3750 EdgeDetectionStrategy::OUT_BOUNDARY);
3751 }
3752
3753 isMouseSelect_ = true;
3754 auto host = GetHost();
3755 CHECK_NULL_VOID(host);
3756 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3757 }
3758
3759 void RichEditorPattern::HandleMouseLeftButtonPress(const MouseInfo& info)
3760 {
3761 isMousePressed_ = true;
3762 if (IsScrollBarPressed(info) || BetweenSelectedPosition(info.GetGlobalLocation())) {
3763 blockPress_ = true;
3764 return;
3765 }
3766 auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
3767 Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
3768 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
3769 int32_t extend = paragraphs_.GetIndex(textOffset);
3770 textSelector_.Update(extend);
3771 leftMousePress_ = true;
3772 mouseStatus_ = MouseStatus::PRESSED;
3773 blockPress_ = false;
3774 caretUpdateType_ = CaretUpdateType::PRESSED;
3775 UseHostToUpdateTextFieldManager();
3776
3777 auto position = paragraphs_.GetIndex(textOffset);
3778 AdjustCursorPosition(position);
3779 auto focusHub = GetHost()->GetOrCreateFocusHub();
3780 if (focusHub && focusHub->RequestFocusImmediately()) {
3781 float caretHeight = 0.0f;
3782 SetCaretPosition(position);
3783 OffsetF caretOffset = CalcCursorOffsetByPosition(GetCaretPosition(), caretHeight, false, false);
3784 MoveCaretToContentRect();
3785 CHECK_NULL_VOID(overlayMod_);
3786 DynamicCast<RichEditorOverlayModifier>(overlayMod_)->SetCaretOffsetAndHeight(caretOffset, caretHeight);
3787 StartTwinkling();
3788 if (overlayMod_) {
3789 RequestKeyboard(false, true, true);
3790 }
3791 }
3792 UseHostToUpdateTextFieldManager();
3793 }
3794
3795 void RichEditorPattern::HandleMouseLeftButtonRelease(const MouseInfo& info)
3796 {
3797 blockPress_ = false;
3798 leftMousePress_ = false;
3799 auto oldMouseStatus = mouseStatus_;
3800 mouseStatus_ = MouseStatus::RELEASED;
3801 isMouseSelect_ = false;
3802 isMousePressed_ = false;
3803 isFirstMouseSelect_ = true;
3804 auto selectStart = std::min(textSelector_.baseOffset, textSelector_.destinationOffset);
3805 auto selectEnd = std::max(textSelector_.baseOffset, textSelector_.destinationOffset);
3806 if (selectStart != selectEnd) {
3807 FireOnSelect(selectStart, selectEnd);
3808 }
3809 StopAutoScroll();
3810 if (textSelector_.IsValid() && !textSelector_.StartEqualToDest() && IsSelectedBindSelectionMenu() &&
3811 oldMouseStatus == MouseStatus::MOVE) {
3812 selectionMenuOffsetByMouse_ = OffsetF(static_cast<float>(info.GetGlobalLocation().GetX()),
3813 static_cast<float>(info.GetGlobalLocation().GetY()));
3814 ShowSelectOverlay(RectF(), RectF(), false, TextResponseType::SELECTED_BY_MOUSE);
3815 }
3816 }
3817
3818 void RichEditorPattern::HandleMouseLeftButton(const MouseInfo& info)
3819 {
3820 if (info.GetAction() == MouseAction::MOVE) {
3821 HandleMouseLeftButtonMove(info);
3822 } else if (info.GetAction() == MouseAction::PRESS) {
3823 HandleMouseLeftButtonPress(info);
3824 } else if (info.GetAction() == MouseAction::RELEASE) {
3825 HandleMouseLeftButtonRelease(info);
3826 }
3827 }
3828
3829 void RichEditorPattern::HandleMouseRightButton(const MouseInfo& info)
3830 {
3831 if (info.GetAction() == MouseAction::PRESS) {
3832 isMousePressed_ = true;
3833 usingMouseRightButton_ = true;
3834 CloseSelectionMenu();
3835 } else if (info.GetAction() == MouseAction::RELEASE) {
3836 selectionMenuOffsetByMouse_ = OffsetF(
3837 static_cast<float>(info.GetGlobalLocation().GetX()), static_cast<float>(info.GetGlobalLocation().GetY()));
3838 if (textSelector_.IsValid() && BetweenSelectedPosition(info.GetGlobalLocation())) {
3839 ShowSelectOverlay(RectF(), RectF(), IsSelectAll(), TextResponseType::RIGHT_CLICK);
3840 isMousePressed_ = false;
3841 usingMouseRightButton_ = false;
3842 return;
3843 }
3844 if (textSelector_.IsValid()) {
3845 CloseSelectOverlay();
3846 ResetSelection();
3847 }
3848 MouseRightFocus(info);
3849 ShowSelectOverlay(RectF(), RectF(), IsSelectAll(), TextResponseType::RIGHT_CLICK);
3850 isMousePressed_ = false;
3851 usingMouseRightButton_ = false;
3852 }
3853 }
3854
3855 void RichEditorPattern::MouseRightFocus(const MouseInfo& info)
3856 {
3857 auto textRect = GetTextRect();
3858 textRect.SetTop(textRect.GetY() - std::min(baselineOffset_, 0.0f));
3859 textRect.SetHeight(textRect.Height() - std::max(baselineOffset_, 0.0f));
3860 Offset textOffset = { info.GetLocalLocation().GetX() - textRect.GetX(),
3861 info.GetLocalLocation().GetY() - textRect.GetY() };
3862 InitSelection(textOffset);
3863 auto selectStart = std::min(textSelector_.baseOffset, textSelector_.destinationOffset);
3864 auto selectEnd = std::max(textSelector_.baseOffset, textSelector_.destinationOffset);
3865 auto host = GetHost();
3866 CHECK_NULL_VOID(host);
3867 auto focusHub = host->GetOrCreateFocusHub();
3868 CHECK_NULL_VOID(focusHub);
3869 focusHub->RequestFocusImmediately();
3870 SetCaretPosition(selectEnd);
3871
3872 TextInsertValueInfo spanInfo;
3873 CalcInsertValueObj(spanInfo);
3874 auto spanNode = DynamicCast<FrameNode>(GetChildByIndex(spanInfo.GetSpanIndex() - 1));
3875 if (spanNode && spanNode->GetTag() == V2::IMAGE_ETS_TAG && spanInfo.GetOffsetInSpan() == 0 &&
3876 selectEnd == selectStart + 1 && BetweenSelectedPosition(info.GetGlobalLocation())) {
3877 selectedType_ = TextSpanType::IMAGE;
3878 FireOnSelect(selectStart, selectEnd);
3879 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3880 return;
3881 }
3882 if (textSelector_.IsValid()) {
3883 ResetSelection();
3884 }
3885 auto position = paragraphs_.GetIndex(textOffset);
3886 float caretHeight = 0.0f;
3887 OffsetF caretOffset = CalcCursorOffsetByPosition(GetCaretPosition(), caretHeight);
3888 SetCaretPosition(position);
3889 selectedType_ = TextSpanType::TEXT;
3890 CHECK_NULL_VOID(overlayMod_);
3891 DynamicCast<RichEditorOverlayModifier>(overlayMod_)->SetCaretOffsetAndHeight(caretOffset, caretHeight);
3892 StartTwinkling();
3893 }
3894
3895 void RichEditorPattern::FireOnSelect(int32_t selectStart, int32_t selectEnd)
3896 {
3897 auto host = GetHost();
3898 CHECK_NULL_VOID(host);
3899 auto eventHub = host->GetEventHub<RichEditorEventHub>();
3900 CHECK_NULL_VOID(eventHub);
3901 auto textSelectInfo = GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT);
3902 if (!textSelectInfo.GetSelection().resultObjects.empty()) {
3903 eventHub->FireOnSelect(&textSelectInfo);
3904 }
3905 UpdateSelectionType(textSelectInfo);
3906 }
3907
3908 void RichEditorPattern::HandleMouseEvent(const MouseInfo& info)
3909 {
3910 auto tmpHost = GetHost();
3911 CHECK_NULL_VOID(tmpHost);
3912 auto frameId = tmpHost->GetId();
3913 auto pipeline = PipelineContext::GetCurrentContext();
3914 CHECK_NULL_VOID(pipeline);
3915 auto scrollBar = GetScrollBar();
3916 if (scrollBar && (scrollBar->IsHover() || scrollBar->IsPressed())) {
3917 pipeline->SetMouseStyleHoldNode(frameId);
3918 pipeline->ChangeMouseStyle(frameId, MouseFormat::DEFAULT);
3919 return;
3920 }
3921
3922 caretUpdateType_ = CaretUpdateType::NONE;
3923 if (info.GetButton() == MouseButton::LEFT_BUTTON) {
3924 HandleMouseLeftButton(info);
3925 } else if (info.GetButton() == MouseButton::RIGHT_BUTTON) {
3926 HandleMouseRightButton(info);
3927 }
3928 }
3929
3930 void RichEditorPattern::OnHandleMoveDone(const RectF& handleRect, bool isFirstHandle)
3931 {
3932 auto host = GetHost();
3933 CHECK_NULL_VOID(host);
3934 auto eventHub = host->GetEventHub<RichEditorEventHub>();
3935 CHECK_NULL_VOID(eventHub);
3936 auto selectStart = std::min(textSelector_.baseOffset, textSelector_.destinationOffset);
3937 auto selectEnd = std::max(textSelector_.baseOffset, textSelector_.destinationOffset);
3938 auto textSelectInfo = GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT);
3939 if (!textSelectInfo.GetSelection().resultObjects.empty()) {
3940 eventHub->FireOnSelect(&textSelectInfo);
3941 }
3942 UpdateSelectionType(textSelectInfo);
3943 SetCaretPosition(selectEnd);
3944 CalculateHandleOffsetAndShowOverlay();
3945 StopAutoScroll();
3946 if (selectOverlayProxy_) {
3947 SelectHandleInfo handleInfo;
3948 if (!selectOverlayProxy_->IsSingleHandle() && textSelector_.firstHandle == textSelector_.secondHandle) {
3949 CloseSelectOverlay();
3950 StartTwinkling();
3951 return;
3952 }
3953 if (isFirstHandle) {
3954 handleInfo.paintRect = textSelector_.firstHandle;
3955 CheckHandles(handleInfo);
3956 selectOverlayProxy_->UpdateFirstSelectHandleInfo(handleInfo);
3957 } else {
3958 handleInfo.paintRect = textSelector_.secondHandle;
3959 CheckHandles(handleInfo);
3960 selectOverlayProxy_->UpdateSecondSelectHandleInfo(handleInfo);
3961 }
3962
3963 if (IsSelectAll() && selectMenuInfo_.showCopyAll == true) {
3964 selectMenuInfo_.showCopyAll = false;
3965 selectOverlayProxy_->UpdateSelectMenuInfo(selectMenuInfo_);
3966 } else if (!IsSelectAll() && selectMenuInfo_.showCopyAll == false) {
3967 selectMenuInfo_.showCopyAll = true;
3968 selectOverlayProxy_->UpdateSelectMenuInfo(selectMenuInfo_);
3969 }
3970 auto handleReverse = selectOverlayProxy_->IsHandleReverse();
3971 selectOverlayProxy_.Reset();
3972 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, IsSelectAll(),
3973 TextResponseType::LONG_PRESS, handleReverse);
3974 return;
3975 }
3976 ShowSelectOverlay(
3977 textSelector_.firstHandle, textSelector_.secondHandle, IsSelectAll(), TextResponseType::LONG_PRESS);
3978 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3979 }
3980
3981 void RichEditorPattern::CopySelectionMenuParams(SelectOverlayInfo& selectInfo, TextResponseType responseType)
3982 {
3983 auto selectType = selectedType_.value_or(TextSpanType::NONE);
3984 std::shared_ptr<SelectionMenuParams> menuParams = nullptr;
3985 menuParams = GetMenuParams(selectType, responseType);
3986 if (menuParams == nullptr) {
3987 return;
3988 }
3989
3990 // long pressing on the image needs to set the position of the pop-up menu following the long pressing position
3991 if (selectType == TextSpanType::IMAGE && !selectInfo.isUsingMouse) {
3992 selectInfo.menuInfo.menuOffset = OffsetF(selectionMenuOffset_.GetX(), selectionMenuOffset_.GetY());
3993 }
3994
3995 CopyBindSelectionMenuParams(selectInfo, menuParams);
3996 }
3997
3998 void RichEditorPattern::ShowSelectOverlay(const RectF& firstHandle, const RectF& secondHandle, bool isCopyAll,
3999 TextResponseType responseType, bool handleReverse)
4000 {
4001 auto pipeline = PipelineContext::GetCurrentContext();
4002 CHECK_NULL_VOID(pipeline);
4003 showSelect_ = true;
4004 auto hasDataCallback = [weak = WeakClaim(this), handleReverse, pipeline, firstHandle, secondHandle, isCopyAll,
4005 responseType](bool hasData) mutable {
4006 auto pattern = weak.Upgrade();
4007 SelectOverlayInfo selectInfo;
4008 selectInfo.handleReverse = handleReverse;
4009 bool usingMouse = pattern->IsUsingMouse();
4010 if (!pattern->IsUsingMouse() && responseType == TextResponseType::LONG_PRESS) {
4011 selectInfo.firstHandle.paintRect = firstHandle;
4012 selectInfo.secondHandle.paintRect = secondHandle;
4013 } else {
4014 if (responseType == TextResponseType::LONG_PRESS) {
4015 responseType = TextResponseType::RIGHT_CLICK;
4016 }
4017 selectInfo.isUsingMouse = true;
4018 selectInfo.rightClickOffset = pattern->GetSelectionMenuOffset();
4019 pattern->ResetIsMousePressed();
4020 }
4021 selectInfo.menuInfo.responseType = static_cast<int32_t>(responseType);
4022 selectInfo.menuInfo.editorType = static_cast<int32_t>(pattern->GetEditorType());
4023 selectInfo.onHandleMove = [weak](const RectF& handleRect, bool isFirst) {
4024 auto pattern = weak.Upgrade();
4025 CHECK_NULL_VOID(pattern);
4026 pattern->OnHandleMove(handleRect, isFirst);
4027 };
4028 selectInfo.onHandleMoveDone = [weak](const RectF& handleRect, bool isFirst) {
4029 auto pattern = weak.Upgrade();
4030 CHECK_NULL_VOID(pattern);
4031 pattern->OnHandleMoveDone(handleRect, isFirst);
4032 };
4033
4034 auto host = pattern->GetHost();
4035 CHECK_NULL_VOID(host);
4036
4037 selectInfo.menuCallback.onCopy = [weak, usingMouse]() {
4038 auto pattern = weak.Upgrade();
4039 CHECK_NULL_VOID(pattern);
4040 pattern->HandleOnCopy();
4041 pattern->CloseSelectOverlay();
4042 if (!usingMouse) {
4043 pattern->ResetSelection();
4044 }
4045 };
4046
4047 selectInfo.menuCallback.onCut = [weak]() {
4048 auto pattern = weak.Upgrade();
4049 CHECK_NULL_VOID(pattern);
4050 pattern->HandleOnCut();
4051 };
4052
4053 selectInfo.menuCallback.onPaste = [weak]() {
4054 auto pattern = weak.Upgrade();
4055 CHECK_NULL_VOID(pattern);
4056 pattern->HandleOnPaste();
4057 pattern->CloseSelectOverlay();
4058 };
4059 selectInfo.menuCallback.onSelectAll = [weak, usingMouse]() {
4060 auto pattern = weak.Upgrade();
4061 CHECK_NULL_VOID(pattern);
4062 pattern->isMousePressed_ = usingMouse;
4063 pattern->HandleMenuCallbackOnSelectAll();
4064 };
4065
4066 selectInfo.menuCallback.onCameraInput = [weak, usingMouse]() {
4067 auto pattern = weak.Upgrade();
4068 CHECK_NULL_VOID(pattern);
4069 pattern->HandleOnCameraInput();
4070 };
4071
4072 if (pattern->GetTextDetectEnable() && !pattern->HasFocus()) {
4073 selectInfo.onClose = [weak](bool closedByGlobalEvent) {
4074 auto pattern = weak.Upgrade();
4075 CHECK_NULL_VOID(pattern);
4076 if (closedByGlobalEvent) {
4077 pattern->ResetSelection();
4078 }
4079 };
4080 }
4081 selectInfo.callerFrameNode = host;
4082 selectInfo.isNewAvoid = true;
4083 selectInfo.selectArea = pattern->GetSelectArea();
4084 selectInfo.checkIsTouchInHostArea = [weak](const PointF& touchPoint) -> bool {
4085 auto pattern = weak.Upgrade();
4086 CHECK_NULL_RETURN(pattern, false);
4087 return pattern->IsTouchInFrameArea(touchPoint);
4088 };
4089 pattern->CopySelectionMenuParams(selectInfo, responseType);
4090 pattern->UpdateSelectMenuInfo(hasData, selectInfo, isCopyAll);
4091 pattern->CheckEditorTypeChange();
4092 pattern->UpdateSelectOverlayOrCreate(selectInfo);
4093 };
4094 CHECK_NULL_VOID(clipboard_);
4095 clipboard_->HasData(hasDataCallback);
4096 }
4097
4098 void RichEditorPattern::UpdateSelectOverlayOrCreate(SelectOverlayInfo& selectInfo, bool animation)
4099 {
4100 bool isOriginMenuShow = true;
4101 if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
4102 isOriginMenuShow = GetOriginIsMenuShow();
4103 }
4104 TextPattern::UpdateSelectOverlayOrCreate(selectInfo, animation);
4105 CHECK_NULL_VOID(selectOverlayProxy_);
4106 selectOverlayProxy_->ShowOrHiddenMenu(!isOriginMenuShow || !isShowMenu_);
4107 }
4108
4109 void RichEditorPattern::CheckEditorTypeChange()
4110 {
4111 CHECK_NULL_VOID(selectOverlayProxy_);
4112 CHECK_NULL_VOID(!selectOverlayProxy_->IsClosed());
4113 if (selectOverlayProxy_->GetSelectOverlayMangerInfo().menuInfo.editorType.value_or(static_cast<int32_t>(
4114 TextSpanType::NONE)) != static_cast<int32_t>(selectedType_.value_or(TextSpanType::NONE))) {
4115 CloseSelectionMenu();
4116 }
4117 }
4118
4119 void RichEditorPattern::HandleOnCopy(bool isUsingExternalKeyboard)
4120 {
4121 CHECK_NULL_VOID(clipboard_);
4122 if (copyOption_ == CopyOptions::None) {
4123 return;
4124 }
4125 auto selectStart = textSelector_.GetTextStart();
4126 auto selectEnd = textSelector_.GetTextEnd();
4127 auto textSelectInfo = GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT);
4128 auto copyResultObjects = textSelectInfo.GetSelection().resultObjects;
4129 caretUpdateType_ = CaretUpdateType::NONE;
4130 if (copyResultObjects.empty()) {
4131 return;
4132 }
4133 RefPtr<PasteDataMix> pasteData = clipboard_->CreatePasteDataMix();
4134 auto resultProcessor = [weak = WeakClaim(this), pasteData, selectStart, selectEnd, clipboard = clipboard_](
4135 const ResultObject& result) {
4136 auto pattern = weak.Upgrade();
4137 CHECK_NULL_VOID(pattern);
4138 if (result.type == SelectSpanType::TYPESPAN) {
4139 auto data = pattern->GetSelectedSpanText(StringUtils::ToWstring(result.valueString),
4140 result.offsetInSpan[RichEditorSpanRange::RANGESTART],
4141 result.offsetInSpan[RichEditorSpanRange::RANGEEND]);
4142 clipboard->AddTextRecord(pasteData, data);
4143 return;
4144 }
4145 if (result.type == SelectSpanType::TYPEIMAGE) {
4146 if (result.valuePixelMap) {
4147 clipboard->AddPixelMapRecord(pasteData, result.valuePixelMap);
4148 } else {
4149 clipboard->AddImageRecord(pasteData, result.valueString);
4150 }
4151 }
4152 };
4153 for (auto resultObj = copyResultObjects.rbegin(); resultObj != copyResultObjects.rend(); ++resultObj) {
4154 resultProcessor(*resultObj);
4155 }
4156 clipboard_->SetData(pasteData, copyOption_);
4157 if (!textDetectEnable_) {
4158 StartTwinkling();
4159 }
4160 }
4161
4162 void RichEditorPattern::ResetAfterPaste()
4163 {
4164 OperationRecord record;
4165 record.beforeCaretPosition = caretPosition_ + moveLength_;
4166 auto pasteStr = GetPasteStr();
4167 record.addText = pasteStr;
4168 SetCaretSpanIndex(-1);
4169 StartTwinkling();
4170 if (textSelector_.IsValid()) {
4171 SetCaretPosition(textSelector_.GetTextStart());
4172 record.beforeCaretPosition = caretPosition_;
4173 auto length = textSelector_.GetTextEnd() - textSelector_.GetTextStart();
4174 textSelector_.Update(-1, -1);
4175 record.deleteText = StringUtils::ToString(DeleteForwardOperation(length));
4176 ResetSelection();
4177 }
4178 InsertValueByPaste(pasteStr);
4179 ClearPasteStr();
4180 record.afterCaretPosition = caretPosition_ + moveLength_;
4181 ClearRedoOperationRecords();
4182 AddOperationRecord(record);
4183 }
4184
4185 void RichEditorPattern::HandleOnPaste()
4186 {
4187 auto host = GetHost();
4188 CHECK_NULL_VOID(host);
4189 auto eventHub = host->GetEventHub<RichEditorEventHub>();
4190 CHECK_NULL_VOID(eventHub);
4191 TextCommonEvent event;
4192 eventHub->FireOnPaste(event);
4193 if (event.IsPreventDefault()) {
4194 CloseSelectOverlay();
4195 ResetSelection();
4196 StartTwinkling();
4197 return;
4198 }
4199 CHECK_NULL_VOID(clipboard_);
4200 auto pasteCallback = [weak = WeakClaim(this)](const std::string& data) {
4201 auto richEditor = weak.Upgrade();
4202 CHECK_NULL_VOID(richEditor);
4203 if (data.empty()) {
4204 richEditor->ResetSelection();
4205 richEditor->StartTwinkling();
4206 return;
4207 }
4208 richEditor->AddPasteStr(data);
4209 richEditor->ResetAfterPaste();
4210 };
4211 clipboard_->GetData(pasteCallback);
4212 }
4213
4214 void RichEditorPattern::InsertValueByPaste(const std::string& insertValue)
4215 {
4216 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "insertValue=[%{public}s]", StringUtils::RestoreEscape(insertValue).c_str());
4217 RefPtr<UINode> child;
4218 TextInsertValueInfo info;
4219 CalcInsertValueObj(info);
4220 TextSpanOptions options;
4221 options.value = insertValue;
4222 if (typingStyle_.has_value() && typingTextStyle_.has_value()) {
4223 options.style = typingTextStyle_.value();
4224 }
4225 auto newSpanOffset = caretPosition_ + moveLength_;
4226 isTextChange_ = true;
4227 moveDirection_ = MoveDirection::FORWARD;
4228 moveLength_ += static_cast<int32_t>(StringUtils::ToWstring(insertValue).length());
4229 if (caretSpanIndex_ == -1) {
4230 child = GetChildByIndex(info.GetSpanIndex());
4231 if (child && child->GetTag() == V2::SPAN_ETS_TAG) {
4232 auto spanNode = DynamicCast<SpanNode>(child);
4233 CHECK_NULL_VOID(spanNode);
4234 if (typingStyle_.has_value() && !HasSameTypingStyle(spanNode)) {
4235 options.offset = newSpanOffset;
4236 caretSpanIndex_ = AddTextSpanOperation(options, true);
4237 } else {
4238 InsertValueToSpanNode(spanNode, insertValue, info);
4239 }
4240 return;
4241 } else if (!child) {
4242 auto spanNodeBefore = DynamicCast<SpanNode>(GetChildByIndex(info.GetSpanIndex() - 1));
4243 if (spanNodeBefore == nullptr) {
4244 caretSpanIndex_ = AddTextSpanOperation(options, true);
4245 } else if ((typingStyle_.has_value() && !HasSameTypingStyle(spanNodeBefore)) ||
4246 spanNodeBefore->GetTag() != V2::SPAN_ETS_TAG) {
4247 auto spanNode = DynamicCast<SpanNode>(child);
4248 CreateTextSpanNode(spanNode, info, insertValue, false);
4249 caretSpanIndex_ = info.GetSpanIndex();
4250 } else {
4251 InsertValueToBeforeSpan(spanNodeBefore, insertValue);
4252 caretSpanIndex_ = info.GetSpanIndex() - 1;
4253 }
4254 return;
4255 }
4256 } else {
4257 child = GetChildByIndex(caretSpanIndex_);
4258 if (child && child->GetTag() == V2::SPAN_ETS_TAG) {
4259 auto spanNode = DynamicCast<SpanNode>(child);
4260 CHECK_NULL_VOID(spanNode);
4261 if (typingStyle_.has_value() && !HasSameTypingStyle(spanNode)) {
4262 options.offset = newSpanOffset;
4263 caretSpanIndex_ = AddTextSpanOperation(options, true);
4264 } else {
4265 InsertValueToBeforeSpan(spanNode, insertValue);
4266 }
4267 return;
4268 }
4269 }
4270 if (child && child->GetTag() == V2::IMAGE_ETS_TAG) {
4271 auto spanNodeBefore = DynamicCast<SpanNode>(GetChildByIndex(info.GetSpanIndex() - 1));
4272 if (spanNodeBefore != nullptr && caretSpanIndex_ == -1) {
4273 if (typingStyle_.has_value() && !HasSameTypingStyle(spanNodeBefore)) {
4274 options.offset = newSpanOffset;
4275 caretSpanIndex_ = AddTextSpanOperation(options, true);
4276 } else {
4277 InsertValueToBeforeSpan(spanNodeBefore, insertValue);
4278 caretSpanIndex_ = info.GetSpanIndex() - 1;
4279 }
4280 } else {
4281 auto imageNode = DynamicCast<FrameNode>(child);
4282 if (imageNode && caretSpanIndex_ == -1) {
4283 caretSpanIndex_ = AddTextSpanOperation(options, true, info.GetSpanIndex(), false, false);
4284 } else {
4285 caretSpanIndex_ = AddTextSpanOperation(options, true, caretSpanIndex_ + 1);
4286 }
4287 }
4288 } else {
4289 caretSpanIndex_ = AddTextSpanOperation(options, true);
4290 }
4291 }
4292
4293 void RichEditorPattern::SetCaretSpanIndex(int32_t index)
4294 {
4295 caretSpanIndex_ = index;
4296 }
4297
4298 void RichEditorPattern::HandleOnCut()
4299 {
4300 if (copyOption_ == CopyOptions::None) {
4301 return;
4302 }
4303 if (!textSelector_.IsValid()) {
4304 return;
4305 }
4306 caretUpdateType_ = CaretUpdateType::NONE;
4307 HandleOnCopy();
4308 DeleteBackward();
4309 }
4310
4311 void RichEditorPattern::OnHandleMove(const RectF& handleRect, bool isFirstHandle)
4312 {
4313 CHECK_NULL_VOID(HasFocus());
4314 TextPattern::OnHandleMove(handleRect, isFirstHandle);
4315 if (!isFirstHandle) {
4316 SetCaretPosition(textSelector_.destinationOffset);
4317 }
4318 auto localOffset = handleRect.GetOffset() - parentGlobalOffset_;
4319 AutoScrollParam param = { .autoScrollEvent = AutoScrollEvent::HANDLE,
4320 .handleRect = handleRect,
4321 .isFirstHandle = isFirstHandle,
4322 .showScrollbar = true };
4323 AutoScrollByEdgeDetection(param, localOffset, EdgeDetectionStrategy::OUT_BOUNDARY);
4324 }
4325
4326 std::function<void(Offset)> RichEditorPattern::GetThumbnailCallback()
4327 {
4328 return [wk = WeakClaim(this)](const Offset& point) {
4329 auto pattern = wk.Upgrade();
4330 CHECK_NULL_VOID(pattern);
4331 if (pattern->BetweenSelectedPosition(point)) {
4332 auto host = pattern->GetHost();
4333 auto children = host->GetChildren();
4334 std::list<RefPtr<FrameNode>> imageChildren;
4335 for (const auto& child : children) {
4336 auto node = DynamicCast<FrameNode>(child);
4337 if (!node) {
4338 continue;
4339 }
4340 auto image = node->GetPattern<ImagePattern>();
4341 if (image) {
4342 imageChildren.emplace_back(node);
4343 }
4344 }
4345 pattern->dragNode_ = RichEditorDragPattern::CreateDragNode(host, imageChildren);
4346 FrameNode::ProcessOffscreenNode(pattern->dragNode_);
4347 }
4348 };
4349 }
4350
4351 void RichEditorPattern::CreateHandles()
4352 {
4353 if (IsDragging()) {
4354 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "do not show handles when dragging");
4355 return;
4356 }
4357 auto host = GetHost();
4358 CHECK_NULL_VOID(host);
4359 float startSelectHeight = 0.0f;
4360 float endSelectHeight = 0.0f;
4361 textSelector_.ReverseTextSelector();
4362 auto firstHandlePosition = CalcCursorOffsetByPosition(textSelector_.GetStart(), startSelectHeight, true, false);
4363 OffsetF firstHandleOffset(firstHandlePosition.GetX() + parentGlobalOffset_.GetX(),
4364 firstHandlePosition.GetY() + parentGlobalOffset_.GetY());
4365 textSelector_.firstHandleOffset_ = firstHandleOffset;
4366 auto secondHandlePosition = CalcCursorOffsetByPosition(textSelector_.GetEnd(), endSelectHeight, false, false);
4367 OffsetF secondHandleOffset(secondHandlePosition.GetX() + parentGlobalOffset_.GetX(),
4368 secondHandlePosition.GetY() + parentGlobalOffset_.GetY());
4369 textSelector_.secondHandleOffset_ = secondHandleOffset;
4370 SizeF firstHandlePaintSize = { SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), startSelectHeight };
4371 SizeF secondHandlePaintSize = { SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), endSelectHeight };
4372 RectF firstHandle = RectF(firstHandleOffset, firstHandlePaintSize);
4373 textSelector_.firstHandle = firstHandle;
4374 RectF secondHandle = RectF(secondHandleOffset, secondHandlePaintSize);
4375 textSelector_.secondHandle = secondHandle;
4376 ShowSelectOverlay(firstHandle, secondHandle, IsSelectAll(), TextResponseType::LONG_PRESS);
4377 }
4378
4379 void RichEditorPattern::OnAreaChangedInner()
4380 {
4381 float selectLineHeight = 0.0f;
4382 auto host = GetHost();
4383 CHECK_NULL_VOID(host);
4384 auto context = PipelineContext::GetCurrentContext();
4385 CHECK_NULL_VOID(context);
4386 auto parentGlobalOffset = host->GetPaintRectOffset() - context->GetRootRect().GetOffset();
4387 if (parentGlobalOffset != parentGlobalOffset_) {
4388 parentGlobalOffset_ = parentGlobalOffset;
4389 UpdateTextFieldManager(Offset(parentGlobalOffset_.GetX(), parentGlobalOffset_.GetY()), frameRect_.Height());
4390 CHECK_NULL_VOID(SelectOverlayIsOn());
4391 textSelector_.selectionBaseOffset.SetX(
4392 CalcCursorOffsetByPosition(textSelector_.GetStart(), selectLineHeight).GetX());
4393 textSelector_.selectionDestinationOffset.SetX(
4394 CalcCursorOffsetByPosition(textSelector_.GetEnd(), selectLineHeight).GetX());
4395 CreateHandles();
4396 selectOverlayProxy_->ShowOrHiddenMenu(true);
4397 }
4398 }
4399
4400 void RichEditorPattern::CloseSelectionMenu()
4401 {
4402 CloseSelectOverlay();
4403 }
4404
4405 void RichEditorPattern::CloseSelectOverlay()
4406 {
4407 TextPattern::CloseSelectOverlay(true);
4408 }
4409
4410 void RichEditorPattern::CalculateHandleOffsetAndShowOverlay(bool isUsingMouse)
4411 {
4412 auto host = GetHost();
4413 CHECK_NULL_VOID(host);
4414 auto pipeline = PipelineContext::GetCurrentContext();
4415 CHECK_NULL_VOID(pipeline);
4416 auto rootOffset = pipeline->GetRootRect().GetOffset();
4417 auto offset = host->GetPaintRectOffset();
4418 auto textPaintOffset = offset - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
4419 float startSelectHeight = 0.0f;
4420 float endSelectHeight = 0.0f;
4421 textSelector_.ReverseTextSelector();
4422 int32_t baseOffset = std::min(textSelector_.baseOffset, GetTextContentLength());
4423 int32_t destinationOffset = std::min(textSelector_.destinationOffset, GetTextContentLength());
4424 auto startOffset = CalcCursorOffsetByPosition(baseOffset, startSelectHeight, true, false);
4425 auto endOffset =
4426 CalcCursorOffsetByPosition(destinationOffset, endSelectHeight, false, false);
4427 if (baseOffset == destinationOffset && NearEqual(startOffset.GetX(), endOffset.GetX())) {
4428 endOffset = CalcCursorOffsetByPosition(destinationOffset, endSelectHeight, true, false);
4429 }
4430 SizeF firstHandlePaintSize = { SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), startSelectHeight };
4431 SizeF secondHandlePaintSize = { SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), endSelectHeight };
4432 OffsetF firstHandleOffset = startOffset + textPaintOffset - rootOffset;
4433 OffsetF secondHandleOffset = endOffset + textPaintOffset - rootOffset;
4434 AdjustHandleRect(firstHandleOffset, secondHandleOffset, firstHandlePaintSize, secondHandlePaintSize);
4435 textSelector_.selectionBaseOffset = firstHandleOffset;
4436 textSelector_.selectionDestinationOffset = secondHandleOffset;
4437 RectF firstHandle;
4438 firstHandle.SetOffset(firstHandleOffset);
4439 firstHandle.SetSize(firstHandlePaintSize);
4440 textSelector_.firstHandle = firstHandle;
4441 RectF secondHandle;
4442 secondHandle.SetOffset(secondHandleOffset);
4443 secondHandle.SetSize(secondHandlePaintSize);
4444 textSelector_.secondHandle = secondHandle;
4445 }
4446
4447 void RichEditorPattern::AdjustHandleRect(
4448 OffsetF& firstHandleOffset, OffsetF& secondHandleOffset, SizeF& firstHandlePaintSize, SizeF& secondHandlePaintSize)
4449 {
4450 if (GetTextContentLength() == 0) {
4451 float caretHeight = DynamicCast<RichEditorOverlayModifier>(overlayMod_)->GetCaretHeight();
4452 secondHandlePaintSize = { SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), caretHeight / 2 };
4453 secondHandleOffset = OffsetF(secondHandleOffset.GetX(), secondHandleOffset.GetY() + caretHeight / 2);
4454 // only show the second handle.
4455 firstHandlePaintSize = SizeF{};
4456 firstHandleOffset = OffsetF{};
4457 }
4458 }
4459
4460 void RichEditorPattern::ResetSelection()
4461 {
4462 bool selectNothing = textSelector_.SelectNothing();
4463 textSelector_.Update(-1, -1);
4464 if (!selectNothing) {
4465 textSelector_.Update(-1, -1);
4466 auto host = GetHost();
4467 CHECK_NULL_VOID(host);
4468 auto eventHub = host->GetEventHub<RichEditorEventHub>();
4469 CHECK_NULL_VOID(eventHub);
4470 auto textSelectInfo = GetSpansInfo(-1, -1, GetSpansMethod::ONSELECT);
4471 eventHub->FireOnSelect(&textSelectInfo);
4472 UpdateSelectionType(textSelectInfo);
4473 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
4474 }
4475 }
4476
4477 bool RichEditorPattern::BetweenSelectedPosition(const Offset& globalOffset)
4478 {
4479 auto host = GetHost();
4480 CHECK_NULL_RETURN(host, false);
4481 auto offset = host->GetPaintRectOffset();
4482 auto localOffset = globalOffset - Offset(offset.GetX(), offset.GetY());
4483 auto eventHub = host->GetEventHub<EventHub>();
4484 if (GreatNotEqual(textSelector_.GetTextEnd(), textSelector_.GetTextStart())) {
4485 // Determine if the pan location is in the selected area
4486 auto selectedRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
4487 auto panOffset = OffsetF(localOffset.GetX(), localOffset.GetY()) - GetTextRect().GetOffset() +
4488 OffsetF(0.0, std::min(baselineOffset_, 0.0f));
4489 for (const auto& selectedRect : selectedRects) {
4490 if (selectedRect.IsInRegion(PointF(panOffset.GetX(), panOffset.GetY()))) {
4491 return true;
4492 }
4493 }
4494 }
4495 return false;
4496 }
4497
4498 void RichEditorPattern::HandleSurfaceChanged(int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight)
4499 {
4500 if (newWidth != prevWidth || newHeight != prevHeight) {
4501 TextPattern::HandleSurfaceChanged(newWidth, newHeight, prevWidth, prevHeight);
4502 UpdateOriginIsMenuShow(false);
4503 }
4504 UpdateCaretInfoToController();
4505 }
4506
4507 void RichEditorPattern::HandleSurfacePositionChanged(int32_t posX, int32_t posY)
4508 {
4509 UpdateCaretInfoToController();
4510 }
4511
4512 void RichEditorPattern::DumpInfo()
4513 {
4514 if (customKeyboardBuilder_) {
4515 DumpLog::GetInstance().AddDesc(std::string("CustomKeyboard: true")
4516 .append(", Attached: ")
4517 .append(std::to_string(isCustomKeyboardAttached_)));
4518 }
4519 auto context = GetHost()->GetContext();
4520 CHECK_NULL_VOID(context);
4521 auto richEditorTheme = context->GetTheme<RichEditorTheme>();
4522 CHECK_NULL_VOID(richEditorTheme);
4523 DumpLog::GetInstance().AddDesc(std::string("caret offset: ").append(GetCaretRect().GetOffset().ToString()));
4524 DumpLog::GetInstance().AddDesc(
4525 std::string("caret height: ")
4526 .append(std::to_string(NearZero(GetCaretRect().Height())
4527 ? richEditorTheme->GetDefaultCaretHeight().ConvertToPx()
4528 : GetCaretRect().Height())));
4529 }
4530
4531 bool RichEditorPattern::HasFocus() const
4532 {
4533 auto focusHub = GetHost()->GetOrCreateFocusHub();
4534 CHECK_NULL_RETURN(focusHub, false);
4535 return focusHub->IsCurrentFocus();
4536 }
4537
4538 void RichEditorPattern::UpdateTextFieldManager(const Offset& offset, float height)
4539 {
4540 if (!HasFocus()) {
4541 return;
4542 }
4543 auto context = GetHost()->GetContext();
4544 CHECK_NULL_VOID(context);
4545 auto richEditorTheme = context->GetTheme<RichEditorTheme>();
4546 CHECK_NULL_VOID(richEditorTheme);
4547 auto textFieldManager = DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
4548 CHECK_NULL_VOID(textFieldManager);
4549 textFieldManager->SetClickPosition(
4550 { offset.GetX() + GetCaretRect().GetX(), offset.GetY() + GetCaretRect().GetY() });
4551 textFieldManager->SetHeight(NearZero(GetCaretRect().Height())
4552 ? richEditorTheme->GetDefaultCaretHeight().ConvertToPx()
4553 : GetCaretRect().Height());
4554 textFieldManager->SetOnFocusTextField(WeakClaim(this));
4555
4556 if (!isTextChange_) {
4557 return;
4558 }
4559 auto taskExecutor = context->GetTaskExecutor();
4560 CHECK_NULL_VOID(taskExecutor);
4561 taskExecutor->PostTask(
4562 [weak = WeakClaim(this)] {
4563 auto pattern = weak.Upgrade();
4564 CHECK_NULL_VOID(pattern);
4565 pattern->ScrollToSafeArea();
4566 },
4567 TaskExecutor::TaskType::UI);
4568 }
4569
4570 bool RichEditorPattern::IsDisabled() const
4571 {
4572 auto eventHub = GetHost()->GetEventHub<RichEditorEventHub>();
4573 CHECK_NULL_RETURN(eventHub, true);
4574 return !eventHub->IsEnabled();
4575 }
4576
4577 void RichEditorPattern::InitSelection(const Offset& pos)
4578 {
4579 int32_t currentPosition = paragraphs_.GetIndex(pos);
4580 currentPosition = std::min(currentPosition, GetTextContentLength());
4581 int32_t nextPosition = currentPosition + GetGraphemeClusterLength(GetWideText(), currentPosition);
4582 nextPosition = std::min(nextPosition, GetTextContentLength());
4583 AdjustPlaceholderSelection(currentPosition, nextPosition, pos);
4584 adjusted_ = AdjustWordSelection(currentPosition, nextPosition);
4585 textSelector_.Update(currentPosition, nextPosition);
4586 auto selectedRects = paragraphs_.GetRects(currentPosition, nextPosition);
4587 if (selectedRects.empty() || (selectedRects.size() == 1 && NearZero((selectedRects[0].Width())))) {
4588 textSelector_.Update(currentPosition, currentPosition);
4589 }
4590 if (adjusted_) {
4591 return;
4592 }
4593
4594 bool selectedSingle =
4595 selectedRects.size() == 1 && (pos.GetX() < selectedRects[0].Left() || pos.GetY() < selectedRects[0].Top());
4596 bool selectedLast = selectedRects.empty() && currentPosition == GetTextContentLength();
4597 if (selectedSingle || selectedLast) {
4598 if (selectedLast) {
4599 nextPosition = currentPosition + 1;
4600 }
4601 auto selectedNextRects = paragraphs_.GetRects(currentPosition - 1, nextPosition - 1);
4602 if (selectedNextRects.size() == 1) {
4603 bool isInRange = pos.GetX() >= selectedNextRects[0].Left() && pos.GetX() <= selectedNextRects[0].Right() &&
4604 pos.GetY() >= selectedNextRects[0].Top() && pos.GetY() <= selectedNextRects[0].Bottom();
4605 if (isInRange || (!selectedLast && selectedRects[0].Top() != selectedNextRects[0].Top())) {
4606 textSelector_.Update(currentPosition - 1, nextPosition - 1);
4607 }
4608 }
4609 }
4610 }
4611
4612 void RichEditorPattern::SetSelection(int32_t start, int32_t end)
4613 {
4614 CHECK_NULL_VOID(HasFocus());
4615 bool changeSelected = false;
4616 if (start > end) {
4617 changeSelected = textSelector_.IsValid();
4618 ResetSelection();
4619 } else {
4620 if (start == -1 && end == -1) {
4621 start = 0;
4622 end = GetTextContentLength();
4623 } else {
4624 start = std::min(std::max(0, start), GetTextContentLength());
4625 end = std::min(std::max(0, end), GetTextContentLength());
4626 }
4627 changeSelected = start != textSelector_.GetTextStart() || end != textSelector_.GetTextEnd();
4628 textSelector_.Update(start, end);
4629 }
4630
4631 auto oldSelectedType = selectedType_;
4632 if (textSelector_.IsValid() && !textSelector_.StartEqualToDest()) {
4633 StopTwinkling();
4634 if (changeSelected) {
4635 FireOnSelect(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
4636 }
4637 }
4638 if (SelectOverlayIsOn()) {
4639 isMousePressed_ = selectOverlayProxy_->GetSelectOverlayMangerInfo().isUsingMouse;
4640 auto selectedTypeChange = (oldSelectedType.has_value() && selectedType_.has_value() &&
4641 oldSelectedType.value() != selectedType_.value()) ||
4642 (!oldSelectedType.has_value() && selectedType_.has_value());
4643 if (!isMousePressed_ || selectedTypeChange) {
4644 CalculateHandleOffsetAndShowOverlay();
4645 CloseSelectOverlay();
4646 auto responseType = static_cast<TextResponseType>(
4647 selectOverlayProxy_->GetSelectOverlayMangerInfo().menuInfo.responseType.value_or(0));
4648 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, IsSelectAll(), responseType);
4649 }
4650 }
4651 SetCaretPosition(textSelector_.GetTextEnd());
4652 MoveCaretToContentRect();
4653 auto host = GetHost();
4654 CHECK_NULL_VOID(host);
4655 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
4656 }
4657
4658 void RichEditorPattern::BindSelectionMenu(TextResponseType type, TextSpanType richEditorType,
4659 std::function<void()>& menuBuilder, std::function<void(int32_t, int32_t)>& onAppear,
4660 std::function<void()>& onDisappear)
4661 {
4662 TextPattern::BindSelectionMenu(richEditorType, type, menuBuilder, onAppear, onDisappear);
4663 }
4664
4665 RefPtr<NodePaintMethod> RichEditorPattern::CreateNodePaintMethod()
4666 {
4667 if (!contentMod_) {
4668 contentMod_ = MakeRefPtr<RichEditorContentModifier>(textStyle_, ¶graphs_, WeakClaim(this));
4669 }
4670 if (!overlayMod_) {
4671 auto scrollBar = GetScrollBar();
4672 if (scrollBar) {
4673 auto scrollBarModifier = AceType::MakeRefPtr<ScrollBarOverlayModifier>();
4674 scrollBarModifier->SetRect(scrollBar->GetActiveRect());
4675 scrollBarModifier->SetPositionMode(scrollBar->GetPositionMode());
4676 SetScrollBarOverlayModifier(scrollBarModifier);
4677 }
4678 SetEdgeEffect(EdgeEffect::FADE, GetAlwaysEnabled());
4679 SetEdgeEffect();
4680 overlayMod_ = AceType::MakeRefPtr<RichEditorOverlayModifier>(
4681 WeakClaim(this), GetScrollBarOverlayModifier(), GetScrollEdgeEffect());
4682 }
4683
4684 if (GetIsCustomFont()) {
4685 contentMod_->SetIsCustomFont(true);
4686 }
4687 return MakeRefPtr<RichEditorPaintMethod>(WeakClaim(this), ¶graphs_, baselineOffset_, contentMod_, overlayMod_);
4688 }
4689
4690 int32_t RichEditorPattern::GetHandleIndex(const Offset& offset) const
4691 {
4692 return paragraphs_.GetIndex(Offset(offset.GetX() + contentRect_.GetX() - richTextRect_.GetX(),
4693 offset.GetY() + contentRect_.GetY() - richTextRect_.GetY()));
4694 }
4695
4696 std::vector<RectF> RichEditorPattern::GetTextBoxes()
4697 {
4698 auto selectedRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
4699 std::vector<RectF> res;
4700 res.reserve(selectedRects.size());
4701 for (auto&& rect : selectedRects) {
4702 res.emplace_back(rect);
4703 }
4704 if (!res.empty() && paragraphs_.IsSelectLineHeadAndUseLeadingMargin(textSelector_.GetTextStart())) {
4705 // To make drag screenshot include LeadingMarginPlaceholder.
4706 res.front().SetLeft(0.0f);
4707 }
4708 return res;
4709 }
4710
4711 float RichEditorPattern::GetLineHeight() const
4712 {
4713 auto selectedRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
4714 CHECK_NULL_RETURN(selectedRects.size(), 0.0f);
4715 return selectedRects.front().Height();
4716 }
4717
4718 void RichEditorPattern::UpdateSelectMenuInfo(bool hasData, SelectOverlayInfo& selectInfo, bool isCopyAll)
4719 {
4720 auto hasValue = (static_cast<int32_t>(GetWideText().length()) + placeholderCount_) > 0;
4721 bool isShowItem = copyOption_ != CopyOptions::None;
4722 selectInfo.menuInfo.showCopy = isShowItem && hasValue && textSelector_.IsValid() &&
4723 !textSelector_.StartEqualToDest();
4724 selectInfo.menuInfo.showCut = isShowItem && hasValue && textSelector_.IsValid() &&
4725 !textSelector_.StartEqualToDest();
4726 selectInfo.menuInfo.showCopyAll = !isCopyAll && hasValue;
4727 selectInfo.menuInfo.showPaste = hasData;
4728 bool isSupportCameraInput = false;
4729 #if defined(ENABLE_STANDARD_INPUT)
4730 auto inputMethod = MiscServices::InputMethodController::GetInstance();
4731 isSupportCameraInput =
4732 inputMethod && inputMethod->IsInputTypeSupported(MiscServices::InputType::CAMERA_INPUT);
4733 #endif
4734 selectInfo.menuInfo.showCameraInput = !IsSelected() && isSupportCameraInput && !customKeyboardBuilder_;
4735 selectInfo.menuInfo.menuIsShow = isShowMenu_ && (hasValue || hasData || selectInfo.menuInfo.showCameraInput);
4736 selectMenuInfo_ = selectInfo.menuInfo;
4737 }
4738
4739 RectF RichEditorPattern::GetCaretRect() const
4740 {
4741 RectF rect;
4742 CHECK_NULL_RETURN(overlayMod_, rect);
4743 auto richEditorOverlay = DynamicCast<RichEditorOverlayModifier>(overlayMod_);
4744 CHECK_NULL_RETURN(richEditorOverlay, rect);
4745 rect.SetOffset(richEditorOverlay->GetCaretOffset());
4746 rect.SetHeight(richEditorOverlay->GetCaretHeight());
4747 return rect;
4748 }
4749
4750 void RichEditorPattern::ScrollToSafeArea() const
4751 {
4752 auto pipeline = PipelineContext::GetCurrentContext();
4753 CHECK_NULL_VOID(pipeline);
4754 auto textFieldManager = DynamicCast<TextFieldManagerNG>(pipeline->GetTextFieldManager());
4755 CHECK_NULL_VOID(textFieldManager);
4756 textFieldManager->ScrollTextFieldToSafeArea();
4757 }
4758
4759 void RichEditorPattern::InitScrollablePattern()
4760 {
4761 if (GetScrollableEvent()) {
4762 return;
4763 }
4764 SetAxis(Axis::VERTICAL);
4765 AddScrollEvent();
4766 SetScrollBar(DisplayMode::AUTO);
4767 auto scrollBar = GetScrollBar();
4768 if (scrollBar) {
4769 auto pipeline = PipelineContext::GetCurrentContext();
4770 CHECK_NULL_VOID(pipeline);
4771 auto richEditorTheme = pipeline->GetTheme<RichEditorTheme>();
4772 CHECK_NULL_VOID(richEditorTheme);
4773 scrollBar->SetMinHeight(richEditorTheme->GetScrollbarMinHeight());
4774 }
4775 if (overlayMod_) {
4776 UpdateScrollBarOffset();
4777 }
4778 auto layoutProperty = GetLayoutProperty<RichEditorLayoutProperty>();
4779 CHECK_NULL_VOID(layoutProperty);
4780 auto& paddingProperty = layoutProperty->GetPaddingProperty();
4781 if (paddingProperty) {
4782 auto offsetY = paddingProperty->top.has_value() ? paddingProperty->top->GetDimension().ConvertToPx() : 0.0f;
4783 auto offsetX = paddingProperty->left.has_value() ? paddingProperty->left->GetDimension().ConvertToPx() : 0.0f;
4784 richTextRect_.SetOffset(OffsetF(offsetX, offsetY));
4785 }
4786 }
4787
4788 void RichEditorPattern::ProcessInnerPadding()
4789 {
4790 auto context = PipelineBase::GetCurrentContext();
4791 CHECK_NULL_VOID(context);
4792 auto theme = context->GetTheme<RichEditorTheme>();
4793 CHECK_NULL_VOID(theme);
4794 auto host = GetHost();
4795 CHECK_NULL_VOID(host);
4796 auto layoutProperty = host->GetLayoutProperty<RichEditorLayoutProperty>();
4797 CHECK_NULL_VOID(layoutProperty);
4798 auto themePadding = theme->GetPadding();
4799 auto& paddingProp = layoutProperty->GetPaddingProperty();
4800 auto left = !paddingProp ? CalcLength(themePadding.Left()).GetDimension()
4801 : paddingProp->left.value_or(CalcLength(themePadding.Left())).GetDimension();
4802 auto top = !paddingProp ? CalcLength(themePadding.Top()).GetDimension()
4803 : paddingProp->top.value_or(CalcLength(themePadding.Top())).GetDimension();
4804 auto bottom = !paddingProp ? CalcLength(themePadding.Bottom()).GetDimension()
4805 : paddingProp->bottom.value_or(CalcLength(themePadding.Bottom())).GetDimension();
4806 auto right = !paddingProp ? CalcLength(themePadding.Right()).GetDimension()
4807 : paddingProp->right.value_or(CalcLength(themePadding.Right())).GetDimension();
4808 PaddingProperty paddings;
4809 paddings.top = NG::CalcLength(top);
4810 paddings.bottom = NG::CalcLength(bottom);
4811 paddings.left = NG::CalcLength(left);
4812 paddings.right = NG::CalcLength(right);
4813 layoutProperty->UpdatePadding(paddings);
4814 }
4815
4816 void RichEditorPattern::UpdateScrollStateAfterLayout(bool shouldDisappear)
4817 {
4818 bool hasTextOffsetChanged = false;
4819 if (GreatNotEqual(richTextRect_.GetY(), contentRect_.GetY())) {
4820 auto offset = richTextRect_.GetOffset();
4821 offset.AddY(contentRect_.GetY() - richTextRect_.GetY());
4822 richTextRect_.SetOffset(offset);
4823 hasTextOffsetChanged = true;
4824 }
4825 if (GreatNotEqual(richTextRect_.Height(), contentRect_.Height()) &&
4826 LessNotEqual(richTextRect_.Bottom(), contentRect_.Bottom())) {
4827 auto offset = richTextRect_.GetOffset();
4828 offset.AddY(contentRect_.Bottom() - richTextRect_.Bottom());
4829 richTextRect_.SetOffset(offset);
4830 hasTextOffsetChanged = true;
4831 }
4832 if (LessOrEqual(richTextRect_.Height(), contentRect_.Height()) &&
4833 LessNotEqual(richTextRect_.GetY(), contentRect_.GetY())) {
4834 richTextRect_.SetOffset(contentRect_.GetOffset());
4835 hasTextOffsetChanged = true;
4836 }
4837 if (hasTextOffsetChanged) {
4838 UpdateChildrenOffset();
4839 }
4840 StopScrollable();
4841 CheckScrollable();
4842 if (overlayMod_) {
4843 UpdateScrollBarOffset();
4844 }
4845 if (!GetScrollBar()) {
4846 return;
4847 }
4848 if (isFirstCallOnReady_) {
4849 isFirstCallOnReady_ = false;
4850 GetScrollBar()->ScheduleDisappearDelayTask();
4851 return;
4852 }
4853 if (shouldDisappear) {
4854 GetScrollBar()->ScheduleDisappearDelayTask();
4855 }
4856 }
4857
4858 bool RichEditorPattern::OnScrollCallback(float offset, int32_t source)
4859 {
4860 if (source == SCROLL_FROM_START) {
4861 auto scrollBar = GetScrollBar();
4862 if (scrollBar) {
4863 scrollBar->PlayScrollBarAppearAnimation();
4864 }
4865 if (SelectOverlayIsOn()) {
4866 selectOverlayProxy_->ShowOrHiddenMenu(true);
4867 }
4868 return true;
4869 }
4870 if (IsReachedBoundary(offset)) {
4871 return false;
4872 }
4873 auto newOffset = MoveTextRect(offset);
4874 MoveFirstHandle(newOffset);
4875 MoveSecondHandle(newOffset);
4876 return true;
4877 }
4878
4879 float RichEditorPattern::MoveTextRect(float offset)
4880 {
4881 if (GreatNotEqual(richTextRect_.Height(), contentRect_.Height())) {
4882 if (GreatNotEqual(richTextRect_.GetY() + offset, contentRect_.GetY())) {
4883 offset = contentRect_.GetY() - richTextRect_.GetY();
4884 } else if (LessNotEqual(richTextRect_.Bottom() + offset, contentRect_.Bottom())) {
4885 offset = contentRect_.Bottom() - richTextRect_.Bottom();
4886 }
4887 } else if (!NearEqual(richTextRect_.GetY(), contentRect_.GetY())) {
4888 offset = contentRect_.GetY() - richTextRect_.GetY();
4889 } else {
4890 return 0.0f;
4891 }
4892 if (NearEqual(offset, 0.0f)) {
4893 return offset;
4894 }
4895 scrollOffset_ = richTextRect_.GetY() + offset;
4896 richTextRect_.SetOffset(OffsetF(richTextRect_.GetX(), scrollOffset_));
4897 UpdateScrollBarOffset();
4898 UpdateChildrenOffset();
4899 return offset;
4900 }
4901
4902 void RichEditorPattern::MoveFirstHandle(float offset)
4903 {
4904 if (SelectOverlayIsOn() && !NearEqual(offset, 0.0f)) {
4905 textSelector_.selectionBaseOffset.AddY(offset);
4906 auto firstHandleOffset = textSelector_.firstHandle.GetOffset();
4907 firstHandleOffset.AddY(offset);
4908 textSelector_.firstHandle.SetOffset(firstHandleOffset);
4909 SelectHandleInfo firstHandleInfo;
4910 firstHandleInfo.paintRect = textSelector_.firstHandle;
4911 firstHandleInfo.needLayout = true;
4912 CheckHandles(firstHandleInfo);
4913 selectOverlayProxy_->UpdateFirstSelectHandleInfo(firstHandleInfo);
4914 }
4915 }
4916
4917 void RichEditorPattern::MoveSecondHandle(float offset)
4918 {
4919 if (SelectOverlayIsOn() && !NearEqual(offset, 0.0f)) {
4920 textSelector_.selectionDestinationOffset.AddY(offset);
4921 auto secondHandleOffset = textSelector_.secondHandle.GetOffset();
4922 secondHandleOffset.AddY(offset);
4923 textSelector_.secondHandle.SetOffset(secondHandleOffset);
4924 SelectHandleInfo secondHandleInfo;
4925 secondHandleInfo.paintRect = textSelector_.secondHandle;
4926 secondHandleInfo.needLayout = true;
4927 CheckHandles(secondHandleInfo);
4928 selectOverlayProxy_->UpdateSecondSelectHandleInfo(secondHandleInfo);
4929 }
4930 }
4931
4932 void RichEditorPattern::MoveCaretToContentRect()
4933 {
4934 float caretHeight = 0.0f;
4935 auto caretOffset = CalcCursorOffsetByPosition(GetCaretPosition(), caretHeight, true);
4936 MoveCaretToContentRect(caretOffset, caretHeight);
4937 }
4938
4939 void RichEditorPattern::MoveCaretToContentRect(const OffsetF& caretOffset, float caretHeight)
4940 {
4941 auto contentRect = GetTextContentRect();
4942 auto textRect = GetTextRect();
4943 if (LessOrEqual(textRect.Height(), contentRect.Height())) {
4944 return;
4945 }
4946 if (LessNotEqual(contentRect.GetSize().Height(), caretHeight) &&
4947 !NearEqual(caretOffset.GetY() + caretHeight, contentRect.Bottom())) {
4948 OnScrollCallback(contentRect.Bottom() - caretOffset.GetY() - caretHeight, SCROLL_FROM_NONE);
4949 }
4950 if (LessNotEqual(contentRect.GetSize().Height(), caretHeight)) {
4951 return;
4952 }
4953 if (LessNotEqual(caretOffset.GetY(), contentRect.GetY())) {
4954 if (LessOrEqual(caretOffset.GetX(), GetTextRect().GetX())) {
4955 OnScrollCallback(contentRect.GetY() - caretOffset.GetY() + caretHeight, SCROLL_FROM_NONE);
4956 } else {
4957 OnScrollCallback(contentRect.GetY() - caretOffset.GetY(), SCROLL_FROM_NONE);
4958 }
4959 } else if (GreatNotEqual(caretOffset.GetY() + caretHeight, contentRect.Bottom())) {
4960 OnScrollCallback(contentRect.Bottom() - caretOffset.GetY() - caretHeight, SCROLL_FROM_NONE);
4961 }
4962 }
4963
4964 void RichEditorPattern::UpdateScrollBarOffset()
4965 {
4966 if (!GetScrollBar() && !GetScrollBarProxy()) {
4967 return;
4968 }
4969 Size size(frameRect_.Width(), frameRect_.Height());
4970 auto verticalGap = frameRect_.Height() - contentRect_.Height();
4971 UpdateScrollBarRegion(
4972 contentRect_.GetY() - richTextRect_.GetY(), richTextRect_.Height() + verticalGap, size, Offset(0.0, 0.0));
4973 auto tmpHost = GetHost();
4974 CHECK_NULL_VOID(tmpHost);
4975 tmpHost->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
4976 }
4977
4978 void RichEditorPattern::OnScrollEndCallback()
4979 {
4980 auto scrollBar = GetScrollBar();
4981 if (scrollBar) {
4982 scrollBar->ScheduleDisappearDelayTask();
4983 }
4984 UpdateOverlaySelectArea();
4985 }
4986
4987 bool RichEditorPattern::IsReachedBoundary(float offset)
4988 {
4989 return (NearEqual(richTextRect_.GetY(), contentRect_.GetY()) && GreatNotEqual(offset, 0.0f)) ||
4990 (NearEqual(richTextRect_.GetY() + richTextRect_.Height(), contentRect_.GetY() + contentRect_.Height()) &&
4991 LessNotEqual(offset, 0.0f));
4992 }
4993
4994 void RichEditorPattern::CheckScrollable()
4995 {
4996 auto host = GetHost();
4997 CHECK_NULL_VOID(host);
4998 auto hub = host->GetEventHub<EventHub>();
4999 CHECK_NULL_VOID(hub);
5000 auto gestureHub = hub->GetOrCreateGestureEventHub();
5001 CHECK_NULL_VOID(gestureHub);
5002 scrollable_ = GetTextContentLength() > 0 && GreatNotEqual(richTextRect_.Height(), contentRect_.Height());
5003 SetScrollEnable(scrollable_);
5004 }
5005
5006 void RichEditorPattern::UpdateChildrenOffset()
5007 {
5008 auto host = GetHost();
5009 CHECK_NULL_VOID(host);
5010 std::vector<int32_t> placeholderIndex;
5011 for (const auto& child : spans_) {
5012 if (!child) {
5013 continue;
5014 }
5015 if (AceType::InstanceOf<ImageSpanItem>(child) || AceType::InstanceOf<PlaceholderSpanItem>(child)) {
5016 placeholderIndex.emplace_back(child->placeholderIndex);
5017 }
5018 }
5019 if (spans_.empty() || placeholderIndex.empty()) {
5020 return;
5021 }
5022 size_t index = 0;
5023 std::vector<RectF> rectsForPlaceholders = paragraphs_.GetPlaceholderRects();
5024 auto childrenNodes = host->GetChildren();
5025 auto textOffset = GetTextRect().GetOffset();
5026 for (const auto& child : childrenNodes) {
5027 auto childNode = AceType::DynamicCast<FrameNode>(child);
5028 if (!childNode) {
5029 continue;
5030 }
5031 if (!(childNode->GetPattern<ImagePattern>() || childNode->GetPattern<PlaceholderSpanPattern>())) {
5032 continue;
5033 }
5034 if (index >= rectsForPlaceholders.size()) {
5035 break;
5036 }
5037 auto rect = rectsForPlaceholders.at(index);
5038 auto geometryNode = childNode->GetGeometryNode();
5039 if (geometryNode) {
5040 geometryNode->SetMarginFrameOffset(textOffset + OffsetF(rect.Left(), rect.Top()));
5041 childNode->ForceSyncGeometryNode();
5042 childNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
5043 }
5044 ++index;
5045 }
5046 }
5047
5048 void RichEditorPattern::AutoScrollByEdgeDetection(AutoScrollParam param, OffsetF offset, EdgeDetectionStrategy strategy)
5049 {
5050 if (NearEqual(prevAutoScrollOffset_.GetY(), offset.GetY())) {
5051 return;
5052 }
5053 prevAutoScrollOffset_ = offset;
5054 auto contentRect = GetTextContentRect();
5055 auto isDragging = param.autoScrollEvent == AutoScrollEvent::DRAG;
5056 float edgeThreshold = isDragging ? AUTO_SCROLL_DRAG_EDGE_DISTANCE.ConvertToPx()
5057 : AUTO_SCROLL_EDGE_DISTANCE.ConvertToPx();
5058 auto maxHeight = isDragging ? frameRect_.Height() : contentRect.Height();
5059 if (GreatNotEqual(edgeThreshold * 2, maxHeight)) {
5060 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "AutoScrollByEdgeDetection: hot area height is great than max height.");
5061 return;
5062 }
5063 float topEdgeThreshold = isDragging ? edgeThreshold : edgeThreshold + contentRect.GetY();
5064 float bottomThreshold = isDragging ? frameRect_.Height() - edgeThreshold : contentRect.Bottom() - edgeThreshold;
5065 if (param.autoScrollEvent == AutoScrollEvent::HANDLE) {
5066 auto handleTopOffset = offset;
5067 auto handleBottomOffset = OffsetF(offset.GetX(), offset.GetY() + param.handleRect.Height());
5068 if (GreatNotEqual(handleBottomOffset.GetY(), bottomThreshold)) {
5069 param.offset = bottomThreshold - handleBottomOffset.GetY();
5070 ScheduleAutoScroll(param);
5071 } else if (LessNotEqual(handleTopOffset.GetY(), topEdgeThreshold)) {
5072 param.offset = topEdgeThreshold - handleTopOffset.GetY();
5073 ScheduleAutoScroll(param);
5074 } else {
5075 StopAutoScroll();
5076 }
5077 return;
5078 }
5079 // drag and mouse
5080 if (GreatNotEqual(offset.GetY(), bottomThreshold)) {
5081 param.offset = isDragging ? -CalcDragSpeed(bottomThreshold, frameRect_.Height(), offset.GetY())
5082 : bottomThreshold - offset.GetY();
5083 ScheduleAutoScroll(param);
5084 } else if (LessNotEqual(offset.GetY(), topEdgeThreshold)) {
5085 param.offset = isDragging ? CalcDragSpeed(topEdgeThreshold, 0, offset.GetY())
5086 : topEdgeThreshold - offset.GetY();
5087 ScheduleAutoScroll(param);
5088 } else {
5089 StopAutoScroll();
5090 }
5091 }
5092
5093 float RichEditorPattern::CalcDragSpeed(float hotAreaStart, float hotAreaEnd, float point)
5094 {
5095 auto distanceRatio = (point - hotAreaStart) / (hotAreaEnd - hotAreaStart);
5096 auto speedFactor = Curves::SHARP->MoveInternal(distanceRatio);
5097 return ((MAX_DRAG_SCROLL_SPEED * speedFactor) / TIME_UNIT) * AUTO_SCROLL_INTERVAL;
5098 }
5099
5100 void RichEditorPattern::ScheduleAutoScroll(AutoScrollParam param)
5101 {
5102 if (GreatNotEqual(param.offset, 0.0f) && IsReachTop()) {
5103 return;
5104 }
5105 if (LessNotEqual(param.offset, 0.0f) && IsReachBottom()) {
5106 return;
5107 }
5108 auto context = PipelineContext::GetCurrentContext();
5109 CHECK_NULL_VOID(context);
5110 auto taskExecutor = context->GetTaskExecutor();
5111 CHECK_NULL_VOID(taskExecutor);
5112 if (param.isFirstRun_) {
5113 param.isFirstRun_ = false;
5114 currentScrollParam_ = param;
5115 if (isAutoScrollRunning_) {
5116 return;
5117 }
5118 }
5119 autoScrollTask_.Reset([weak = WeakClaim(this)]() {
5120 auto client = weak.Upgrade();
5121 CHECK_NULL_VOID(client);
5122 client->OnAutoScroll(client->currentScrollParam_);
5123 if (client->IsReachTop() || client->IsReachBottom()) {
5124 client->StopAutoScroll();
5125 }
5126 });
5127 isAutoScrollRunning_ = true;
5128 taskExecutor->PostDelayedTask(autoScrollTask_, TaskExecutor::TaskType::UI, AUTO_SCROLL_INTERVAL);
5129 }
5130
5131 void RichEditorPattern::OnAutoScroll(AutoScrollParam param)
5132 {
5133 if (param.showScrollbar) {
5134 auto scrollBar = GetScrollBar();
5135 if (scrollBar) {
5136 scrollBar->PlayScrollBarAppearAnimation();
5137 }
5138 param.showScrollbar = false;
5139 }
5140
5141 if (param.autoScrollEvent == AutoScrollEvent::HANDLE) {
5142 auto newOffset = MoveTextRect(param.offset);
5143 if (param.isFirstHandle) {
5144 MoveSecondHandle(newOffset);
5145 } else {
5146 MoveFirstHandle(newOffset);
5147 }
5148 TextPattern::OnHandleMove(param.handleRect, param.isFirstHandle);
5149 if (NearEqual(newOffset, 0.0f)) {
5150 return;
5151 }
5152 ScheduleAutoScroll(param);
5153 return;
5154 }
5155
5156 if (param.autoScrollEvent == AutoScrollEvent::DRAG) {
5157 auto newOffset = MoveTextRect(param.offset);
5158 if (NearEqual(newOffset, 0.0f)) {
5159 return;
5160 }
5161 ScheduleAutoScroll(param);
5162 return;
5163 }
5164
5165 if (param.autoScrollEvent == AutoScrollEvent::MOUSE) {
5166 auto newOffset = MoveTextRect(param.offset);
5167 auto textOffset =
5168 Offset(param.eventOffset.GetX() - GetTextRect().GetX(), param.eventOffset.GetY() - GetTextRect().GetY());
5169 int32_t extend = paragraphs_.GetIndex(textOffset);
5170 textSelector_.Update(textSelector_.baseOffset, extend);
5171 SetCaretPosition(std::max(textSelector_.baseOffset, extend));
5172 if (NearEqual(newOffset, 0.0f)) {
5173 return;
5174 }
5175 ScheduleAutoScroll(param);
5176 }
5177 }
5178
5179 void RichEditorPattern::StopAutoScroll()
5180 {
5181 isAutoScrollRunning_ = false;
5182 autoScrollTask_.Cancel();
5183 prevAutoScrollOffset_ = OffsetF(0.0f, 0.0f);
5184 auto scrollBar = GetScrollBar();
5185 if (scrollBar) {
5186 scrollBar->PlayScrollBarDisappearAnimation();
5187 }
5188 }
5189
5190 bool RichEditorPattern::NeedAiAnalysis(
5191 const CaretUpdateType targeType, const int32_t pos, const int32_t& spanStart, const std::string& content)
5192 {
5193 if (spanStart < 0) {
5194 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "NeedAiAnalysis -spanStart%{public}d,return!", spanStart);
5195 return false;
5196 }
5197
5198 if (!InputAIChecker::NeedAIAnalysis(content, targeType, lastClickTimeStamp_ - lastAiPosTimeStamp_)) {
5199 return false;
5200 }
5201
5202 if (pos == static_cast<int32_t>(content.length())) {
5203 return false;
5204 }
5205
5206 if (IsClickBoundary(pos) && targeType == CaretUpdateType::PRESSED) {
5207 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "NeedAiAnalysis IsClickBoundary,return!");
5208 return false;
5209 }
5210 return true;
5211 }
5212
5213 void RichEditorPattern::AdjustCursorPosition(int32_t& pos)
5214 {
5215 // the rich text has some spans, the pos is belong to the whole richtext content, should use (pos - spanStarint)
5216 int32_t spanStart = -1;
5217 // get the span text by the position, maybe text is empty
5218 std::string content = GetPositionSpansText(pos, spanStart);
5219
5220 if (NeedAiAnalysis(CaretUpdateType::PRESSED, pos, spanStart, content)) {
5221 int32_t aiPos = pos - spanStart;
5222 DataDetectorMgr::GetInstance().AdjustCursorPosition(aiPos, content, lastAiPosTimeStamp_, lastClickTimeStamp_);
5223 if (aiPos < 0) {
5224 return;
5225 }
5226 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "get ai pos:%{public}d--spanStart%{public}d", aiPos, spanStart);
5227 pos = aiPos + spanStart;
5228 }
5229 }
5230
5231 bool RichEditorPattern::AdjustWordSelection(int32_t& start, int32_t& end)
5232 {
5233 // the rich text has some spans, the pos is belong to the whole richtext content, should use (pos - spanStarint)
5234 int32_t spanStart = -1;
5235 // get the span text by the position, maybe text is empty
5236 std::string content = GetPositionSpansText(start, spanStart);
5237 if (NeedAiAnalysis(CaretUpdateType::DOUBLE_CLICK, start, spanStart, content)) {
5238 int32_t aiPosStart = start - spanStart;
5239 int32_t aiPosEnd = end - spanStart;
5240 DataDetectorMgr::GetInstance().AdjustWordSelection(aiPosStart, content, aiPosStart, aiPosEnd);
5241 if (aiPosStart < 0 || aiPosEnd < 0) {
5242 return false;
5243 }
5244
5245 start = std::min(aiPosStart + spanStart, GetTextContentLength());
5246 end = std::min(aiPosEnd + spanStart, GetTextContentLength());
5247 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "get ai selector [%{public}d--%{public}d", start, end);
5248 return true;
5249 }
5250 return false;
5251 }
5252
5253 void RichEditorPattern::AdjustPlaceholderSelection(int32_t& start, int32_t& end, const Offset& touchPos)
5254 {
5255 CHECK_NULL_VOID(!spans_.empty());
5256 float selectLineHeight = 0.0f;
5257 auto clickPositionOffset = paragraphs_.ComputeCursorInfoByClick(start, selectLineHeight,
5258 OffsetF(static_cast<float>(touchPos.GetX()), static_cast<float>(touchPos.GetY())));
5259 if (touchPos.GetX() > clickPositionOffset.GetX()) {
5260 return;
5261 }
5262 auto it = std::find_if(spans_.begin(), spans_.end(), [start](const RefPtr<SpanItem>& spanItem) {
5263 return spanItem->position == start;
5264 });
5265 if (it != spans_.end()) {
5266 // adjust selection if touch right of image or placeholder
5267 auto spanIndex = std::distance(spans_.begin(), it);
5268 auto spanNodeBefore = DynamicCast<FrameNode>(GetChildByIndex(spanIndex));
5269 if (spanNodeBefore && (spanNodeBefore->GetTag() == V2::IMAGE_ETS_TAG ||
5270 spanNodeBefore->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG)) {
5271 end = start;
5272 --start;
5273 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "get placeholder selector [%{public}d--%{public}d", start, end);
5274 }
5275 }
5276 }
5277
5278 bool RichEditorPattern::IsClickBoundary(const int32_t position)
5279 {
5280 if (InputAIChecker::IsSingleClickAtBoundary(position, GetTextContentLength())) {
5281 return true;
5282 }
5283
5284 float height = 0;
5285 auto handleOffset = CalcCursorOffsetByPosition(position, height);
5286 if (InputAIChecker::IsMultiClickAtBoundary(handleOffset, TextPattern::GetTextRect())) {
5287 return true;
5288 }
5289 return false;
5290 }
5291
5292 std::string RichEditorPattern::GetPositionSpansText(int32_t position, int32_t& startSpan)
5293 {
5294 int32_t start = position - AI_TEXT_RANGE_LEFT;
5295 int32_t end = position + AI_TEXT_RANGE_RIGHT;
5296
5297 start = std::clamp(start, 0, GetTextContentLength());
5298 end = std::clamp(end, 0, GetTextContentLength());
5299
5300 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "get span pos:%{public}d-start:%{public}d-end:%{public}d", position, start, end);
5301
5302 // get all the spans between start and end, then filter the valid text
5303 auto infos = GetSpansInfo(start, end, GetSpansMethod::ONSELECT);
5304 if (infos.GetSelection().resultObjects.empty()) {
5305 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "get spans text is null pos:%{public}d,return", position);
5306 return "";
5307 }
5308 auto list = infos.GetSelection().resultObjects;
5309
5310 std::stringstream sstream;
5311 for (const auto& obj : list) {
5312 if (obj.type == SelectSpanType::TYPEIMAGE) {
5313 if (obj.spanPosition.spanRange[1] <= position) {
5314 sstream.str("");
5315 startSpan = -1;
5316 } else {
5317 break;
5318 }
5319 } else if (obj.type == SelectSpanType::TYPESPAN) {
5320 if (startSpan < 0) {
5321 startSpan = obj.spanPosition.spanRange[0] + obj.offsetInSpan[0];
5322 }
5323 // we should use the wide string deal to avoid crash
5324 auto wideText = StringUtils::ToWstring(obj.valueString);
5325 sstream << StringUtils::ToString(
5326 wideText.substr(obj.offsetInSpan[0], obj.offsetInSpan[1] - obj.offsetInSpan[0]));
5327 }
5328 }
5329
5330 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "get spans text ret spanStart:%{public}d", startSpan);
5331 return sstream.str();
5332 }
5333
5334 void RichEditorPattern::CheckHandles(SelectHandleInfo& handleInfo)
5335 {
5336 handleInfo.isShow = CheckHandleVisible(handleInfo.paintRect);
5337 }
5338
5339 bool RichEditorPattern::CheckHandleVisible(const RectF& paintRect)
5340 {
5341 auto host = GetHost();
5342 CHECK_NULL_RETURN(host, false);
5343 // use global offset.
5344 RectF visibleContentRect(contentRect_.GetOffset() + parentGlobalOffset_, contentRect_.GetSize());
5345 auto parent = host->GetAncestorNodeOfFrame();
5346 visibleContentRect = GetVisibleContentRect(parent, visibleContentRect);
5347 PointF bottomPoint = { paintRect.Left(), paintRect.Bottom() - BOX_EPSILON };
5348 PointF topPoint = { paintRect.Left(), paintRect.Top() + BOX_EPSILON };
5349 return visibleContentRect.IsInRegion(bottomPoint) && visibleContentRect.IsInRegion(topPoint);
5350 }
5351
5352 bool RichEditorPattern::IsShowSelectMenuUsingMouse()
5353 {
5354 auto pipeline = PipelineContext::GetCurrentContext();
5355 CHECK_NULL_RETURN(pipeline, false);
5356 auto selectOverlayManager = pipeline->GetSelectOverlayManager();
5357 CHECK_NULL_RETURN(selectOverlayManager, false);
5358 return selectOverlayManager->GetSelectOverlayInfo().isUsingMouse;
5359 }
5360
5361 RefPtr<FocusHub> RichEditorPattern::GetFocusHub() const
5362 {
5363 auto host = GetHost();
5364 CHECK_NULL_RETURN(host, nullptr);
5365 auto focusHub = host->GetOrCreateFocusHub();
5366 return focusHub;
5367 }
5368
5369 void RichEditorPattern::HandleCursorOnDragMoved(const RefPtr<NotifyDragEvent>& notifyDragEvent)
5370 {
5371 auto host = GetHost();
5372 CHECK_NULL_VOID(host);
5373 if (isCursorAlwaysDisplayed_) {
5374 if (SystemProperties::GetDebugEnabled()) {
5375 TAG_LOGI(AceLogTag::ACE_TEXT_FIELD,
5376 "In OnDragMoved, the cursor has always Displayed in the textField, id:%{public}d", host->GetId());
5377 }
5378 return;
5379 }
5380 TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
5381 "In OnDragMoved, the dragging node is moving in the richEditor, id:%{public}d", host->GetId());
5382 auto focusHub = GetFocusHub();
5383 CHECK_NULL_VOID(focusHub);
5384 focusHub->RequestFocusImmediately();
5385 isCursorAlwaysDisplayed_ = true;
5386 StartTwinkling();
5387 };
5388
5389 void RichEditorPattern::HandleCursorOnDragLeaved(const RefPtr<NotifyDragEvent>& notifyDragEvent)
5390 {
5391 auto host = GetHost();
5392 CHECK_NULL_VOID(host);
5393 TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
5394 "In OnDragLeaved, the dragging node has left from richEditor, id:%{public}d", host->GetId());
5395 auto focusHub = GetFocusHub();
5396 CHECK_NULL_VOID(focusHub);
5397 focusHub->LostFocus();
5398 isCursorAlwaysDisplayed_ = false;
5399 StopTwinkling();
5400 };
5401
5402 void RichEditorPattern::HandleCursorOnDragEnded(const RefPtr<NotifyDragEvent>& notifyDragEvent)
5403 {
5404 auto host = GetHost();
5405 CHECK_NULL_VOID(host);
5406 auto focusHub = GetFocusHub();
5407 CHECK_NULL_VOID(focusHub);
5408 StopAutoScroll();
5409 if (!isCursorAlwaysDisplayed_) {
5410 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "In OnDragEnded,"
5411 " the released location is not in the current richEditor, id:%{public}d", host->GetId());
5412 focusHub->LostFocus();
5413 StopTwinkling();
5414 return;
5415 }
5416 TAG_LOGI(AceLogTag::ACE_RICH_TEXT,
5417 "In OnDragEnded, the released location is in the current richEditor, id:%{public}d", host->GetId());
5418 if (HasFocus()) {
5419 RequestKeyboard(false, true, true);
5420 } else {
5421 focusHub->RequestFocusImmediately();
5422 }
5423 isCursorAlwaysDisplayed_ = false;
5424 StartTwinkling();
5425 };
5426
5427 void RichEditorPattern::HandleOnDragStatusCallback(
5428 const DragEventType& dragEventType, const RefPtr<NotifyDragEvent>& notifyDragEvent)
5429 {
5430 ScrollablePattern::HandleOnDragStatusCallback(dragEventType, notifyDragEvent);
5431 switch (dragEventType) {
5432 case DragEventType::MOVE:
5433 isDragging_ = true;
5434 HandleCursorOnDragMoved(notifyDragEvent);
5435 break;
5436 case DragEventType::LEAVE:
5437 HandleCursorOnDragLeaved(notifyDragEvent);
5438 break;
5439 case DragEventType::DROP:
5440 isDragging_ = false;
5441 HandleCursorOnDragEnded(notifyDragEvent);
5442 break;
5443 default:
5444 break;
5445 }
5446 }
5447
5448 void RichEditorPattern::HandleOnCameraInput()
5449 {
5450 #if defined(ENABLE_STANDARD_INPUT)
5451 if (richEditTextChangeListener_ == nullptr) {
5452 richEditTextChangeListener_ = new OnTextChangedListenerImpl(WeakClaim(this));
5453 }
5454 auto inputMethod = MiscServices::InputMethodController::GetInstance();
5455 if (!inputMethod) {
5456 return;
5457 }
5458 StartTwinkling();
5459 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
5460 if (imeShown_) {
5461 inputMethod->StartInputType(MiscServices::InputType::CAMERA_INPUT);
5462 } else {
5463 auto optionalTextConfig = GetMiscTextConfig();
5464 CHECK_NULL_VOID(optionalTextConfig.has_value());
5465 MiscServices::TextConfig textConfig = optionalTextConfig.value();
5466 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "HandleOnCameraInput set calling window id is : %{public}u",
5467 textConfig.windowId);
5468 #ifdef WINDOW_SCENE_SUPPORTED
5469 auto systemWindowId = GetSCBSystemWindowId();
5470 if (systemWindowId) {
5471 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "windowId From %{public}u to %{public}u.", textConfig.windowId,
5472 systemWindowId);
5473 textConfig.windowId = systemWindowId;
5474 }
5475 #endif
5476 inputMethod->Attach(richEditTextChangeListener_, false, textConfig);
5477 inputMethod->StartInputType(MiscServices::InputType::CAMERA_INPUT);
5478 inputMethod->ShowTextInput();
5479 }
5480 #endif
5481 #endif
5482 }
5483
5484 bool RichEditorPattern::CanStartAITask()
5485 {
5486 return TextPattern::CanStartAITask() && !HasFocus();
5487 }
5488
5489 bool RichEditorPattern::NeedShowAIDetect()
5490 {
5491 return TextPattern::NeedShowAIDetect() && !HasFocus();
5492 }
5493
5494 void RichEditorPattern::ToJsonValue(std::unique_ptr<JsonValue>& json) const
5495 {
5496 json->Put("enableDataDetector", textDetectEnable_ ? "true" : "false");
5497 auto jsonValue = JsonUtil::Create(true);
5498 jsonValue->Put("types", "");
5499 json->Put("dataDetectorConfig", jsonValue->ToString().c_str());
5500 }
5501
5502 void RichEditorPattern::GetCaretMetrics(CaretMetricsF& caretCaretMetric)
5503 {
5504 float caretHeight = 0.0f;
5505 OffsetF caretOffset = CalcCursorOffsetByPosition(GetCaretPosition(), caretHeight);
5506 auto host = GetHost();
5507 CHECK_NULL_VOID(host);
5508 auto offset = host->GetPaintRectOffset();
5509 caretOffset += offset;
5510 caretCaretMetric.offset = caretOffset;
5511 caretCaretMetric.height = caretHeight;
5512 }
5513
5514 void RichEditorPattern::OnVirtualKeyboardAreaChanged()
5515 {
5516 CHECK_NULL_VOID(SelectOverlayIsOn());
5517 float selectLineHeight = 0.0f;
5518 textSelector_.selectionBaseOffset.SetX(
5519 CalcCursorOffsetByPosition(textSelector_.GetStart(), selectLineHeight).GetX());
5520 textSelector_.selectionDestinationOffset.SetX(
5521 CalcCursorOffsetByPosition(textSelector_.GetEnd(), selectLineHeight).GetX());
5522 CreateHandles();
5523 }
5524
5525 void RichEditorPattern::ResetDragOption()
5526 {
5527 auto host = GetHost();
5528 CHECK_NULL_VOID(host);
5529 auto gestureEventHub = host->GetOrCreateGestureEventHub();
5530 CHECK_NULL_VOID(gestureEventHub);
5531 if (gestureEventHub->GetIsTextDraggable()) {
5532 CloseSelectOverlay();
5533 ResetSelection();
5534 }
5535 }
5536
5537 RectF RichEditorPattern::GetSelectArea()
5538 {
5539 auto selectRects = paragraphs_.GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
5540 if (selectRects.empty()) {
5541 float caretHeight = 0.0f;
5542 auto caretOffset = CalcCursorOffsetByPosition(GetCaretPosition(), caretHeight);
5543 auto caretWidth = Dimension(1.5f, DimensionUnit::VP).ConvertToPx();
5544 return RectF(caretOffset + parentGlobalOffset_, SizeF(caretWidth, caretHeight));
5545 }
5546 auto frontRect = selectRects.front();
5547 auto backRect = selectRects.back();
5548 RectF res;
5549 if (GreatNotEqual(backRect.Bottom(), frontRect.Bottom())) {
5550 res.SetRect(contentRect_.GetX() + parentGlobalOffset_.GetX(),
5551 frontRect.GetY() + richTextRect_.GetY() + parentGlobalOffset_.GetY(), contentRect_.Width(),
5552 backRect.Bottom() - frontRect.Top());
5553 } else {
5554 res.SetRect(frontRect.GetX() + richTextRect_.GetX() + parentGlobalOffset_.GetX(),
5555 frontRect.GetY() + richTextRect_.GetY() + parentGlobalOffset_.GetY(), backRect.Right() - frontRect.Left(),
5556 backRect.Bottom() - frontRect.Top());
5557 }
5558 auto contentRect = contentRect_;
5559 contentRect.SetOffset(contentRect.GetOffset() + parentGlobalOffset_);
5560 auto host = GetHost();
5561 CHECK_NULL_RETURN(host, RectF(0, 0, 0, 0));
5562 auto parent = host->GetAncestorNodeOfFrame();
5563 contentRect = GetVisibleContentRect(parent, contentRect);
5564 return res.IntersectRectT(contentRect);
5565 }
5566
5567 void RichEditorPattern::UpdateOverlaySelectArea()
5568 {
5569 CHECK_NULL_VOID(selectOverlayProxy_);
5570 selectOverlayProxy_->UpdateSelectArea(GetSelectArea());
5571 }
5572
5573 bool RichEditorPattern::IsTouchInFrameArea(const PointF& touchPoint)
5574 {
5575 auto host = GetHost();
5576 CHECK_NULL_RETURN(host, false);
5577 auto viewPort = RectF(parentGlobalOffset_, frameRect_.GetSize());
5578 auto parent = host->GetAncestorNodeOfFrame();
5579 viewPort = GetVisibleContentRect(parent, viewPort);
5580 return viewPort.IsInRegion(touchPoint);
5581 }
5582 } // namespace OHOS::Ace::NG
5583