• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/text/text_pattern.h"
17 
18 #include <cstdint>
19 #include <iterator>
20 #include <stack>
21 #include <string>
22 
23 #include "adapter/ohos/capability/clipboard/clipboard_impl.h"
24 #include "base/geometry/ng/offset_t.h"
25 #include "base/geometry/ng/point_t.h"
26 #include "base/geometry/ng/rect_t.h"
27 #include "base/geometry/offset.h"
28 #include "base/log/dump_log.h"
29 #include "base/log/log_wrapper.h"
30 #include "base/utils/utf_helper.h"
31 #include "base/utils/string_utils.h"
32 #include "base/utils/utils.h"
33 #include "base/window/drag_window.h"
34 #include "core/common/ace_engine_ext.h"
35 #include "core/common/ai/data_detector_mgr.h"
36 #include "core/common/container.h"
37 #include "core/common/container_scope.h"
38 #include "core/common/font_manager.h"
39 #include "core/common/recorder/node_data_cache.h"
40 #include "core/common/udmf/udmf_client.h"
41 #include "core/common/vibrator/vibrator_utils.h"
42 #include "core/components/common/properties/text_style_parser.h"
43 #include "core/components_ng/gestures/recognizers/gesture_recognizer.h"
44 #include "core/components_ng/pattern/rich_editor_drag/rich_editor_drag_pattern.h"
45 #include "core/text/text_emoji_processor.h"
46 #ifdef ENABLE_ROSEN_BACKEND
47 #include "core/components/custom_paint/rosen_render_custom_paint.h"
48 #endif
49 
50 namespace OHOS::Ace::NG {
51 namespace {
52 constexpr double DIMENSION_VALUE = 16.0;
53 constexpr char COPY[] = "copy";
54 constexpr char SELECT_TEXT[] = "selectText";
55 constexpr const char SYMBOL_COLOR[] = "BLACK";
56 constexpr int32_t API_PROTEXTION_GREATER_NINE = 9;
57 const std::u16string SYMBOL_TRANS = u"\uF0001";
58 const std::u16string WIDE_NEWLINE = u"\n";
59 constexpr float RICH_DEFAULT_SHADOW_COLOR = 0x33000000;
60 constexpr float RICH_DEFAULT_ELEVATION = 120.0f;
61 constexpr Dimension CLICK_THRESHOLD = 5.0_vp;
62 const OffsetF DEFAULT_NEGATIVE_CARET_OFFSET {-1.0f, -1.0f};
63 
IsJumpLink(const std::string & content)64 bool IsJumpLink(const std::string& content)
65 {
66     // start with http:// or https://
67     std::regex pattern(R"(https?://[^\s]+)");
68     return std::regex_match(content, pattern);
69 }
70 }; // namespace
71 
~TextPattern()72 TextPattern::~TextPattern()
73 {
74     // node destruct, need to stop text race animation
75     CHECK_NULL_VOID(contentMod_);
76     contentMod_->StopTextRace();
77 }
78 
OnWindowHide()79 void TextPattern::OnWindowHide()
80 {
81     if (magnifierController_) {
82         magnifierController_->RemoveMagnifierFrameNode();
83     }
84     CHECK_NULL_VOID(contentMod_);
85     contentMod_->PauseAnimation();
86     auto host = GetHost();
87     CHECK_NULL_VOID(host);
88     TAG_LOGD(AceLogTag::ACE_TEXT, "OnWindowHide [%{public}d]", host->GetId());
89     PauseSymbolAnimation();
90 }
91 
OnWindowShow()92 void TextPattern::OnWindowShow()
93 {
94     CHECK_NULL_VOID(contentMod_);
95     contentMod_->ResumeAnimation();
96     auto host = GetHost();
97     CHECK_NULL_VOID(host);
98     TAG_LOGD(AceLogTag::ACE_TEXT, "OnWindowShow [%{public}d]", host->GetId());
99     ResumeSymbolAnimation();
100 }
101 
OnAttachToFrameNode()102 void TextPattern::OnAttachToFrameNode()
103 {
104     auto host = GetHost();
105     CHECK_NULL_VOID(host);
106     auto pipeline = host->GetContext();
107     CHECK_NULL_VOID(pipeline);
108     pipeline_ = pipeline;
109     auto fontManager = pipeline->GetFontManager();
110     if (fontManager) {
111         fontManager->AddFontNodeNG(host);
112     }
113     if (host->LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
114         if (pipeline->GetMinPlatformVersion() > API_PROTEXTION_GREATER_NINE) {
115             host->GetRenderContext()->UpdateClipEdge(true);
116             host->GetRenderContext()->SetClipToFrame(true);
117         }
118     }
119     InitSurfaceChangedCallback();
120     InitSurfacePositionChangedCallback();
121     pipeline->AddWindowStateChangedCallback(host->GetId());
122     pipeline->AddWindowSizeChangeCallback(host->GetId());
123     if (host->GetTag() == V2::SYMBOL_ETS_TAG) {
124         ProcessVisibleAreaCallback();
125     }
126     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
127     CHECK_NULL_VOID(textLayoutProperty);
128     auto theme = pipeline->GetTheme<TextTheme>();
129     CHECK_NULL_VOID(theme);
130     textLayoutProperty->UpdateTextAlign(theme->GetTextStyle().GetTextAlign());
131     textLayoutProperty->UpdateAlignment(Alignment::CENTER_LEFT);
132 }
133 
OnDetachFromFrameNode(FrameNode * node)134 void TextPattern::OnDetachFromFrameNode(FrameNode* node)
135 {
136     dataDetectorAdapter_->aiDetectDelayTask_.Cancel();
137     CloseSelectOverlay();
138     auto pipeline = pipeline_.Upgrade();
139     CHECK_NULL_VOID(pipeline);
140     if (HasSurfaceChangedCallback()) {
141         pipeline->UnregisterSurfaceChangedCallback(surfaceChangedCallbackId_.value_or(-1));
142     }
143     if (HasSurfacePositionChangedCallback()) {
144         pipeline->UnregisterSurfacePositionChangedCallback(surfacePositionChangedCallbackId_.value_or(-1));
145     }
146     auto frameNode = WeakClaim(node);
147     pipeline->RemoveFontNodeNG(frameNode);
148     auto fontManager = pipeline->GetFontManager();
149     if (fontManager) {
150         fontManager->UnRegisterCallbackNG(frameNode);
151         fontManager->RemoveVariationNodeNG(frameNode);
152     }
153     pipeline->RemoveOnAreaChangeNode(node->GetId());
154     pipeline->RemoveWindowStateChangedCallback(node->GetId());
155     pipeline->RemoveVisibleAreaChangeNode(node->GetId());
156     pipeline->RemoveWindowSizeChangeCallback(node->GetId());
157 }
158 
CloseSelectOverlay()159 void TextPattern::CloseSelectOverlay()
160 {
161     CloseSelectOverlay(false);
162 }
163 
CloseSelectOverlay(bool animation)164 void TextPattern::CloseSelectOverlay(bool animation)
165 {
166     // Deprecated use selectOverlay_ instead.
167     if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
168         selectOverlayProxy_->Close(animation);
169         RemoveAreaChangeInner();
170     }
171     selectOverlay_->CloseOverlay(animation, CloseReason::CLOSE_REASON_NORMAL);
172 }
173 
ResetSelection()174 void TextPattern::ResetSelection()
175 {
176     if (textSelector_.IsValid() && !shiftFlag_) {
177         HandleSelectionChange(-1, -1);
178         auto host = GetHost();
179         CHECK_NULL_VOID(host);
180         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
181     }
182 }
183 
InitSelection(const Offset & pos)184 void TextPattern::InitSelection(const Offset& pos)
185 {
186     CHECK_NULL_VOID(pManager_);
187     auto selectionOffset = pos;
188     if (GreatNotEqual(selectionOffset.GetY(), pManager_->GetHeight())) {
189         selectionOffset.SetX(contentRect_.Width());
190         selectionOffset.SetY(pManager_->GetHeight());
191     }
192     int32_t extend = pManager_->GetGlyphIndexByCoordinate(selectionOffset, true);
193     if (pManager_->GetParagraphs().size() > 1) {
194         // paragraph may contain only newlines, look forward for non-newlines characters.
195         auto selectRects = pManager_->GetRects(extend, extend + 1);
196         if (selectRects.size() == 1 && NearZero(selectRects.back().Width())) {
197             auto selectStr = GetSelectedText(extend, extend + 1);
198             while (selectStr == u"\n" && extend > 0) {
199                 --extend;
200                 selectStr = GetSelectedText(extend, extend + 1);
201             }
202         }
203     }
204     int32_t start = 0;
205     int32_t end = 0;
206     if (!pManager_->GetWordBoundary(extend, start, end)) {
207         start = extend;
208         end = std::min(static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_,
209             extend + GetGraphemeClusterLength(textForDisplay_, extend));
210     }
211     HandleSelectionChange(start, end);
212 }
213 
CalcCaretMetricsByPosition(int32_t extent,CaretMetricsF & caretCaretMetric,TextAffinity textAffinity)214 void TextPattern::CalcCaretMetricsByPosition(int32_t extent, CaretMetricsF& caretCaretMetric, TextAffinity textAffinity)
215 {
216     auto host = GetHost();
217     CHECK_NULL_VOID(host);
218     auto rect = host->GetGeometryNode()->GetFrameRect();
219     CHECK_NULL_VOID(pManager_);
220     auto computeSuccess = pManager_->CalcCaretMetricsByPosition(extent, caretCaretMetric, textAffinity);
221     if (!computeSuccess) {
222         caretCaretMetric = CaretMetricsF(OffsetF(0.0f, rect.Height()), 0.0f);
223     }
224 }
225 
CalculateHandleOffsetAndShowOverlay(bool isUsingMouse)226 void TextPattern::CalculateHandleOffsetAndShowOverlay(bool isUsingMouse)
227 {
228     parentGlobalOffset_ = GetParentGlobalOffset();
229     auto textContentGlobalOffset = selectOverlay_->GetHandleGlobalOffset() + contentRect_.GetOffset();
230     auto paragraphPaintOffset = textContentGlobalOffset - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
231 
232     // calculate firstHandleOffset, secondHandleOffset and handlePaintSize
233     CaretMetricsF firstHandleMetrics;
234     CaretMetricsF secondHandleMetrics;
235     CalcCaretMetricsByPosition(textSelector_.baseOffset, firstHandleMetrics, TextAffinity::DOWNSTREAM);
236     CalcCaretMetricsByPosition(textSelector_.destinationOffset, secondHandleMetrics, TextAffinity::UPSTREAM);
237     OffsetF firstHandleOffset = firstHandleMetrics.offset + paragraphPaintOffset;
238     OffsetF secondHandleOffset = secondHandleMetrics.offset + paragraphPaintOffset;
239 
240     textSelector_.selectionBaseOffset = firstHandleOffset;
241     textSelector_.selectionDestinationOffset = secondHandleOffset;
242 
243     RectF firstHandle;
244     firstHandle.SetOffset(firstHandleOffset);
245     firstHandle.SetSize({ SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), firstHandleMetrics.height });
246     firstHandle.SetOffset(OffsetF(firstHandle.GetX() - firstHandle.Width() / 2.0f, firstHandle.GetY()));
247     textSelector_.firstHandle = firstHandle;
248 
249     RectF secondHandle;
250     secondHandle.SetOffset(secondHandleOffset);
251     secondHandle.SetSize({ SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), secondHandleMetrics.height });
252     secondHandle.SetHeight(secondHandleMetrics.height);
253     secondHandle.SetOffset(OffsetF(secondHandle.GetX() - secondHandle.Width() / 2.0f, secondHandle.GetY()));
254     textSelector_.secondHandle = secondHandle;
255 }
256 
GetSpansInfoInStyledString(int32_t start,int32_t end)257 std::list<ResultObject> TextPattern::GetSpansInfoInStyledString(int32_t start, int32_t end)
258 {
259     std::list<ResultObject> resultObjects;
260     int32_t imageIndex = 0;
261     for (const auto& item : spans_) {
262         auto obj = item->GetSpanResultObject(start, end);
263         if (obj.type == SelectSpanType::TYPEIMAGE) {
264             obj.spanPosition.spanIndex = imageIndex;
265             ++imageIndex;
266         }
267         if (obj.isInit) {
268             resultObjects.emplace_back(obj);
269         }
270     }
271     return resultObjects;
272 }
273 
GetSpansInfo(int32_t start,int32_t end,GetSpansMethod method)274 SelectionInfo TextPattern::GetSpansInfo(int32_t start, int32_t end, GetSpansMethod method)
275 {
276     int32_t index = 0;
277     std::int32_t realEnd = 0;
278     std::int32_t realStart = 0;
279     SelectionInfo selection;
280     std::list<ResultObject> resultObjects;
281     auto length = GetTextContentLength();
282     if (method == GetSpansMethod::GETSPANS) {
283         realStart = (start == -1) ? 0 : start;
284         realEnd = (end == -1) ? length : end;
285         if (realStart > realEnd) {
286             std::swap(realStart, realEnd);
287         }
288         realStart = std::max(0, realStart);
289         realEnd = std::min(length, realEnd);
290     } else if (method == GetSpansMethod::ONSELECT) {
291         realEnd = std::min(length, end);
292         realStart = std::min(length, start);
293     }
294     selection.SetSelectionEnd(realEnd);
295     selection.SetSelectionStart(realStart);
296     // Verify that realStart, realEnd, and spans_ are valid
297     if (realStart > length || realEnd < 0 || spans_.empty() || (start > length && end > length) ||
298         (method == GetSpansMethod::ONSELECT && realStart == realEnd)) {
299         selection.SetResultObjectList(resultObjects);
300         return selection;
301     }
302     if (isSpanStringMode_) {
303         auto result = GetSpansInfoInStyledString(realStart, realEnd);
304         selection.SetResultObjectList(result);
305         return selection;
306     }
307     const auto& children = GetAllChildren();
308     for (const auto& uinode : children) {
309         if (uinode->GetTag() == V2::IMAGE_ETS_TAG) {
310             ResultObject resultObject = GetImageResultObject(uinode, index, realStart, realEnd);
311             if (!resultObject.valueString.empty() || resultObject.valuePixelMap) {
312                 resultObjects.emplace_back(resultObject);
313             }
314         } else if (uinode->GetTag() == V2::SPAN_ETS_TAG) {
315             ResultObject resultObject = GetTextResultObject(uinode, index, realStart, realEnd);
316             if (!resultObject.valueString.empty()) {
317                 resultObjects.emplace_back(resultObject);
318             }
319         } else if (uinode->GetTag() == V2::SYMBOL_SPAN_ETS_TAG) {
320             ResultObject resultObject = GetSymbolSpanResultObject(uinode, index, realStart, realEnd);
321             if (!resultObject.valueString.empty()) {
322                 resultObjects.emplace_back(resultObject);
323             }
324         } else if (uinode->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG ||
325             uinode->GetTag() == V2::CUSTOM_SPAN_NODE_ETS_TAG) {
326             ResultObject resultObject = GetBuilderResultObject(uinode, index, realStart, realEnd);
327             if (!resultObject.valueString.empty()) {
328                 resultObjects.emplace_back(resultObject);
329             }
330         }
331         index++;
332     }
333     selection.SetResultObjectList(resultObjects);
334     return selection;
335 }
336 
GetTextContentLength()337 int32_t TextPattern::GetTextContentLength()
338 {
339     if (!spans_.empty()) {
340         return static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_;
341     }
342     return 0;
343 }
344 
StartVibratorByLongPress()345 void TextPattern::StartVibratorByLongPress()
346 {
347     CHECK_NULL_VOID(isEnableHapticFeedback_);
348     VibratorUtils::StartVibraFeedback("longPress.light");
349 }
350 
HandleLongPress(GestureEvent & info)351 void TextPattern::HandleLongPress(GestureEvent& info)
352 {
353     HandleSpanLongPressEvent(info);
354     if (!IsSelectableAndCopy() || isMousePressed_ || selectOverlay_->GetIsHandleDragging()) {
355         return;
356     }
357     auto host = GetHost();
358     CHECK_NULL_VOID(host);
359     auto hub = host->GetEventHub<EventHub>();
360     CHECK_NULL_VOID(hub);
361     auto gestureHub = hub->GetOrCreateGestureEventHub();
362     CHECK_NULL_VOID(gestureHub);
363     auto localOffset = info.GetLocalLocation();
364     if (selectOverlay_->HasRenderTransform()) {
365         localOffset = ConvertGlobalToLocalOffset(info.GetGlobalLocation());
366     }
367 
368     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
369     if ((textLayoutProperty && textLayoutProperty->GetMaxLines() != 0) && textForDisplay_.length() != 0) {
370         StartVibratorByLongPress();
371     }
372 
373     if (IsDraggable(localOffset)) {
374         // prevent long press event from being triggered when dragging
375         gestureHub->SetIsTextDraggable(true);
376         return;
377     }
378     gestureHub->SetIsTextDraggable(false);
379     auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
380     Offset textOffset = { localOffset.GetX() - textPaintOffset.GetX(), localOffset.GetY() - textPaintOffset.GetY() };
381     InitSelection(textOffset);
382     textResponseType_ = TextResponseType::LONG_PRESS;
383     UpdateSelectionSpanType(std::min(textSelector_.baseOffset, textSelector_.destinationOffset),
384         std::max(textSelector_.baseOffset, textSelector_.destinationOffset));
385     oldSelectedType_ = selectedType_.value_or(TextSpanType::NONE);
386     parentGlobalOffset_ = GetParentGlobalOffset();
387     CalculateHandleOffsetAndShowOverlay();
388     CloseSelectOverlay(true);
389     if (GetOrCreateMagnifier() && HasContent()) {
390         magnifierController_->SetLocalOffset({ localOffset.GetX(), localOffset.GetY() });
391     }
392     StartGestureSelection(textSelector_.GetStart(), textSelector_.GetEnd(), localOffset);
393     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
394 }
395 
ShowShadow(const PointF & textOffset,const Color & color)396 bool TextPattern::ShowShadow(const PointF& textOffset, const Color& color)
397 {
398     CHECK_NULL_RETURN(overlayMod_, false);
399     CHECK_NULL_RETURN(hasUrlSpan_, false);
400     CHECK_NULL_RETURN(!spans_.empty() && pManager_, false);
401     int32_t start = 0;
402     for (const auto& item : spans_) {
403         if (!item) {
404             continue;
405         }
406         auto selectedRects = GetSelectedRects(start, item->position);
407         for (auto&& rect : selectedRects) {
408             if (!rect.IsInRegion(textOffset)) {
409                 continue;
410             }
411             if (!item->urlOnRelease) {
412                 overlayMod_->ClearSelectedForegroundColorAndRects();
413                 MarkDirtySelf();
414                 return false;
415             }
416             auto inter = GetStartAndEnd(start, item);
417             auto rects = GetSelectedRects(inter.first, inter.second);
418             overlayMod_->SetSelectedForegroundColorAndRects(rects, color.GetValue());
419             MarkDirtySelf();
420             return true;
421         }
422         start = item->position;
423     }
424     overlayMod_->ClearSelectedForegroundColorAndRects();
425     MarkDirtySelf();
426     return false;
427 }
428 
GetStartAndEnd(int32_t start,const RefPtr<SpanItem> & spanItem)429 std::pair<int32_t, int32_t> TextPattern::GetStartAndEnd(int32_t start, const RefPtr<SpanItem>& spanItem)
430 {
431     auto spanBases = styledString_->GetSpans(0, styledString_->GetLength(), SpanType::Url);
432     for (const auto& spanBase : spanBases) {
433         if (start >= spanBase->GetStartIndex() && start < spanBase->GetEndIndex()) {
434             return {spanBase->GetStartIndex(), spanBase->GetEndIndex()};
435         }
436     }
437     return {0, 0};
438 }
439 
HandleSpanLongPressEvent(GestureEvent & info)440 void TextPattern::HandleSpanLongPressEvent(GestureEvent& info)
441 {
442     RectF textContentRect = contentRect_;
443     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
444     textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
445 
446     auto localLocation = info.GetLocalLocation();
447     if (selectOverlay_->HasRenderTransform()) {
448         localLocation = ConvertGlobalToLocalOffset(info.GetGlobalLocation());
449     }
450 
451     auto host = GetHost();
452     CHECK_NULL_VOID(host);
453     auto renderContext = host->GetRenderContext();
454     CHECK_NULL_VOID(renderContext);
455     PointF textOffset = { static_cast<float>(localLocation.GetX()) - textContentRect.GetX(),
456         static_cast<float>(localLocation.GetY()) - textContentRect.GetY() };
457     if (renderContext->GetClipEdge().has_value() && !renderContext->GetClipEdge().value() && overlayMod_) {
458         textContentRect = overlayMod_->GetBoundsRect();
459         textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
460     }
461     auto longPressFunc = [](RefPtr<SpanItem> item, GestureEvent& info, const RectF& rect,
462                              const PointF& textOffset) -> bool {
463         if (rect.IsInRegion(textOffset)) {
464             if (item && item->onLongPress) {
465                 item->onLongPress(info);
466             }
467             return true;
468         }
469         return false;
470     };
471 
472     if (textContentRect.IsInRegion(
473         PointF(static_cast<float>(localLocation.GetX()), static_cast<float>(localLocation.GetY()))) &&
474         !spans_.empty() && pManager_) {
475         int32_t start = 0;
476         for (const auto& item : spans_) {
477             if (!item) {
478                 continue;
479             }
480             auto selectedRects = GetSelectedRects(start, item->position);
481             for (auto&& rect : selectedRects) {
482                 CHECK_NULL_VOID(!longPressFunc(item, info, rect, textOffset));
483             }
484             start = item->position;
485         }
486     }
487 }
488 
489 // Deprecated: Use the TextSelectOverlay::OnHandleMove() instead.
490 // It is currently used by RichEditorPattern.
OnHandleMove(const RectF & handleRect,bool isFirstHandle)491 void TextPattern::OnHandleMove(const RectF& handleRect, bool isFirstHandle)
492 {
493     auto host = GetHost();
494     CHECK_NULL_VOID(host);
495     auto textContentGlobalOffset = parentGlobalOffset_ + contentRect_.GetOffset();
496     auto textPaintOffset = textContentGlobalOffset - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
497 
498     auto localOffset = handleRect.GetOffset();
499 
500     auto renderContext = host->GetRenderContext();
501     CHECK_NULL_VOID(renderContext);
502     auto clip = false;
503     if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
504         clip = true;
505     }
506     if (renderContext->GetClipEdge().value_or(clip)) {
507         if (localOffset.GetX() < textContentGlobalOffset.GetX()) {
508             localOffset.SetX(textContentGlobalOffset.GetX());
509         } else if (GreatOrEqual(localOffset.GetX(), textContentGlobalOffset.GetX() + contentRect_.Width())) {
510             localOffset.SetX(textContentGlobalOffset.GetX() + contentRect_.Width());
511         }
512 
513         if (localOffset.GetY() < textContentGlobalOffset.GetY()) {
514             localOffset.SetY(textContentGlobalOffset.GetY());
515         } else if (GreatNotEqual(localOffset.GetY(), textContentGlobalOffset.GetY() + contentRect_.Height())) {
516             localOffset.SetY(textContentGlobalOffset.GetY() + contentRect_.Height());
517         }
518     }
519 
520     localOffset -= textPaintOffset;
521 
522     CHECK_NULL_VOID(pManager_);
523     // the handle position is calculated based on the middle of the handle height.
524     UpdateSelectorOnHandleMove(localOffset, handleRect.Height(), isFirstHandle);
525     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
526 
527     CHECK_NULL_VOID(selectOverlayProxy_);
528     auto start = textSelector_.GetTextStart();
529     auto end = textSelector_.GetTextEnd();
530     selectOverlayProxy_->SetSelectInfo(UtfUtils::Str16DebugToStr8(GetSelectedText(start, end)));
531 }
532 
UpdateSelectorOnHandleMove(const OffsetF & localOffset,float handleHeight,bool isFirstHandle)533 void TextPattern::UpdateSelectorOnHandleMove(const OffsetF& localOffset, float handleHeight, bool isFirstHandle)
534 {
535     if (isFirstHandle) {
536         auto start = GetHandleIndex(Offset(
537             localOffset.GetX(), localOffset.GetY() + (selectOverlayProxy_->IsHandleReverse() ? handleHeight : 0)));
538         HandleSelectionChange(start, textSelector_.destinationOffset);
539     } else {
540         auto end = GetHandleIndex(Offset(localOffset.GetX(),
541             localOffset.GetY() +
542                 (selectOverlayProxy_->IsHandleReverse() || NearEqual(localOffset.GetY(), 0) ? 0 : handleHeight)));
543         HandleSelectionChange(textSelector_.baseOffset, end);
544     }
545 }
546 
IsSelectAll()547 bool TextPattern::IsSelectAll()
548 {
549     return textSelector_.GetTextStart() == 0 &&
550            textSelector_.GetTextEnd() == static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_;
551 }
552 
GetSelectedText(int32_t start,int32_t end,bool includeStartHalf,bool includeEndHalf,bool getSubstrDirectly) const553 std::u16string TextPattern::GetSelectedText(int32_t start, int32_t end, bool includeStartHalf,
554     bool includeEndHalf, bool getSubstrDirectly) const
555 {
556     if (spans_.empty()) {
557         auto min = std::clamp(std::max(std::min(start, end), 0), 0, static_cast<int32_t>(textForDisplay_.length()));
558         auto max = std::clamp(std::min(std::max(start, end), static_cast<int32_t>(textForDisplay_.length())), 0,
559             static_cast<int32_t>(textForDisplay_.length()));
560         if (max - min < 0) {
561             return std::u16string();
562         }
563         if (getSubstrDirectly) {
564             return textForDisplay_.substr(min, max - min);
565         } else {
566             return TextEmojiProcessor::SubU16string(min, max - min, textForDisplay_, includeStartHalf, includeEndHalf);
567         }
568     }
569     std::u16string value;
570     int32_t tag = 0;
571     for (const auto& span : spans_) {
572         if (span->GetSymbolUnicode() != 0) {
573             tag = span->position == -1 ? tag + 1 : span->position;
574             continue;
575         }
576         if (span->position - 1 >= start && span->placeholderIndex == -1 && span->position != -1) {
577             auto wideString = span->GetSpanContent();
578             auto max = std::min(span->position, end);
579             auto min = std::max(start, tag);
580             if (getSubstrDirectly) {
581                 value += wideString.substr(std::clamp((min - tag), 0, static_cast<int32_t>(wideString.length())),
582                     std::clamp((max - min), 0, static_cast<int32_t>(wideString.length())));
583             } else {
584                 value += TextEmojiProcessor::SubU16string(
585                     std::clamp((min - tag), 0, static_cast<int32_t>(wideString.length())),
586                     std::clamp((max - min), 0, static_cast<int32_t>(wideString.length())),
587                     wideString, includeStartHalf, includeEndHalf);
588             }
589         } else if (span->position - 1 >= start && span->position != -1) {
590             // image span or custom span (span->placeholderIndex != -1)
591             value += u" ";
592         }
593         tag = span->position == -1 ? tag + 1 : span->position;
594         if (span->position >= end) {
595             break;
596         }
597     }
598     return value;
599 }
600 
HandleOnCopy()601 void TextPattern::HandleOnCopy()
602 {
603     CHECK_NULL_VOID(clipboard_);
604     if (textSelector_.IsValid() && textSelector_.GetTextStart() == textSelector_.GetTextEnd()) {
605         HandleSelectionChange(-1, -1);
606         return;
607     }
608     auto value = GetSelectedText(textSelector_.GetTextStart(), textSelector_.GetTextEnd(), false, false, true);
609     if (IsSelectableAndCopy() || dataDetectorAdapter_->hasClickedMenuOption_) {
610         if (isSpanStringMode_ && !externalParagraph_) {
611             HandleOnCopySpanString();
612         } else if (!value.empty()) {
613             HandleOnCopyWithoutSpanString(UtfUtils::Str16DebugToStr8(value));
614         }
615     }
616     HiddenMenu();
617     CHECK_NULL_VOID(!value.empty());
618     auto host = GetHost();
619     CHECK_NULL_VOID(host);
620     auto eventHub = host->GetEventHub<TextEventHub>();
621     CHECK_NULL_VOID(eventHub);
622     eventHub->FireOnCopy(value);
623 }
624 
HandleOnCopySpanString()625 void TextPattern::HandleOnCopySpanString()
626 {
627     auto subSpanString = styledString_->GetSubSpanString(textSelector_.GetTextStart(),
628         textSelector_.GetTextEnd() - textSelector_.GetTextStart());
629 #if defined(PREVIEW)
630     clipboard_->SetData(subSpanString->GetString(), copyOption_);
631     return;
632 #endif
633     RefPtr<PasteDataMix> pasteData = clipboard_->CreatePasteDataMix();
634     std::vector<uint8_t> tlvData;
635     subSpanString->EncodeTlv(tlvData);
636     clipboard_->AddSpanStringRecord(pasteData, tlvData);
637     clipboard_->AddTextRecord(pasteData, subSpanString->GetString());
638     clipboard_->SetData(pasteData, copyOption_);
639 }
640 
HandleOnCopyWithoutSpanString(const std::string & pasteData)641 void TextPattern::HandleOnCopyWithoutSpanString(const std::string& pasteData)
642 {
643 #if defined(PREVIEW)
644     clipboard_->SetData(pasteData, copyOption_);
645     return;
646 #endif
647     RefPtr<PasteDataMix> pasteDataMix = clipboard_->CreatePasteDataMix();
648     auto multiTypeRecordImpl = AceType::MakeRefPtr<MultiTypeRecordImpl>();
649     if (spans_.empty()) {
650         EncodeTlvNoChild(pasteData, multiTypeRecordImpl->GetSpanStringBuffer());
651     } else {
652         EncodeTlvSpanItems(pasteData, multiTypeRecordImpl->GetSpanStringBuffer());
653     }
654     multiTypeRecordImpl->SetPlainText(pasteData);
655     clipboard_->AddMultiTypeRecord(pasteDataMix, multiTypeRecordImpl);
656     clipboard_->SetData(pasteDataMix, copyOption_);
657 }
658 
659 #define WRITE_TLV_INHERIT(group, name, tag, type, inheritName)   \
660     if ((group)->Has##name()) {                                  \
661         TLVUtil::WriteUint8(buff, (tag));                        \
662         TLVUtil::Write##type(buff, (group)->prop##name.value()); \
663     } else if (textStyle_.has_value()) {                         \
664         auto temp##name = textStyle_->Get##inheritName();        \
665         TLVUtil::WriteUint8(buff, (tag));                        \
666         TLVUtil::Write##type(buff, temp##name);                  \
667     }
668 
669 #define WRITE_TEXT_STYLE_TLV(group, name, tag, type)                 \
670     do {                                                             \
671         if ((group)->Has##name()) {                                  \
672             TLVUtil::WriteUint8(buff, (tag));                        \
673             TLVUtil::Write##type(buff, (group)->prop##name.value()); \
674         }                                                            \
675     } while (false)
676 
EncodeTlvNoChild(const std::string & pasteData,std::vector<uint8_t> & buff)677 void TextPattern::EncodeTlvNoChild(const std::string& pasteData, std::vector<uint8_t>& buff)
678 {
679     TLVUtil::WriteUint8(buff, TLV_SPAN_STRING_SPANS);
680     TLVUtil::WriteInt32(buff, 1);
681 
682     TLVUtil::WriteInt32(buff, static_cast<int32_t>(NG::SpanItemType::NORMAL));
683     TLVUtil::WriteUint8(buff, TLV_SPANITEM_TAG);
684     TLVUtil::WriteInt32(buff, 0);
685     TLVUtil::WriteInt32(buff, pasteData.length());
686     TLVUtil::WriteString(buff, pasteData);
687     EncodeTlvFontStyleNoChild(buff);
688     EncodeTlvTextLineStyleNoChild(buff);
689     TLVUtil::WriteUint8(buff, TLV_SPANITEM_END_TAG);
690 
691     TLVUtil::WriteUint8(buff, TLV_SPAN_STRING_CONTENT);
692     TLVUtil::WriteString(buff, pasteData);
693     TLVUtil::WriteUint8(buff, TLV_END);
694 }
695 
EncodeTlvFontStyleNoChild(std::vector<uint8_t> & buff)696 void TextPattern::EncodeTlvFontStyleNoChild(std::vector<uint8_t>& buff)
697 {
698     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
699     CHECK_NULL_VOID(textLayoutProperty);
700     auto& fontStyle = textLayoutProperty->GetFontStyle();
701     CHECK_NULL_VOID(fontStyle);
702     WRITE_TLV_INHERIT(fontStyle, FontSize, TLV_SPAN_FONT_STYLE_FONTSIZE, Dimension, FontSize);
703     WRITE_TLV_INHERIT(fontStyle, TextColor, TLV_SPAN_FONT_STYLE_TEXTCOLOR, Color, TextColor);
704     WRITE_TLV_INHERIT(fontStyle, TextShadow, TLV_SPAN_FONT_STYLE_TEXTSHADOW, TextShadows, TextShadows);
705     WRITE_TLV_INHERIT(fontStyle, ItalicFontStyle, TLV_SPAN_FONT_STYLE_ITALICFONTSTYLE, FontStyle, FontStyle);
706     WRITE_TLV_INHERIT(fontStyle, FontWeight, TLV_SPAN_FONT_STYLE_FONTWEIGHT, FontWeight, FontWeight);
707     WRITE_TLV_INHERIT(fontStyle, FontFamily, TLV_SPAN_FONT_STYLE_FONTFAMILY, FontFamily, FontFamilies);
708     WRITE_TLV_INHERIT(fontStyle, FontFeature, TLV_SPAN_FONT_STYLE_FONTFEATURE, FontFeature, FontFeatures);
709     WRITE_TLV_INHERIT(fontStyle, TextDecoration, TLV_SPAN_FONT_STYLE_TEXTDECORATION, TextDecoration, TextDecoration);
710     WRITE_TLV_INHERIT(
711         fontStyle, TextDecorationColor, TLV_SPAN_FONT_STYLE_TEXTDECORATIONCOLOR, Color, TextDecorationColor);
712     WRITE_TLV_INHERIT(fontStyle, TextDecorationStyle, TLV_SPAN_FONT_STYLE_TEXTDECORATIONSTYLE, TextDecorationStyle,
713         TextDecorationStyle);
714     WRITE_TLV_INHERIT(fontStyle, TextCase, TLV_SPAN_FONT_STYLE_TEXTCASE, TextCase, TextCase);
715     WRITE_TLV_INHERIT(fontStyle, AdaptMinFontSize, TLV_SPAN_FONT_STYLE_ADPATMINFONTSIZE, Dimension, AdaptMinFontSize);
716     WRITE_TLV_INHERIT(fontStyle, AdaptMaxFontSize, TLV_SPAN_FONT_STYLE_ADPATMAXFONTSIZE, Dimension, AdaptMaxFontSize);
717     WRITE_TLV_INHERIT(fontStyle, LetterSpacing, TLV_SPAN_FONT_STYLE_LETTERSPACING, Dimension, LetterSpacing);
718 }
719 
EncodeTlvTextLineStyleNoChild(std::vector<uint8_t> & buff)720 void TextPattern::EncodeTlvTextLineStyleNoChild(std::vector<uint8_t>& buff)
721 {
722     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
723     CHECK_NULL_VOID(textLayoutProperty);
724     auto& textLineStyle = textLayoutProperty->GetTextLineStyle();
725     CHECK_NULL_VOID(textLineStyle);
726     WRITE_TLV_INHERIT(textLineStyle, LineHeight, TLV_SPAN_TEXT_LINE_STYLE_LINEHEIGHT, Dimension, LineHeight);
727     WRITE_TLV_INHERIT(textLineStyle, LineSpacing, TLV_SPAN_TEXT_LINE_STYLE_LINESPACING, Dimension, LineSpacing);
728     WRITE_TLV_INHERIT(textLineStyle, TextBaseline, TLV_SPAN_TEXT_LINE_STYLE_TEXTBASELINE, TextBaseline, TextBaseline);
729     WRITE_TLV_INHERIT(textLineStyle, TextOverflow, TLV_SPAN_TEXT_LINE_STYLE_TEXTOVERFLOW, TextOverflow, TextOverflow);
730     WRITE_TLV_INHERIT(textLineStyle, TextAlign, TLV_SPAN_TEXT_LINE_STYLE_TEXTALIGN, TextAlign, TextAlign);
731     WRITE_TEXT_STYLE_TLV(textLineStyle, MaxLength, TLV_SPAN_TEXT_LINE_STYLE_MAXLENGTH, Int32);
732     WRITE_TLV_INHERIT(textLineStyle, MaxLines, TLV_SPAN_TEXT_LINE_STYLE_MAXLINES, Int32, MaxLines);
733     WRITE_TEXT_STYLE_TLV(
734         textLineStyle, HeightAdaptivePolicy, TLV_SPAN_TEXT_LINE_STYLE_HEIGHTADAPTIVEPOLICY, TextHeightAdaptivePolicy);
735     WRITE_TLV_INHERIT(textLineStyle, TextIndent, TLV_SPAN_TEXT_LINE_STYLE_TEXTINDENT, Dimension, TextIndent);
736     WRITE_TEXT_STYLE_TLV(textLineStyle, LeadingMargin, TLV_SPAN_TEXT_LINE_STYLE_LEADINGMARGIN, LeadingMargin);
737     WRITE_TLV_INHERIT(textLineStyle, WordBreak, TLV_SPAN_TEXT_LINE_STYLE_WORDBREAK, WordBreak, WordBreak);
738     WRITE_TLV_INHERIT(textLineStyle, LineBreakStrategy, TLV_SPAN_TEXT_LINE_STYLE_LINEBREAKSTRATEGY, LineBreakStrategy,
739         LineBreakStrategy);
740     WRITE_TLV_INHERIT(textLineStyle, EllipsisMode, TLV_SPAN_TEXT_LINE_STYLE_ELLIPSISMODE, EllipsisMode, EllipsisMode);
741 }
742 
EncodeTlvSpanItems(const std::string & pasteData,std::vector<uint8_t> & buff)743 void TextPattern::EncodeTlvSpanItems(const std::string& pasteData, std::vector<uint8_t>& buff)
744 {
745     auto start = textSelector_.GetTextStart();
746     auto end = textSelector_.GetTextEnd();
747     std::list<RefPtr<NG::SpanItem>> selectSpanItems;
748     int32_t ignoreLength = 0;
749     for (const auto& spanItem : spans_) {
750         int32_t oldStart = spanItem->position - static_cast<int32_t>(spanItem->length);
751         int32_t oldEnd = spanItem->position;
752         if (oldEnd <= start || end <= oldStart) {
753             continue;
754         }
755         if (spanItem->spanItemType == NG::SpanItemType::SYMBOL) {
756             ignoreLength += static_cast<int32_t>(spanItem->length);
757             continue;
758         }
759         auto spanStart = oldStart <= start ? 0 : oldStart - start;
760         auto spanEnd = oldEnd < end ? oldEnd - start : end - start;
761         auto newSpanItem = spanItem->GetSameStyleSpanItem(true);
762         newSpanItem->interval = { spanStart - ignoreLength, spanEnd - ignoreLength };
763         newSpanItem->content = spanItem->content
764                 .substr(std::max(start - oldStart, 0), std::min(end, oldEnd) - std::max(start, oldStart));
765         selectSpanItems.emplace_back(newSpanItem);
766     }
767 
768     TLVUtil::WriteUint8(buff, TLV_SPAN_STRING_SPANS);
769     TLVUtil::WriteInt32(buff, selectSpanItems.size());
770     for (auto it = selectSpanItems.begin(); it != selectSpanItems.end(); ++it) {
771         auto spanItem = (*it);
772         if (spanItem->spanItemType == NG::SpanItemType::CustomSpan) {
773             TLVUtil::WriteInt32(buff, static_cast<int32_t>(NG::SpanItemType::NORMAL));
774             auto placeHolderSpan = AceType::MakeRefPtr<NG::SpanItem>();
775             placeHolderSpan->content = u" ";
776             placeHolderSpan->interval = spanItem->interval;
777             placeHolderSpan->EncodeTlv(buff);
778             continue;
779         }
780         TLVUtil::WriteInt32(buff, static_cast<int32_t>(spanItem->spanItemType));
781         spanItem->EncodeTlv(buff);
782     }
783     TLVUtil::WriteUint8(buff, TLV_SPAN_STRING_CONTENT);
784     TLVUtil::WriteString(buff, pasteData);
785     TLVUtil::WriteUint8(buff, TLV_END);
786 }
787 
HiddenMenu()788 void TextPattern::HiddenMenu()
789 {
790     if (IsUsingMouse()) {
791         CloseSelectOverlay();
792     } else {
793         selectOverlay_->HideMenu();
794     }
795 }
796 
SetTextSelection(int32_t selectionStart,int32_t selectionEnd)797 void TextPattern::SetTextSelection(int32_t selectionStart, int32_t selectionEnd)
798 {
799     auto host = GetHost();
800     CHECK_NULL_VOID(host);
801     auto eventHub = host->GetEventHub<EventHub>();
802     CHECK_NULL_VOID(eventHub);
803     auto context = host->GetContext();
804     if (context) {
805         context->AddAfterLayoutTask([weak = WeakClaim(this), selectionStart, selectionEnd, eventHub]() {
806             auto textPattern = weak.Upgrade();
807             CHECK_NULL_VOID(textPattern);
808             auto textLayoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
809             CHECK_NULL_VOID(textLayoutProperty);
810             if (textLayoutProperty->GetCalcLayoutConstraint() &&
811                 textLayoutProperty->GetCalcLayoutConstraint()->selfIdealSize.has_value()) {
812                 auto selfIdealSizeWidth = textLayoutProperty->GetCalcLayoutConstraint()->selfIdealSize->Width();
813                 auto selfIdealSizeHeight = textLayoutProperty->GetCalcLayoutConstraint()->selfIdealSize->Height();
814                 auto constraint = textLayoutProperty->GetLayoutConstraint();
815                 if ((selfIdealSizeWidth.has_value() && NearZero(selfIdealSizeWidth->GetDimension().ConvertToPxWithSize(
816                             constraint->percentReference.Width()))) ||
817                     (selfIdealSizeHeight.has_value() &&
818                         NearZero(selfIdealSizeHeight->GetDimension().ConvertToPxWithSize(
819                             constraint->percentReference.Height())))) {
820                     return;
821                 }
822             }
823 
824             auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
825             if (mode == TextSelectableMode::UNSELECTABLE ||
826                 textLayoutProperty->GetCopyOptionValue(CopyOptions::None) == CopyOptions::None ||
827                 textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE) {
828                 return;
829             }
830             if (!textPattern->IsSetObscured() && eventHub->IsEnabled()) {
831                 textPattern->ActSetSelection(selectionStart, selectionEnd);
832             }
833         });
834     }
835     host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
836 }
837 
GetRenderContext()838 RefPtr<RenderContext> TextPattern::GetRenderContext()
839 {
840     auto frameNode = GetHost();
841     CHECK_NULL_RETURN(frameNode, nullptr);
842     return frameNode->GetRenderContext();
843 }
844 
ShowSelectOverlay(const OverlayRequest & request)845 void TextPattern::ShowSelectOverlay(const OverlayRequest& request)
846 {
847     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
848     CHECK_NULL_VOID(textLayoutProperty);
849     if (textLayoutProperty->GetMaxLines() == 0) {
850         CloseSelectOverlay();
851         ResetSelection();
852         return;
853     }
854     selectOverlay_->ProcessOverlay(request);
855 }
856 
HandleOnSelectAll()857 void TextPattern::HandleOnSelectAll()
858 {
859     auto textSize = static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_;
860     HandleSelectionChange(0, textSize);
861     CalculateHandleOffsetAndShowOverlay();
862     CloseSelectOverlay(true);
863     if (IsUsingMouse()) {
864         if (IsSelected()) {
865             selectOverlay_->SetSelectionHoldCallback();
866         }
867     } else {
868         ShowSelectOverlay({ .animation = true });
869     }
870     auto host = GetHost();
871     CHECK_NULL_VOID(host);
872     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
873     ResetOriginCaretPosition();
874 }
875 
IsShowTranslate()876 bool TextPattern::IsShowTranslate()
877 {
878     auto host = GetHost();
879     CHECK_NULL_RETURN(host, false);
880     auto context = host->GetContext();
881     CHECK_NULL_RETURN(context, false);
882     auto textTheme = context->GetTheme<TextTheme>();
883     CHECK_NULL_RETURN(textTheme, false);
884     return textTheme->IsShowTranslate();
885 }
886 
IsShowSearch()887 bool TextPattern::IsShowSearch()
888 {
889     auto container = Container::Current();
890     if (container && container->IsScenceBoardWindow()) {
891         return false;
892     }
893     auto host = GetHost();
894     CHECK_NULL_RETURN(host, false);
895     auto context = host->GetContext();
896     CHECK_NULL_RETURN(context, false);
897     auto textTheme = context->GetTheme<TextTheme>();
898     CHECK_NULL_RETURN(textTheme, false);
899     return textTheme->IsShowSearch();
900 }
901 
InitLongPressEvent(const RefPtr<GestureEventHub> & gestureHub)902 void TextPattern::InitLongPressEvent(const RefPtr<GestureEventHub>& gestureHub)
903 {
904     constexpr int32_t longPressDelay = 600;
905     if (longPressEvent_) {
906         gestureHub->SetLongPressEvent(longPressEvent_, false, false, longPressDelay);
907         return;
908     }
909     auto longPressCallback = [weak = WeakClaim(this)](GestureEvent& info) {
910         auto pattern = weak.Upgrade();
911         CHECK_NULL_VOID(pattern);
912         pattern->sourceType_ = info.GetSourceDevice();
913         pattern->HandleLongPress(info);
914     };
915     longPressEvent_ = MakeRefPtr<LongPressEvent>(std::move(longPressCallback));
916 
917     // Default time is 500, used by drag event. Drag event would trigger if text is selected, but we want
918     // it to only trigger on the second long press, after selection. Therefore, long press delay of Selection needs to
919     // be slightly longer to ensure that order.
920     gestureHub->SetLongPressEvent(longPressEvent_, false, false, longPressDelay);
921 
922     auto onTextSelectorChange = [weak = WeakClaim(this)]() {
923         auto pattern = weak.Upgrade();
924         CHECK_NULL_VOID(pattern);
925         auto frameNode = pattern->GetHost();
926         CHECK_NULL_VOID(frameNode);
927         frameNode->OnAccessibilityEvent(AccessibilityEventType::TEXT_SELECTION_UPDATE);
928     };
929     textSelector_.SetOnAccessibility(std::move(onTextSelectorChange));
930 }
931 
OnHandleTouchUp()932 void TextPattern::OnHandleTouchUp()
933 {
934     CloseSelectOverlay();
935     ResetSelection();
936 }
937 
HandleClickEvent(GestureEvent & info)938 void TextPattern::HandleClickEvent(GestureEvent& info)
939 {
940     if ((selectOverlay_->IsClickAtHandle(info) && !multipleClickRecognizer_->IsRunning()) ||
941         selectOverlay_->GetIsHandleDragging()) {
942         return;
943     }
944     if (dataDetectorAdapter_->hasClickedAISpan_) {
945         dataDetectorAdapter_->hasClickedAISpan_ = false;
946     }
947     multipleClickRecognizer_->Start(info);
948     if (multipleClickRecognizer_->IsDoubleClick()) {
949         HandleDoubleClickEvent(info);
950     } else {
951         HandleSingleClickEvent(info);
952     }
953 }
954 
HandleUrlClick()955 bool TextPattern::HandleUrlClick()
956 {
957     if (LessNotEqual(clickedSpanPosition_, 0)) {
958         return false;
959     }
960     auto iter = spans_.begin();
961     std::advance(iter, clickedSpanPosition_);
962     RefPtr<SpanItem> span;
963     if (iter == spans_.end()) {
964         span = spans_.back();
965     } else {
966         span = *iter;
967     }
968     if (span && span->urlOnRelease) {
969         span->urlOnRelease();
970         return true;
971     }
972     return false;
973 }
974 
HandleSingleClickEvent(GestureEvent & info)975 void TextPattern::HandleSingleClickEvent(GestureEvent& info)
976 {
977     RectF textContentRect = contentRect_;
978     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
979     textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
980     PointF textOffset = { info.GetLocalLocation().GetX() - textContentRect.GetX(),
981         info.GetLocalLocation().GetY() - textContentRect.GetY() };
982 
983     if (IsUsingMouse() && isMousePressed_ && leftMousePressed_ && moveOverClickThreshold_) {
984         TAG_LOGI(ACE_TEXT, "not trigger click after mouse move");
985         moveOverClickThreshold_ = false;
986         return;
987     }
988 
989     if (IsSelectableAndCopy() || NeedShowAIDetect()) {
990         CheckClickedOnSpanOrText(textContentRect, info.GetLocalLocation());
991     }
992     if (HandleUrlClick()) {
993         return;
994     }
995     if (selectOverlay_->SelectOverlayIsOn() && !selectOverlay_->IsUsingMouse() &&
996         GlobalOffsetInSelectedArea(info.GetGlobalLocation())) {
997         if (!IsLocationInFrameRegion(info.GetLocalLocation())) {
998             return;
999         }
1000         selectOverlay_->ToggleMenu();
1001         selectOverlay_->SwitchToOverlayMode();
1002         return;
1003     }
1004     if (!isMousePressed_) {
1005         HandleClickAISpanEvent(textOffset);
1006     }
1007     if (dataDetectorAdapter_->hasClickedAISpan_) {
1008         selectOverlay_->HideMenu();
1009         return;
1010     }
1011     HandleClickOnTextAndSpan(info);
1012 }
1013 
HandleClickOnTextAndSpan(GestureEvent & info)1014 void TextPattern::HandleClickOnTextAndSpan(GestureEvent& info)
1015 {
1016     if (textSelector_.IsValid() && mouseStatus_ != MouseStatus::MOVE && !isMousePressed_) {
1017         CloseSelectOverlay(true);
1018         ResetSelection();
1019     }
1020     if (clickedSpanPosition_ == -1) {
1021         ActTextOnClick(info);
1022         return;
1023     }
1024     auto iter = spans_.begin();
1025     std::advance(iter, clickedSpanPosition_);
1026     RefPtr<SpanItem> span;
1027     if (iter == spans_.end()) {
1028         span = spans_.back();
1029     } else {
1030         span = *iter;
1031     }
1032     if (span && span->onClick) {
1033         GestureEvent spanClickinfo = info;
1034         EventTarget target = info.GetTarget();
1035         target.area.SetWidth(Dimension(0.0f));
1036         target.area.SetHeight(Dimension(0.0f));
1037         spanClickinfo.SetTarget(target);
1038         if (!TryLinkJump(span)) {
1039             span->onClick(spanClickinfo);
1040             // todo: RecordSpanClickEvent
1041         }
1042     } else {
1043         ActTextOnClick(info);
1044     }
1045 }
1046 
1047 // return: whether execute link jump callback
TryLinkJump(const RefPtr<SpanItem> & span)1048 bool TextPattern::TryLinkJump(const RefPtr<SpanItem>& span)
1049 {
1050     auto host = GetHost();
1051     CHECK_NULL_RETURN(host, false);
1052     auto pipelineContext = host->GetContext();
1053     CHECK_NULL_RETURN(pipelineContext, false);
1054 
1055     bool isCloudConfOpen = pipelineContext->GetIsLinkJumpOpen();
1056     if (isCloudConfOpen) {
1057         std::string spanContent = UtfUtils::Str16DebugToStr8(span->GetSpanContent()); // change for u16string
1058         auto isJumpLink = IsJumpLink(spanContent);
1059         TAG_LOGI(AceLogTag::ACE_TEXT, "TextPattern::TryLinkJump, spanContentLen: %{public}zu, isJumpLink: %{public}d",
1060             spanContent.size(), isJumpLink);
1061         if (isJumpLink) {
1062             pipelineContext->ExecuteLinkJumpCallback(spanContent);
1063             // todo: RecordSpanClickEvent
1064             return true;
1065         }
1066     }
1067     return false;
1068 }
1069 
ActTextOnClick(GestureEvent & info)1070 void TextPattern::ActTextOnClick(GestureEvent& info)
1071 {
1072     if (onClick_) {
1073         auto onClick = onClick_;
1074         onClick(info);
1075     }
1076 }
1077 
GlobalOffsetInSelectedArea(const Offset & globalOffset)1078 bool TextPattern::GlobalOffsetInSelectedArea(const Offset& globalOffset)
1079 {
1080     auto host = GetHost();
1081     CHECK_NULL_RETURN(host, false);
1082     auto offset = host->GetPaintRectOffset(false, true);
1083     auto localOffset = globalOffset - Offset(offset.GetX(), offset.GetY());
1084     if (selectOverlay_->HasRenderTransform()) {
1085         localOffset = ConvertGlobalToLocalOffset(globalOffset);
1086     }
1087     return LocalOffsetInSelectedArea(localOffset);
1088 }
1089 
LocalOffsetInSelectedArea(const Offset & localOffset)1090 bool TextPattern::LocalOffsetInSelectedArea(const Offset& localOffset)
1091 {
1092     if (IsSelectableAndCopy() && GreatNotEqual(textSelector_.GetTextEnd(), textSelector_.GetTextStart())) {
1093         // Determine if the pan location is in the selected area
1094         auto selectedRects = pManager_->GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
1095         TextBase::CalculateSelectedRect(selectedRects, contentRect_.Width());
1096         auto panOffset = OffsetF(localOffset.GetX(), localOffset.GetY()) - contentRect_.GetOffset() +
1097                          OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
1098         for (const auto& selectedRect : selectedRects) {
1099             if (selectedRect.IsInRegion(PointF(panOffset.GetX(), panOffset.GetY()))) {
1100                 return true;
1101             }
1102         }
1103     }
1104     return false;
1105 }
1106 
HandleClickAISpanEvent(const PointF & textOffset)1107 void TextPattern::HandleClickAISpanEvent(const PointF& textOffset)
1108 {
1109     dataDetectorAdapter_->hasClickedAISpan_ = false;
1110     if (!NeedShowAIDetect() || mouseStatus_ == MouseStatus::MOVE || IsDragging()) {
1111         return;
1112     }
1113 
1114     for (const auto& kv : dataDetectorAdapter_->aiSpanMap_) {
1115         auto& aiSpan = kv.second;
1116         ClickAISpan(textOffset, aiSpan);
1117         if (dataDetectorAdapter_->hasClickedAISpan_) {
1118             return;
1119         }
1120     }
1121 }
1122 
CheckClickedOnSpanOrText(RectF textContentRect,const Offset & localLocation)1123 bool TextPattern::CheckClickedOnSpanOrText(RectF textContentRect, const Offset& localLocation)
1124 {
1125     clickedSpanPosition_ = -1;
1126     auto host = GetHost();
1127     CHECK_NULL_RETURN(host, false);
1128     auto renderContext = host->GetRenderContext();
1129     CHECK_NULL_RETURN(host, false);
1130     PointF textOffset = GetTextOffset(localLocation, textContentRect);
1131     auto clip = false;
1132     if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1133         clip = true;
1134     }
1135     if (renderContext->GetClipEdge().has_value() && !renderContext->GetClipEdge().value_or(clip) && overlayMod_) {
1136         textContentRect = overlayMod_->GetBoundsRect();
1137         textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
1138     }
1139     if (textContentRect.IsInRegion(
1140         PointF(static_cast<float>(localLocation.GetX()), static_cast<float>(localLocation.GetY()))) &&
1141         !spans_.empty() && pManager_) {
1142         if (CalculateClickedSpanPosition(textOffset)) {
1143             return true;
1144         }
1145     }
1146     if (onClick_) {
1147         return true;
1148     }
1149     return false;
1150 }
1151 
GetTextOffset(const Offset & localLocation,const RectF & contentRect)1152 PointF TextPattern::GetTextOffset(const Offset &localLocation, const RectF &contentRect)
1153 {
1154     PointF textOffset = {static_cast<float>(localLocation.GetX()) - contentRect.GetX(),
1155                          static_cast<float>(localLocation.GetY()) - contentRect.GetY()};
1156     return textOffset;
1157 }
1158 
CalculateClickedSpanPosition(const PointF & textOffset)1159 bool TextPattern::CalculateClickedSpanPosition(const PointF& textOffset)
1160 {
1161     int32_t start = 0;
1162     for (const auto& item : spans_) {
1163         clickedSpanPosition_++;
1164         if (!item) {
1165             continue;
1166         }
1167         auto end = isSpanStringMode_ && item->position == -1 ? item->interval.second : item->position;
1168         auto selectedRects = GetSelectedRects(start, end);
1169         start = end;
1170         for (auto&& rect : selectedRects) {
1171             if (!rect.IsInRegion(textOffset)) {
1172                 continue;
1173             }
1174             return CheckAndClick(item);
1175         }
1176     }
1177     clickedSpanPosition_ = -1;
1178     return false;
1179 }
1180 
CheckAndClick(const RefPtr<SpanItem> & item)1181 bool TextPattern::CheckAndClick(const RefPtr<SpanItem>& item)
1182 {
1183     if (item->onClick || item->urlOnRelease) {
1184         return true;
1185     }
1186     clickedSpanPosition_ = -1;
1187     return false;
1188 }
1189 
GetSelectedRects(int32_t start,int32_t end)1190 std::vector<RectF> TextPattern::GetSelectedRects(int32_t start, int32_t end)
1191 {
1192     return pManager_->GetRects(start, end);
1193 }
1194 
ClickAISpan(const PointF & textOffset,const AISpan & aiSpan)1195 bool TextPattern::ClickAISpan(const PointF& textOffset, const AISpan& aiSpan)
1196 {
1197     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1198     CHECK_NULL_RETURN(textLayoutProperty, false);
1199     int32_t start = aiSpan.start;
1200     int32_t end = aiSpan.end;
1201     if (textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::ELLIPSIS) {
1202         auto range = pManager_->GetEllipsisTextRange();
1203         int32_t ellipsisStart = static_cast<int32_t>(range.first);
1204         int32_t ellipsisEnd = static_cast<int32_t>(range.second);
1205         if (ellipsisStart != -1 && ellipsisEnd > 0 && ellipsisStart < ellipsisEnd) {
1206             if (ellipsisStart <= aiSpan.start && ellipsisEnd >= aiSpan.end) {
1207                 // ellipsisTextRange contains [aispan.start, aispan.end)
1208                 return false;
1209             } else if (ellipsisStart <= aiSpan.start && ellipsisEnd >= aiSpan.start) {
1210                 // ellipsisTextRange covers [aispan.start, ellipsisEnd)
1211                 start = ellipsisEnd;
1212             } else if (ellipsisStart <= aiSpan.end && ellipsisEnd >= aiSpan.end) {
1213                 // ellipsisTextRange covers [ellipsisStart, aiSpan.end);
1214                 end = ellipsisStart;
1215             }
1216         }
1217     }
1218 
1219     auto aiRects = pManager_->GetRects(start, end);
1220     for (auto&& rect : aiRects) {
1221         if (rect.IsInRegion(textOffset)) {
1222             dataDetectorAdapter_->hasClickedAISpan_ = true;
1223             if (leftMousePressed_) {
1224                 dataDetectorAdapter_->ResponseBestMatchItem(aiSpan);
1225                 return true;
1226             }
1227             return ShowAIEntityMenu(aiSpan);
1228         }
1229     }
1230     return false;
1231 }
1232 
InitUrlMouseEvent()1233 void TextPattern::InitUrlMouseEvent()
1234 {
1235     CHECK_NULL_VOID(!urlMouseEventInitialized_);
1236     auto host = GetHost();
1237     CHECK_NULL_VOID(host);
1238     auto eventHub = host->GetEventHub<EventHub>();
1239     CHECK_NULL_VOID(eventHub);
1240     auto inputHub = eventHub->GetOrCreateInputEventHub();
1241     CHECK_NULL_VOID(inputHub);
1242     auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
1243         auto pattern = weak.Upgrade();
1244         if (pattern) {
1245             pattern->HandleUrlMouseEvent(info);
1246         }
1247     };
1248     auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
1249     inputHub->AddOnMouseEvent(mouseEvent);
1250     auto mouseHoverTask = [weak = WeakClaim(this)](bool isHover) {
1251         auto pattern = weak.Upgrade();
1252         CHECK_NULL_VOID(pattern);
1253         pattern->URLOnHover(isHover);
1254     };
1255     auto mouseHoverEvent = MakeRefPtr<InputEvent>(std::move(mouseHoverTask));
1256     inputHub->AddOnHoverEvent(mouseHoverEvent);
1257     urlMouseEventInitialized_ = true;
1258 }
1259 
URLOnHover(bool isHover)1260 void TextPattern::URLOnHover(bool isHover)
1261 {
1262     CHECK_NULL_VOID(!isHover);
1263     auto host = GetHost();
1264     CHECK_NULL_VOID(host);
1265     auto nodeId = host->GetId();
1266     auto pipelineContext = host->GetContext();
1267     CHECK_NULL_VOID(pipelineContext);
1268     pipelineContext->ChangeMouseStyle(nodeId, MouseFormat::DEFAULT);
1269     pipelineContext->FreeMouseStyleHoldNode(nodeId);
1270     CHECK_NULL_VOID(overlayMod_);
1271     overlayMod_->ClearSelectedForegroundColorAndRects();
1272     MarkDirtySelf();
1273 }
1274 
HandleUrlMouseEvent(const MouseInfo & info)1275 void TextPattern::HandleUrlMouseEvent(const MouseInfo& info)
1276 {
1277     if (isMousePressed_) {
1278         return;
1279     }
1280     RectF textContentRect = contentRect_;
1281     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
1282     textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
1283     auto localLocation = info.GetLocalLocation();
1284     if (selectOverlay_->HasRenderTransform()) {
1285         localLocation = ConvertGlobalToLocalOffset(info.GetGlobalLocation());
1286     }
1287     auto host = GetHost();
1288     CHECK_NULL_VOID(host);
1289     auto hostId = host->GetId();
1290     auto renderContext = host->GetRenderContext();
1291     CHECK_NULL_VOID(renderContext);
1292     PointF textOffset = { static_cast<float>(localLocation.GetX()) - textContentRect.GetX(),
1293         static_cast<float>(localLocation.GetY()) - textContentRect.GetY() };
1294     auto show = ShowShadow(textOffset, GetUrlHoverColor());
1295     auto pipelineContext = host->GetContext();
1296     CHECK_NULL_VOID(pipelineContext);
1297     if (show) {
1298         pipelineContext->SetMouseStyleHoldNode(hostId);
1299         pipelineContext->ChangeMouseStyle(hostId, MouseFormat::HAND_POINTING);
1300     } else {
1301         pipelineContext->ChangeMouseStyle(hostId, MouseFormat::DEFAULT);
1302         pipelineContext->FreeMouseStyleHoldNode(hostId);
1303     }
1304 }
1305 
HandleUrlTouchEvent(const TouchEventInfo & info)1306 void TextPattern::HandleUrlTouchEvent(const TouchEventInfo& info)
1307 {
1308     CHECK_NULL_VOID(overlayMod_);
1309     CHECK_NULL_VOID(!IsDragging());
1310     if (selectOverlay_->IsTouchAtHandle(info)) {
1311         return;
1312     }
1313     auto touchType = info.GetTouches().front().GetTouchType();
1314     if (touchType != TouchType::DOWN && touchType != TouchType::UP) {
1315         return;
1316     }
1317     if (touchType == TouchType::DOWN) {
1318         RectF textContentRect = contentRect_;
1319         auto touchOffset = info.GetTouches().front().GetLocalLocation();
1320         PointF textOffset = { static_cast<float>(touchOffset.GetX()) - textContentRect.GetX(),
1321             static_cast<float>(touchOffset.GetY()) - textContentRect.GetY() };
1322         ShowShadow(textOffset, GetUrlPressColor());
1323     } else {
1324         overlayMod_->ClearSelectedForegroundColorAndRects();
1325         MarkDirtySelf();
1326     }
1327 }
SetOnClickMenu(const AISpan & aiSpan,const CalculateHandleFunc & calculateHandleFunc,const ShowSelectOverlayFunc & showSelectOverlayFunc)1328 void TextPattern::SetOnClickMenu(const AISpan& aiSpan, const CalculateHandleFunc& calculateHandleFunc,
1329     const ShowSelectOverlayFunc& showSelectOverlayFunc)
1330 
1331 {
1332     dataDetectorAdapter_->onClickMenu_ = [aiSpan, weak = WeakClaim(this), calculateHandleFunc, showSelectOverlayFunc](
1333                                              const std::string& action) {
1334         auto pattern = weak.Upgrade();
1335         CHECK_NULL_VOID(pattern);
1336         pattern->CloseSelectOverlay();
1337         pattern->HandleSelectionChange(aiSpan.start, aiSpan.end);
1338         if (action == COPY) {
1339             pattern->HandleOnCopy();
1340             pattern->ResetSelection();
1341         } else if (action == SELECT_TEXT) {
1342             if (calculateHandleFunc == nullptr) {
1343                 pattern->CalculateHandleOffsetAndShowOverlay();
1344             } else {
1345                 calculateHandleFunc();
1346             }
1347             if (showSelectOverlayFunc == nullptr) {
1348                 pattern->ShowSelectOverlay({ .animation = true });
1349             } else {
1350                 showSelectOverlayFunc(pattern->textSelector_.firstHandle, pattern->textSelector_.secondHandle);
1351             }
1352             auto frameNode = pattern->GetHost();
1353             CHECK_NULL_VOID(frameNode);
1354             frameNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1355         }
1356     };
1357 }
1358 
CalcAIMenuPosition(const AISpan & aiSpan,const CalculateHandleFunc & calculateHandleFunc)1359 RectF TextPattern::CalcAIMenuPosition(const AISpan& aiSpan, const CalculateHandleFunc& calculateHandleFunc)
1360 {
1361     // save information
1362     auto baseOffset = textSelector_.baseOffset;
1363     auto destinationOffset = textSelector_.destinationOffset;
1364     // calculate result
1365     textSelector_.Update(aiSpan.start, aiSpan.end);
1366     parentGlobalOffset_ = GetParentGlobalOffset();
1367     if (calculateHandleFunc == nullptr) {
1368         CalculateHandleOffsetAndShowOverlay();
1369     } else {
1370         calculateHandleFunc();
1371     }
1372     RectF aiRect;
1373     if (textSelector_.firstHandle.Top() != textSelector_.secondHandle.Top()) {
1374         auto top = std::min(textSelector_.firstHandle.Top(), textSelector_.secondHandle.Top());
1375         auto bottom = std::max(textSelector_.firstHandle.Bottom(), textSelector_.secondHandle.Bottom());
1376         auto textContentGlobalOffset = parentGlobalOffset_ + contentRect_.GetOffset();
1377         auto left = textContentGlobalOffset.GetX();
1378         auto right = textContentGlobalOffset.GetX() + contentRect_.Width();
1379         aiRect = RectT(left, top, right - left, bottom - top);
1380     } else {
1381         aiRect = textSelector_.firstHandle.CombineRectT(textSelector_.secondHandle);
1382     }
1383     // restore textSelector_
1384     textSelector_.Update(baseOffset, destinationOffset);
1385     if (calculateHandleFunc == nullptr) {
1386         CalculateHandleOffsetAndShowOverlay();
1387     } else {
1388         calculateHandleFunc();
1389     }
1390     return aiRect;
1391 }
1392 
ShowAIEntityMenu(const AISpan & aiSpan,const CalculateHandleFunc & calculateHandleFunc,const ShowSelectOverlayFunc & showSelectOverlayFunc)1393 bool TextPattern::ShowAIEntityMenu(const AISpan& aiSpan, const CalculateHandleFunc& calculateHandleFunc,
1394     const ShowSelectOverlayFunc& showSelectOverlayFunc)
1395 {
1396     auto host = GetHost();
1397     CHECK_NULL_RETURN(host, false);
1398     auto context = host->GetContext();
1399     CHECK_NULL_RETURN(context, false);
1400     auto safeAreaManager = context->GetSafeAreaManager();
1401     CHECK_NULL_RETURN(safeAreaManager, false);
1402     SetOnClickMenu(aiSpan, calculateHandleFunc, showSelectOverlayFunc);
1403     RectF aiRect = CalcAIMenuPosition(aiSpan, calculateHandleFunc);
1404 
1405     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1406     CHECK_NULL_RETURN(textLayoutProperty, false);
1407     auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
1408     if (!NearEqual(safeAreaManager->GetKeyboardInset().Length(), 0)
1409         && mode == TextSelectableMode::SELECTABLE_FOCUSABLE) {
1410         aiRect.SetTop(aiRect.GetY() - safeAreaManager->GetKeyboardOffset());
1411     }
1412 
1413     bool isShowCopy = true;
1414     bool isShowSelectText = true;
1415     if (copyOption_ == CopyOptions::None) {
1416         isShowCopy = false;
1417         isShowSelectText = false;
1418     } else if (mode == TextSelectableMode::UNSELECTABLE) {
1419         isShowSelectText = false;
1420     }
1421     return dataDetectorAdapter_->ShowAIEntityMenu(aiSpan, aiRect, host, isShowCopy, isShowSelectText);
1422 }
1423 
HandleDoubleClickEvent(GestureEvent & info)1424 void TextPattern::HandleDoubleClickEvent(GestureEvent& info)
1425 {
1426     CheckOnClickEvent(info);
1427     auto textSize = static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_;
1428     if (!IsSelectableAndCopy() || (textSize == 0)) {
1429         return;
1430     }
1431     auto host = GetHost();
1432     CHECK_NULL_VOID(host);
1433     auto hub = host->GetEventHub<EventHub>();
1434     CHECK_NULL_VOID(hub);
1435     auto gestureHub = hub->GetOrCreateGestureEventHub();
1436     CHECK_NULL_VOID(gestureHub);
1437     isDoubleClick_ = true;
1438     auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
1439     Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
1440         info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
1441     InitSelection(textOffset);
1442     textResponseType_ = TextResponseType::NONE;
1443     UpdateSelectionSpanType(std::min(textSelector_.baseOffset, textSelector_.destinationOffset),
1444         std::max(textSelector_.baseOffset, textSelector_.destinationOffset));
1445     parentGlobalOffset_ = GetParentGlobalOffset();
1446     CalculateHandleOffsetAndShowOverlay();
1447     if (!isMousePressed_) {
1448         ShowSelectOverlay({ .animation = true });
1449     }
1450     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1451 }
1452 
CheckOnClickEvent(GestureEvent & info)1453 void TextPattern::CheckOnClickEvent(GestureEvent& info)
1454 {
1455     RectF textContentRect = contentRect_;
1456     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
1457     textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
1458     PointF textOffset = { info.GetLocalLocation().GetX() - textContentRect.GetX(),
1459         info.GetLocalLocation().GetY() - textContentRect.GetY() };
1460     if (IsSelectableAndCopy() || NeedShowAIDetect()) {
1461         CheckClickedOnSpanOrText(textContentRect, info.GetLocalLocation());
1462     }
1463     HandleClickOnTextAndSpan(info);
1464 }
1465 
InitClickEvent(const RefPtr<GestureEventHub> & gestureHub)1466 void TextPattern::InitClickEvent(const RefPtr<GestureEventHub>& gestureHub)
1467 {
1468     gestureHub->SetNodeClickDistance(distanceThreshold_);
1469     CHECK_NULL_VOID(!clickEventInitialized_);
1470     auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
1471         auto pattern = weak.Upgrade();
1472         CHECK_NULL_VOID(pattern);
1473         pattern->sourceType_ = info.GetSourceDevice();
1474         pattern->HandleClickEvent(info);
1475     };
1476     auto clickListener = MakeRefPtr<ClickEvent>(std::move(clickCallback));
1477     clickListener->SetSysJudge([weak = WeakClaim(this)](const RefPtr<GestureInfo>& gestureInfo,
1478                                    const std::shared_ptr<BaseGestureEvent>& info) -> GestureJudgeResult {
1479         auto textPattern = weak.Upgrade();
1480         CHECK_NULL_RETURN(textPattern, GestureJudgeResult::CONTINUE);
1481         if (info->GetFingerList().empty()) {
1482             return GestureJudgeResult::CONTINUE;
1483         }
1484         auto localLocation = info->GetFingerList().begin()->localLocation_;
1485         auto contentRect = textPattern->GetTextContentRect();
1486         auto baselineOffset = textPattern->GetBaselineOffset();
1487 
1488         RectF textContentRect = contentRect;
1489         textContentRect.SetTop(contentRect.GetY() - std::min(baselineOffset, 0.0f));
1490         textContentRect.SetHeight(contentRect.Height() - std::max(baselineOffset, 0.0f));
1491         if (textPattern->GetCopyOptions() == CopyOptions::None && !textPattern->NeedShowAIDetect() &&
1492             !textPattern->CheckClickedOnSpanOrText(textContentRect, localLocation)) {
1493             return GestureJudgeResult::REJECT;
1494         }
1495         return GestureJudgeResult::CONTINUE;
1496     });
1497     gestureHub->AddClickEvent(clickListener);
1498     clickEventInitialized_ = true;
1499 }
1500 
InitAISpanHoverEvent()1501 void TextPattern::InitAISpanHoverEvent()
1502 {
1503     CHECK_NULL_VOID(!aiSpanHoverEventInitialized_);
1504     auto host = GetHost();
1505     CHECK_NULL_VOID(host);
1506     auto eventHub = host->GetEventHub<EventHub>();
1507     CHECK_NULL_VOID(eventHub);
1508     auto inputHub = eventHub->GetOrCreateInputEventHub();
1509     CHECK_NULL_VOID(inputHub);
1510 
1511     auto aiSpanHoverTask = [weak = WeakClaim(this)](MouseInfo& info) {
1512         auto pattern = weak.Upgrade();
1513         CHECK_NULL_VOID(pattern);
1514         pattern->HandleAISpanHoverEvent(info);
1515     };
1516     auto aiSpanHoverEvent = MakeRefPtr<InputEvent>(std::move(aiSpanHoverTask));
1517     inputHub->AddOnMouseEvent(aiSpanHoverEvent);
1518     aiSpanHoverEventInitialized_ = true;
1519 }
1520 
HandleAISpanHoverEvent(const MouseInfo & info)1521 void TextPattern::HandleAISpanHoverEvent(const MouseInfo& info)
1522 {
1523     if (info.GetAction() != MouseAction::MOVE || !NeedShowAIDetect() || !isHover_) {
1524         return;
1525     }
1526     if (dataDetectorAdapter_->aiSpanRects_.empty()) {
1527         for (const auto& kv : dataDetectorAdapter_->aiSpanMap_) {
1528             auto& aiSpan = kv.second;
1529             const auto& aiRects = pManager_->GetRects(aiSpan.start, aiSpan.end);
1530             dataDetectorAdapter_->aiSpanRects_.insert(
1531                 dataDetectorAdapter_->aiSpanRects_.end(), aiRects.begin(), aiRects.end());
1532         }
1533     }
1534 
1535     auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
1536     PointF textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
1537         info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
1538     auto host = GetHost();
1539     CHECK_NULL_VOID(host);
1540     auto pipeline = GetContext();
1541     CHECK_NULL_VOID(pipeline);
1542     auto nodeId = host->GetId();
1543     pipeline->SetMouseStyleHoldNode(nodeId);
1544     for (auto&& rect : dataDetectorAdapter_->aiSpanRects_) {
1545         if (!rect.IsInRegion(textOffset)) {
1546             continue;
1547         }
1548         if (currentMouseStyle_ != MouseFormat::HAND_POINTING) {
1549             bool changeSuccess = pipeline->ChangeMouseStyle(nodeId, MouseFormat::HAND_POINTING);
1550             CHECK_NULL_VOID(changeSuccess);
1551             currentMouseStyle_ = MouseFormat::HAND_POINTING;
1552         }
1553         return;
1554     }
1555     if (currentMouseStyle_ != MouseFormat::DEFAULT) {
1556         bool changeSuccess = pipeline->ChangeMouseStyle(nodeId, MouseFormat::DEFAULT);
1557         CHECK_NULL_VOID(changeSuccess);
1558         currentMouseStyle_ = MouseFormat::DEFAULT;
1559     }
1560 }
1561 
OnHover(bool isHover)1562 void TextPattern::OnHover(bool isHover)
1563 {
1564     isHover_ = isHover;
1565     TAG_LOGI(AceLogTag::ACE_TEXT, "isHover=%{public}d", isHover);
1566     auto host = GetHost();
1567     CHECK_NULL_VOID(host);
1568     auto pipeline = GetContext();
1569     CHECK_NULL_VOID(pipeline);
1570     auto nodeId = host->GetId();
1571     if (isHover) {
1572         pipeline->SetMouseStyleHoldNode(nodeId);
1573         pipeline->ChangeMouseStyle(nodeId, MouseFormat::DEFAULT);
1574         currentMouseStyle_ = MouseFormat::DEFAULT;
1575     } else {
1576         pipeline->ChangeMouseStyle(nodeId, MouseFormat::DEFAULT);
1577         currentMouseStyle_ = MouseFormat::DEFAULT;
1578         pipeline->FreeMouseStyleHoldNode(nodeId);
1579     }
1580 }
1581 
InitMouseEvent()1582 void TextPattern::InitMouseEvent()
1583 {
1584     CHECK_NULL_VOID(!mouseEventInitialized_);
1585     auto host = GetHost();
1586     CHECK_NULL_VOID(host);
1587     auto eventHub = host->GetEventHub<EventHub>();
1588     CHECK_NULL_VOID(eventHub);
1589     auto inputHub = eventHub->GetOrCreateInputEventHub();
1590     CHECK_NULL_VOID(inputHub);
1591 
1592     auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
1593         auto pattern = weak.Upgrade();
1594         CHECK_NULL_VOID(pattern);
1595         pattern->HandleMouseEvent(info);
1596     };
1597     auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
1598     inputHub->AddOnMouseEvent(mouseEvent);
1599 
1600     auto hoverTask = [weak = WeakClaim(this)](bool isHover) {
1601         TAG_LOGI(AceLogTag::ACE_TEXT, "on hover event isHover=%{public}d", isHover);
1602         auto pattern = weak.Upgrade();
1603         if (pattern) {
1604             pattern->OnHover(isHover);
1605         }
1606     };
1607     auto hoverEvent = MakeRefPtr<InputEvent>(std::move(hoverTask));
1608     inputHub->AddOnHoverEvent(hoverEvent);
1609     mouseEventInitialized_ = true;
1610 }
1611 
InitFocusEvent()1612 void TextPattern::InitFocusEvent()
1613 {
1614     CHECK_NULL_VOID(!focusInitialized_);
1615     auto host = GetHost();
1616     auto focusHub = host->GetFocusHub();
1617     CHECK_NULL_VOID(focusHub);
1618     auto focusTask = [weak = WeakClaim(this)]() {
1619         auto pattern = weak.Upgrade();
1620         CHECK_NULL_VOID(pattern);
1621         auto contentModifier = pattern->GetContentModifier();
1622         CHECK_NULL_VOID(contentModifier);
1623         contentModifier->SetIsFocused(true);
1624         pattern->AddIsFocusActiveUpdateEvent();
1625     };
1626     focusHub->SetOnFocusInternal(focusTask);
1627 
1628     auto blurTask = [weak = WeakClaim(this)]() {
1629         auto pattern = weak.Upgrade();
1630         CHECK_NULL_VOID(pattern);
1631         auto contentModifier = pattern->GetContentModifier();
1632         CHECK_NULL_VOID(contentModifier);
1633         contentModifier->SetIsFocused(false);
1634         pattern->RemoveIsFocusActiveUpdateEvent();
1635         pattern->ResetOriginCaretPosition();
1636     };
1637     focusHub->SetOnBlurInternal(blurTask);
1638 
1639     focusInitialized_ = true;
1640 }
1641 
AddIsFocusActiveUpdateEvent()1642 void TextPattern::AddIsFocusActiveUpdateEvent()
1643 {
1644     if (!isFocusActiveUpdateEvent_) {
1645         isFocusActiveUpdateEvent_ = [weak = WeakClaim(this)](bool isFocusAcitve) {
1646             auto pattern = weak.Upgrade();
1647             CHECK_NULL_VOID(pattern);
1648             pattern->OnIsFocusActiveUpdate(isFocusAcitve);
1649         };
1650     }
1651 
1652     auto pipline = PipelineContext::GetCurrentContextSafelyWithCheck();
1653     CHECK_NULL_VOID(pipline);
1654     pipline->AddIsFocusActiveUpdateEvent(GetHost(), isFocusActiveUpdateEvent_);
1655 }
1656 
RemoveIsFocusActiveUpdateEvent()1657 void TextPattern::RemoveIsFocusActiveUpdateEvent()
1658 {
1659     auto pipline = PipelineContext::GetCurrentContextSafelyWithCheck();
1660     CHECK_NULL_VOID(pipline);
1661     pipline->RemoveIsFocusActiveUpdateEvent(GetHost());
1662 }
1663 
OnIsFocusActiveUpdate(bool isFocusAcitve)1664 void TextPattern::OnIsFocusActiveUpdate(bool isFocusAcitve)
1665 {
1666     auto host = GetHost();
1667     CHECK_NULL_VOID(host);
1668     auto pattern = host->GetPattern<TextPattern>();
1669     CHECK_NULL_VOID(pattern);
1670     auto contentModifier = pattern->GetContentModifier();
1671     CHECK_NULL_VOID(contentModifier);
1672     contentModifier->SetIsFocused(isFocusAcitve);
1673 }
1674 
InitHoverEvent()1675 void TextPattern::InitHoverEvent()
1676 {
1677     CHECK_NULL_VOID(!hoverInitialized_);
1678     auto host = GetHost();
1679     CHECK_NULL_VOID(host);
1680     auto eventHub = host->GetEventHub<EventHub>();
1681     CHECK_NULL_VOID(eventHub);
1682     auto inputHub = eventHub->GetOrCreateInputEventHub();
1683     CHECK_NULL_VOID(inputHub);
1684 
1685     auto mouseTask = [weak = WeakClaim(this)](bool isHover) {
1686         auto pattern = weak.Upgrade();
1687         CHECK_NULL_VOID(pattern);
1688         auto contentModifier = pattern->GetContentModifier();
1689         CHECK_NULL_VOID(contentModifier);
1690         contentModifier->SetIsHovered(isHover);
1691     };
1692     auto mouseEvent_ = MakeRefPtr<InputEvent>(std::move(mouseTask));
1693     inputHub->AddOnHoverEvent(mouseEvent_);
1694 
1695     hoverInitialized_ = true;
1696 }
1697 
RecoverCopyOption()1698 void TextPattern::RecoverCopyOption()
1699 {
1700     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1701     CHECK_NULL_VOID(textLayoutProperty);
1702     auto host = GetHost();
1703     CHECK_NULL_VOID(host);
1704 
1705     copyOption_ = textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE
1706                       ? CopyOptions::None
1707                       : textLayoutProperty->GetCopyOption().value_or(CopyOptions::None);
1708 
1709     const auto& children = host->GetChildren();
1710     if (children.empty()) {
1711         if (IsSetObscured() && !isSpanStringMode_) {
1712             copyOption_ = CopyOptions::None;
1713         }
1714     }
1715     auto gestureEventHub = host->GetOrCreateGestureEventHub();
1716     CHECK_NULL_VOID(gestureEventHub);
1717     auto eventHub = host->GetEventHub<EventHub>();
1718     CHECK_NULL_VOID(eventHub);
1719     if (copyOption_ == CopyOptions::None && !textDetectEnable_ && !textLayoutProperty->GetTextOverflow() &&
1720         !onClick_ && !longPressEvent_) { // performance prune
1721         if (host->IsDraggable() || gestureEventHub->GetTextDraggable()) {
1722             gestureEventHub->SetTextDraggable(false);
1723             eventHub->SetDefaultOnDragStart(nullptr);
1724             if (!eventHub->HasOnDragStart() && IsTextNode()) {
1725                 gestureEventHub->RemoveDragEvent();
1726             }
1727         }
1728         return;
1729     }
1730     if (copyOption_ == CopyOptions::None) {
1731         CloseSelectOverlay();
1732         ResetSelection();
1733     }
1734     if (children.empty() && CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
1735         dataDetectorAdapter_->textForAI_ = textForDisplay_;
1736         dataDetectorAdapter_->StartAITask();
1737     }
1738     ProcessMarqueeVisibleAreaCallback();
1739     InitCopyOption(gestureEventHub, eventHub);
1740     bool enabledCache = eventHub->IsEnabled();
1741     selectOverlay_->SetMenuTranslateIsSupport(IsShowTranslate());
1742     selectOverlay_->SetIsSupportMenuSearch(IsShowSearch());
1743     selectOverlay_->UpdateHandleColor();
1744     if (textDetectEnable_ && enabledCache != enabled_) {
1745         enabled_ = enabledCache;
1746         host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE);
1747     }
1748 }
1749 
InitCopyOption(const RefPtr<GestureEventHub> & gestureEventHub,const RefPtr<EventHub> & eventHub)1750 void TextPattern::InitCopyOption(const RefPtr<GestureEventHub>& gestureEventHub, const RefPtr<EventHub>& eventHub)
1751 {
1752     CHECK_NULL_VOID(gestureEventHub);
1753     CHECK_NULL_VOID(eventHub);
1754     auto host = GetHost();
1755     CHECK_NULL_VOID(host);
1756     if (IsSelectableAndCopy()) {
1757         auto context = host->GetContext();
1758         CHECK_NULL_VOID(context);
1759         if (!clipboard_ && context) {
1760             clipboard_ = ClipboardProxy::GetInstance()->GetClipboard(context->GetTaskExecutor());
1761         }
1762         InitLongPressEvent(gestureEventHub);
1763         if (host->IsDraggable() && !shiftFlag_) {
1764             InitDragEvent();
1765         }
1766         InitKeyEvent();
1767         InitMouseEvent();
1768         InitTouchEvent();
1769         SetAccessibilityAction();
1770     } else {
1771         if (host->IsDraggable() || gestureEventHub->GetTextDraggable()) {
1772             gestureEventHub->SetTextDraggable(false);
1773             eventHub->SetDefaultOnDragStart(nullptr);
1774             if (!eventHub->HasOnDragStart() && IsTextNode()) {
1775                 gestureEventHub->RemoveDragEvent();
1776             }
1777         }
1778         if (longPressEvent_ && !hasSpanStringLongPressEvent_) {
1779             gestureEventHub->SetLongPressEvent(nullptr);
1780             longPressEvent_ = nullptr;
1781         }
1782     }
1783     if (onClick_ || IsSelectableAndCopy() || CanStartAITask()) {
1784         InitClickEvent(gestureEventHub);
1785         if (CanStartAITask()) {
1786             auto context = host->GetContext();
1787             CHECK_NULL_VOID(context);
1788             if (!clipboard_ && context) {
1789                 clipboard_ = ClipboardProxy::GetInstance()->GetClipboard(context->GetTaskExecutor());
1790             }
1791             InitMouseEvent();
1792             InitAISpanHoverEvent();
1793         }
1794     }
1795 }
1796 
HandleMouseEvent(const MouseInfo & info)1797 void TextPattern::HandleMouseEvent(const MouseInfo& info)
1798 {
1799     auto localLocation = info.GetLocalLocation();
1800     if (isAutoScrollByMouse_ && GetHost()) {
1801         NG::PointF localPoint(info.GetGlobalLocation().GetX(), info.GetGlobalLocation().GetY());
1802         NG::NGGestureRecognizer::Transform(localPoint, GetHost(), true);
1803         localLocation.SetX(localPoint.GetX());
1804         localLocation.SetY(localPoint.GetY());
1805     }
1806     auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
1807     Offset textOffset = { localLocation.GetX() - textPaintOffset.GetX(),
1808         localLocation.GetY() - textPaintOffset.GetY() };
1809     if (info.GetButton() == MouseButton::LEFT_BUTTON) {
1810         lastLeftMouseMoveLocation_ = info.GetGlobalLocation();
1811         HandleMouseLeftButton(info, textOffset);
1812         if (IsSelected()) {
1813             selectOverlay_->SetSelectionHoldCallback();
1814         }
1815         sourceType_ = info.GetSourceDevice();
1816     } else if (info.GetButton() == MouseButton::RIGHT_BUTTON) {
1817         HandleMouseRightButton(info, textOffset);
1818         sourceType_ = info.GetSourceDevice();
1819     }
1820     if (!IsSelected()) {
1821         ResetOriginCaretPosition();
1822     }
1823 }
1824 
HandleMouseLeftButton(const MouseInfo & info,const Offset & textOffset)1825 void TextPattern::HandleMouseLeftButton(const MouseInfo& info, const Offset& textOffset)
1826 {
1827     if (info.GetAction() == MouseAction::PRESS) {
1828         HandleMouseLeftPressAction(info, textOffset);
1829     } else if (info.GetAction() == MouseAction::MOVE) {
1830         HandleMouseLeftMoveAction(info, textOffset);
1831     } else if (info.GetAction() == MouseAction::RELEASE) {
1832         HandleMouseLeftReleaseAction(info, textOffset);
1833     }
1834 
1835     auto host = GetHost();
1836     CHECK_NULL_VOID(host);
1837     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1838 }
1839 
HandleMouseLeftPressAction(const MouseInfo & info,const Offset & textOffset)1840 void TextPattern::HandleMouseLeftPressAction(const MouseInfo& info, const Offset& textOffset)
1841 {
1842     isMousePressed_ = true;
1843     CheckPressedSpanPosition(textOffset);
1844     leftMousePressed_ = true;
1845     ShowShadow({ textOffset.GetX(), textOffset.GetY() }, GetUrlPressColor());
1846     if (BetweenSelectedPosition(info.GetGlobalLocation())) {
1847         blockPress_ = true;
1848         return;
1849     }
1850     mouseStatus_ = MouseStatus::PRESSED;
1851     CHECK_NULL_VOID(pManager_);
1852     if (shiftFlag_) {
1853         auto end = pManager_->GetGlyphIndexByCoordinate(textOffset);
1854         HandleSelectionChange(textSelector_.lastValidStart, end);
1855     } else {
1856         auto start = pManager_->GetGlyphIndexByCoordinate(textOffset);
1857         textSelector_.Update(start, start);
1858     }
1859     // auto scroll.
1860     scrollableParent_ = selectOverlay_->FindScrollableParent();
1861     auto host = GetHost();
1862     if (scrollableParent_.Upgrade() && host) {
1863         host->RegisterNodeChangeListener();
1864     }
1865 }
1866 
CheckPressedSpanPosition(const Offset & textOffset)1867 void TextPattern::CheckPressedSpanPosition(const Offset& textOffset)
1868 {
1869     leftMousePressedOffset_ = textOffset;
1870 }
1871 
HandleMouseLeftReleaseAction(const MouseInfo & info,const Offset & textOffset)1872 void TextPattern::HandleMouseLeftReleaseAction(const MouseInfo& info, const Offset& textOffset)
1873 {
1874     if (blockPress_) {
1875         blockPress_ = false;
1876     }
1877     auto oldMouseStatus = mouseStatus_;
1878     mouseStatus_ = MouseStatus::RELEASED;
1879     ShowShadow({ textOffset.GetX(), textOffset.GetY() }, GetUrlHoverColor());
1880     if (isDoubleClick_) {
1881         isDoubleClick_ = false;
1882         isMousePressed_ = false;
1883         leftMousePressed_ = false;
1884         return;
1885     }
1886     if (oldMouseStatus != MouseStatus::MOVE && oldMouseStatus == MouseStatus::PRESSED && !IsDragging()) {
1887         HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY()));
1888         if (dataDetectorAdapter_->hasClickedAISpan_) {
1889             selectOverlay_->DisableMenu();
1890             isMousePressed_ = false;
1891             leftMousePressed_ = false;
1892             return;
1893         }
1894     }
1895 
1896     CHECK_NULL_VOID(pManager_);
1897     auto start = textSelector_.baseOffset;
1898     auto end = textSelector_.destinationOffset;
1899     if (!IsSelected()) {
1900         start = -1;
1901         end = -1;
1902     }
1903     if (isMousePressed_ || oldMouseStatus == MouseStatus::MOVE || shiftFlag_) {
1904         HandleSelectionChange(start, end);
1905     }
1906 
1907     if (IsSelected() && oldMouseStatus == MouseStatus::MOVE && IsSelectedBindSelectionMenu()) {
1908         selectOverlay_->SetMouseMenuOffset(OffsetF(
1909             static_cast<float>(info.GetGlobalLocation().GetX()), static_cast<float>(info.GetGlobalLocation().GetY())));
1910         textResponseType_ = TextResponseType::SELECTED_BY_MOUSE;
1911         ShowSelectOverlay({ .animation = true });
1912     }
1913     isMousePressed_ = false;
1914     leftMousePressed_ = false;
1915     moveOverClickThreshold_ = false;
1916     // stop auto scroll.
1917     auto host = GetHost();
1918     if (host && scrollableParent_.Upgrade() && !selectOverlay_->SelectOverlayIsOn()) {
1919         host->UnregisterNodeChangeListener();
1920     }
1921     selectOverlay_->TriggerScrollableParentToScroll(scrollableParent_.Upgrade(), info.GetGlobalLocation(), true);
1922     isAutoScrollByMouse_ = false;
1923 }
1924 
HandleMouseLeftMoveAction(const MouseInfo & info,const Offset & textOffset)1925 void TextPattern::HandleMouseLeftMoveAction(const MouseInfo& info, const Offset& textOffset)
1926 {
1927     if (!IsSelectableAndCopy()) {
1928         isMousePressed_ = false;
1929         leftMousePressed_ = false;
1930         return;
1931     }
1932     if (isMousePressed_) {
1933         mouseStatus_ = MouseStatus::MOVE;
1934         CHECK_NULL_VOID(pManager_);
1935         auto end = pManager_->GetGlyphIndexByCoordinate(textOffset);
1936         HandleSelectionChange(textSelector_.baseOffset, end);
1937         selectOverlay_->TriggerScrollableParentToScroll(scrollableParent_.Upgrade(), info.GetGlobalLocation(), false);
1938         auto distance = (textOffset - leftMousePressedOffset_).GetDistance();
1939         if (distance >= CLICK_THRESHOLD.ConvertToPx()) {
1940             moveOverClickThreshold_ = true;
1941             return;
1942         }
1943     }
1944 }
1945 
HandleMouseRightButton(const MouseInfo & info,const Offset & textOffset)1946 void TextPattern::HandleMouseRightButton(const MouseInfo& info, const Offset& textOffset)
1947 {
1948     if (info.GetAction() == MouseAction::RELEASE) {
1949         selectOverlay_->SetMouseMenuOffset(OffsetF(
1950             static_cast<float>(info.GetGlobalLocation().GetX()), static_cast<float>(info.GetGlobalLocation().GetY())));
1951         if (!BetweenSelectedPosition(info.GetGlobalLocation())) {
1952             HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY()));
1953             if (dataDetectorAdapter_->hasClickedAISpan_) {
1954                 isMousePressed_ = false;
1955                 return;
1956             }
1957         }
1958         if (!IsSelectableAndCopy()) {
1959             return;
1960         }
1961 
1962         CalculateHandleOffsetAndShowOverlay(true);
1963         if (selectOverlay_->SelectOverlayIsOn()) {
1964             CloseSelectOverlay(true);
1965         }
1966         textResponseType_ = TextResponseType::RIGHT_CLICK;
1967         if (!IsSelected()) {
1968             auto spanNode = DynamicCast<FrameNode>(GetChildByIndex(GetSelectionSpanItemIndex(info)));
1969             if (spanNode && spanNode->GetTag() == V2::IMAGE_ETS_TAG) {
1970                 selectedType_ = TextSpanType::IMAGE;
1971             } else {
1972                 selectedType_ = TextSpanType::TEXT;
1973             }
1974         }
1975         ShowSelectOverlay({ .animation = true });
1976         isMousePressed_ = false;
1977         auto host = GetHost();
1978         CHECK_NULL_VOID(host);
1979         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1980     } else if (info.GetAction() == MouseAction::PRESS) {
1981         isMousePressed_ = true;
1982         CloseSelectOverlay(true);
1983     }
1984 }
1985 
InitTouchEvent()1986 void TextPattern::InitTouchEvent()
1987 {
1988     CHECK_NULL_VOID(!touchEventInitialized_);
1989     auto host = GetHost();
1990     CHECK_NULL_VOID(host);
1991     auto gesture = host->GetOrCreateGestureEventHub();
1992     CHECK_NULL_VOID(gesture);
1993 
1994     auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
1995         auto pattern = weak.Upgrade();
1996         CHECK_NULL_VOID(pattern);
1997         pattern->sourceType_ = info.GetSourceDevice();
1998         pattern->HandleTouchEvent(info);
1999     };
2000     auto touchListener = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
2001     gesture->AddTouchEvent(touchListener);
2002     touchEventInitialized_ = true;
2003 }
2004 
InitUrlTouchEvent()2005 void TextPattern::InitUrlTouchEvent()
2006 {
2007     CHECK_NULL_VOID(!urlTouchEventInitialized_);
2008     auto host = GetHost();
2009     CHECK_NULL_VOID(host);
2010     auto gesture = host->GetOrCreateGestureEventHub();
2011     CHECK_NULL_VOID(gesture);
2012 
2013     auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
2014         auto pattern = weak.Upgrade();
2015         CHECK_NULL_VOID(pattern);
2016         pattern->HandleUrlTouchEvent(info);
2017     };
2018     auto touchListener = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
2019     gesture->AddTouchEvent(touchListener);
2020     urlTouchEventInitialized_ = true;
2021 }
2022 
MarkDirtySelf()2023 void TextPattern::MarkDirtySelf()
2024 {
2025     auto host = GetHost();
2026     CHECK_NULL_VOID(host);
2027     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2028 }
2029 
HandleTouchEvent(const TouchEventInfo & info)2030 void TextPattern::HandleTouchEvent(const TouchEventInfo& info)
2031 {
2032     DoGestureSelection(info);
2033     ResetOriginCaretPosition();
2034 }
2035 
InitKeyEvent()2036 void TextPattern::InitKeyEvent()
2037 {
2038     CHECK_NULL_VOID(!keyEventInitialized_);
2039     auto host = GetHost();
2040     CHECK_NULL_VOID(host);
2041     auto focusHub = host->GetOrCreateFocusHub();
2042     CHECK_NULL_VOID(focusHub);
2043 
2044     auto keyTask = [weak = WeakClaim(this)](const KeyEvent& event) -> bool {
2045         auto pattern = weak.Upgrade();
2046         CHECK_NULL_RETURN(pattern, false);
2047         return pattern->HandleKeyEvent(event);
2048     };
2049     focusHub->SetOnKeyEventInternal(std::move(keyTask));
2050     keyEventInitialized_ = true;
2051 }
2052 
UpdateShiftFlag(const KeyEvent & keyEvent)2053 void TextPattern::UpdateShiftFlag(const KeyEvent& keyEvent)
2054 {
2055     bool flag = false;
2056     if (keyEvent.action == KeyAction::DOWN) {
2057         if (keyEvent.HasKey(KeyCode::KEY_SHIFT_LEFT) || keyEvent.HasKey(KeyCode::KEY_SHIFT_RIGHT)) {
2058             flag = true;
2059         }
2060     }
2061     if (flag != shiftFlag_) {
2062         shiftFlag_ = flag;
2063         if (!shiftFlag_) {
2064             // open drag
2065             InitDragEvent();
2066         } else  {
2067             // close drag
2068             ClearDragEvent();
2069         }
2070     }
2071 }
2072 
HandleKeyEvent(const KeyEvent & keyEvent)2073 bool TextPattern::HandleKeyEvent(const KeyEvent& keyEvent)
2074 {
2075     UpdateShiftFlag(keyEvent);
2076     if (keyEvent.action != KeyAction::DOWN) {
2077         return false;
2078     }
2079 
2080     if (keyEvent.IsCtrlWith(KeyCode::KEY_C)) {
2081         HandleOnCopy();
2082         return true;
2083     }
2084 
2085     if (keyEvent.IsCtrlWith(KeyCode::KEY_A)) {
2086         auto textSize = static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_;
2087         HandleSelectionChange(0, textSize);
2088         CloseSelectOverlay();
2089         auto host = GetHost();
2090         CHECK_NULL_RETURN(host, false);
2091         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2092         return true;
2093     }
2094 
2095     if (keyEvent.IsShiftWith(keyEvent.code)) {
2096         HandleOnSelect(keyEvent.code);
2097         return true;
2098     }
2099     return false;
2100 }
2101 
HandleOnSelect(KeyCode code)2102 void TextPattern::HandleOnSelect(KeyCode code)
2103 {
2104     auto end = textSelector_.GetEnd();
2105     switch (code) {
2106         case KeyCode::KEY_DPAD_LEFT: {
2107             HandleSelection(true, end - 1);
2108             break;
2109         }
2110         case KeyCode::KEY_DPAD_RIGHT: {
2111             HandleSelection(false, end + 1);
2112             break;
2113         }
2114         case KeyCode::KEY_DPAD_UP: {
2115             HandleSelectionUp();
2116             break;
2117         }
2118         case KeyCode::KEY_DPAD_DOWN: {
2119             HandleSelectionDown();
2120             break;
2121         }
2122         default:
2123             break;
2124     }
2125     if (!(shiftFlag_ && (code == KeyCode::KEY_DPAD_UP ||
2126                          code == KeyCode::KEY_DPAD_DOWN))) {
2127         ResetOriginCaretPosition();
2128     }
2129 }
2130 
HandleSelectionUp()2131 void TextPattern::HandleSelectionUp()
2132 {
2133     auto end = textSelector_.GetEnd();
2134     auto line = pManager_->GetLineCount();
2135     if (line == 1) {
2136         HandleSelection(true, 0);
2137         return;
2138     }
2139     CaretMetricsF secondHandleMetrics;
2140     CalcCaretMetricsByPosition(textSelector_.destinationOffset, secondHandleMetrics, TextAffinity::UPSTREAM);
2141     auto secondOffsetX = secondHandleMetrics.offset.GetX();
2142     auto secondOffsetY = secondHandleMetrics.offset.GetY();
2143     RecordOriginCaretPosition({ secondOffsetX, secondOffsetY });
2144     OffsetF originCaretPosition;
2145     auto caretXPosition = GetOriginCaretPosition(originCaretPosition) ? // recorded offset x
2146         originCaretPosition.GetX() : secondOffsetX;
2147     double height = GetTextHeight(end, false);
2148     Offset offset = { caretXPosition, secondOffsetY - height * 0.5 };
2149     auto caculateIndex = GetHandleIndex(offset);
2150     if (end == caculateIndex) {
2151         caculateIndex = 0;
2152     }
2153     HandleSelection(true, caculateIndex);
2154 }
2155 
HandleSelectionDown()2156 void TextPattern::HandleSelectionDown()
2157 {
2158     auto end = textSelector_.GetEnd();
2159     auto line = pManager_->GetLineCount();
2160     auto lastIndex = GetActualTextLength();
2161     if (line == 1) {
2162         HandleSelection(true, lastIndex);
2163         return;
2164     }
2165     CaretMetricsF secondHandleMetrics;
2166     CalcCaretMetricsByPosition(textSelector_.destinationOffset, secondHandleMetrics, TextAffinity::UPSTREAM);
2167     auto secondOffsetX = secondHandleMetrics.offset.GetX();
2168     RecordOriginCaretPosition({ secondOffsetX, secondHandleMetrics.offset.GetY() });
2169     OffsetF originCaretPosition;
2170     auto caretXPosition = GetOriginCaretPosition(originCaretPosition) ? // recorded offset x
2171         originCaretPosition.GetX() : secondOffsetX;
2172     double height = GetTextHeight(end, true);
2173     auto caculateIndex = GetHandleIndex({ caretXPosition, height });
2174     if (NearZero(height) || caculateIndex == end || caculateIndex > lastIndex) {
2175         caculateIndex = lastIndex;
2176     }
2177     HandleSelection(true, caculateIndex);
2178 }
2179 
HandleSelection(bool isEmojiStart,int32_t end)2180 void TextPattern::HandleSelection(bool isEmojiStart, int32_t end)
2181 {
2182     auto start = textSelector_.GetStart();
2183     auto lastIndex = GetActualTextLength();
2184     if (start < 0 || start > lastIndex || end < 0 || end > lastIndex) {
2185         return;
2186     }
2187     int32_t emojiStartIndex;
2188     int32_t emojiEndIndex;
2189     bool isIndexInEmoji = TextEmojiProcessor::IsIndexInEmoji(end, GetSelectedText(0, lastIndex),
2190         emojiStartIndex, emojiEndIndex);
2191     if (isIndexInEmoji) {
2192         end = isEmojiStart ? emojiStartIndex : emojiEndIndex;
2193     }
2194     HandleSelectionChange(start, end);
2195     CalculateHandleOffsetAndShowOverlay();
2196     CloseSelectOverlay(true);
2197     auto host = GetHost();
2198     CHECK_NULL_VOID(host);
2199     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2200 }
2201 
GetTextHeight(int32_t index,bool isNextLine)2202 double TextPattern::GetTextHeight(int32_t index, bool isNextLine)
2203 {
2204     double lineHeight = 0.0;
2205     auto lineCount = static_cast<int32_t>(pManager_->GetLineCount());
2206     for (auto lineNumber = 0; lineNumber < lineCount; lineNumber++) {
2207         auto lineMetrics = GetLineMetrics(lineNumber);
2208         auto startIndex = static_cast<int32_t>(lineMetrics.startIndex);
2209         auto endIndex = static_cast<int32_t>(lineMetrics.endIndex);
2210         lineHeight += lineMetrics.height;
2211         if (isNextLine) {
2212             if (index <= endIndex && endIndex != GetActualTextLength()) {
2213                 return lineHeight;
2214             }
2215         } else {
2216             auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
2217             CHECK_NULL_RETURN(textLayoutProperty, 0);
2218             auto maxLines = textLayoutProperty->GetMaxLinesValue(Infinity<uint32_t>());
2219             if ((index <= endIndex && startIndex != 0) ||
2220                 ((lineNumber + 1) == static_cast<int32_t>(maxLines) && lineNumber != 0)) {
2221                 return GetLineMetrics(lineNumber - 1).height;
2222             }
2223         }
2224     }
2225     return 0.0;
2226 }
2227 
GetActualTextLength()2228 int32_t TextPattern::GetActualTextLength()
2229 {
2230     auto lineCount = static_cast<int32_t>(pManager_->GetLineCount());
2231     return GetLineMetrics(lineCount - 1).endIndex;
2232 }
2233 
SetTextSelectableMode(TextSelectableMode value)2234 void TextPattern::SetTextSelectableMode(TextSelectableMode value)
2235 {
2236     auto host = GetHost();
2237     CHECK_NULL_VOID(host);
2238     auto focusHub = host->GetOrCreateFocusHub();
2239     CHECK_NULL_VOID(focusHub);
2240     if (value == TextSelectableMode::SELECTABLE_FOCUSABLE) {
2241         focusHub->SetFocusable(true);
2242         focusHub->SetIsFocusOnTouch(true);
2243     } else {
2244         focusHub->SetFocusable(false);
2245         focusHub->SetIsFocusOnTouch(false);
2246     }
2247 }
2248 
IsSelectableAndCopy()2249 bool TextPattern::IsSelectableAndCopy()
2250 {
2251     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
2252     CHECK_NULL_RETURN(textLayoutProperty, false);
2253     auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
2254     return mode != TextSelectableMode::UNSELECTABLE && copyOption_ != CopyOptions::None;
2255 }
2256 
IsDraggable(const Offset & offset)2257 bool TextPattern::IsDraggable(const Offset& offset)
2258 {
2259     auto host = GetHost();
2260     CHECK_NULL_RETURN(host, false);
2261     auto eventHub = host->GetEventHub<EventHub>();
2262     bool draggable = eventHub->HasOnDragStart();
2263     return draggable && LocalOffsetInSelectedArea(offset);
2264 }
2265 
OnDragStart(const RefPtr<Ace::DragEvent> & event,const std::string & extraParams)2266 NG::DragDropInfo TextPattern::OnDragStart(const RefPtr<Ace::DragEvent>& event, const std::string& extraParams)
2267 {
2268     DragDropInfo itemInfo;
2269     auto host = GetHost();
2270     CHECK_NULL_RETURN(host, itemInfo);
2271     if (overlayMod_) {
2272         overlayMod_->ClearSelectedForegroundColorAndRects();
2273     }
2274     auto hub = host->GetEventHub<EventHub>();
2275     auto gestureHub = hub->GetOrCreateGestureEventHub();
2276     auto selectStart = textSelector_.GetTextStart();
2277     auto selectEnd = textSelector_.GetTextEnd();
2278     recoverStart_ = selectStart;
2279     recoverEnd_ = selectEnd;
2280     auto textSelectInfo = GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT);
2281     dragResultObjects_ = textSelectInfo.GetSelection().resultObjects;
2282     ResetDragRecordSize(dragResultObjects_.empty() ? -1 : 1);
2283     dragBoxes_ = GetTextBoxes();
2284     status_ = Status::DRAGGING;
2285     if (dragResultObjects_.empty() || !gestureHub->GetIsTextDraggable()) {
2286         return itemInfo;
2287     }
2288     auto data = event->GetData();
2289     if (!data) {
2290         AddUdmfData(event);
2291     }
2292     CloseOperate();
2293     host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
2294     return itemInfo;
2295 }
2296 
AddUdmfTxtPreProcessor(const ResultObject src,ResultObject & result,bool isAppend)2297 void TextPattern::AddUdmfTxtPreProcessor(const ResultObject src, ResultObject& result, bool isAppend)
2298 {
2299     auto valueString = GetSelectedSpanText(src.valueString,
2300         src.offsetInSpan[RichEditorSpanRange::RANGESTART], src.offsetInSpan[RichEditorSpanRange::RANGEEND],
2301         false, true, false);
2302     if (isAppend) {
2303         result.valueString = result.valueString + valueString;
2304     } else {
2305         result.valueString = valueString;
2306     }
2307 }
2308 
AddUdmfData(const RefPtr<Ace::DragEvent> & event)2309 void TextPattern::AddUdmfData(const RefPtr<Ace::DragEvent>& event)
2310 {
2311     RefPtr<UnifiedData> unifiedData = UdmfClient::GetInstance()->CreateUnifiedData();
2312     if (isSpanStringMode_) {
2313         std::vector<uint8_t> arr;
2314         auto dragSpanString = styledString_->GetSubSpanString(recoverStart_, recoverEnd_ - recoverStart_,
2315             false, true, false);
2316         dragSpanString->EncodeTlv(arr);
2317         UdmfClient::GetInstance()->AddSpanStringRecord(unifiedData, arr);
2318     } else {
2319         ProcessNormalUdmfData(unifiedData);
2320     }
2321     event->SetData(unifiedData);
2322 }
2323 
ProcessNormalUdmfData(const RefPtr<UnifiedData> & unifiedData)2324 void TextPattern::ProcessNormalUdmfData(const RefPtr<UnifiedData>& unifiedData)
2325 {
2326     std::list<ResultObject> finalResult;
2327     auto type = SelectSpanType::TYPESPAN;
2328     for (const auto& resultObj : dragResultObjects_) {
2329         if (finalResult.empty() || resultObj.type != SelectSpanType::TYPESPAN || type != SelectSpanType::TYPESPAN) {
2330             type = resultObj.type;
2331             finalResult.emplace_back(resultObj);
2332             if (resultObj.type == SelectSpanType::TYPESPAN) {
2333                 AddUdmfTxtPreProcessor(resultObj, finalResult.back(), false);
2334             }
2335         } else {
2336             AddUdmfTxtPreProcessor(resultObj, finalResult.back(), true);
2337         }
2338     }
2339     auto resultProcessor = [unifiedData, weak = WeakClaim(this)](const ResultObject& result) {
2340         auto pattern = weak.Upgrade();
2341         CHECK_NULL_VOID(pattern);
2342         std::string u8ValueString = UtfUtils::Str16DebugToStr8(result.valueString);
2343         if (result.type == SelectSpanType::TYPESPAN) {
2344             UdmfClient::GetInstance()->AddPlainTextRecord(unifiedData, u8ValueString);
2345             return;
2346         }
2347         if (result.type == SelectSpanType::TYPEIMAGE) {
2348             if (result.valuePixelMap) {
2349                 pattern->AddPixelMapToUdmfData(result.valuePixelMap, unifiedData);
2350             } else if (u8ValueString.size() > 1) {
2351                 UdmfClient::GetInstance()->AddImageRecord(unifiedData, u8ValueString);
2352             } else {
2353                 // builder span, fill pixelmap data
2354                 auto builderNode = DynamicCast<FrameNode>(pattern->GetChildByIndex(result.spanPosition.spanIndex));
2355                 CHECK_NULL_VOID(builderNode);
2356                 pattern->AddPixelMapToUdmfData(builderNode->GetDragPixelMap(), unifiedData);
2357             }
2358         }
2359     };
2360     for (const auto& resultObj : finalResult) {
2361         resultProcessor(resultObj);
2362     }
2363 }
2364 
AddPixelMapToUdmfData(const RefPtr<PixelMap> & pixelMap,const RefPtr<UnifiedData> & unifiedData)2365 void TextPattern::AddPixelMapToUdmfData(const RefPtr<PixelMap>& pixelMap, const RefPtr<UnifiedData>& unifiedData)
2366 {
2367     CHECK_NULL_VOID(pixelMap && unifiedData);
2368     const uint8_t* pixels = pixelMap->GetPixels();
2369     CHECK_NULL_VOID(pixels);
2370     int32_t length = pixelMap->GetByteCount();
2371     std::vector<uint8_t> data(pixels, pixels + length);
2372     PixelMapRecordDetails details = { pixelMap->GetWidth(), pixelMap->GetHeight(),
2373         pixelMap->GetPixelFormat(), pixelMap->GetAlphaType() };
2374     UdmfClient::GetInstance()->AddPixelMapRecord(unifiedData, data, details);
2375 }
2376 
CloseOperate()2377 void TextPattern::CloseOperate()
2378 {
2379     UpdateSpanItemDragStatus(dragResultObjects_, true);
2380     recoverDragResultObjects_ = dragResultObjects_;
2381     AceEngineExt::GetInstance().DragStartExt();
2382     CloseKeyboard(true);
2383     CloseSelectOverlay();
2384     ResetSelection();
2385 }
2386 
OnDragStartNoChild(const RefPtr<Ace::DragEvent> & event,const std::string & extraParams)2387 DragDropInfo TextPattern::OnDragStartNoChild(const RefPtr<Ace::DragEvent>& event, const std::string& extraParams)
2388 {
2389     auto weakPtr = WeakClaim(this);
2390     DragDropInfo itemInfo;
2391     auto pattern = weakPtr.Upgrade();
2392     auto host = pattern->GetHost();
2393     auto hub = host->GetEventHub<EventHub>();
2394     auto gestureHub = hub->GetOrCreateGestureEventHub();
2395     CHECK_NULL_RETURN(gestureHub, itemInfo);
2396     if (!gestureHub->GetIsTextDraggable()) {
2397         return itemInfo;
2398     }
2399     auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
2400     dragBoxes_ = GetTextBoxes();
2401     pattern->status_ = Status::DRAGGING;
2402     pattern->contentMod_->ChangeDragStatus();
2403     pattern->showSelect_ = false;
2404     auto start = textSelector_.GetTextStart();
2405     pattern->recoverStart_ = start;
2406     auto end = textSelector_.GetTextEnd();
2407     pattern->recoverEnd_ = end;
2408     auto beforeStr = GetSelectedText(0, start, false, true);
2409     auto selectedStr = GetSelectedText(textSelector_.GetTextStart(), textSelector_.GetTextEnd(), false, true);
2410     auto afterStr = GetSelectedText(end, textForDisplay_.length(), false, true);
2411     pattern->dragContents_ = { beforeStr, selectedStr, afterStr };
2412     auto selectedUtf8Str = UtfUtils::Str16DebugToStr8(selectedStr);
2413     itemInfo.extraInfo = selectedUtf8Str;
2414     RefPtr<UnifiedData> unifiedData = UdmfClient::GetInstance()->CreateUnifiedData();
2415     UdmfClient::GetInstance()->AddPlainTextRecord(unifiedData, selectedUtf8Str);
2416     event->SetData(unifiedData);
2417     host->MarkDirtyWithOnProChange(layoutProperty->GetMaxLinesValue(Infinity<float>()) <= 1
2418                                        ? PROPERTY_UPDATE_MEASURE_SELF
2419                                        : PROPERTY_UPDATE_MEASURE);
2420 
2421     CloseSelectOverlay();
2422     ResetSelection();
2423     return itemInfo;
2424 }
2425 
UpdateSpanItemDragStatus(const std::list<ResultObject> & resultObjects,bool isDragging)2426 void TextPattern::UpdateSpanItemDragStatus(const std::list<ResultObject>& resultObjects, bool isDragging)
2427 {
2428     if (resultObjects.empty()) {
2429         return;
2430     }
2431     auto dragStatusUpdateAction = [weakPtr = WeakClaim(this), isDragging](const ResultObject& resultObj) {
2432         auto pattern = weakPtr.Upgrade();
2433         CHECK_NULL_VOID(pattern);
2434         if (pattern->spans_.empty()) {
2435             return;
2436         }
2437         auto it = pattern->spans_.begin();
2438         if (resultObj.spanPosition.spanIndex >= static_cast<int32_t>(pattern->spans_.size())) {
2439             std::advance(it, !pattern->spans_.empty() ? static_cast<int32_t>(pattern->spans_.size()) - 1 : 0);
2440             TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "resultObj.spanPosition.spanIndex is larger than spans size.");
2441         } else {
2442             std::advance(it, resultObj.spanPosition.spanIndex);
2443         }
2444         auto spanItem = *it;
2445         CHECK_NULL_VOID(spanItem);
2446         if (resultObj.type == SelectSpanType::TYPESPAN) {
2447             if (pattern->isSpanStringMode_) {
2448                 spanItem = resultObj.span.Upgrade();
2449                 CHECK_NULL_VOID(spanItem);
2450             }
2451             if (isDragging) {
2452                 spanItem->StartDrag(resultObj.offsetInSpan[RichEditorSpanRange::RANGESTART],
2453                     resultObj.offsetInSpan[RichEditorSpanRange::RANGEEND]);
2454                 pattern->dragSpanItems_.emplace_back(spanItem);
2455             } else {
2456                 spanItem->EndDrag();
2457             }
2458             return;
2459         }
2460 
2461         if (resultObj.type == SelectSpanType::TYPEIMAGE) {
2462             if (isDragging) {
2463                 pattern->dragSpanItems_.emplace_back(spanItem);
2464             }
2465             auto imageNode = DynamicCast<FrameNode>(pattern->GetChildByIndex(resultObj.spanPosition.spanIndex));
2466             CHECK_NULL_VOID(imageNode);
2467             auto renderContext = imageNode->GetRenderContext();
2468             CHECK_NULL_VOID(renderContext);
2469             renderContext->UpdateOpacity(isDragging ? (double)DRAGGED_TEXT_OPACITY / 255 : 1);
2470             imageNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2471         }
2472     };
2473     for (const auto& resultObj : resultObjects) {
2474         dragStatusUpdateAction(resultObj);
2475     }
2476 }
2477 
OnDragEnd(const RefPtr<Ace::DragEvent> & event)2478 void TextPattern::OnDragEnd(const RefPtr<Ace::DragEvent>& event)
2479 {
2480     ResetDragRecordSize(-1);
2481     auto wk = WeakClaim(this);
2482     auto pattern = wk.Upgrade();
2483     CHECK_NULL_VOID(pattern);
2484     auto host = GetHost();
2485     CHECK_NULL_VOID(host);
2486     isMousePressed_ = false;
2487     if (status_ == Status::DRAGGING) {
2488         status_ = Status::NONE;
2489     }
2490     dragSpanItems_.clear();
2491     if (dragResultObjects_.empty()) {
2492         return;
2493     }
2494     UpdateSpanItemDragStatus(dragResultObjects_, false);
2495     dragResultObjects_.clear();
2496     if (event && event->GetResult() != DragRet::DRAG_SUCCESS && IsSelectableAndCopy()) {
2497         HandleSelectionChange(recoverStart_, recoverEnd_);
2498         isShowMenu_ = false;
2499         if (GetCurrentDragTool() == SourceTool::FINGER) {
2500             CalculateHandleOffsetAndShowOverlay();
2501             ShowSelectOverlay({ .menuIsShow = false });
2502         }
2503     }
2504     host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
2505 }
2506 
OnDragEndNoChild(const RefPtr<Ace::DragEvent> & event)2507 void TextPattern::OnDragEndNoChild(const RefPtr<Ace::DragEvent>& event)
2508 {
2509     auto wk = WeakClaim(this);
2510     auto pattern = wk.Upgrade();
2511     CHECK_NULL_VOID(pattern);
2512     auto host = pattern->GetHost();
2513     CHECK_NULL_VOID(host);
2514     isMousePressed_ = false;
2515     if (pattern->status_ == Status::DRAGGING) {
2516         pattern->status_ = Status::NONE;
2517         pattern->MarkContentChange();
2518         pattern->contentMod_->ChangeDragStatus();
2519         if (event && event->GetResult() != DragRet::DRAG_SUCCESS && IsSelectableAndCopy()) {
2520             HandleSelectionChange(recoverStart_, recoverEnd_);
2521             isShowMenu_ = false;
2522             if (GetCurrentDragTool() == SourceTool::FINGER) {
2523                 CalculateHandleOffsetAndShowOverlay();
2524                 ShowSelectOverlay({ .menuIsShow = false });
2525             }
2526         }
2527         auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
2528         host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
2529     }
2530 }
2531 
OnDragMove(const RefPtr<Ace::DragEvent> & event)2532 void TextPattern::OnDragMove(const RefPtr<Ace::DragEvent>& event)
2533 {
2534     auto weakPtr = WeakClaim(this);
2535     auto pattern = weakPtr.Upgrade();
2536     if (pattern->status_ == Status::DRAGGING) {
2537         CloseSelectOverlay();
2538         pattern->showSelect_ = false;
2539     }
2540 }
2541 
InitDragEvent()2542 void TextPattern::InitDragEvent()
2543 {
2544     auto host = GetHost();
2545     CHECK_NULL_VOID(host);
2546     auto eventHub = host->GetEventHub<EventHub>();
2547     CHECK_NULL_VOID(eventHub);
2548     auto gestureHub = host->GetOrCreateGestureEventHub();
2549     CHECK_NULL_VOID(gestureHub);
2550     gestureHub->InitDragDropEvent();
2551     gestureHub->SetTextDraggable(true);
2552     gestureHub->SetThumbnailCallback(GetThumbnailCallback());
2553     auto onDragStart = [weakPtr = WeakClaim(this)](
2554                            const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& extraParams) -> DragDropInfo {
2555         NG::DragDropInfo itemInfo;
2556         auto pattern = weakPtr.Upgrade();
2557         CHECK_NULL_RETURN(pattern, itemInfo);
2558         auto eventHub = pattern->GetEventHub<EventHub>();
2559         CHECK_NULL_RETURN(eventHub, itemInfo);
2560         pattern->SetCurrentDragTool(event->GetSourceTool());
2561         if (pattern->spans_.empty() && !pattern->isSpanStringMode_) {
2562             return pattern->OnDragStartNoChild(event, extraParams);
2563         }
2564         return pattern->OnDragStart(event, extraParams);
2565     };
2566     eventHub->SetDefaultOnDragStart(std::move(onDragStart));
2567     auto onDragMove = [weakPtr = WeakClaim(this)](
2568                           const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& extraParams) {
2569         auto pattern = weakPtr.Upgrade();
2570         CHECK_NULL_VOID(pattern);
2571         pattern->OnDragMove(event);
2572     };
2573     eventHub->SetOnDragMove(std::move(onDragMove));
2574     auto onDragEnd = [weakPtr = WeakClaim(this)](const RefPtr<OHOS::Ace::DragEvent>& event) {
2575         auto pattern = weakPtr.Upgrade();
2576         CHECK_NULL_VOID(pattern);
2577         // 拖拽框架强引用导致退出页面后还能够运行到这里
2578         if (pattern->isDetachFromMainTree_) {
2579             return;
2580         }
2581         ContainerScope scope(pattern->GetHostInstanceId());
2582         pattern->showSelect_ = true;
2583         if (pattern->spans_.empty()) {
2584             pattern->OnDragEndNoChild(event);
2585         } else {
2586             pattern->OnDragEnd(event);
2587         }
2588     };
2589     eventHub->SetOnDragEnd(std::move(onDragEnd));
2590 }
2591 
ClearDragEvent()2592 void TextPattern::ClearDragEvent()
2593 {
2594     auto host = GetHost();
2595     CHECK_NULL_VOID(host);
2596     auto eventHub = host->GetEventHub<EventHub>();
2597     CHECK_NULL_VOID(eventHub);
2598     auto gestureHub = host->GetOrCreateGestureEventHub();
2599     CHECK_NULL_VOID(gestureHub);
2600     gestureHub->SetTextDraggable(false);
2601     gestureHub->SetIsTextDraggable(false);
2602     gestureHub->SetThumbnailCallback(nullptr);
2603     eventHub->SetDefaultOnDragStart(nullptr);
2604     eventHub->SetOnDragMove(nullptr);
2605     eventHub->SetOnDragEnd(nullptr);
2606 }
2607 
GetThumbnailCallback()2608 std::function<void(Offset)> TextPattern::GetThumbnailCallback()
2609 {
2610     return [wk = WeakClaim(this)](const Offset& point) {
2611         auto pattern = wk.Upgrade();
2612         CHECK_NULL_VOID(pattern);
2613         if (pattern->BetweenSelectedPosition(point)) {
2614             const auto& children = pattern->GetChildNodes();
2615             std::list<RefPtr<FrameNode>> imageChildren;
2616             for (const auto& child : children) {
2617                 auto node = DynamicCast<FrameNode>(child);
2618                 if (!node) {
2619                     continue;
2620                 }
2621                 auto image = node->GetPattern<ImagePattern>();
2622                 if (image) {
2623                     imageChildren.emplace_back(node);
2624                 }
2625             }
2626             auto info = pattern->CreateTextDragInfo();
2627             pattern->dragNode_ = RichEditorDragPattern::CreateDragNode(pattern->GetHost(), imageChildren, info);
2628             auto textDragPattern = pattern->dragNode_->GetPattern<TextDragPattern>();
2629             if (textDragPattern) {
2630                 auto option = pattern->GetHost()->GetDragPreviewOption();
2631                 option.options.shadowPath = textDragPattern->GetBackgroundPath()->ConvertToSVGString();
2632                 option.options.shadow = Shadow(RICH_DEFAULT_ELEVATION, {0.0, 0.0}, Color(RICH_DEFAULT_SHADOW_COLOR),
2633                 ShadowStyle::OuterFloatingSM);
2634                 pattern->GetHost()->SetDragPreviewOptions(option);
2635             }
2636             FrameNode::ProcessOffscreenNode(pattern->dragNode_);
2637         }
2638     };
2639 }
2640 
CreateTextDragInfo()2641 TextDragInfo TextPattern::CreateTextDragInfo()
2642 {
2643     TextDragInfo info;
2644     auto context = PipelineContext::GetCurrentContextSafelyWithCheck();
2645     CHECK_NULL_RETURN(context, info);
2646     auto theme = context->GetTheme<TextTheme>();
2647     CHECK_NULL_RETURN(theme, info);
2648     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
2649     CHECK_NULL_RETURN(textLayoutProperty, info);
2650     info.handleColor = theme->GetCaretColor();
2651     info.selectedBackgroundColor = theme->GetSelectedColor();
2652     auto selectOverlayInfo = selectOverlay_->GetSelectOverlayInfos();
2653     if (selectOverlayInfo.has_value()) {
2654         if (selectOverlayInfo->firstHandle.isShow) {
2655             info.firstHandle = selectOverlayInfo->firstHandle.paintRect;
2656         }
2657         if (selectOverlayInfo->secondHandle.isShow) {
2658             info.secondHandle =  selectOverlayInfo->secondHandle.paintRect;
2659         }
2660     }
2661     return info;
2662 }
2663 
GetAllChildren() const2664 const std::list<RefPtr<UINode>>& TextPattern::GetAllChildren() const
2665 {
2666     return childNodes_;
2667 }
2668 
GetSelectedSpanText(std::u16string value,int32_t start,int32_t end,bool includeStartHalf,bool includeEndHalf,bool getSubstrDirectly) const2669 std::u16string TextPattern::GetSelectedSpanText(std::u16string value, int32_t start, int32_t end, bool includeStartHalf,
2670     bool includeEndHalf, bool getSubstrDirectly) const
2671 {
2672     if (start < 0 || end > static_cast<int32_t>(value.length()) || start >= end) {
2673         return u"";
2674     }
2675     auto min = std::min(start, end);
2676     auto max = std::max(start, end);
2677     if (getSubstrDirectly) {
2678         min = std::clamp(min, 0, static_cast<int32_t>(value.length()));
2679         return value.substr(min, max - min);
2680     } else {
2681         return TextEmojiProcessor::SubU16string(min, max - min, value, includeStartHalf, includeEndHalf);
2682     }
2683 }
2684 
GetTextStyleObject(const RefPtr<SpanNode> & node)2685 TextStyleResult TextPattern::GetTextStyleObject(const RefPtr<SpanNode>& node)
2686 {
2687     TextStyleResult textStyle;
2688     textStyle.fontColor = node->GetTextColorValue(Color::BLACK).ColorToString();
2689     textStyle.fontStyle = static_cast<int32_t>(node->GetItalicFontStyleValue(OHOS::Ace::FontStyle::NORMAL));
2690     textStyle.fontWeight = static_cast<int32_t>(node->GetFontWeightValue(FontWeight::NORMAL));
2691     std::string fontFamilyValue;
2692     const std::vector<std::string> defaultFontFamily = { "HarmonyOS Sans" };
2693     auto fontFamily = node->GetFontFamilyValue(defaultFontFamily);
2694     for (const auto& str : fontFamily) {
2695         fontFamilyValue += str;
2696         fontFamilyValue += ",";
2697     }
2698     fontFamilyValue =
2699         fontFamilyValue.substr(0, !fontFamilyValue.empty() ? static_cast<int32_t>(fontFamilyValue.size()) - 1 : 0);
2700     textStyle.fontFamily = !fontFamilyValue.empty() ? fontFamilyValue : defaultFontFamily.front();
2701     textStyle.decorationType = static_cast<int32_t>(node->GetTextDecorationValue(TextDecoration::NONE));
2702     textStyle.decorationColor = node->GetTextDecorationColorValue(Color::BLACK).ColorToString();
2703     textStyle.decorationStyle = static_cast<int32_t>(node->GetTextDecorationStyleValue(TextDecorationStyle::SOLID));
2704     textStyle.textAlign = static_cast<int32_t>(node->GetTextAlignValue(TextAlign::START));
2705     auto lm = node->GetLeadingMarginValue({});
2706     if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
2707         textStyle.fontSize = node->GetFontSizeValue(Dimension(16.0f, DimensionUnit::VP)).ConvertToFp();
2708         textStyle.lineHeight = node->GetLineHeightValue(Dimension()).ConvertToFp();
2709         textStyle.letterSpacing = node->GetLetterSpacingValue(Dimension()).ConvertToFp();
2710         textStyle.lineSpacing = node->GetLineSpacingValue(Dimension()).ConvertToFp();
2711     } else {
2712         textStyle.fontSize = node->GetFontSizeValue(Dimension(16.0f, DimensionUnit::VP)).ConvertToVp();
2713         textStyle.lineHeight = node->GetLineHeightValue(Dimension()).ConvertToVp();
2714         textStyle.letterSpacing = node->GetLetterSpacingValue(Dimension()).ConvertToVp();
2715         textStyle.lineSpacing = node->GetLineSpacingValue(Dimension()).ConvertToVp();
2716     }
2717     textStyle.halfLeading = node->GetHalfLeadingValue(false);
2718     textStyle.fontFeature = node->GetFontFeatureValue(ParseFontFeatureSettings("\"pnum\" 1"));
2719     textStyle.leadingMarginSize[RichEditorLeadingRange::LEADING_START] = lm.size.Width().ToString();
2720     textStyle.leadingMarginSize[RichEditorLeadingRange::LEADING_END] = lm.size.Height().ToString();
2721     textStyle.wordBreak = static_cast<int32_t>(node->GetWordBreakValue(WordBreak::BREAK_WORD));
2722     textStyle.lineBreakStrategy = static_cast<int32_t>(node->GetLineBreakStrategyValue(LineBreakStrategy::GREEDY));
2723     textStyle.textShadows = node->GetTextShadowValue({});
2724     textStyle.textBackgroundStyle = node->GetTextBackgroundStyle();
2725     textStyle.paragraphSpacing = node->GetParagraphSpacing();
2726     return textStyle;
2727 }
2728 
GetChildByIndex(int32_t index) const2729 RefPtr<UINode> TextPattern::GetChildByIndex(int32_t index) const
2730 {
2731     const auto& children = childNodes_;
2732     int32_t size = static_cast<int32_t>(children.size());
2733     if (index < 0 || index >= size) {
2734         return nullptr;
2735     }
2736     auto pos = children.begin();
2737     std::advance(pos, index);
2738     return *pos;
2739 }
2740 
GetSpanItemByIndex(int32_t index) const2741 RefPtr<SpanItem> TextPattern::GetSpanItemByIndex(int32_t index) const
2742 {
2743     int32_t size = static_cast<int32_t>(spans_.size());
2744     if (index < 0 || index >= size) {
2745         return nullptr;
2746     }
2747     auto pos = spans_.begin();
2748     std::advance(pos, index);
2749     return *pos;
2750 }
2751 
GetTextResultObject(RefPtr<UINode> uinode,int32_t index,int32_t start,int32_t end)2752 ResultObject TextPattern::GetTextResultObject(RefPtr<UINode> uinode, int32_t index, int32_t start, int32_t end)
2753 {
2754     bool selectFlag = false;
2755     ResultObject resultObject;
2756     if (!DynamicCast<SpanNode>(uinode)) {
2757         return resultObject;
2758     }
2759     auto spanItem = DynamicCast<SpanNode>(uinode)->GetSpanItem();
2760     int32_t itemLength = static_cast<int32_t>(spanItem->content.length());
2761     int32_t endPosition = std::min(GetTextContentLength(), spanItem->position);
2762     int32_t startPosition = endPosition - itemLength;
2763 
2764     if (startPosition >= start && endPosition <= end) {
2765         selectFlag = true;
2766         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
2767         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
2768     } else if (startPosition < start && endPosition <= end && endPosition > start) {
2769         selectFlag = true;
2770         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
2771         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
2772     } else if (startPosition >= start && startPosition < end && endPosition >= end) {
2773         selectFlag = true;
2774         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
2775         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
2776     } else if (startPosition <= start && endPosition >= end) {
2777         selectFlag = true;
2778         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
2779         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
2780     }
2781     if (selectFlag) {
2782         resultObject.spanPosition.spanIndex = index;
2783         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
2784         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
2785         resultObject.type = SelectSpanType::TYPESPAN;
2786         SetResultObjectText(resultObject, spanItem);
2787         auto spanNode = DynamicCast<SpanNode>(uinode);
2788         resultObject.textStyle = GetTextStyleObject(spanNode);
2789     }
2790     return resultObject;
2791 }
2792 
SetResultObjectText(ResultObject & resultObject,const RefPtr<SpanItem> & spanItem)2793 void TextPattern::SetResultObjectText(ResultObject& resultObject, const RefPtr<SpanItem>& spanItem)
2794 {
2795     CHECK_NULL_VOID(spanItem);
2796     resultObject.valueString = spanItem->content;
2797 }
2798 
GetSymbolSpanResultObject(RefPtr<UINode> uinode,int32_t index,int32_t start,int32_t end)2799 ResultObject TextPattern::GetSymbolSpanResultObject(RefPtr<UINode> uinode, int32_t index, int32_t start, int32_t end)
2800 {
2801     bool selectFlag = false;
2802     ResultObject resultObject;
2803     resultObject.isDraggable = false;
2804     if (!DynamicCast<SpanNode>(uinode)) {
2805         return resultObject;
2806     }
2807     auto spanItem = DynamicCast<SpanNode>(uinode)->GetSpanItem();
2808     int32_t itemLength = static_cast<int32_t>(spanItem->content.length());
2809     int32_t endPosition = std::min(GetTextContentLength(), spanItem->position);
2810     int32_t startPosition = endPosition - itemLength;
2811 
2812     if (startPosition >= start && endPosition <= end) {
2813         selectFlag = true;
2814         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
2815         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
2816     } else if (startPosition < start && endPosition <= end && endPosition > start) {
2817         selectFlag = true;
2818         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
2819         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
2820     } else if (startPosition >= start && startPosition < end && endPosition >= end) {
2821         selectFlag = true;
2822         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
2823         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
2824     } else if (startPosition <= start && endPosition >= end) {
2825         selectFlag = true;
2826         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
2827         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
2828     }
2829     if (selectFlag) {
2830         resultObject.valueResource = spanItem->GetResourceObject();
2831         resultObject.spanPosition.spanIndex = index;
2832         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
2833         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
2834         resultObject.type = SelectSpanType::TYPESYMBOLSPAN;
2835         resultObject.valueString = UtfUtils::Str8ToStr16(std::to_string(spanItem->unicode));
2836         auto spanNode = DynamicCast<SpanNode>(uinode);
2837         resultObject.symbolSpanStyle = GetSymbolSpanStyleObject(spanNode);
2838     }
2839     return resultObject;
2840 }
2841 
GetSymbolSpanStyleObject(const RefPtr<SpanNode> & node)2842 SymbolSpanStyle TextPattern::GetSymbolSpanStyleObject(const RefPtr<SpanNode>& node)
2843 {
2844     SymbolSpanStyle symbolSpanStyle;
2845     std::string symbolColorValue;
2846     auto symbolColors = node->GetSymbolColorList();
2847     if (symbolColors.has_value()) {
2848         for (const auto& color : *symbolColors) {
2849             symbolColorValue += color.ColorToString() + ",";
2850         }
2851     }
2852     symbolColorValue =
2853         symbolColorValue.substr(0, !symbolColorValue.empty() ? static_cast<int32_t>(symbolColorValue.size()) - 1 : 0);
2854     symbolSpanStyle.symbolColor = !symbolColorValue.empty() ? symbolColorValue : SYMBOL_COLOR;
2855     if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
2856         symbolSpanStyle.fontSize = node->GetFontSizeValue(Dimension(DIMENSION_VALUE, DimensionUnit::VP)).ConvertToFp();
2857     } else {
2858         symbolSpanStyle.fontSize = node->GetFontSizeValue(Dimension(DIMENSION_VALUE, DimensionUnit::VP)).ConvertToVp();
2859     }
2860     symbolSpanStyle.fontWeight = static_cast<int32_t>(node->GetFontWeightValue(FontWeight::NORMAL));
2861     symbolSpanStyle.renderingStrategy = static_cast<int32_t>(node->GetSymbolRenderingStrategyValue(0));
2862     symbolSpanStyle.effectStrategy = static_cast<int32_t>(node->GetSymbolEffectStrategyValue(0));
2863     return symbolSpanStyle;
2864 }
2865 
GetImageResultObject(RefPtr<UINode> uinode,int32_t index,int32_t start,int32_t end)2866 ResultObject TextPattern::GetImageResultObject(RefPtr<UINode> uinode, int32_t index, int32_t start, int32_t end)
2867 {
2868     int32_t itemLength = 1;
2869     ResultObject resultObject;
2870     if (!DynamicCast<FrameNode>(uinode) || !GetSpanItemByIndex(index)) {
2871         return resultObject;
2872     }
2873     int32_t endPosition = std::min(GetTextContentLength(), GetSpanItemByIndex(index)->position);
2874     int32_t startPosition = endPosition - itemLength;
2875     if ((start <= startPosition) && (end >= endPosition)) {
2876         auto imageNode = DynamicCast<FrameNode>(uinode);
2877         auto imageLayoutProperty = DynamicCast<ImageLayoutProperty>(imageNode->GetLayoutProperty());
2878         resultObject.spanPosition.spanIndex = index;
2879         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
2880         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
2881         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
2882         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
2883         resultObject.type = SelectSpanType::TYPEIMAGE;
2884         if (!imageLayoutProperty->GetImageSourceInfo()->GetPixmap()) {
2885             resultObject.valueString = UtfUtils::Str8DebugToStr16(imageLayoutProperty->GetImageSourceInfo()->GetSrc());
2886         } else {
2887             resultObject.valuePixelMap = imageLayoutProperty->GetImageSourceInfo()->GetPixmap();
2888         }
2889         auto geometryNode = imageNode->GetGeometryNode();
2890         resultObject.imageStyle.size[RichEditorImageSize::SIZEWIDTH] = geometryNode->GetMarginFrameSize().Width();
2891         resultObject.imageStyle.size[RichEditorImageSize::SIZEHEIGHT] = geometryNode->GetMarginFrameSize().Height();
2892         if (imageLayoutProperty->HasImageFit()) {
2893             resultObject.imageStyle.objectFit = static_cast<int32_t>(imageLayoutProperty->GetImageFitValue());
2894         }
2895         if (imageLayoutProperty->HasVerticalAlign()) {
2896             resultObject.imageStyle.verticalAlign = static_cast<int32_t>(imageLayoutProperty->GetVerticalAlignValue());
2897         }
2898         if (imageLayoutProperty->GetMarginProperty()) {
2899             resultObject.imageStyle.margin = imageLayoutProperty->GetMarginProperty()->ToString();
2900         }
2901         auto imageRenderCtx = imageNode->GetRenderContext();
2902         if (imageRenderCtx->GetBorderRadius()) {
2903             BorderRadiusProperty brp;
2904             auto jsonObject = JsonUtil::Create(true);
2905             auto jsonBorder = JsonUtil::Create(true);
2906             InspectorFilter emptyFilter;
2907             imageRenderCtx->GetBorderRadiusValue(brp).ToJsonValue(jsonObject, jsonBorder, emptyFilter);
2908             resultObject.imageStyle.borderRadius = jsonObject->GetValue("borderRadius")->IsObject()
2909                                                        ? jsonObject->GetValue("borderRadius")->ToString()
2910                                                        : jsonObject->GetString("borderRadius");
2911         }
2912     }
2913     return resultObject;
2914 }
2915 
2916 // ===========================================================
2917 // TextDragBase implementations
GetLineHeight() const2918 float TextPattern::GetLineHeight() const
2919 {
2920     auto selectedRects = pManager_->GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
2921     CHECK_NULL_RETURN(selectedRects.size(), {});
2922     return selectedRects.front().Height();
2923 }
2924 
GetTextBoxes()2925 std::vector<RectF> TextPattern::GetTextBoxes()
2926 {
2927     return pManager_->GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
2928 }
2929 
GetParentGlobalOffset() const2930 OffsetF TextPattern::GetParentGlobalOffset() const
2931 {
2932     selectOverlay_->UpdateHandleGlobalOffset();
2933     auto host = GetHost();
2934     CHECK_NULL_RETURN(host, {});
2935     auto pipeline = host->GetContext();
2936     CHECK_NULL_RETURN(pipeline, {});
2937     auto rootOffset = pipeline->GetRootRect().GetOffset();
2938     return host->GetPaintRectOffset(false, true) - rootOffset;
2939 }
2940 
CreateHandles()2941 void TextPattern::CreateHandles()
2942 {
2943     if (IsDragging()) {
2944         TAG_LOGI(AceLogTag::ACE_TEXT, "do not show handles when dragging");
2945         return;
2946     }
2947     ShowSelectOverlay({ .menuIsShow = false });
2948 }
2949 
BetweenSelectedPosition(const Offset & globalOffset)2950 bool TextPattern::BetweenSelectedPosition(const Offset& globalOffset)
2951 {
2952     auto host = GetHost();
2953     CHECK_NULL_RETURN(host, false);
2954     auto offset = host->GetPaintRectOffset(false, true);
2955     auto localOffset = globalOffset - Offset(offset.GetX(), offset.GetY());
2956     if (selectOverlay_->HasRenderTransform()) {
2957         localOffset = ConvertGlobalToLocalOffset(globalOffset);
2958     }
2959     return IsDraggable(localOffset);
2960 }
2961 
LogForFormRender(const std::string & logTag)2962 void TextPattern::LogForFormRender(const std::string& logTag)
2963 {
2964     auto host = GetHost();
2965     CHECK_NULL_VOID(host);
2966     auto pipeline = host->GetContext();
2967     CHECK_NULL_VOID(pipeline);
2968     if (pipeline->IsFormRender() && !IsSetObscured() && !IsSensitiveEnalbe()) {
2969         auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
2970         CHECK_NULL_VOID(textLayoutProperty);
2971         auto content = textLayoutProperty->GetContent().value_or(u"");
2972         if (content.length() == 1) {
2973             TAG_LOGI(AceLogTag::ACE_TEXT, "%{public}s, content:%{public}s id:%{public}d", logTag.c_str(),
2974                 UtfUtils::Str16ToStr8(content).c_str(), host->GetId());
2975         }
2976     }
2977 }
2978 
2979 // end of TextDragBase implementations
2980 // ===========================================================
2981 
OnModifyDone()2982 void TextPattern::OnModifyDone()
2983 {
2984     Pattern::OnModifyDone();
2985     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
2986     CHECK_NULL_VOID(textLayoutProperty);
2987     auto host = GetHost();
2988     CHECK_NULL_VOID(host);
2989     auto renderContext = host->GetRenderContext();
2990     CHECK_NULL_VOID(renderContext);
2991     auto nowTime = static_cast<unsigned long long>(GetSystemTimestamp());
2992     ACE_TEXT_SCOPED_TRACE("OnModifyDone[Text][id:%d][time:%llu]", host->GetId(), nowTime);
2993     auto logTag = "OnModifyDone:" + std::to_string(nowTime);
2994     LogForFormRender(logTag);
2995     auto pipeline = host->GetContext();
2996     if (!(pipeline && pipeline->GetMinPlatformVersion() > API_PROTEXTION_GREATER_NINE)) {
2997         bool shouldClipToContent =
2998             textLayoutProperty->GetTextOverflow().value_or(TextOverflow::CLIP) == TextOverflow::CLIP;
2999         host->GetRenderContext()->SetClipToFrame(shouldClipToContent);
3000     }
3001     if (textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE) {
3002         if (!renderContext->GetClipEdge().has_value()) {
3003             renderContext->UpdateClipEdge(true);
3004             renderContext->SetClipToFrame(true);
3005         }
3006         UpdateMarqueeStartPolicy();
3007     }
3008     const auto& children = host->GetChildren();
3009     if (children.empty()) {
3010         std::u16string textCache = textForDisplay_;
3011         if (!isSpanStringMode_) {
3012             textForDisplay_ = textLayoutProperty->GetContent().value_or(u"");
3013         }
3014         if (textCache != textForDisplay_) {
3015             host->OnAccessibilityEvent(AccessibilityEventType::TEXT_CHANGE, UtfUtils::Str16DebugToStr8(textCache),
3016                 UtfUtils::Str16DebugToStr8(textForDisplay_));
3017             dataDetectorAdapter_->aiDetectInitialized_ = false;
3018             CloseSelectOverlay();
3019             ResetSelection();
3020         }
3021 
3022         if (CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
3023             ParseOriText(textForDisplay_);
3024         }
3025     }
3026     RecoverCopyOption();
3027 }
3028 
UpdateMarqueeStartPolicy()3029 void TextPattern::UpdateMarqueeStartPolicy()
3030 {
3031     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
3032     CHECK_NULL_VOID(textLayoutProperty);
3033     if (!textLayoutProperty->HasTextMarqueeStartPolicy()) {
3034         auto host = GetHost();
3035         CHECK_NULL_VOID(host);
3036         auto context = host->GetContext();
3037         CHECK_NULL_VOID(context);
3038         auto theme = context->GetTheme<TextTheme>();
3039         CHECK_NULL_VOID(theme);
3040         textLayoutProperty->UpdateTextMarqueeStartPolicy(theme->GetMarqueeStartPolicy());
3041     }
3042     if (textLayoutProperty->GetTextMarqueeStartPolicyValue(MarqueeStartPolicy::DEFAULT) ==
3043         MarqueeStartPolicy::ON_FOCUS) {
3044         InitFocusEvent();
3045         InitHoverEvent();
3046     }
3047 }
3048 
SetActionExecSubComponent()3049 bool TextPattern::SetActionExecSubComponent()
3050 {
3051     auto host = GetHost();
3052     CHECK_NULL_RETURN(host, false);
3053     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
3054     CHECK_NULL_RETURN(accessibilityProperty, false);
3055     accessibilityProperty->SetActionExecSubComponent([weakPtr = WeakClaim(this)](int32_t spanId) -> bool {
3056             const auto& pattern = weakPtr.Upgrade();
3057             CHECK_NULL_RETURN(pattern, false);
3058             return pattern->ExecSubComponent(spanId);
3059         });
3060     return true;
3061 }
3062 
GetSubComponentInfos(std::vector<SubComponentInfo> & subComponentInfos)3063 size_t TextPattern::GetSubComponentInfos(std::vector<SubComponentInfo>& subComponentInfos)
3064 {
3065     subComponentInfos.clear();
3066     subComponentInfos_.clear();
3067     if (spans_.empty()) {
3068         GetSubComponentInfosForAISpans(subComponentInfos);
3069     } else {
3070         GetSubComponentInfosForSpans(subComponentInfos);
3071     }
3072     SetActionExecSubComponent();
3073     return subComponentInfos.size();
3074 }
3075 
GetSubComponentInfosForAISpans(std::vector<SubComponentInfo> & subComponentInfos)3076 void TextPattern::GetSubComponentInfosForAISpans(std::vector<SubComponentInfo>& subComponentInfos)
3077 {
3078     CHECK_NULL_VOID(dataDetectorAdapter_);
3079     for (const auto& kv : dataDetectorAdapter_->aiSpanMap_) {
3080         auto& aiSpan = kv.second;
3081         AddSubComponentInfoForAISpan(subComponentInfos, aiSpan.content, aiSpan);
3082     }
3083 }
3084 
GetSubComponentInfosForSpans(std::vector<SubComponentInfo> & subComponentInfos)3085 void TextPattern::GetSubComponentInfosForSpans(std::vector<SubComponentInfo>& subComponentInfos)
3086 {
3087     for (const auto& span : spans_) {
3088         if (span == nullptr) {
3089             continue; // skip null
3090         }
3091         if ((span->imageNodeId >= 0) || (span->unicode > 0)) {
3092             continue; // skip ImageSpan and SymbolSpan
3093         }
3094         if (span->spanItemType == SpanItemType::CustomSpan) {
3095             continue; // skip CustomSpan
3096         }
3097         auto placeholderSpan = DynamicCast<PlaceholderSpanItem>(span);
3098         if ((placeholderSpan != nullptr) && (placeholderSpan->placeholderSpanNodeId >=0)) {
3099             continue; // skip PlaceholderSpan
3100         }
3101         if (span->content.empty()) {
3102             continue; // skip empty text
3103         }
3104         AddSubComponentInfoForSpan(subComponentInfos, UtfUtils::Str16DebugToStr8(span->content), span);
3105         AddSubComponentInfosByDataDetectorForSpan(subComponentInfos, span);
3106     }
3107 }
3108 
AddSubComponentInfosByDataDetectorForSpan(std::vector<SubComponentInfo> & subComponentInfos,const RefPtr<SpanItem> & span)3109 void TextPattern::AddSubComponentInfosByDataDetectorForSpan(std::vector<SubComponentInfo>& subComponentInfos,
3110     const RefPtr<SpanItem>& span)
3111 {
3112     CHECK_NULL_VOID(span);
3113     CHECK_NULL_VOID(dataDetectorAdapter_);
3114     int32_t wSpanContentLength = static_cast<int32_t>(span->content.length());
3115     int32_t spanStart = span->position - wSpanContentLength;
3116     if (span->needRemoveNewLine) {
3117         spanStart -= 1;
3118     }
3119     int32_t preEnd = spanStart;
3120     auto aiSpanMap = dataDetectorAdapter_->aiSpanMap_;
3121     while (!aiSpanMap.empty()) {
3122         auto aiSpan = aiSpanMap.begin()->second;
3123         if (aiSpan.start >= span->position || preEnd >= span->position) {
3124             break;
3125         }
3126         int32_t aiSpanStartInSpan = std::max(spanStart, aiSpan.start);
3127         int32_t aiSpanEndInSpan = std::min(span->position, aiSpan.end);
3128         if (aiSpan.end <= spanStart || aiSpanStartInSpan < preEnd) {
3129             TAG_LOGI(AceLogTag::ACE_TEXT, "Error prediction");
3130             aiSpanMap.erase(aiSpanMap.begin());
3131             continue;
3132         }
3133         AddSubComponentInfoForAISpan(subComponentInfos, aiSpan.content, aiSpan);
3134         preEnd = aiSpanEndInSpan;
3135         if (aiSpan.end > span->position) {
3136             return;
3137         } else {
3138             aiSpanMap.erase(aiSpanMap.begin());
3139         }
3140     }
3141 }
3142 
ExecSubComponent(int32_t spanId)3143 bool TextPattern::ExecSubComponent(int32_t spanId)
3144 {
3145     if ((spanId < 0) || (spanId >= static_cast<int32_t>(subComponentInfos_.size()))) {
3146         return false;
3147     }
3148     auto subComponentInfo = subComponentInfos_[spanId];
3149     if (subComponentInfo.aiSpan.has_value()) {
3150         CHECK_NULL_RETURN(dataDetectorAdapter_, false);
3151         dataDetectorAdapter_->ResponseBestMatchItem(subComponentInfo.aiSpan.value());
3152         return true;
3153     }
3154     const auto& span = subComponentInfo.span.Upgrade();
3155     CHECK_NULL_RETURN(span, false);
3156     CHECK_NULL_RETURN(span->onClick, false);
3157     GestureEvent info;
3158     std::chrono::microseconds microseconds(GetMicroTickCount());
3159     TimeStamp time(microseconds);
3160     info.SetTimeStamp(time);
3161     span->onClick(info);
3162     return true;
3163 }
3164 
AddSubComponentInfoForSpan(std::vector<SubComponentInfo> & subComponentInfos,const std::string & content,const RefPtr<SpanItem> & span)3165 void TextPattern::AddSubComponentInfoForSpan(std::vector<SubComponentInfo>& subComponentInfos,
3166     const std::string& content, const RefPtr<SpanItem>& span)
3167 {
3168     CHECK_NULL_VOID(span);
3169     CHECK_NULL_VOID(span->onClick); // skip null onClick
3170     SubComponentInfo subComponentInfo;
3171     subComponentInfo.spanId = static_cast<int32_t>(subComponentInfos.size());
3172     subComponentInfo.spanText = content;
3173     if (span->accessibilityProperty == nullptr) {
3174         subComponentInfo.accessibilityLevel = AccessibilityProperty::Level::AUTO;
3175     } else {
3176         subComponentInfo.accessibilityText = span->accessibilityProperty->GetAccessibilityText();
3177         subComponentInfo.accessibilityDescription =
3178             span->accessibilityProperty->GetAccessibilityDescription();
3179         subComponentInfo.accessibilityLevel = span->accessibilityProperty->GetAccessibilityLevel();
3180     }
3181     subComponentInfos.emplace_back(subComponentInfo);
3182 
3183     SubComponentInfoEx subComponentInfoEx;
3184     subComponentInfoEx.span = span;
3185     subComponentInfos_.emplace_back(subComponentInfoEx);
3186 }
3187 
AddSubComponentInfoForAISpan(std::vector<SubComponentInfo> & subComponentInfos,const std::string & content,const AISpan & aiSpan)3188 void TextPattern::AddSubComponentInfoForAISpan(std::vector<SubComponentInfo>& subComponentInfos,
3189     const std::string& content, const AISpan& aiSpan)
3190 {
3191     SubComponentInfo subComponentInfo;
3192     subComponentInfo.spanId = static_cast<int32_t>(subComponentInfos.size());
3193     subComponentInfo.spanText = content;
3194     subComponentInfo.accessibilityLevel = AccessibilityProperty::Level::AUTO;
3195     subComponentInfos.emplace_back(subComponentInfo);
3196 
3197     SubComponentInfoEx subComponentInfoEx;
3198     subComponentInfoEx.aiSpan = aiSpan;
3199     subComponentInfos_.emplace_back(subComponentInfoEx);
3200 }
3201 
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const3202 void TextPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
3203 {
3204     json->PutFixedAttr("content", UtfUtils::Str16ToStr8(textForDisplay_).c_str(), filter, FIXED_ATTR_CONTENT);
3205     /* no fixed attr below, just return */
3206     if (filter.IsFastFilter()) {
3207         return;
3208     }
3209     json->PutExtAttr("enableDataDetector", textDetectEnable_ ? "true" : "false", filter);
3210     json->PutExtAttr("dataDetectorConfig", dataDetectorAdapter_->textDetectConfigStr_.c_str(), filter);
3211     const auto& selector = GetTextSelector();
3212     auto result = "[" + std::to_string(selector.GetTextStart()) + "," + std::to_string(selector.GetTextEnd()) + "]";
3213     json->PutExtAttr("selection", result.c_str(), filter);
3214     auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
3215     CHECK_NULL_VOID(textLayoutProp);
3216     json->PutExtAttr("fontSize", GetFontSizeWithThemeInJson(textLayoutProp->GetFontSize()).c_str(), filter);
3217     if (textStyle_.has_value() && textStyle_->GetAdaptTextSize()) {
3218         auto adaptedFontSize = textStyle_->GetFontSize();
3219         json->PutExtAttr("actualFontSize", adaptedFontSize.ToString().c_str(), filter);
3220     } else {
3221         json->PutExtAttr("actualFontSize", GetFontSizeWithThemeInJson(textLayoutProp->GetFontSize()).c_str(), filter);
3222     }
3223     json->PutExtAttr("font", GetFontInJson().c_str(), filter);
3224     json->PutExtAttr("bindSelectionMenu", GetBindSelectionMenuInJson().c_str(), filter);
3225     json->PutExtAttr("caretColor", GetCaretColor().c_str(), filter);
3226     json->PutExtAttr("selectedBackgroundColor", GetSelectedBackgroundColor().c_str(), filter);
3227     json->PutExtAttr("enableHapticFeedback", isEnableHapticFeedback_ ? "true" : "false", filter);
3228 }
3229 
GetBindSelectionMenuInJson() const3230 std::string TextPattern::GetBindSelectionMenuInJson() const
3231 {
3232     auto jsonArray = JsonUtil::CreateArray(true);
3233     for (auto& [spanResponsePair, params] : selectionMenuMap_) {
3234         auto& [spanType, responseType] = spanResponsePair;
3235         auto jsonItem = JsonUtil::Create(true);
3236         jsonItem->Put("spanType", TextSpanTypeMapper::GetJsSpanType(spanType, params->isValid));
3237         jsonItem->Put("responseType", static_cast<int32_t>(responseType));
3238         jsonItem->Put("menuType", static_cast<int32_t>(SelectionMenuType::SELECTION_MENU));
3239         jsonArray->Put(jsonItem);
3240     }
3241     FillPreviewMenuInJson(jsonArray);
3242     return StringUtils::RestoreBackslash(jsonArray->ToString());
3243 }
3244 
GetFontInJson() const3245 std::string TextPattern::GetFontInJson() const
3246 {
3247     auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
3248     CHECK_NULL_RETURN(textLayoutProp, "");
3249     auto jsonValue = JsonUtil::Create(true);
3250     jsonValue->Put("style", GetFontStyleInJson(textLayoutProp->GetItalicFontStyle()).c_str());
3251     jsonValue->Put("size", GetFontSizeWithThemeInJson(textLayoutProp->GetFontSize()).c_str());
3252     jsonValue->Put("weight", GetFontWeightInJson(textLayoutProp->GetFontWeight()).c_str());
3253     jsonValue->Put("variableFontWeight", std::to_string(textLayoutProp->GetVariableFontWeight().value_or(0)).c_str());
3254     jsonValue->Put("enableVariableFontWeight",
3255                    textLayoutProp->GetEnableVariableFontWeight().value_or(false) ? "true" : "false");
3256     jsonValue->Put("family", GetFontFamilyInJson(textLayoutProp->GetFontFamily()).c_str());
3257     return jsonValue->ToString();
3258 }
3259 
GetFontSizeWithThemeInJson(const std::optional<Dimension> & value) const3260 std::string TextPattern::GetFontSizeWithThemeInJson(const std::optional<Dimension>& value) const
3261 {
3262     auto host = GetHost();
3263     CHECK_NULL_RETURN(host, "");
3264     auto pipeline = host->GetContext();
3265     CHECK_NULL_RETURN(pipeline, "");
3266     auto theme = pipeline->GetTheme<TextTheme>();
3267     CHECK_NULL_RETURN(theme, "");
3268     return value.value_or(theme->GetTextStyle().GetFontSize()).ToString();
3269 }
3270 
ToTreeJson(std::unique_ptr<JsonValue> & json,const InspectorConfig & config) const3271 void TextPattern::ToTreeJson(std::unique_ptr<JsonValue>& json, const InspectorConfig& config) const
3272 {
3273     Pattern::ToTreeJson(json, config);
3274     if (!textForDisplay_.empty()) {
3275         json->Put(TreeKey::CONTENT, UtfUtils::Str16DebugToStr8(textForDisplay_).c_str());
3276     }
3277 }
3278 
OnAfterModifyDone()3279 void TextPattern::OnAfterModifyDone()
3280 {
3281     auto host = GetHost();
3282     CHECK_NULL_VOID(host);
3283     auto inspectorId = host->GetInspectorId().value_or("");
3284     if (!inspectorId.empty()) {
3285         auto prop = host->GetAccessibilityProperty<NG::AccessibilityProperty>();
3286         Recorder::NodeDataCache::Get().PutString(host, inspectorId, prop->GetText());
3287     }
3288 }
3289 
ActSetSelection(int32_t start,int32_t end)3290 void TextPattern::ActSetSelection(int32_t start, int32_t end)
3291 {
3292     int32_t min = 0;
3293     int32_t textSize = static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_;
3294     start = start < min ? min : start;
3295     end = end < min ? min : end;
3296     start = start > textSize ? textSize : start;
3297     end = end > textSize ? textSize : end;
3298     if (start >= end) {
3299         ResetSelection();
3300         CloseSelectOverlay();
3301         return;
3302     }
3303     HandleSelectionChange(start, end);
3304     parentGlobalOffset_ = GetParentGlobalOffset();
3305     CalculateHandleOffsetAndShowOverlay();
3306     showSelected_ = true;
3307     if (textSelector_.firstHandle == textSelector_.secondHandle && pManager_) {
3308         ResetSelection();
3309         CloseSelectOverlay();
3310         return;
3311     }
3312     showSelected_ = false;
3313     if (IsShowHandle()) {
3314         ShowSelectOverlay();
3315     } else {
3316         CloseSelectOverlay();
3317         if (IsSelected()) {
3318             selectOverlay_->SetSelectionHoldCallback();
3319         }
3320     }
3321     auto host = GetHost();
3322     CHECK_NULL_VOID(host);
3323     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3324 }
3325 
IsShowHandle()3326 bool TextPattern::IsShowHandle()
3327 {
3328     auto host = GetHost();
3329     CHECK_NULL_RETURN(host, false);
3330     auto pipeline = host->GetContext();
3331     CHECK_NULL_RETURN(pipeline, false);
3332     auto theme = pipeline->GetTheme<TextTheme>(GetThemeScopeId());
3333     CHECK_NULL_RETURN(theme, false);
3334     return !theme->IsShowHandle();
3335 }
3336 
GetUrlHoverColor()3337 Color TextPattern::GetUrlHoverColor()
3338 {
3339     auto host = GetHost();
3340     CHECK_NULL_RETURN(host, Color());
3341     auto pipeline = host->GetContext();
3342     CHECK_NULL_RETURN(pipeline, Color());
3343     auto theme = pipeline->GetTheme<TextTheme>();
3344     CHECK_NULL_RETURN(theme, Color());
3345     return theme->GetUrlHoverColor();
3346 }
3347 
GetUrlPressColor()3348 Color TextPattern::GetUrlPressColor()
3349 {
3350     auto host = GetHost();
3351     CHECK_NULL_RETURN(host, Color());
3352     auto pipeline = host->GetContext();
3353     CHECK_NULL_RETURN(pipeline, Color());
3354     auto theme = pipeline->GetTheme<TextTheme>();
3355     CHECK_NULL_RETURN(theme, Color());
3356     return theme->GetUrlPressColor();
3357 }
3358 
GetUrlSpanColor()3359 Color TextPattern::GetUrlSpanColor()
3360 {
3361     auto host = GetHost();
3362     CHECK_NULL_RETURN(host, Color());
3363     auto pipeline = host->GetContext();
3364     CHECK_NULL_RETURN(pipeline, Color());
3365     auto theme = pipeline->GetTheme<TextTheme>();
3366     CHECK_NULL_RETURN(theme, Color());
3367 
3368     auto eventHub = host->GetEventHub<EventHub>();
3369     CHECK_NULL_RETURN(eventHub, Color());
3370 
3371     if (eventHub && !eventHub->IsEnabled()) {
3372         return theme->GetUrlDisabledColor();
3373     } else {
3374         return theme->GetUrlDefaultColor();
3375     }
3376 }
3377 
3378 // Deprecated: Use the TextSelectOverlay::ProcessOverlay() instead.
3379 // It is currently used by RichEditorPattern.
UpdateSelectOverlayOrCreate(SelectOverlayInfo & selectInfo,bool animation)3380 void TextPattern::UpdateSelectOverlayOrCreate(SelectOverlayInfo& selectInfo, bool animation)
3381 {
3382     if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
3383         SelectHandleInfo firstHandleInfo;
3384         firstHandleInfo.paintRect = textSelector_.firstHandle;
3385         CheckHandles(firstHandleInfo);
3386 
3387         SelectHandleInfo secondHandleInfo;
3388         secondHandleInfo.paintRect = textSelector_.secondHandle;
3389         CheckHandles(secondHandleInfo);
3390 
3391         auto start = textSelector_.GetTextStart();
3392         auto end = textSelector_.GetTextEnd();
3393         selectOverlayProxy_->SetSelectInfo(UtfUtils::Str16DebugToStr8(GetSelectedText(start, end)));
3394         if (selectInfo.isNewAvoid) {
3395             selectOverlayProxy_->UpdateSelectArea(selectInfo.selectArea);
3396         }
3397         selectOverlayProxy_->UpdateFirstAndSecondHandleInfo(firstHandleInfo, secondHandleInfo);
3398         selectOverlayProxy_->ShowOrHiddenMenu(!firstHandleInfo.isShow && !secondHandleInfo.isShow);
3399     } else {
3400         auto host = GetHost();
3401         CHECK_NULL_VOID(host);
3402         auto pipeline = host->GetContext();
3403         CHECK_NULL_VOID(pipeline);
3404         pipeline->AddOnAreaChangeNode(host->GetId());
3405         selectInfo.callerFrameNode = GetHost();
3406         selectInfo.hitTestMode = HitTestMode::HTMDEFAULT;
3407         if (!selectInfo.isUsingMouse) {
3408             CheckHandles(selectInfo.firstHandle);
3409             CheckHandles(selectInfo.secondHandle);
3410         }
3411         selectOverlayProxy_ =
3412             pipeline->GetSelectOverlayManager()->CreateAndShowSelectOverlay(selectInfo, WeakClaim(this), animation);
3413         CHECK_NULL_VOID(selectOverlayProxy_);
3414         auto start = textSelector_.GetTextStart();
3415         auto end = textSelector_.GetTextEnd();
3416         selectOverlayProxy_->SetSelectInfo(UtfUtils::Str16DebugToStr8(GetSelectedText(start, end)));
3417     }
3418 }
3419 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)3420 bool TextPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
3421 {
3422     if (config.skipMeasure || dirty->SkipMeasureContent()) {
3423         return false;
3424     }
3425 
3426     contentRect_ = dirty->GetGeometryNode()->GetContentRect();
3427     dataDetectorAdapter_->aiSpanRects_.clear();
3428 
3429     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
3430     CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
3431     auto textLayoutAlgorithm = DynamicCast<TextLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
3432     CHECK_NULL_RETURN(textLayoutAlgorithm, false);
3433     baselineOffset_ = textLayoutAlgorithm->GetBaselineOffset();
3434     contentOffset_ = dirty->GetGeometryNode()->GetContentOffset();
3435     textStyle_ = textLayoutAlgorithm->GetTextStyle();
3436     ProcessOverlayAfterLayout();
3437     return true;
3438 }
3439 
ProcessOverlayAfterLayout()3440 void TextPattern::ProcessOverlayAfterLayout()
3441 {
3442     if (selectOverlay_->SelectOverlayIsOn() || showSelected_) {
3443         showSelected_ = false;
3444         CalculateHandleOffsetAndShowOverlay();
3445         selectOverlay_->UpdateAllHandlesOffset();
3446         selectOverlay_->UpdateViewPort();
3447     }
3448 }
3449 
PreCreateLayoutWrapper()3450 void TextPattern::PreCreateLayoutWrapper()
3451 {
3452     auto host = GetHost();
3453     CHECK_NULL_VOID(host);
3454 
3455     auto paintProperty = GetPaintProperty<PaintProperty>();
3456     CHECK_NULL_VOID(paintProperty);
3457     auto flag = paintProperty->GetPropertyChangeFlag();
3458     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
3459     CHECK_NULL_VOID(textLayoutProperty);
3460     auto layoutFlag = textLayoutProperty->GetPropertyChangeFlag();
3461     if (!CheckNeedMeasure(flag) && !CheckNeedMeasure(layoutFlag)) {
3462         return;
3463     }
3464     auto beforeSpanSize = spans_.size();
3465     spans_.clear();
3466     childNodes_.clear();
3467 
3468     // When dirty areas are marked because of child node changes, the text rendering node tree is reset.
3469     const auto& children = host->GetChildren();
3470     if (children.empty()) {
3471         placeholderCount_ = 0;
3472         return;
3473     }
3474 
3475     // Depth-first iterates through all host's child nodes to collect the SpanNode object, building a text rendering
3476     // tree.
3477     std::stack<SpanNodeInfo> nodes;
3478     for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
3479         nodes.push({ .node = *iter });
3480     }
3481 
3482     InitSpanItem(nodes);
3483     CHECK_NULL_VOID(beforeSpanSize != spans_.size());
3484     textLayoutProperty->OnPropertyChangeMeasure();
3485 }
3486 
InitSpanItem(std::stack<SpanNodeInfo> nodes)3487 void TextPattern::InitSpanItem(std::stack<SpanNodeInfo> nodes)
3488 {
3489     auto host = GetHost();
3490     CHECK_NULL_VOID(host);
3491     std::u16string textCache;
3492     std::u16string textForAICache;
3493     int32_t oldPlaceholderCount = placeholderCount_;
3494     placeholderCount_ = 0;
3495     if (!nodes.empty()) {
3496         textCache = textForDisplay_;
3497         textForAICache = dataDetectorAdapter_->textForAI_;
3498         textForDisplay_.clear();
3499         dataDetectorAdapter_->textForAI_.clear();
3500     }
3501 
3502     bool isSpanHasClick = false;
3503     CollectSpanNodes(nodes, isSpanHasClick);
3504     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
3505     CHECK_NULL_VOID(textLayoutProperty);
3506     if (childNodes_.empty()) {
3507         textForDisplay_ = textLayoutProperty->GetContent().value_or(u"");
3508     }
3509     if (oldPlaceholderCount != placeholderCount_) {
3510         CloseSelectOverlay();
3511         ResetSelection();
3512     }
3513 
3514     if (textCache != textForDisplay_) {
3515         host->OnAccessibilityEvent(AccessibilityEventType::TEXT_CHANGE, UtfUtils::Str16DebugToStr8(textCache),
3516             UtfUtils::Str16DebugToStr8(textForDisplay_));
3517         OnAfterModifyDone();
3518         for (const auto& item : spans_) {
3519             if (item->inspectId.empty()) {
3520                 continue;
3521             }
3522             Recorder::NodeDataCache::Get().PutString(host, item->inspectId, UtfUtils::Str16DebugToStr8(item->content));
3523         }
3524         ResetAfterTextChange();
3525     }
3526     if (isSpanHasClick) {
3527         auto gestureEventHub = host->GetOrCreateGestureEventHub();
3528         InitClickEvent(gestureEventHub);
3529     }
3530     if (textForAICache != dataDetectorAdapter_->textForAI_) {
3531         dataDetectorAdapter_->aiDetectInitialized_ = false;
3532     }
3533     if (CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
3534         ParseOriText(textLayoutProperty->GetContent().value_or(u""));
3535         if (!dataDetectorAdapter_->aiDetectInitialized_) {
3536             dataDetectorAdapter_->StartAITask();
3537         }
3538     }
3539 }
3540 
ResetAfterTextChange()3541 void TextPattern::ResetAfterTextChange()
3542 {
3543     CloseSelectOverlay();
3544     ResetSelection();
3545     ResetOriginCaretPosition();
3546 }
3547 
ParseOriText(const std::u16string & currentText)3548 void TextPattern::ParseOriText(const std::u16string& currentText)
3549 {
3550     auto entityJson = JsonUtil::ParseJsonString(UtfUtils::Str16DebugToStr8(currentText));
3551     bool entityIsJson = !entityJson->IsNull();
3552     TAG_LOGI(AceLogTag::ACE_TEXT, "text content is the json format: %{public}d", entityIsJson);
3553     if (entityIsJson && !entityJson->GetValue("bundleName")->IsNull() &&
3554         dataDetectorAdapter_->ParseOriText(entityJson, textForDisplay_)) {
3555         if (childNodes_.empty()) {
3556             auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
3557             CHECK_NULL_VOID(textLayoutProperty);
3558             textLayoutProperty->UpdateContent(textForDisplay_);
3559         }
3560     }
3561 }
3562 
BeforeCreateLayoutWrapper()3563 void TextPattern::BeforeCreateLayoutWrapper()
3564 {
3565     if (!isSpanStringMode_) {
3566         PreCreateLayoutWrapper();
3567     }
3568     selectOverlay_->MarkOverlayDirty();
3569 }
3570 
CollectSpanNodes(std::stack<SpanNodeInfo> nodes,bool & isSpanHasClick)3571 void TextPattern::CollectSpanNodes(std::stack<SpanNodeInfo> nodes, bool& isSpanHasClick)
3572 {
3573     while (!nodes.empty()) {
3574         auto current = nodes.top();
3575         nodes.pop();
3576         if (!current.node) {
3577             continue;
3578         }
3579         UpdateContainerChildren(current.containerSpanNode, current.node);
3580         auto spanNode = DynamicCast<SpanNode>(current.node);
3581         auto tag = current.node->GetTag();
3582         if (spanNode && tag == V2::SYMBOL_SPAN_ETS_TAG && spanNode->GetSpanItem()->GetSymbolUnicode() != 0) {
3583             spanNode->CleanSpanItemChildren();
3584             spanNode->MountToParagraph();
3585             textForDisplay_.append(u"    ");
3586             dataDetectorAdapter_->textForAI_.append(SYMBOL_TRANS);
3587             childNodes_.push_back(current.node);
3588         } else if (spanNode && tag != V2::PLACEHOLDER_SPAN_ETS_TAG) {
3589             CollectTextSpanNodes(spanNode, isSpanHasClick);
3590             childNodes_.push_back(current.node);
3591         } else if (tag == V2::IMAGE_ETS_TAG || tag == V2::PLACEHOLDER_SPAN_ETS_TAG) {
3592             placeholderCount_++;
3593             AddChildSpanItem(current.node);
3594             dataDetectorAdapter_->textForAI_.append(u"\n");
3595             auto imageNode = DynamicCast<FrameNode>(current.node);
3596             if (!imageNode) {
3597                 continue;
3598             }
3599             auto focus_hub = imageNode->GetOrCreateFocusHub();
3600             if (focus_hub && focus_hub->GetOnClickCallback()) {
3601                 isSpanHasClick = true;
3602             }
3603             childNodes_.push_back(current.node);
3604         } else if (tag == V2::CUSTOM_SPAN_NODE_ETS_TAG) {
3605             placeholderCount_++;
3606             AddChildSpanItem(current.node);
3607             dataDetectorAdapter_->textForAI_.append(u"\n");
3608             childNodes_.emplace_back(current.node);
3609         }
3610         if (tag == V2::PLACEHOLDER_SPAN_ETS_TAG) {
3611             continue;
3612         }
3613         const auto& nextChildren = current.node->GetChildren();
3614         if (nextChildren.empty()) {
3615             continue;
3616         }
3617         auto containerSpanNode = tag == V2::CONTAINER_SPAN_ETS_TAG ? current.node : current.containerSpanNode;
3618         for (auto iter = nextChildren.rbegin(); iter != nextChildren.rend(); ++iter) {
3619             nodes.push({ .node = *iter, .containerSpanNode = containerSpanNode });
3620         }
3621     }
3622 }
3623 
CollectTextSpanNodes(const RefPtr<SpanNode> & spanNode,bool & isSpanHasClick)3624 void TextPattern::CollectTextSpanNodes(const RefPtr<SpanNode>& spanNode, bool& isSpanHasClick)
3625 {
3626     spanNode->CleanSpanItemChildren();
3627     spanNode->MountToParagraph();
3628     textForDisplay_.append(spanNode->GetSpanItem()->content);
3629     dataDetectorAdapter_->textForAI_.append(spanNode->GetSpanItem()->content);
3630     if (spanNode->GetSpanItem()->onClick) {
3631         isSpanHasClick = true;
3632     }
3633 }
3634 
UpdateContainerChildren(const RefPtr<UINode> & parentNode,const RefPtr<UINode> & child)3635 void TextPattern::UpdateContainerChildren(const RefPtr<UINode>& parentNode, const RefPtr<UINode>& child)
3636 {
3637     CHECK_NULL_VOID(child);
3638     auto parent = DynamicCast<ContainerSpanNode>(parentNode);
3639     CHECK_NULL_VOID(parent);
3640     auto baseSpan = DynamicCast<BaseSpan>(child);
3641     if (baseSpan) {
3642         if (baseSpan->HasTextBackgroundStyle()) {
3643             return;
3644         }
3645         baseSpan->UpdateTextBackgroundFromParent(parent->GetTextBackgroundStyle());
3646         return;
3647     }
3648     if (child->GetTag() == V2::IMAGE_ETS_TAG) {
3649         auto imageNode = DynamicCast<FrameNode>(child);
3650         CHECK_NULL_VOID(imageNode);
3651         auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
3652         CHECK_NULL_VOID(imageLayoutProperty);
3653         if (imageLayoutProperty->GetHasPlaceHolderStyleValue(false)) {
3654             return;
3655         }
3656         if (parent->GetTextBackgroundStyle().has_value()) {
3657             imageLayoutProperty->UpdatePlaceHolderStyle(parent->GetTextBackgroundStyle().value());
3658         }
3659     }
3660 }
3661 
GetGlobalOffset(Offset & offset)3662 void TextPattern::GetGlobalOffset(Offset& offset)
3663 {
3664     auto host = GetHost();
3665     CHECK_NULL_VOID(host);
3666     auto pipeline = host->GetContext();
3667     CHECK_NULL_VOID(pipeline);
3668     auto rootOffset = pipeline->GetRootRect().GetOffset();
3669     auto globalOffset = host->GetPaintRectOffset(false, true) - rootOffset;
3670     offset = Offset(globalOffset.GetX(), globalOffset.GetY());
3671 }
3672 
OnVisibleChange(bool isVisible)3673 void TextPattern::OnVisibleChange(bool isVisible)
3674 {
3675     if (!isVisible) {
3676         if (textSelector_.IsValid()) {
3677             CloseSelectOverlay();
3678             ResetSelection();
3679         }
3680         if (textDetectEnable_) {
3681             dataDetectorAdapter_->aiDetectDelayTask_.Cancel();
3682         }
3683         PauseSymbolAnimation();
3684     } else {
3685         if (CanStartAITask()) {
3686             dataDetectorAdapter_->StartAITask();
3687         }
3688         ResumeSymbolAnimation();
3689     }
3690 }
3691 
ProcessVisibleAreaCallback()3692 void TextPattern::ProcessVisibleAreaCallback()
3693 {
3694     auto host = GetHost();
3695     CHECK_NULL_VOID(host);
3696     auto pipeline = GetContext();
3697     CHECK_NULL_VOID(pipeline);
3698     auto callback = [weak = WeakClaim(this)](bool visible, double ratio) {
3699         auto pattern = weak.Upgrade();
3700         CHECK_NULL_VOID(pattern);
3701         pattern->OnVisibleChange(visible);
3702     };
3703     std::vector<double> ratioList = { 0.0 };
3704     pipeline->AddVisibleAreaChangeNode(host, ratioList, callback, false, true);
3705 }
3706 
PauseSymbolAnimation()3707 void TextPattern::PauseSymbolAnimation()
3708 {
3709     auto host = GetHost();
3710     CHECK_NULL_VOID(host);
3711     if (host->GetTag() != V2::SYMBOL_ETS_TAG) {
3712         return;
3713     }
3714     auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
3715     CHECK_NULL_VOID(layoutProperty);
3716     if (!layoutProperty->GetIsLoopAnimation()) {
3717         return;
3718     }
3719     auto symbolEffectOptions = layoutProperty->GetSymbolEffectOptionsValue(SymbolEffectOptions());
3720     if (!symbolEffectOptions.GetIsTxtActive()) {
3721         return;
3722     }
3723     symbolEffectOptions.SetIsTxtActive(false);
3724     layoutProperty->UpdateSymbolEffectOptions(symbolEffectOptions);
3725     host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
3726 }
3727 
ResumeSymbolAnimation()3728 void TextPattern::ResumeSymbolAnimation()
3729 {
3730     auto host = GetHost();
3731     CHECK_NULL_VOID(host);
3732     if (host->GetTag() != V2::SYMBOL_ETS_TAG) {
3733         return;
3734     }
3735     auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
3736     CHECK_NULL_VOID(layoutProperty);
3737     if (!layoutProperty->GetIsLoopAnimation()) {
3738         return;
3739     }
3740     auto symbolEffectOptions = layoutProperty->GetSymbolEffectOptionsValue(SymbolEffectOptions());
3741     if (symbolEffectOptions.GetIsTxtActive()) {
3742         return;
3743     }
3744     symbolEffectOptions.SetIsTxtActive(true);
3745     layoutProperty->UpdateSymbolEffectOptions(symbolEffectOptions);
3746     host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
3747 }
3748 
InitSurfaceChangedCallback()3749 void TextPattern::InitSurfaceChangedCallback()
3750 {
3751     auto host = GetHost();
3752     CHECK_NULL_VOID(host);
3753     auto pipeline = host->GetContext();
3754     CHECK_NULL_VOID(pipeline);
3755     if (!HasSurfaceChangedCallback()) {
3756         auto callbackId = pipeline->RegisterSurfaceChangedCallback(
3757             [weak = WeakClaim(this)](int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight,
3758                 WindowSizeChangeReason type) {
3759                 auto pattern = weak.Upgrade();
3760                 if (pattern) {
3761                     pattern->HandleSurfaceChanged(newWidth, newHeight, prevWidth, prevHeight, type);
3762                 }
3763             });
3764         UpdateSurfaceChangedCallbackId(callbackId);
3765     }
3766 }
3767 
HandleSurfaceChanged(int32_t newWidth,int32_t newHeight,int32_t prevWidth,int32_t prevHeight,WindowSizeChangeReason type)3768 void TextPattern::HandleSurfaceChanged(
3769     int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight, WindowSizeChangeReason type)
3770 {
3771     if (newWidth == prevWidth && newHeight == prevHeight) {
3772         return;
3773     }
3774     if (type != WindowSizeChangeReason::DRAG) {
3775         auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
3776         CHECK_NULL_VOID(textLayoutProperty);
3777         textLayoutProperty->OnPropertyChangeMeasure();
3778     }
3779     CHECK_NULL_VOID(selectOverlay_->SelectOverlayIsOn());
3780     if (selectOverlay_->IsShowMouseMenu()) {
3781         CloseSelectOverlay();
3782     } else {
3783         auto host = GetHost();
3784         CHECK_NULL_VOID(host);
3785         auto context = host->GetContext();
3786         if (context) {
3787             context->AddAfterLayoutTask([weak = WeakClaim(this)]() {
3788                 auto pattern = weak.Upgrade();
3789                 CHECK_NULL_VOID(pattern);
3790                 pattern->CalculateHandleOffsetAndShowOverlay();
3791                 pattern->ShowSelectOverlay({ .menuIsShow = false });
3792             });
3793         }
3794     }
3795 }
3796 
InitSurfacePositionChangedCallback()3797 void TextPattern::InitSurfacePositionChangedCallback()
3798 {
3799     auto host = GetHost();
3800     CHECK_NULL_VOID(host);
3801     auto pipeline = host->GetContext();
3802     CHECK_NULL_VOID(pipeline);
3803     if (!HasSurfacePositionChangedCallback()) {
3804         auto callbackId =
3805             pipeline->RegisterSurfacePositionChangedCallback([weak = WeakClaim(this)](int32_t posX, int32_t posY) {
3806                 auto pattern = weak.Upgrade();
3807                 if (pattern) {
3808                     pattern->HandleSurfacePositionChanged(posX, posY);
3809                 }
3810             });
3811         UpdateSurfacePositionChangedCallbackId(callbackId);
3812     }
3813 }
3814 
AddChildSpanItem(const RefPtr<UINode> & child)3815 void TextPattern::AddChildSpanItem(const RefPtr<UINode>& child)
3816 {
3817     CHECK_NULL_VOID(child);
3818     auto chidNode = DynamicCast<FrameNode>(child);
3819     if (chidNode && chidNode->GetLayoutProperty() && chidNode->GetLayoutProperty()->IsOverlayNode()) {
3820         return;
3821     }
3822 
3823     if (child->GetTag() == V2::SPAN_ETS_TAG || child->GetTag() == V2::SYMBOL_SPAN_ETS_TAG) {
3824         auto spanNode = DynamicCast<SpanNode>(child);
3825         if (spanNode) {
3826             spans_.emplace_back(spanNode->GetSpanItem());
3827         }
3828     } else if (child->GetTag() == V2::IMAGE_ETS_TAG) {
3829         AddImageToSpanItem(child);
3830     } else if (child->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG) {
3831         auto placeholderSpanNode = DynamicCast<PlaceholderSpanNode>(child);
3832         if (placeholderSpanNode) {
3833             auto placeholderSpan = placeholderSpanNode->GetSpanItem();
3834             placeholderSpan->placeholderSpanNodeId = placeholderSpanNode->GetId();
3835             spans_.emplace_back(placeholderSpan);
3836         }
3837     } else if (child->GetTag() == V2::CUSTOM_SPAN_NODE_ETS_TAG) {
3838         auto customSpanNode = DynamicCast<CustomSpanNode>(child);
3839         if (customSpanNode) {
3840             auto customSpan = customSpanNode->GetSpanItem();
3841             customSpan->placeholderSpanNodeId = customSpanNode->GetId();
3842             spans_.emplace_back(customSpan);
3843         }
3844     }
3845 }
3846 
AddImageToSpanItem(const RefPtr<UINode> & child)3847 void TextPattern::AddImageToSpanItem(const RefPtr<UINode>& child)
3848 {
3849     auto imageSpanNode = DynamicCast<ImageSpanNode>(child);
3850     if (imageSpanNode) {
3851         auto host = GetHost();
3852         CHECK_NULL_VOID(host);
3853         auto imageSpanItem = imageSpanNode->GetSpanItem();
3854         if (host->GetTag() != V2::RICH_EDITOR_ETS_TAG) {
3855             auto focus_hub = imageSpanNode->GetOrCreateFocusHub();
3856             CHECK_NULL_VOID(focus_hub);
3857             auto clickCall = focus_hub->GetOnClickCallback();
3858             if (clickCall) {
3859                 imageSpanItem->SetOnClickEvent(std::move(clickCall));
3860             }
3861             auto gesture = imageSpanNode->GetOrCreateGestureEventHub();
3862             CHECK_NULL_VOID(gesture);
3863             gesture->SetHitTestMode(HitTestMode::HTMNONE);
3864         }
3865         imageSpanItem->UpdatePlaceholderBackgroundStyle(imageSpanNode);
3866         spans_.emplace_back(imageSpanItem);
3867         spans_.back()->imageNodeId = imageSpanNode->GetId();
3868         return;
3869     }
3870     auto imageNode = DynamicCast<FrameNode>(child);
3871     if (imageNode) {
3872         auto imageSpanItem = MakeRefPtr<ImageSpanItem>();
3873         imageSpanItem->imageNodeId = imageNode->GetId();
3874         imageSpanItem->UpdatePlaceholderBackgroundStyle(imageNode);
3875         auto focus_hub = imageNode->GetOrCreateFocusHub();
3876         CHECK_NULL_VOID(focus_hub);
3877         auto clickCall = focus_hub->GetOnClickCallback();
3878         if (clickCall) {
3879             imageSpanItem->SetOnClickEvent(std::move(clickCall));
3880         }
3881         spans_.emplace_back(imageSpanItem);
3882         auto gesture = imageNode->GetOrCreateGestureEventHub();
3883         CHECK_NULL_VOID(gesture);
3884         gesture->SetHitTestMode(HitTestMode::HTMNONE);
3885         return;
3886     }
3887 }
3888 
DumpSimplifyInfo(std::unique_ptr<JsonValue> & json)3889 void TextPattern::DumpSimplifyInfo(std::unique_ptr<JsonValue>& json)
3890 {
3891     auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
3892     CHECK_NULL_VOID(textLayoutProp);
3893     if (!IsSetObscured()) {
3894         json->Put("content", UtfUtils::Str16DebugToStr8(textLayoutProp->GetContent().value_or(u" ")).c_str());
3895     }
3896 }
3897 
DumpAdvanceInfo()3898 void TextPattern::DumpAdvanceInfo()
3899 {
3900     DumpLog::GetInstance().AddDesc(std::string("-----DumpAdvanceInfo-----"));
3901     DumpLog::GetInstance().AddDesc(
3902         std::string("BindSelectionMenu: ").append(std::to_string(selectionMenuMap_.empty())));
3903     DumpLog::GetInstance().AddDesc(std::string("Selection: ").append("(").append(textSelector_.ToString()).append(")"));
3904 }
3905 
DumpInfo()3906 void TextPattern::DumpInfo()
3907 {
3908     auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
3909     CHECK_NULL_VOID(textLayoutProp);
3910     auto& dumpLog = DumpLog::GetInstance();
3911     auto nowTime = GetSystemTimestamp();
3912     dumpLog.AddDesc(std::string("frameRecord: ").append(frameRecord_));
3913     dumpLog.AddDesc(std::string("time: ").append(std::to_string(nowTime)));
3914     if (!IsSetObscured() && !IsSensitiveEnalbe()) {
3915         dumpLog.AddDesc(std::string("Content: ").append(
3916             UtfUtils::Str16DebugToStr8(textLayoutProp->GetContent().value_or(u" "))));
3917     }
3918     dumpLog.AddDesc(std::string("isSpanStringMode: ").append(std::to_string(isSpanStringMode_)));
3919     dumpLog.AddDesc(std::string("externalParagraph: ").append(std::to_string(externalParagraph_.has_value())));
3920     DumpTextStyleInfo();
3921     if (contentMod_) {
3922         contentMod_->ContentModifierDump();
3923     }
3924     dumpLog.AddDesc(
3925         std::string("HeightAdaptivePolicy: ")
3926             .append(V2::ConvertWrapTextHeightAdaptivePolicyToString(
3927                 textLayoutProp->GetHeightAdaptivePolicy().value_or(TextHeightAdaptivePolicy::MAX_LINES_FIRST))));
3928     if (pManager_) {
3929         auto num = static_cast<int32_t>(pManager_->GetParagraphs().size());
3930         dumpLog.AddDesc(std::string("Paragraphs num: ").append(std::to_string(num)));
3931         dumpLog.AddDesc(std::string("PaintInfo: ").append(paintInfo_));
3932     }
3933     DumpScaleInfo();
3934     DumpTextEngineInfo();
3935     if (SystemProperties::GetDebugEnabled()) {
3936         DumpAdvanceInfo();
3937     }
3938     DumpSpanItem();
3939 }
3940 
DumpSpanItem()3941 void TextPattern::DumpSpanItem()
3942 {
3943     CHECK_NULL_VOID(isSpanStringMode_);
3944     auto& dumpLog = DumpLog::GetInstance();
3945     dumpLog.AddDesc(std::string("-----SpanDumpInfo-----"));
3946     for (const auto& item : spans_) {
3947         if (!item) {
3948             continue;
3949         }
3950         item->SpanDumpInfo();
3951     }
3952 }
3953 
DumpTextStyleInfo()3954 void TextPattern::DumpTextStyleInfo()
3955 {
3956     auto& dumpLog = DumpLog::GetInstance();
3957     auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
3958     CHECK_NULL_VOID(textLayoutProp);
3959     auto host = GetHost();
3960     CHECK_NULL_VOID(host);
3961     auto renderContext = host->GetRenderContext();
3962     CHECK_NULL_VOID(renderContext);
3963     dumpLog.AddDesc(
3964         std::string("FontColor: ")
3965             .append((textStyle_.has_value() ? textStyle_->GetTextColor() : Color::BLACK).ColorToString())
3966             .append(" pro: ")
3967             .append(
3968             textLayoutProp->HasTextColor() ? textLayoutProp->GetTextColorValue(Color::BLACK).ColorToString() : "Na")
3969             .append(" ForegroundColor: ")
3970             .append(
3971             renderContext->HasForegroundColor() ? renderContext->GetForegroundColorValue().ColorToString() : "Na"));
3972     if (renderContext->HasForegroundColorStrategy()) {
3973         auto strategy = static_cast<int32_t>(renderContext->GetForegroundColorStrategyValue());
3974         DumpLog::GetInstance().AddDesc(std::string("ForegroundColorStrategy: ").append(std::to_string(strategy)));
3975     }
3976     dumpLog.AddDesc(
3977         std::string("FontSize: ")
3978             .append((textStyle_.has_value() ? textStyle_->GetFontSize() : Dimension(DIMENSION_VALUE, DimensionUnit::FP))
3979                         .ToString())
3980             .append(" pro: ")
3981             .append(textLayoutProp->HasFontSize()
3982                         ? textLayoutProp->GetFontSizeValue(Dimension(0.0, DimensionUnit::FP)).ToString()
3983                         : "Na"));
3984     if (textStyle_.has_value()) {
3985         dumpLog.AddDesc(
3986             std::string("MaxFontSize: ")
3987                 .append(textStyle_->GetAdaptMaxFontSize().ToString())
3988                 .append(" pro: ")
3989                 .append(textLayoutProp->HasAdaptMaxFontSize()
3990                             ? textLayoutProp->GetAdaptMaxFontSizeValue(Dimension(0.0, DimensionUnit::FP)).ToString()
3991                             : "Na"));
3992         dumpLog.AddDesc(
3993             std::string("MinFontSize: ")
3994                 .append(textStyle_->GetAdaptMinFontSize().ToString())
3995                 .append(" pro: ")
3996                 .append(textLayoutProp->HasAdaptMinFontSize()
3997                             ? textLayoutProp->GetAdaptMinFontSizeValue(Dimension(0.0, DimensionUnit::FP)).ToString()
3998                             : "Na"));
3999     }
4000     DumpTextStyleInfo2();
4001     DumpTextStyleInfo3();
4002 }
4003 
DumpTextStyleInfo2()4004 void TextPattern::DumpTextStyleInfo2()
4005 {
4006     auto& dumpLog = DumpLog::GetInstance();
4007     auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
4008     CHECK_NULL_VOID(textLayoutProp);
4009     if (textStyle_.has_value()) {
4010         dumpLog.AddDesc(std::string("FontWeight: ")
4011                 .append(StringUtils::ToString(textStyle_->GetFontWeight()))
4012                 .append(" pro: ")
4013                 .append(textLayoutProp->HasFontWeight()
4014                             ? StringUtils::ToString(textLayoutProp->GetFontWeightValue(FontWeight::NORMAL))
4015                             : "Na"));
4016         dumpLog.AddDesc(std::string("FontStyle: ").append(StringUtils::ToString(textStyle_->GetFontStyle())));
4017         dumpLog.AddDesc(
4018             std::string("LineHeight: ")
4019                 .append(textStyle_->GetLineHeight().ToString())
4020                 .append(" pro: ")
4021                 .append(textLayoutProp->HasLineHeight()
4022                             ? textLayoutProp->GetLineHeightValue(Dimension(0.0, DimensionUnit::FP)).ToString()
4023                             : "Na"));
4024         dumpLog.AddDesc(
4025             std::string("LineSpacing: ")
4026                 .append(textStyle_->GetLineSpacing().ToString())
4027                 .append(" pro: ").append(textLayoutProp->HasLineSpacing()
4028                             ? textLayoutProp->GetLineSpacingValue(Dimension(0.0, DimensionUnit::FP)).ToString()
4029                             : "Na"));
4030         dumpLog.AddDesc(
4031             std::string("maxLines: ")
4032                 .append(std::to_string(textStyle_->GetMaxLines()))
4033                 .append(" pro: ")
4034                 .append(textLayoutProp->HasMaxLines() ? std::to_string(textLayoutProp->GetMaxLinesValue(UINT32_MAX))
4035                                                       : "Na"));
4036         dumpLog.AddDesc(
4037             std::string("BaselineOffset: ")
4038                 .append(textStyle_->GetBaselineOffset().ToString())
4039                 .append(" pro: ")
4040                 .append(textLayoutProp->HasBaselineOffset()
4041                             ? textLayoutProp->GetBaselineOffsetValue(Dimension(0.0, DimensionUnit::FP)).ToString()
4042                             : "Na"));
4043         dumpLog.AddDesc(
4044             std::string("TextIndent: ")
4045                 .append(textStyle_->GetTextIndent().ToString())
4046                 .append(" pro: ")
4047                 .append(textLayoutProp->HasTextIndent()
4048                             ? textLayoutProp->GetTextIndentValue(Dimension(0.0, DimensionUnit::FP)).ToString()
4049                             : "Na"));
4050     }
4051 }
4052 
DumpTextStyleInfo3()4053 void TextPattern::DumpTextStyleInfo3()
4054 {
4055     auto& dumpLog = DumpLog::GetInstance();
4056     auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
4057     CHECK_NULL_VOID(textLayoutProp);
4058     if (textStyle_.has_value()) {
4059         dumpLog.AddDesc(
4060             std::string("fontFamily: ")
4061                 .append(GetFontFamilyInJson(textStyle_->GetFontFamilies()))
4062                 .append(" pro: ")
4063                 .append(textLayoutProp->HasFontFamily() ? GetFontFamilyInJson(textLayoutProp->GetFontFamily().value())
4064                                                         : "Na"));
4065         dumpLog.AddDesc(
4066             std::string("LetterSpacing: ")
4067                 .append(textStyle_->GetLetterSpacing().ToString())
4068                 .append(" pro: ")
4069                 .append(textLayoutProp->HasLetterSpacing()
4070                             ? textLayoutProp->GetLetterSpacingValue(Dimension(0.0, DimensionUnit::FP)).ToString()
4071                             : "Na"));
4072         dumpLog.AddDesc(std::string("TextOverflow: ").append(StringUtils::ToString(textStyle_->GetTextOverflow())));
4073         dumpLog.AddDesc(std::string("TextAlign: ")
4074                             .append(StringUtils::ToString(textStyle_->GetTextAlign()))
4075                             .append(" pro: ")
4076                             .append(textLayoutProp->HasTextAlign()
4077                                         ? StringUtils::ToString(textLayoutProp->GetTextAlignValue(TextAlign::START))
4078                                         : "Na"));
4079         dumpLog.AddDesc(std::string("WordBreak: ").append(StringUtils::ToString(textStyle_->GetWordBreak())));
4080         dumpLog.AddDesc(std::string("TextCase: ").append(StringUtils::ToString(textStyle_->GetTextCase())));
4081         dumpLog.AddDesc(std::string("EllipsisMode: ").append(StringUtils::ToString(textStyle_->GetEllipsisMode())));
4082         dumpLog.AddDesc(
4083             std::string("LineBreakStrategy: ").append(GetLineBreakStrategyInJson(textStyle_->GetLineBreakStrategy())));
4084         dumpLog.AddDesc(std::string("SymbolColorList: ")
4085                             .append(StringUtils::SymbolColorListToString(textStyle_->GetSymbolColorList())));
4086     }
4087 }
4088 
DumpScaleInfo()4089 void TextPattern::DumpScaleInfo()
4090 {
4091     auto& dumpLog = DumpLog::GetInstance();
4092     dumpLog.AddDesc(std::string("-----DumpScaleInfo-----"));
4093     auto host = GetHost();
4094     CHECK_NULL_VOID(host);
4095     auto pipeline = host->GetContext();
4096     CHECK_NULL_VOID(pipeline);
4097     auto fontScale = pipeline->GetFontScale();
4098     auto fontWeightScale = pipeline->GetFontWeightScale();
4099     auto followSystem = pipeline->IsFollowSystem();
4100     float maxFontScale = pipeline->GetMaxAppFontScale();
4101     auto halfLeading = pipeline->GetHalfLeading();
4102     dumpLog.AddDesc(std::string("fontScale: ").append(std::to_string(fontScale))
4103         .append(std::string(", fontWeightScale: ")).append(std::to_string(fontWeightScale))
4104         .append(std::string(", IsFollowSystem: ")).append(std::to_string(followSystem))
4105         .append(std::string(", maxFontScale: ")).append(std::to_string(maxFontScale))
4106         .append(std::string(", ConfigHalfLeading: ")).append(std::to_string(halfLeading)));
4107     auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
4108     CHECK_NULL_VOID(textLayoutProp);
4109     auto minFontScale = textLayoutProp->GetMinFontScale().value_or(0.0f);
4110     auto maxfontScale = textLayoutProp->GetMaxFontScale().value_or(static_cast<float>(INT32_MAX));
4111     dumpLog.AddDesc(std::string("minFontScale: ").append(std::to_string(minFontScale))
4112         .append(std::string(", maxFontScale: ")).append(std::to_string(maxfontScale)));
4113     auto flag = textLayoutProp->HasHalfLeading();
4114     dumpLog.AddDesc(
4115         std::string("HalfLeading: ").append(flag ? std::to_string(textLayoutProp->GetHalfLeadingValue(false)) : "NA"));
4116 }
4117 
DumpTextEngineInfo()4118 void TextPattern::DumpTextEngineInfo()
4119 {
4120     auto& dumpLog = DumpLog::GetInstance();
4121     dumpLog.AddDesc(std::string("-----TextEngine paragraphs_ info-----"));
4122     dumpLog.AddDesc(std::string("contentRect :").append(contentRect_.ToString()));
4123     if (pManager_) {
4124         dumpLog.AddDesc(std::string("from TextEngine paragraphs_ info :"));
4125         auto paragraphs = pManager_->GetParagraphs();
4126         if (paragraphs.empty()) {
4127             dumpLog.AddDesc(std::string("paragraphs is empty!"));
4128             return;
4129         }
4130         dumpLog.AddDesc(std::string("DidExceedMaxLines:").append(std::to_string(pManager_->DidExceedMaxLines())));
4131         dumpLog.AddDesc(std::string("GetTextWidth:")
4132                             .append(std::to_string(pManager_->GetTextWidth()))
4133                             .append(" GetHeight:")
4134                             .append(std::to_string(pManager_->GetHeight()))
4135                             .append(" GetMaxWidth:")
4136                             .append(std::to_string(pManager_->GetMaxWidth()))
4137                             .append(" GetMaxIntrinsicWidth:")
4138                             .append(std::to_string(pManager_->GetMaxIntrinsicWidth())));
4139         dumpLog.AddDesc(std::string("GetLineCount:")
4140                             .append(std::to_string(pManager_->GetLineCount()))
4141                             .append(" GetLongestLine:")
4142                             .append(std::to_string(pManager_->GetLongestLine()))
4143                             .append(std::to_string(pManager_->GetLongestLineWithIndent())));
4144     }
4145     dumpLog.AddDesc(std::string("spans size :").append(std::to_string(spans_.size())));
4146     if (!IsSetObscured() && !IsSensitiveEnalbe()) {
4147         DumpParagraphsInfo();
4148     }
4149 }
4150 
DumpParagraphsInfo()4151 void TextPattern::DumpParagraphsInfo()
4152 {
4153     CHECK_NULL_VOID(pManager_);
4154     auto& dumpLog = DumpLog::GetInstance();
4155     auto paragraphs = pManager_->GetParagraphs();
4156     if (paragraphs.empty()) {
4157         dumpLog.AddDesc(std::string("paragraphs is empty!"));
4158         return;
4159     }
4160     dumpLog.AddDesc(std::string("paragraphs size:").append(std::to_string(paragraphs.size())));
4161     for (auto&& info : paragraphs) {
4162         auto paragraph = info.paragraph;
4163         if (paragraph) {
4164             auto text = StringUtils::Str16ToStr8(paragraph->GetParagraphText());
4165             auto textStyle = paragraph->GetParagraphStyle();
4166             auto direction = V2::ConvertTextDirectionToString(textStyle.direction);
4167             dumpLog.AddDesc(std::string("paragraph: ").append(text).append("; direction:").append(direction));
4168         }
4169     }
4170 }
4171 
SetAccessibilityAction()4172 void TextPattern::SetAccessibilityAction()
4173 {
4174     auto host = GetHost();
4175     CHECK_NULL_VOID(host);
4176     auto textAccessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
4177     CHECK_NULL_VOID(textAccessibilityProperty);
4178     textAccessibilityProperty->SetActionSetSelection(
4179         [weakPtr = WeakClaim(this)](int32_t start, int32_t end, bool isForward) {
4180             const auto& pattern = weakPtr.Upgrade();
4181             CHECK_NULL_VOID(pattern);
4182             auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
4183             CHECK_NULL_VOID(textLayoutProperty);
4184             auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
4185             if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None &&
4186                 mode != TextSelectableMode::UNSELECTABLE) {
4187                 pattern->ActSetSelection(start, end);
4188             }
4189         });
4190 
4191     textAccessibilityProperty->SetActionClearSelection([weakPtr = WeakClaim(this)]() {
4192         const auto& pattern = weakPtr.Upgrade();
4193         CHECK_NULL_VOID(pattern);
4194         auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
4195         CHECK_NULL_VOID(textLayoutProperty);
4196         auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
4197         if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None &&
4198             mode != TextSelectableMode::UNSELECTABLE) {
4199             pattern->CloseSelectOverlay(true);
4200             pattern->ResetSelection();
4201         }
4202     });
4203 
4204     textAccessibilityProperty->SetActionCopy([weakPtr = WeakClaim(this)]() {
4205         const auto& pattern = weakPtr.Upgrade();
4206         CHECK_NULL_VOID(pattern);
4207         auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
4208         CHECK_NULL_VOID(textLayoutProperty);
4209         auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
4210         if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None &&
4211             mode != TextSelectableMode::UNSELECTABLE) {
4212             pattern->HandleOnCopy();
4213             pattern->CloseSelectOverlay(true);
4214             pattern->ResetSelection();
4215         }
4216     });
4217 }
4218 
OnColorConfigurationUpdate()4219 void TextPattern::OnColorConfigurationUpdate()
4220 {
4221     auto host = GetHost();
4222     CHECK_NULL_VOID(host);
4223     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4224     CHECK_NULL_VOID(textLayoutProperty);
4225     if (!textLayoutProperty->HasTextColor()) {
4226         host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
4227     }
4228     if (GetOrCreateMagnifier()) {
4229         magnifierController_->SetColorModeChange(true);
4230     }
4231     ACE_TEXT_SCOPED_TRACE("OnColorConfigurationUpdate[Text][self:%d]", host->GetId());
4232 }
4233 
OnThemeScopeUpdate(int32_t themeScopeId)4234 bool TextPattern::OnThemeScopeUpdate(int32_t themeScopeId)
4235 {
4236     auto host = GetHost();
4237     CHECK_NULL_RETURN(host, false);
4238     auto contex = host->GetRenderContext();
4239     CHECK_NULL_RETURN(contex, false);
4240     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4241     CHECK_NULL_RETURN(textLayoutProperty, false);
4242 
4243     if (!textLayoutProperty->HasTextColor() && !contex->HasForegroundColor()) {
4244         auto pipeline = host->GetContext();
4245         CHECK_NULL_RETURN(pipeline, false);
4246         auto textTheme = pipeline->GetTheme<TextTheme>(themeScopeId);
4247         CHECK_NULL_RETURN(textTheme, false);
4248         UpdateFontColor(textTheme->GetTextStyle().GetTextColor());
4249     }
4250     return false;
4251 }
4252 
4253 // return: whether the offset is valid, return false if invalid
GetOriginCaretPosition(OffsetF & offset) const4254 bool TextPattern::GetOriginCaretPosition(OffsetF& offset) const
4255 {
4256     if (!originCaretPosition_.NonNegative()) {
4257         return false;
4258     }
4259     offset = originCaretPosition_;
4260     return true;
4261 }
4262 
ResetOriginCaretPosition()4263 void TextPattern::ResetOriginCaretPosition()
4264 {
4265     originCaretPosition_ = DEFAULT_NEGATIVE_CARET_OFFSET;
4266 }
4267 
4268 // Record current caret position if originCaretPosition_ is invalid
4269 // return: whether the current offset is recorded and valid
RecordOriginCaretPosition(const OffsetF & offset)4270 bool TextPattern::RecordOriginCaretPosition(const OffsetF& offset)
4271 {
4272     if (originCaretPosition_.NonNegative() || !offset.NonNegative()) {
4273         return false;
4274     }
4275     originCaretPosition_ = offset;
4276     return true;
4277 }
4278 
ResetCustomFontColor()4279 void TextPattern::ResetCustomFontColor()
4280 {
4281     auto host = GetHost();
4282     CHECK_NULL_VOID(host);
4283     auto pipeline = host->GetContext();
4284     CHECK_NULL_VOID(pipeline);
4285     auto textTheme = pipeline->GetTheme<TextTheme>(host->GetThemeScopeId());
4286     CHECK_NULL_VOID(textTheme);
4287     auto color = textTheme->GetTextStyle().GetTextColor();
4288     UpdateFontColor(color);
4289 }
4290 
GetDragUpperLeftCoordinates()4291 OffsetF TextPattern::GetDragUpperLeftCoordinates()
4292 {
4293     auto dragBoxes = GetTextBoxes();
4294     if (dragBoxes.empty()) {
4295         return { 0.0f, 0.0f };
4296     }
4297     auto startY = dragBoxes.front().Top();
4298     auto startX = dragBoxes.front().Left();
4299 
4300     auto endY = dragBoxes.back().Top();
4301     OffsetF offset;
4302     if (NearEqual(startY, endY)) {
4303         offset = { contentRect_.GetX() + startX, startY + contentRect_.GetY() };
4304     } else {
4305         offset = { contentRect_.GetX(), startY + contentRect_.GetY() };
4306     }
4307 
4308     return GetParentGlobalOffset() + offset;
4309 }
4310 
ProcessBoundRectByTextShadow(RectF & rect)4311 void TextPattern::ProcessBoundRectByTextShadow(RectF& rect)
4312 {
4313     auto property = GetHost()->GetLayoutProperty<TextLayoutProperty>();
4314     auto shadows = property->GetTextShadow();
4315     if (!shadows.has_value()) {
4316         return;
4317     }
4318     float leftOffsetX = 0.0f;
4319     float rightOffsetX = 0.0f;
4320     float upOffsetY = 0.0f;
4321     float downOffsetY = 0.0f;
4322     for (const auto& shadow : shadows.value()) {
4323         auto shadowBlurRadius = shadow.GetBlurRadius() * 2.0f;
4324         if (LessNotEqual(shadow.GetOffset().GetX() - shadowBlurRadius, leftOffsetX)) {
4325             leftOffsetX = shadow.GetOffset().GetX() - shadowBlurRadius;
4326         }
4327 
4328         if (GreatNotEqual(shadow.GetOffset().GetX() + shadowBlurRadius, rightOffsetX)) {
4329             rightOffsetX = shadow.GetOffset().GetX() + shadowBlurRadius;
4330         }
4331 
4332         if (LessNotEqual(shadow.GetOffset().GetY() - shadowBlurRadius, upOffsetY)) {
4333             upOffsetY = shadow.GetOffset().GetY() - shadowBlurRadius;
4334         }
4335 
4336         if (GreatNotEqual(shadow.GetOffset().GetY() + shadowBlurRadius, downOffsetY)) {
4337             downOffsetY = shadow.GetOffset().GetY() + shadowBlurRadius;
4338         }
4339     }
4340     rect.SetRect(
4341         leftOffsetX, upOffsetY, rect.Width() + rightOffsetX - leftOffsetX, rect.Height() + downOffsetY - upOffsetY);
4342 }
4343 
ProcessBoundRectByTextMarquee(RectF & rect)4344 void TextPattern::ProcessBoundRectByTextMarquee(RectF& rect)
4345 {
4346     auto host = GetHost();
4347     CHECK_NULL_VOID(host);
4348     auto textLayoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
4349     CHECK_NULL_VOID(textLayoutProperty);
4350     if (textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) != TextOverflow::MARQUEE) {
4351         return;
4352     }
4353     auto geometryNode = host->GetGeometryNode();
4354     CHECK_NULL_VOID(geometryNode);
4355     auto contentSize = geometryNode->GetContentSize();
4356     CHECK_NULL_VOID(pManager_);
4357     if (pManager_->GetTextWidth() < contentSize.Width()) {
4358         return;
4359     }
4360     auto frameSize = geometryNode->GetFrameSize();
4361     auto relativeSelfLeftOffsetX =
4362         std::max(-1 * host->GetOffsetRelativeToWindow().GetX(), rect.GetOffset().GetX() - pManager_->GetTextWidth());
4363     rect.SetLeft(relativeSelfLeftOffsetX);
4364     rect.SetWidth(frameSize.Width() + pManager_->GetTextWidth() - relativeSelfLeftOffsetX);
4365 }
4366 
CreateNodePaintMethod()4367 RefPtr<NodePaintMethod> TextPattern::CreateNodePaintMethod()
4368 {
4369     CreateModifier();
4370     auto paintMethod = MakeRefPtr<TextPaintMethod>(WeakClaim(this), baselineOffset_, contentMod_, overlayMod_);
4371     auto host = GetHost();
4372     CHECK_NULL_RETURN(host, paintMethod);
4373     auto context = host->GetRenderContext();
4374     CHECK_NULL_RETURN(context, paintMethod);
4375     auto geometryNode = host->GetGeometryNode();
4376     CHECK_NULL_RETURN(geometryNode, paintMethod);
4377     auto frameSize = geometryNode->GetFrameSize();
4378     if (context->GetClipEdge().value_or(host->LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE))) {
4379         SetResponseRegion(frameSize, frameSize);
4380         return paintMethod;
4381     }
4382     CHECK_NULL_RETURN(pManager_, paintMethod);
4383     RectF boundsRect = overlayMod_->GetBoundsRect();
4384     auto boundsWidth = contentRect_.GetX() + std::ceil(pManager_->GetLongestLineWithIndent());
4385     auto boundsHeight = contentRect_.GetY() + static_cast<float>(pManager_->GetHeight() + std::fabs(baselineOffset_));
4386     boundsRect.SetWidth(boundsWidth);
4387     boundsRect.SetHeight(boundsHeight);
4388     SetResponseRegion(frameSize, boundsRect.GetSize());
4389     ProcessBoundRectByTextShadow(boundsRect);
4390     ProcessBoundRectByTextMarquee(boundsRect);
4391     boundsRect.SetWidth(std::max(frameSize.Width(), boundsRect.Width()));
4392     boundsRect.SetHeight(std::max(frameSize.Height(), boundsRect.Height()));
4393     auto baselineOffset = LessOrEqual(baselineOffset_, 0) ? std::fabs(baselineOffset_) : 0;
4394     pManager_->GetPaintRegion(boundsRect, contentRect_.GetX(), contentRect_.GetY() + baselineOffset);
4395     overlayMod_->SetBoundsRect(boundsRect);
4396     return paintMethod;
4397 }
4398 
SetResponseRegion(const SizeF & frameSize,const SizeF & boundsSize)4399 void TextPattern::SetResponseRegion(const SizeF& frameSize, const SizeF& boundsSize)
4400 {
4401     auto host = GetHost();
4402     CHECK_NULL_VOID(host);
4403     auto gestureHub = host->GetOrCreateGestureEventHub();
4404     CHECK_NULL_VOID(gestureHub);
4405     if (isUserSetResponseRegion_) {
4406         return;
4407     }
4408     std::vector<DimensionRect> hotZoneRegions;
4409     DimensionRect hotZoneRegion;
4410     hotZoneRegion.SetSize(DimensionSize(Dimension(std::max(boundsSize.Width(), frameSize.Width())),
4411         Dimension(std::max(frameSize.Height(), boundsSize.Height()))));
4412     hotZoneRegions.emplace_back(hotZoneRegion);
4413     gestureHub->SetResponseRegion(hotZoneRegions);
4414 }
4415 
CreateModifier()4416 void TextPattern::CreateModifier()
4417 {
4418     if (!contentMod_) {
4419         contentMod_ = MakeRefPtr<TextContentModifier>(textStyle_, WeakClaim(this));
4420     }
4421     if (!overlayMod_) {
4422         overlayMod_ = MakeRefPtr<TextOverlayModifier>();
4423     }
4424     if (isCustomFont_) {
4425         contentMod_->SetIsCustomFont(true);
4426     }
4427 }
4428 
GetHandleIndex(const Offset & offset) const4429 int32_t TextPattern::GetHandleIndex(const Offset& offset) const
4430 {
4431     return pManager_->GetGlyphIndexByCoordinate(offset);
4432 }
4433 
OnHandleAreaChanged()4434 void TextPattern::OnHandleAreaChanged()
4435 {
4436     if (selectOverlay_->SelectOverlayIsOn()) {
4437         auto parentGlobalOffset = GetParentGlobalOffset();
4438         if (parentGlobalOffset != parentGlobalOffset_) {
4439             parentGlobalOffset_ = parentGlobalOffset;
4440             CalculateHandleOffsetAndShowOverlay();
4441             ShowSelectOverlay({ .menuIsShow = false, .animation = true });
4442         }
4443     }
4444 }
4445 
RemoveAreaChangeInner()4446 void TextPattern::RemoveAreaChangeInner()
4447 {
4448     auto host = GetHost();
4449     CHECK_NULL_VOID(host);
4450     auto pipeline = host->GetContext();
4451     CHECK_NULL_VOID(pipeline);
4452     auto eventHub = host->GetEventHub<TextEventHub>();
4453     CHECK_NULL_VOID(eventHub);
4454     if (eventHub->HasOnAreaChanged()) {
4455         return;
4456     }
4457     pipeline->RemoveOnAreaChangeNode(host->GetId());
4458 }
4459 
SetTextDetectEnable(bool enable)4460 void TextPattern::SetTextDetectEnable(bool enable)
4461 {
4462     auto host = GetHost();
4463     CHECK_NULL_VOID(host);
4464     dataDetectorAdapter_->frameNode_ = host;
4465     if (enable == textDetectEnable_) {
4466         return;
4467     }
4468     textDetectEnable_ = enable;
4469     if (textDetectEnable_) {
4470         auto pipeline = host->GetContext();
4471         CHECK_NULL_VOID(pipeline);
4472         auto callback = [weak = WeakClaim(this)]() {
4473             auto pattern = weak.Upgrade();
4474             CHECK_NULL_VOID(pattern);
4475             pattern->dataDetectorAdapter_->GetAIEntityMenu();
4476         };
4477         pipeline->SetConfigChangedCallback(host->GetId(), callback);
4478     } else {
4479         dataDetectorAdapter_->CancelAITask();
4480     }
4481     host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE);
4482 }
4483 
CanStartAITask()4484 bool TextPattern::CanStartAITask()
4485 {
4486     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4487     if (textLayoutProperty) {
4488         return textDetectEnable_ && enabled_ && !IsSetObscured() &&
4489                textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) != TextOverflow::MARQUEE;
4490     } else {
4491         return textDetectEnable_ && enabled_;
4492     }
4493 }
4494 
NeedShowAIDetect()4495 bool TextPattern::NeedShowAIDetect()
4496 {
4497     return CanStartAITask() && !dataDetectorAdapter_->aiSpanMap_.empty();
4498 }
4499 
BindSelectionMenu(TextSpanType spanType,TextResponseType responseType,std::function<void ()> & menuBuilder,const SelectMenuParam & menuParam)4500 void TextPattern::BindSelectionMenu(TextSpanType spanType, TextResponseType responseType,
4501     std::function<void()>& menuBuilder, const SelectMenuParam& menuParam)
4502 {
4503     auto key = std::make_pair(spanType, responseType);
4504     auto it = selectionMenuMap_.find(key);
4505     if (it != selectionMenuMap_.end()) {
4506         if (menuBuilder == nullptr) {
4507             selectionMenuMap_.erase(it);
4508             return;
4509         }
4510         it->second->buildFunc = menuBuilder;
4511         it->second->onAppear = menuParam.onAppear;
4512         it->second->onDisappear = menuParam.onDisappear;
4513         it->second->onMenuShow = menuParam.onMenuShow;
4514         it->second->onMenuHide = menuParam.onMenuHide;
4515         it->second->isValid = menuParam.isValid;
4516         return;
4517     }
4518 
4519     auto selectionMenuParams = std::make_shared<SelectionMenuParams>(
4520         spanType, menuBuilder, menuParam.onAppear, menuParam.onDisappear, responseType);
4521     selectionMenuParams->onMenuShow = menuParam.onMenuShow;
4522     selectionMenuParams->onMenuHide = menuParam.onMenuHide;
4523     selectionMenuParams->isValid = menuParam.isValid;
4524     selectionMenuMap_[key] = selectionMenuParams;
4525     auto host = GetHost();
4526     CHECK_NULL_VOID(host);
4527     host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
4528 }
4529 
CloseSelectionMenu()4530 void TextPattern::CloseSelectionMenu()
4531 {
4532     textResponseType_ = TextResponseType::NONE;
4533     CloseSelectOverlay(true);
4534 }
4535 
GetMenuParams(TextSpanType spanType,TextResponseType responseType)4536 std::shared_ptr<SelectionMenuParams> TextPattern::GetMenuParams(TextSpanType spanType, TextResponseType responseType)
4537 {
4538     // JS TextSpanType.DEFAULT = TextSpanType::NONE
4539     // JS TextResponseType.DEFAULT = TextResponseType::NONE
4540     std::vector<std::pair<TextSpanType, TextResponseType>> searchPairs = {
4541         { spanType, responseType },
4542         { spanType, TextResponseType::NONE },
4543     };
4544     if (spanType != TextSpanType::NONE) {
4545         searchPairs.push_back({ TextSpanType::NONE, responseType });
4546         searchPairs.push_back({ TextSpanType::NONE, TextResponseType::NONE });
4547     }
4548     for (const auto& key : searchPairs) {
4549         auto it = selectionMenuMap_.find(key);
4550         if (it != selectionMenuMap_.end()) {
4551             return it->second;
4552         }
4553     }
4554     TAG_LOGD(AceLogTag::ACE_TEXT, "The key not in selectionMenuMap_");
4555     return nullptr;
4556 }
4557 
CopySelectionMenuParams(SelectOverlayInfo & selectInfo,TextResponseType responseType)4558 void TextPattern::CopySelectionMenuParams(SelectOverlayInfo& selectInfo, TextResponseType responseType)
4559 {
4560     auto currentSpanType = selectedType_.value_or(TextSpanType::NONE);
4561     std::shared_ptr<SelectionMenuParams> menuParams = nullptr;
4562     menuParams = GetMenuParams(currentSpanType, responseType);
4563     if (menuParams == nullptr || !menuParams->isValid) {
4564         return;
4565     }
4566     CopyBindSelectionMenuParams(selectInfo, menuParams);
4567 }
4568 
CopyBindSelectionMenuParams(SelectOverlayInfo & selectInfo,std::shared_ptr<SelectionMenuParams> menuParams)4569 void TextPattern::CopyBindSelectionMenuParams(
4570     SelectOverlayInfo& selectInfo, std::shared_ptr<SelectionMenuParams> menuParams)
4571 {
4572     selectInfo.menuInfo.menuBuilder = menuParams->buildFunc;
4573     auto weak = AceType::WeakClaim(this);
4574     selectInfo.menuCallback.onAppear = [weak, menuParams]() {
4575         auto pattern = weak.Upgrade();
4576         CHECK_NULL_VOID(pattern);
4577         pattern->OnHandleSelectionMenuCallback(SelectionMenuCalblackId::MENU_APPEAR, menuParams);
4578     };
4579     selectInfo.menuCallback.onDisappear = menuParams->onDisappear;
4580     selectInfo.menuCallback.onMenuShow = [weak, menuParams]() {
4581         auto pattern = weak.Upgrade();
4582         CHECK_NULL_VOID(pattern);
4583         pattern->OnHandleSelectionMenuCallback(SelectionMenuCalblackId::MENU_SHOW, menuParams);
4584     };
4585     selectInfo.menuCallback.onMenuHide = [weak, menuParams]() {
4586         auto pattern = weak.Upgrade();
4587         CHECK_NULL_VOID(pattern);
4588         pattern->OnHandleSelectionMenuCallback(SelectionMenuCalblackId::MENU_HIDE, menuParams);
4589     };
4590 }
4591 
OnHandleSelectionMenuCallback(SelectionMenuCalblackId callbackId,std::shared_ptr<SelectionMenuParams> menuParams)4592 void TextPattern::OnHandleSelectionMenuCallback(
4593     SelectionMenuCalblackId callbackId, std::shared_ptr<SelectionMenuParams> menuParams)
4594 {
4595     std::function<void(int32_t, int32_t)> callback;
4596     switch (callbackId) {
4597         case SelectionMenuCalblackId::MENU_SHOW:
4598             callback = menuParams->onMenuShow;
4599             break;
4600         case SelectionMenuCalblackId::MENU_HIDE:
4601             callback = menuParams->onMenuHide;
4602             break;
4603         case SelectionMenuCalblackId::MENU_APPEAR:
4604             callback = menuParams->onAppear;
4605             break;
4606         default:
4607             callback = nullptr;
4608     }
4609     CHECK_NULL_VOID(callback);
4610     auto selectStart = std::min(textSelector_.baseOffset, textSelector_.destinationOffset);
4611     auto selectEnd = std::max(textSelector_.baseOffset, textSelector_.destinationOffset);
4612     callback(selectStart, selectEnd);
4613 }
4614 
FireOnSelectionChange(int32_t start,int32_t end)4615 void TextPattern::FireOnSelectionChange(int32_t start, int32_t end)
4616 {
4617     auto host = GetHost();
4618     CHECK_NULL_VOID(host);
4619     auto eventHub = host->GetEventHub<TextEventHub>();
4620     CHECK_NULL_VOID(eventHub);
4621     eventHub->FireOnSelectionChange(start, end);
4622 }
4623 
FireOnMarqueeStateChange(const TextMarqueeState & state)4624 void TextPattern::FireOnMarqueeStateChange(const TextMarqueeState& state)
4625 {
4626     auto host = GetHost();
4627     CHECK_NULL_VOID(host);
4628     auto eventHub = host->GetEventHub<TextEventHub>();
4629     CHECK_NULL_VOID(eventHub);
4630     eventHub->FireOnMarqueeStateChange(static_cast<int32_t>(state));
4631 
4632     if (TextMarqueeState::START == state) {
4633         CloseSelectOverlay();
4634         ResetSelection();
4635         isMarqueeRunning_ = true;
4636     } else if (TextMarqueeState::FINISH == state) {
4637         isMarqueeRunning_ = false;
4638     }
4639 
4640     RecoverCopyOption();
4641 }
4642 
OnSelectionMenuOptionsUpdate(const NG::OnCreateMenuCallback && onCreateMenuCallback,const NG::OnMenuItemClickCallback && onMenuItemClick)4643 void TextPattern::OnSelectionMenuOptionsUpdate(
4644     const NG::OnCreateMenuCallback&& onCreateMenuCallback, const NG::OnMenuItemClickCallback&& onMenuItemClick)
4645 {
4646     selectOverlay_->OnSelectionMenuOptionsUpdate(std::move(onCreateMenuCallback), std::move(onMenuItemClick));
4647 }
4648 
StartVibratorByIndexChange(int32_t currentIndex,int32_t preIndex)4649 void TextPattern::StartVibratorByIndexChange(int32_t currentIndex, int32_t preIndex)
4650 {
4651     CHECK_NULL_VOID(isEnableHapticFeedback_ && (currentIndex != preIndex));
4652     VibratorUtils::StartVibraFeedback("slide");
4653 }
4654 
HandleSelectionChange(int32_t start,int32_t end)4655 void TextPattern::HandleSelectionChange(int32_t start, int32_t end)
4656 {
4657     if (textSelector_.GetStart() == start && textSelector_.GetEnd() == end) {
4658         return;
4659     }
4660 
4661     bool changeSymbolEffect = false;
4662     for (auto& span: spans_) {
4663         if (span->GetSymbolUnicode() == 0) {
4664             continue;
4665         }
4666         bool nextEffectSwitch = start != -1 && end != -1 ? false : true;
4667         if (span->GetSymbolEffectSwitch() != nextEffectSwitch) {
4668             span->SetSymbolEffectSwitch(nextEffectSwitch);
4669             changeSymbolEffect = true;
4670         }
4671     }
4672     textSelector_.Update(start, end);
4673     UpdateSelectionSpanType(std::min(start, end), std::max(start, end));
4674     FireOnSelectionChange(std::min(start, end), std::max(start, end));
4675     if (changeSymbolEffect) {
4676         auto host = GetHost();
4677         CHECK_NULL_VOID(host);
4678         host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
4679     }
4680 }
4681 
IsSelectedBindSelectionMenu()4682 bool TextPattern::IsSelectedBindSelectionMenu()
4683 {
4684     auto currentSpanType = selectedType_.value_or(TextSpanType::TEXT);
4685     return GetMenuParams(currentSpanType, TextResponseType::SELECTED_BY_MOUSE) != nullptr;
4686 }
4687 
UpdateSelectionSpanType(int32_t selectStart,int32_t selectEnd)4688 void TextPattern::UpdateSelectionSpanType(int32_t selectStart, int32_t selectEnd)
4689 {
4690     UpdateSelectionType(GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT));
4691     if ((selectedType_ == TextSpanType::NONE && !textSelector_.StartEqualToDest()) ||
4692         textSelector_.StartEqualToDest()) {
4693         selectedType_ = TextSpanType::TEXT;
4694     }
4695 }
4696 
UpdateSelectionType(const SelectionInfo & selection)4697 void TextPattern::UpdateSelectionType(const SelectionInfo& selection)
4698 {
4699     selectedType_ = TextSpanType::NONE;
4700     auto list = selection.GetSelection().resultObjects;
4701     bool imageSelected = false;
4702     bool textSelected = false;
4703     bool builderSelected = false;
4704     for (const auto& obj : list) {
4705         if (obj.type == SelectSpanType::TYPEIMAGE) {
4706             imageSelected = true;
4707         } else if (obj.type == SelectSpanType::TYPESPAN) {
4708             textSelected = true;
4709         } else if (obj.type == SelectSpanType::TYPEBUILDERSPAN) {
4710             builderSelected = true;
4711         }
4712         if ((imageSelected && textSelected) || (builderSelected && textSelected) ||
4713             (imageSelected && builderSelected)) {
4714             selectedType_ = TextSpanType::MIXED;
4715             return;
4716         }
4717     }
4718     if (imageSelected) {
4719         selectedType_ = TextSpanType::IMAGE;
4720     } else if (textSelected) {
4721         selectedType_ = TextSpanType::TEXT;
4722     } else if (builderSelected) {
4723         selectedType_ = TextSpanType::BUILDER;
4724     }
4725 
4726     TAG_LOGD(AceLogTag::ACE_TEXT, "UpdateSelectionSpanType, selectedType_: %{public}d", selectedType_.value());
4727 }
4728 
GetSelectionSpanItemIndex(const MouseInfo & info)4729 int32_t TextPattern::GetSelectionSpanItemIndex(const MouseInfo& info)
4730 {
4731     RectF textContentRect = contentRect_;
4732     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
4733     textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
4734     PointF textOffset = { info.GetLocalLocation().GetX() - textContentRect.GetX(),
4735         info.GetLocalLocation().GetY() - textContentRect.GetY() };
4736     if (!textContentRect.IsInRegion(PointF(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY())) ||
4737         spans_.empty() || pManager_->GetParagraphs().empty()) {
4738         return -1;
4739     }
4740     int32_t start = 0;
4741     bool isFind = false;
4742     int32_t index = -1;
4743     for (const auto& item : spans_) {
4744         index++;
4745         if (!item) {
4746             continue;
4747         }
4748         auto selectedRects = pManager_->GetRects(start, item->position);
4749         start = item->position;
4750         for (auto&& rect : selectedRects) {
4751             if (rect.IsInRegion(textOffset)) {
4752                 isFind = true;
4753                 break;
4754             }
4755         }
4756         if (isFind) {
4757             TAG_LOGD(AceLogTag::ACE_TEXT, "GetSelectionSpanItemIndex index: %{public}d", index);
4758             return index;
4759         }
4760     }
4761     return -1;
4762 }
4763 
GetBuilderResultObject(RefPtr<UINode> uiNode,int32_t index,int32_t start,int32_t end)4764 ResultObject TextPattern::GetBuilderResultObject(RefPtr<UINode> uiNode, int32_t index, int32_t start, int32_t end)
4765 {
4766     int32_t itemLength = 1;
4767     ResultObject resultObject;
4768     resultObject.isDraggable = true;
4769     if (!DynamicCast<FrameNode>(uiNode) || !GetSpanItemByIndex(index)) {
4770         return resultObject;
4771     }
4772     int32_t endPosition = std::min(GetTextContentLength(), GetSpanItemByIndex(index)->position);
4773     int32_t startPosition = endPosition - itemLength;
4774     if ((start <= startPosition) && (end >= endPosition)) {
4775         auto builderNode = DynamicCast<FrameNode>(uiNode);
4776         CHECK_NULL_RETURN(builderNode, resultObject);
4777         resultObject.spanPosition.spanIndex = index;
4778         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
4779         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
4780         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
4781         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
4782         resultObject.type = SelectSpanType::TYPEIMAGE;
4783         auto geometryNode = builderNode->GetGeometryNode();
4784         CHECK_NULL_RETURN(geometryNode, resultObject);
4785         resultObject.imageStyle.size[RichEditorImageSize::SIZEWIDTH] = geometryNode->GetMarginFrameSize().Width();
4786         resultObject.imageStyle.size[RichEditorImageSize::SIZEHEIGHT] = geometryNode->GetMarginFrameSize().Height();
4787         resultObject.valueString = u" ";
4788     }
4789     return resultObject;
4790 }
4791 
SetStyledString(const RefPtr<SpanString> & value,bool closeSelectOverlay)4792 void TextPattern::SetStyledString(const RefPtr<SpanString>& value, bool closeSelectOverlay)
4793 {
4794     AllocStyledString();
4795     isSpanStringMode_ = true;
4796     auto host = GetHost();
4797     CHECK_NULL_VOID(host);
4798     if (closeSelectOverlay) {
4799         CloseSelectOverlay();
4800     }
4801     auto length = styledString_->GetLength();
4802     styledString_->RemoveCustomSpan();
4803     styledString_->ReplaceSpanString(0, length, value);
4804     spans_ = styledString_->GetSpanItems();
4805     ProcessSpanString();
4806     styledString_->AddCustomSpan();
4807     styledString_->SetFramNode(WeakClaim(Referenced::RawPtr(host)));
4808     host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE);
4809 }
4810 
MountImageNode(const RefPtr<ImageSpanItem> & imageItem)4811 void TextPattern::MountImageNode(const RefPtr<ImageSpanItem>& imageItem)
4812 {
4813     auto host = GetHost();
4814     CHECK_NULL_VOID(host);
4815     auto imageNode = ImageSpanNode::GetOrCreateSpanNode(V2::IMAGE_ETS_TAG,
4816         ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<ImagePattern>(); });
4817     auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
4818     auto options = imageItem->options;
4819     imageLayoutProperty->UpdateImageSourceInfo(CreateImageSourceInfo(options));
4820     imageNode->MountToParent(host, host->GetChildren().size());
4821     SetImageNodeGesture(imageNode);
4822     if (options.imageAttribute.has_value()) {
4823         auto imgAttr = options.imageAttribute.value();
4824         auto imagePattern = imageNode->GetPattern<ImagePattern>();
4825         CHECK_NULL_VOID(imagePattern);
4826         imagePattern->SetSyncLoad(imgAttr.syncLoad);
4827         if (imgAttr.size.has_value()) {
4828             imageLayoutProperty->UpdateUserDefinedIdealSize(imgAttr.size->GetSize());
4829         }
4830         if (imgAttr.verticalAlign.has_value()) {
4831             imageLayoutProperty->UpdateVerticalAlign(imgAttr.verticalAlign.value());
4832         }
4833         if (imgAttr.objectFit.has_value()) {
4834             imageLayoutProperty->UpdateImageFit(imgAttr.objectFit.value());
4835         }
4836         if (imgAttr.marginProp.has_value()) {
4837             imageLayoutProperty->UpdateMargin(imgAttr.marginProp.value());
4838         }
4839         if (imgAttr.paddingProp.has_value()) {
4840             imageLayoutProperty->UpdatePadding(imgAttr.paddingProp.value());
4841         }
4842         if (imgAttr.borderRadius.has_value()) {
4843             auto imageRenderCtx = imageNode->GetRenderContext();
4844             imageRenderCtx->UpdateBorderRadius(imgAttr.borderRadius.value());
4845             imageRenderCtx->SetClipToBounds(true);
4846         }
4847         auto paintProperty = imageNode->GetPaintProperty<ImageRenderProperty>();
4848         if (imgAttr.colorFilterMatrix.has_value() && paintProperty) {
4849             paintProperty->UpdateColorFilter(imgAttr.colorFilterMatrix.value());
4850             paintProperty->ResetDrawingColorFilter();
4851         } else if (imgAttr.drawingColorFilter.has_value() && paintProperty) {
4852             paintProperty->UpdateDrawingColorFilter(imgAttr.drawingColorFilter.value());
4853             paintProperty->ResetColorFilter();
4854         }
4855     }
4856     imageNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
4857     imageNode->MarkModifyDone();
4858     imageItem->imageNodeId = imageNode->GetId();
4859     imageNode->SetImageItem(imageItem);
4860     childNodes_.emplace_back(imageNode);
4861 }
4862 
SetImageNodeGesture(RefPtr<ImageSpanNode> imageNode)4863 void TextPattern::SetImageNodeGesture(RefPtr<ImageSpanNode> imageNode)
4864 {
4865     auto gesture = imageNode->GetOrCreateGestureEventHub();
4866     CHECK_NULL_VOID(gesture);
4867     gesture->SetHitTestMode(HitTestMode::HTMNONE);
4868 }
4869 
CreateImageSourceInfo(const ImageSpanOptions & options)4870 ImageSourceInfo TextPattern::CreateImageSourceInfo(const ImageSpanOptions& options)
4871 {
4872     std::string src;
4873     RefPtr<PixelMap> pixMap = nullptr;
4874     std::string bundleName;
4875     std::string moduleName;
4876     if (options.image.has_value()) {
4877         src = options.image.value();
4878     }
4879     if (options.imagePixelMap.has_value()) {
4880         pixMap = options.imagePixelMap.value();
4881     }
4882     if (options.bundleName.has_value()) {
4883         bundleName = options.bundleName.value();
4884     }
4885     if (options.moduleName.has_value()) {
4886         moduleName = options.moduleName.value();
4887     }
4888     ImageSourceInfo info;
4889 #if defined(PIXEL_MAP_SUPPORTED)
4890     if (!options.imagePixelMap.has_value()) {
4891         info = ImageSourceInfo{ src, bundleName, moduleName };
4892     } else {
4893         info = ImageSourceInfo(pixMap);
4894     }
4895 #else
4896     info = ImageSourceInfo{ src, bundleName, moduleName };
4897 #endif
4898     info.SetIsUriPureNumber(options.isUriPureNumber.value_or(false));
4899     return info;
4900 }
4901 
ProcessSpanString()4902 void TextPattern::ProcessSpanString()
4903 {
4904     auto host = GetHost();
4905     CHECK_NULL_VOID(host);
4906     textForDisplay_.clear();
4907     childNodes_.clear();
4908     dataDetectorAdapter_->textForAI_.clear();
4909     host->Clean();
4910     hasSpanStringLongPressEvent_ = false;
4911     hasUrlSpan_ = false;
4912 
4913     // 适配AI&&挂载image节点
4914     for (const auto& span : spans_) {
4915         auto imageSpan = DynamicCast<ImageSpanItem>(span);
4916         if (imageSpan) {
4917             dataDetectorAdapter_->textForAI_ += u'\n';
4918             MountImageNode(imageSpan);
4919         } else {
4920             dataDetectorAdapter_->textForAI_ += span->content;
4921         }
4922         if (span->onClick || span->urlOnRelease) {
4923             auto gestureEventHub = host->GetOrCreateGestureEventHub();
4924             InitClickEvent(gestureEventHub);
4925         }
4926         if (span->onLongPress) {
4927             auto gestureEventHub = host->GetOrCreateGestureEventHub();
4928             InitLongPressEvent(gestureEventHub);
4929             hasSpanStringLongPressEvent_ = true;
4930         }
4931         if (span->urlOnRelease) {
4932             hasUrlSpan_ = true;
4933             InitUrlMouseEvent();
4934             InitUrlTouchEvent();
4935         }
4936         textForDisplay_ += span->content;
4937     }
4938     if (dataDetectorAdapter_->textForAI_ != textForDisplay_) {
4939         dataDetectorAdapter_->aiDetectInitialized_ = false;
4940     }
4941     if (CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
4942         dataDetectorAdapter_->StartAITask();
4943     }
4944 
4945     auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
4946     CHECK_NULL_VOID(layoutProperty);
4947     layoutProperty->UpdateContent(textForDisplay_);
4948 }
4949 
OnSensitiveStyleChange(bool isSensitive)4950 void TextPattern::OnSensitiveStyleChange(bool isSensitive)
4951 {
4952     auto host = GetHost();
4953     CHECK_NULL_VOID(host);
4954     isSensitive_ = isSensitive;
4955     host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE);
4956 }
4957 
IsSensitiveEnalbe()4958 bool TextPattern::IsSensitiveEnalbe()
4959 {
4960     auto host = GetHost();
4961     CHECK_NULL_RETURN(host, false);
4962     return isSensitive_ && host->IsPrivacySensitive();
4963 }
4964 
ConvertGlobalToLocalOffset(const Offset & globalOffset)4965 Offset TextPattern::ConvertGlobalToLocalOffset(const Offset& globalOffset)
4966 {
4967     auto localPoint = OffsetF(globalOffset.GetX(), globalOffset.GetY());
4968     selectOverlay_->RevertLocalPointWithTransform(localPoint);
4969     return Offset(localPoint.GetX(), localPoint.GetY());
4970 }
4971 
SetExternalSpanItem(const std::list<RefPtr<SpanItem>> & spans)4972 void TextPattern::SetExternalSpanItem(const std::list<RefPtr<SpanItem>>& spans)
4973 {
4974     isSpanStringMode_ = !spans.empty();
4975     if (isSpanStringMode_) {
4976         AllocStyledString();
4977     }
4978     spans_ = spans;
4979     ProcessSpanString();
4980     auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
4981     CHECK_NULL_VOID(layoutProperty);
4982     layoutProperty->UpdateContent(textForDisplay_);
4983 }
4984 
GetTextContentRect(bool isActualText) const4985 RectF TextPattern::GetTextContentRect(bool isActualText) const
4986 {
4987     auto textRect = contentRect_;
4988     auto host = GetHost();
4989     CHECK_NULL_RETURN(host, textRect);
4990     auto renderContext = host->GetRenderContext();
4991     CHECK_NULL_RETURN(renderContext, textRect);
4992     CHECK_NULL_RETURN(pManager_, textRect);
4993     if (!renderContext->GetClipEdge().value_or(false) &&
4994         LessNotEqual(textRect.Width(), pManager_->GetLongestLine())) {
4995         textRect.SetWidth(pManager_->GetLongestLine());
4996     }
4997     if (isActualText && !renderContext->GetClipEdge().value_or(false) &&
4998         LessNotEqual(textRect.Height(), pManager_->GetHeight())) {
4999         textRect.SetHeight(pManager_->GetHeight());
5000     }
5001     return textRect;
5002 }
5003 
GetLineCount() const5004 size_t TextPattern::GetLineCount() const
5005 {
5006     CHECK_NULL_RETURN(pManager_, 0);
5007     return pManager_->GetLineCount();
5008 }
5009 
DidExceedMaxLines() const5010 bool TextPattern::DidExceedMaxLines() const
5011 {
5012     CHECK_NULL_RETURN(pManager_, false);
5013     return pManager_->DidExceedMaxLines();
5014 }
5015 
IsSetObscured()5016 bool TextPattern::IsSetObscured()
5017 {
5018     auto host = GetHost();
5019     CHECK_NULL_RETURN(host, false);
5020     auto renderContext = host->GetRenderContext();
5021     CHECK_NULL_RETURN(renderContext, false);
5022     auto obscuredReasons = renderContext->GetObscured().value_or(std::vector<ObscuredReasons>());
5023     bool ifHaveObscured = spans_.empty() && std::any_of(obscuredReasons.begin(), obscuredReasons.end(),
5024         [](const auto& reason) { return reason == ObscuredReasons::PLACEHOLDER; });
5025     return ifHaveObscured;
5026 }
5027 
GetLineMetrics(int32_t lineNumber)5028 TextLineMetrics TextPattern::GetLineMetrics(int32_t lineNumber)
5029 {
5030     CHECK_NULL_RETURN(pManager_, TextLineMetrics());
5031     if (lineNumber < 0 || GetLineCount() == 0 || lineNumber > static_cast<int32_t>(GetLineCount()) - 1) {
5032         TAG_LOGI(AceLogTag::ACE_TEXT, "GetLineMetrics failed, lineNumber not between 0 and max lines:%{public}d",
5033             lineNumber);
5034         return TextLineMetrics();
5035     }
5036     auto lineMetrics = pManager_->GetLineMetrics(lineNumber);
5037     RectF textContentRect = contentRect_;
5038     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
5039     lineMetrics.x += textContentRect.GetX();
5040     lineMetrics.y += textContentRect.GetY();
5041     lineMetrics.baseline += textContentRect.GetY();
5042     return lineMetrics;
5043 }
5044 
GetRectsForRange(int32_t start,int32_t end,RectHeightStyle heightStyle,RectWidthStyle widthStyle)5045 std::vector<ParagraphManager::TextBox> TextPattern::GetRectsForRange(
5046     int32_t start, int32_t end, RectHeightStyle heightStyle, RectWidthStyle widthStyle)
5047 {
5048     if (start < 0 || end < 0 || start > end) {
5049         return {};
5050     }
5051     std::vector<ParagraphManager::TextBox> textBoxes = pManager_->GetRectsForRange(start, end, heightStyle, widthStyle);
5052     RectF textContentRect = contentRect_;
5053     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
5054     std::vector<ParagraphManager::TextBox> adjustedTextBoxes;
5055     for (auto& textBox : textBoxes) {
5056         ParagraphManager::TextBox adjustedTextBox = textBox;
5057         adjustedTextBox.rect_.SetLeft(textBox.rect_.Left() + textContentRect.Left());
5058         adjustedTextBox.rect_.SetTop(textBox.rect_.Top() + textContentRect.Top());
5059         adjustedTextBoxes.push_back(adjustedTextBox);
5060     }
5061     return adjustedTextBoxes;
5062 }
5063 
ConvertLocalOffsetToParagraphOffset(const Offset & offset)5064 Offset TextPattern::ConvertLocalOffsetToParagraphOffset(const Offset& offset)
5065 {
5066     RectF textContentRect = contentRect_;
5067     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
5068     Offset paragraphOffset = { offset.GetX() - textContentRect.GetX(), offset.GetY() - textContentRect.GetY() };
5069     return paragraphOffset;
5070 }
5071 
GetGlyphPositionAtCoordinate(int32_t x,int32_t y)5072 PositionWithAffinity TextPattern::GetGlyphPositionAtCoordinate(int32_t x, int32_t y)
5073 {
5074     Offset offset(x, y);
5075     return pManager_->GetGlyphPositionAtCoordinate(ConvertLocalOffsetToParagraphOffset(offset));
5076 }
5077 
ProcessMarqueeVisibleAreaCallback()5078 void TextPattern::ProcessMarqueeVisibleAreaCallback()
5079 {
5080     OnTextOverflowChanged();
5081     if (!IsMarqueeOverflow()) {
5082         return;
5083     }
5084     auto host = GetHost();
5085     CHECK_NULL_VOID(host);
5086     auto pipeline = GetContext();
5087     CHECK_NULL_VOID(pipeline);
5088     auto callback = [weak = WeakClaim(this)](bool visible, double ratio) {
5089         auto pattern = weak.Upgrade();
5090         CHECK_NULL_VOID(pattern);
5091         CHECK_NULL_VOID(pattern->contentMod_);
5092         if (!pattern->IsMarqueeOverflow()) {
5093             return;
5094         }
5095         if (visible && Positive(ratio)) {
5096             pattern->contentMod_->ResumeAnimation();
5097         }
5098         if (!visible && NonPositive(ratio)) {
5099             pattern->contentMod_->PauseAnimation();
5100         }
5101     };
5102     std::vector<double> ratioList = { 0.0 };
5103     pipeline->AddVisibleAreaChangeNode(host, ratioList, callback, false, true);
5104 }
5105 
OnTextOverflowChanged()5106 void TextPattern::OnTextOverflowChanged()
5107 {
5108     auto host = GetHost();
5109     CHECK_NULL_VOID(host);
5110     if (host->GetTag() == V2::SYMBOL_ETS_TAG) {
5111         return;
5112     }
5113     auto pipeline = GetContext();
5114     CHECK_NULL_VOID(pipeline);
5115     auto eventHub = host->GetEventHub<TextEventHub>();
5116     CHECK_NULL_VOID(eventHub);
5117     auto hasInnerCallabck = eventHub->HasVisibleAreaCallback(false);
5118     if (!hasInnerCallabck) {
5119         return;
5120     }
5121     auto hasUserCallback = eventHub->HasVisibleAreaCallback(true);
5122     if (!hasUserCallback) {
5123         pipeline->RemoveVisibleAreaChangeNode(host->GetId());
5124     }
5125     eventHub->CleanVisibleAreaCallback(false);
5126 }
5127 
OnFrameNodeChanged(FrameNodeChangeInfoFlag flag)5128 void TextPattern::OnFrameNodeChanged(FrameNodeChangeInfoFlag flag)
5129 {
5130     if (selectOverlay_->SelectOverlayIsOn()) {
5131         selectOverlay_->OnAncestorNodeChanged(flag);
5132     }
5133     if (leftMousePressed_ && mouseStatus_ == MouseStatus::MOVE && scrollableParent_.Upgrade()) {
5134         auto host = GetHost();
5135         CHECK_NULL_VOID(host);
5136         auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
5137         NG::PointF localPoint(lastLeftMouseMoveLocation_.GetX(), lastLeftMouseMoveLocation_.GetY());
5138         NG::NGGestureRecognizer::Transform(localPoint, WeakClaim(Referenced::RawPtr(host)), true);
5139         Offset textOffset = { localPoint.GetX() - textPaintOffset.GetX(), localPoint.GetY() - textPaintOffset.GetY() };
5140         CHECK_NULL_VOID(pManager_);
5141         auto end = pManager_->GetGlyphIndexByCoordinate(textOffset);
5142         HandleSelectionChange(textSelector_.baseOffset, end);
5143         isAutoScrollByMouse_ = true;
5144         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
5145     }
5146 }
5147 
IsMarqueeOverflow() const5148 bool TextPattern::IsMarqueeOverflow() const
5149 {
5150     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
5151     CHECK_NULL_RETURN(textLayoutProperty, false);
5152     return textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE;
5153 }
5154 
UpdateFontColor(const Color & value)5155 void TextPattern::UpdateFontColor(const Color& value)
5156 {
5157     auto host = GetHost();
5158     CHECK_NULL_VOID(host);
5159     const auto& children = host->GetChildren();
5160     if (children.empty() && spans_.empty() && !NeedShowAIDetect()) {
5161         if (contentMod_) {
5162             contentMod_->TextColorModifier(value);
5163         } else if (pManager_) {
5164             if (textStyle_.has_value()) {
5165                 textStyle_->SetTextColor(value);
5166             }
5167             for (auto&& info : pManager_->GetParagraphs()) {
5168                 auto paragraph = info.paragraph;
5169                 CHECK_NULL_VOID(paragraph);
5170                 auto length = paragraph->GetParagraphText().length();
5171                 paragraph->UpdateColor(0, length, value);
5172             }
5173         }
5174     } else {
5175         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
5176     }
5177     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
5178     CHECK_NULL_VOID(textLayoutProperty);
5179     textLayoutProperty->OnPropertyChangeMeasure();
5180 }
5181 
MarkDirtyNodeRender()5182 void TextPattern::MarkDirtyNodeRender()
5183 {
5184     auto host = GetHost();
5185     CHECK_NULL_VOID(host);
5186     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
5187 }
5188 
BeforeCreatePaintWrapper()5189 void TextPattern::BeforeCreatePaintWrapper()
5190 {
5191     // mark content dirty
5192     if (contentMod_) {
5193         contentMod_->ContentChange();
5194     }
5195 }
5196 
StartGestureSelection(int32_t start,int32_t end,const Offset & startOffset)5197 void TextPattern::StartGestureSelection(int32_t start, int32_t end, const Offset& startOffset)
5198 {
5199     scrollableParent_ = selectOverlay_->FindScrollableParent();
5200     SetupMagnifier();
5201     TextGestureSelector::StartGestureSelection(start, end, startOffset);
5202 }
5203 
GetTouchIndex(const OffsetF & offset)5204 int32_t TextPattern::GetTouchIndex(const OffsetF& offset)
5205 {
5206     OffsetF deltaOffset;
5207     if (scrollableParent_.Upgrade()) {
5208         auto parentGlobalOffset = GetParentGlobalOffset();
5209         deltaOffset = parentGlobalOffset - parentGlobalOffset_;
5210     }
5211     auto paragraphOffset =
5212         offset - deltaOffset - GetTextContentRect().GetOffset() + OffsetF(0.0f, std::min(GetBaselineOffset(), 0.0f));
5213     return GetHandleIndex({ paragraphOffset.GetX(), paragraphOffset.GetY() });
5214 }
5215 
OnTextGestureSelectionUpdate(int32_t start,int32_t end,const TouchEventInfo & info)5216 void TextPattern::OnTextGestureSelectionUpdate(int32_t start, int32_t end, const TouchEventInfo& info)
5217 {
5218     if (!HasContent()) {
5219         return;
5220     }
5221     selectOverlay_->TriggerScrollableParentToScroll(
5222         scrollableParent_.Upgrade(), info.GetTouches().front().GetGlobalLocation(), false);
5223     auto localOffset = info.GetTouches().front().GetLocalLocation();
5224     if (GetOrCreateMagnifier()) {
5225         magnifierController_->SetLocalOffset({ localOffset.GetX(), localOffset.GetY() });
5226     }
5227     if (start != textSelector_.GetStart()) {
5228         StartVibratorByIndexChange(start, textSelector_.GetStart());
5229     } else if (end != textSelector_.GetEnd()) {
5230         StartVibratorByIndexChange(end, textSelector_.GetEnd());
5231     }
5232     auto host = GetHost();
5233     CHECK_NULL_VOID(host);
5234     HandleSelectionChange(start, end);
5235     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
5236 }
5237 
OnTextGenstureSelectionEnd(const TouchLocationInfo & locationInfo)5238 void TextPattern::OnTextGenstureSelectionEnd(const TouchLocationInfo& locationInfo)
5239 {
5240     selectOverlay_->TriggerScrollableParentToScroll(scrollableParent_.Upgrade(), Offset(), true);
5241     if (magnifierController_) {
5242         magnifierController_->RemoveMagnifierFrameNode();
5243     }
5244     if (HasContent()) {
5245         CalculateHandleOffsetAndShowOverlay();
5246         oldSelectedType_ = selectedType_.value_or(TextSpanType::NONE);
5247         ShowSelectOverlay({ .animation = true });
5248     }
5249 }
5250 
ChangeHandleHeight(const GestureEvent & event,bool isFirst,bool isOverlayMode)5251 void TextPattern::ChangeHandleHeight(const GestureEvent& event, bool isFirst, bool isOverlayMode)
5252 {
5253     auto touchOffset = event.GetGlobalLocation();
5254     auto& currentHandle = isFirst ? textSelector_.firstHandle : textSelector_.secondHandle;
5255     bool isChangeFirstHandle = isFirst ? (!textSelector_.StartGreaterDest()) : textSelector_.StartGreaterDest();
5256     if (isChangeFirstHandle) {
5257         ChangeFirstHandleHeight(touchOffset, currentHandle);
5258     } else {
5259         ChangeSecondHandleHeight(touchOffset, currentHandle);
5260     }
5261 }
5262 
ChangeFirstHandleHeight(const Offset & touchOffset,RectF & handleRect)5263 void TextPattern::ChangeFirstHandleHeight(const Offset& touchOffset, RectF& handleRect)
5264 {
5265     auto height = handleRect.Height();
5266     CalculateDefaultHandleHeight(height);
5267     bool isTouchHandleCircle = LessNotEqual(touchOffset.GetY(), handleRect.Top());
5268     if (!isTouchHandleCircle) {
5269         handleRect.SetTop(static_cast<float>(touchOffset.GetY()) - height / 2.0f);
5270     }
5271     handleRect.SetHeight(height);
5272 }
5273 
ChangeSecondHandleHeight(const Offset & touchOffset,RectF & handleRect)5274 void TextPattern::ChangeSecondHandleHeight(const Offset& touchOffset, RectF& handleRect)
5275 {
5276     auto height = handleRect.Height();
5277     CalculateDefaultHandleHeight(height);
5278     bool isTouchHandleCircle = GreatNotEqual(touchOffset.GetY(), handleRect.Bottom());
5279     auto handleOffsetY = isTouchHandleCircle
5280                             ? handleRect.Bottom() - height
5281                             : static_cast<float>(touchOffset.GetY()) - height / 2.0f;
5282     handleRect.SetTop(handleOffsetY);
5283     handleRect.SetHeight(height);
5284 }
5285 
CalculateDefaultHandleHeight(float & height)5286 void TextPattern::CalculateDefaultHandleHeight(float& height)
5287 {
5288     CHECK_NULL_VOID(textStyle_.has_value());
5289 #ifdef ENABLE_ROSEN_BACKEND
5290     MeasureContext content;
5291     content.textContent = "a";
5292     content.fontSize = textStyle_.value().GetFontSize();
5293     auto fontweight = StringUtils::FontWeightToString(textStyle_.value().GetFontWeight());
5294     content.fontWeight = fontweight;
5295     height = std::max(static_cast<float>(RosenRenderCustomPaint::MeasureTextSizeInner(content).Height()), 0.0f);
5296 #endif
5297 }
5298 
DumpAdvanceInfo(std::unique_ptr<JsonValue> & json)5299 void TextPattern::DumpAdvanceInfo(std::unique_ptr<JsonValue>& json)
5300 {
5301     json->Put("contentRect", contentRect_.ToString().c_str());
5302     if (SystemProperties::GetDebugEnabled() && pManager_) {
5303         std::unique_ptr<JsonValue> children = JsonUtil::Create(true);
5304         children->Put("DidExceedMaxLines", std::to_string(pManager_->DidExceedMaxLines()).c_str());
5305         children->Put("GetTextWidth", std::to_string(pManager_->GetTextWidth()).c_str());
5306         children->Put("GetHeight", std::to_string(pManager_->GetHeight()).c_str());
5307         children->Put("GetMaxWidth", std::to_string(pManager_->GetMaxWidth()).c_str());
5308         children->Put("GetMaxIntrinsicWidth", std::to_string(pManager_->GetMaxIntrinsicWidth()).c_str());
5309         children->Put("GetLineCount", std::to_string(pManager_->GetLineCount()).c_str());
5310         children->Put("GetLongestLine", std::to_string(pManager_->GetLongestLine()).c_str());
5311         children->Put("GetLongestLineWithIndent", std::to_string(pManager_->GetLongestLineWithIndent()).c_str());
5312         json->Put("from TextEngine paragraphs_ info", children);
5313     }
5314     json->Put("BindSelectionMenu", std::to_string(selectionMenuMap_.empty()).c_str());
5315     auto host = GetHost();
5316     CHECK_NULL_VOID(host);
5317     auto pipeline = host->GetContext();
5318     CHECK_NULL_VOID(pipeline);
5319     auto fontScale = pipeline->GetFontScale();
5320     auto fontWeightScale = pipeline->GetFontWeightScale();
5321     json->Put("fontScale", std::to_string(fontScale).c_str());
5322     json->Put("fontWeightScale", std::to_string(fontWeightScale).c_str());
5323     auto renderContext = host->GetRenderContext();
5324     CHECK_NULL_VOID(renderContext);
5325     if (renderContext->HasForegroundColor()) {
5326         json->Put("ForegroundColor", renderContext->GetForegroundColorValue().ColorToString().c_str());
5327     }
5328     if (renderContext->GetForegroundColorStrategy().has_value()) {
5329         auto strategy = static_cast<int32_t>(renderContext->GetForegroundColorStrategyValue());
5330         json->Put("ForegroundColorStrategy", strategy);
5331     }
5332 }
5333 
SetTextStyleDumpInfo(std::unique_ptr<JsonValue> & json)5334 void TextPattern::SetTextStyleDumpInfo(std::unique_ptr<JsonValue>& json)
5335 {
5336     if (textStyle_.has_value()) {
5337         json->Put("MaxFontSize", textStyle_->GetAdaptMaxFontSize().ToString().c_str());
5338         json->Put("MinFontSize", textStyle_->GetAdaptMinFontSize().ToString().c_str());
5339         json->Put("FontWeight", StringUtils::ToString(textStyle_->GetFontWeight()).c_str());
5340         json->Put("FontStyle", StringUtils::ToString(textStyle_->GetFontStyle()).c_str());
5341         json->Put("LineHeight", textStyle_->GetLineHeight().ToString().c_str());
5342         json->Put("LineSpacing", textStyle_->GetLineSpacing().ToString().c_str());
5343         json->Put("BaselineOffset", textStyle_->GetBaselineOffset().ToString().c_str());
5344         json->Put("TextIndent", textStyle_->GetTextIndent().ToString().c_str());
5345         json->Put("LetterSpacing", textStyle_->GetLetterSpacing().ToString().c_str());
5346         json->Put("TextOverflow", StringUtils::ToString(textStyle_->GetTextOverflow()).c_str());
5347         json->Put("TextAlign", StringUtils::ToString(textStyle_->GetTextAlign()).c_str());
5348         json->Put("WordBreak", StringUtils::ToString(textStyle_->GetWordBreak()).c_str());
5349         json->Put("TextCase", StringUtils::ToString(textStyle_->GetTextCase()).c_str());
5350         json->Put("EllipsisMode", StringUtils::ToString(textStyle_->GetEllipsisMode()).c_str());
5351         json->Put("LineBreakStrategy", GetLineBreakStrategyInJson(textStyle_->GetLineBreakStrategy()).c_str());
5352     }
5353 }
5354 
DumpInfo(std::unique_ptr<JsonValue> & json)5355 void TextPattern::DumpInfo(std::unique_ptr<JsonValue>& json)
5356 {
5357     auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
5358     CHECK_NULL_VOID(textLayoutProp);
5359     auto nowTime = GetSystemTimestamp();
5360     json->Put("time", std::to_string(nowTime).c_str());
5361     if (!IsSetObscured() && !IsSensitiveEnalbe()) {
5362         json->Put("Content", UtfUtils::Str16DebugToStr8(textLayoutProp->GetContent().value_or(u" ")).c_str());
5363     }
5364     json->Put("ConteFontColornt",
5365         (textStyle_.has_value() ? textStyle_->GetTextColor() : Color::BLACK).ColorToString().c_str());
5366     json->Put(
5367         "FontSize", (textStyle_.has_value() ? textStyle_->GetFontSize() : Dimension(DIMENSION_VALUE, DimensionUnit::FP))
5368                         .ToString()
5369                         .c_str());
5370     SetTextStyleDumpInfo(json);
5371     json->Put("HeightAdaptivePolicy",
5372         V2::ConvertWrapTextHeightAdaptivePolicyToString(
5373             textLayoutProp->GetHeightAdaptivePolicy().value_or(TextHeightAdaptivePolicy::MAX_LINES_FIRST))
5374             .c_str());
5375 
5376     json->Put("Selection", textSelector_.ToString().c_str());
5377 
5378     if (pManager_ && !pManager_->GetParagraphs().empty()) {
5379         auto num = static_cast<int32_t>(pManager_->GetParagraphs().size());
5380         json->Put("Paragraphs num", std::to_string(num).c_str());
5381         json->Put("PaintInfo", paintInfo_.c_str());
5382     }
5383     if (SystemProperties::GetDebugEnabled()) {
5384         DumpAdvanceInfo(json);
5385     }
5386 }
5387 
HasContent()5388 bool TextPattern::HasContent()
5389 {
5390     if (GetTextForDisplay().empty()) {
5391         for (const auto& span : spans_) {
5392             if (span->spanItemType != SpanItemType::NORMAL) {
5393                 return true;
5394             }
5395         }
5396         return false;
5397     }
5398     return true;
5399 }
5400 
SetupMagnifier()5401 void TextPattern::SetupMagnifier()
5402 {
5403     GetOrCreateMagnifier();
5404     CHECK_NULL_VOID(magnifierController_);
5405     auto host = GetHost();
5406     CHECK_NULL_VOID(host);
5407     auto renderContext = host->GetRenderContext();
5408     CHECK_NULL_VOID(renderContext);
5409     if (renderContext->GetClipEdge().value_or(false)) {
5410         return;
5411     }
5412     RectF viewPort;
5413     if (selectOverlay_->GetClipHandleViewPort(viewPort)) {
5414         viewPort.SetHeight(std::min(pManager_->GetHeight(), viewPort.Height()));
5415         magnifierController_->SetHostViewPort(viewPort);
5416     }
5417 }
5418 
DoTextSelectionTouchCancel()5419 void TextPattern::DoTextSelectionTouchCancel()
5420 {
5421     CHECK_NULL_VOID(magnifierController_);
5422     magnifierController_->RemoveMagnifierFrameNode();
5423     ResetSelection();
5424 }
5425 
BeforeSyncGeometryProperties(const DirtySwapConfig & config)5426 void TextPattern::BeforeSyncGeometryProperties(const DirtySwapConfig& config)
5427 {
5428     if (afterLayoutCallback_.has_value()) {
5429         (*afterLayoutCallback_)();
5430     }
5431 }
5432 
GetCaretColor() const5433 std::string TextPattern::GetCaretColor() const
5434 {
5435     auto context = PipelineContext::GetCurrentContextSafelyWithCheck();
5436     CHECK_NULL_RETURN(context, "");
5437     auto theme = context->GetTheme<TextTheme>();
5438     CHECK_NULL_RETURN(theme, "");
5439     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
5440     CHECK_NULL_RETURN(textLayoutProperty, "");
5441     return textLayoutProperty->GetCursorColorValue(theme->GetCaretColor()).ColorToString();
5442 }
5443 
GetSelectedBackgroundColor() const5444 std::string TextPattern::GetSelectedBackgroundColor() const
5445 {
5446     auto context = PipelineContext::GetCurrentContextSafelyWithCheck();
5447     CHECK_NULL_RETURN(context, "");
5448     auto theme = context->GetTheme<TextTheme>();
5449     CHECK_NULL_RETURN(theme, "");
5450     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
5451     CHECK_NULL_RETURN(textLayoutProperty, "");
5452     return textLayoutProperty->GetSelectedBackgroundColorValue(theme->GetSelectedColor()).ColorToString();
5453 }
5454 
OnWindowSizeChanged(int32_t width,int32_t height,WindowSizeChangeReason type)5455 void TextPattern::OnWindowSizeChanged(int32_t width, int32_t height, WindowSizeChangeReason type)
5456 {
5457     CHECK_NULL_VOID(selectOverlay_);
5458     selectOverlay_->UpdateMenuOnWindowSizeChanged(type);
5459 }
5460 
IsLocationInFrameRegion(const Offset & localOffset) const5461 bool TextPattern::IsLocationInFrameRegion(const Offset& localOffset) const
5462 {
5463     auto host = GetHost();
5464     CHECK_NULL_RETURN(host, false);
5465     auto geometryNode = host->GetGeometryNode();
5466     CHECK_NULL_RETURN(geometryNode, false);
5467     auto frameSize = geometryNode->GetFrameSize();
5468     auto frameRect = RectF(OffsetF(0.0f, 0.0f), frameSize);
5469     return frameRect.IsInRegion(PointF(localOffset.GetX(), localOffset.GetY()));
5470 }
5471 } // namespace OHOS::Ace::NG
5472