• 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 <future>
20 #include <iterator>
21 #include <stack>
22 #include <string>
23 
24 #include "adapter/ohos/capability/clipboard/clipboard_impl.h"
25 #include "base/geometry/ng/offset_t.h"
26 #include "base/geometry/ng/point_t.h"
27 #include "base/geometry/ng/rect_t.h"
28 #include "base/geometry/offset.h"
29 #include "base/log/dump_log.h"
30 #include "base/log/log_wrapper.h"
31 #include "base/utils/multi_thread.h"
32 #include "base/utils/string_utils.h"
33 #include "base/utils/utf_helper.h"
34 #include "base/utils/utils.h"
35 #include "base/window/drag_window.h"
36 #include "core/common/ace_engine_ext.h"
37 #include "core/common/ai/data_detector_mgr.h"
38 #include "core/common/container.h"
39 #include "core/common/container_scope.h"
40 #include "core/common/font_manager.h"
41 #include "core/common/recorder/node_data_cache.h"
42 #include "core/common/udmf/udmf_client.h"
43 #include "core/common/vibrator/vibrator_utils.h"
44 #include "core/components/common/properties/text_style_parser.h"
45 #include "core/components_ng/gestures/recognizers/gesture_recognizer.h"
46 #include "core/components_ng/pattern/rich_editor_drag/rich_editor_drag_pattern.h"
47 #include "core/components_ng/pattern/text/text_styles.h"
48 #include "core/text/html_utils.h"
49 #include "core/components_ng/pattern/text/paragraph_util.h"
50 #include "core/text/text_emoji_processor.h"
51 #include "core/components_ng/render/render_property.h"
52 #ifdef ENABLE_ROSEN_BACKEND
53 #include "core/components/custom_paint/rosen_render_custom_paint.h"
54 #include "render_service_client/core/ui/rs_ui_director.h"
55 #endif
56 
57 namespace OHOS::Ace::NG {
58 namespace {
59 constexpr double DIMENSION_VALUE = 16.0;
60 constexpr char COPY[] = "copy";
61 constexpr char SELECT_TEXT[] = "selectText";
62 constexpr const char SYMBOL_COLOR[] = "BLACK";
63 constexpr int32_t API_PROTEXTION_GREATER_NINE = 9;
64 const std::u16string SYMBOL_TRANS = u"\uF0001";
65 const std::u16string WIDE_NEWLINE = u"\n";
66 constexpr float RICH_DEFAULT_SHADOW_COLOR = 0x33000000;
67 constexpr float RICH_DEFAULT_ELEVATION = 120.0f;
68 constexpr Dimension CLICK_THRESHOLD = 5.0_vp;
69 const OffsetF DEFAULT_NEGATIVE_CARET_OFFSET {-1.0f, -1.0f};
70 constexpr int MAX_SELECTED_AI_ENTITY = 1;
71 constexpr int32_t PREVIEW_MENU_DELAY = 600;
72 constexpr int32_t DRAG_NODE_HIDE = 300;
73 
74 const std::unordered_map<TextDataDetectType, std::string> TEXT_DETECT_MAP = {
75     { TextDataDetectType::PHONE_NUMBER, "phoneNum" }, { TextDataDetectType::URL, "url" },
76     { TextDataDetectType::EMAIL, "email" }, { TextDataDetectType::ADDRESS, "location" },
77     { TextDataDetectType::DATE_TIME, "datetime" }
78 };
79 
IsJumpLink(const std::string & content)80 bool IsJumpLink(const std::string& content)
81 {
82     // start with http:// or https://
83     std::regex pattern(R"(https?://[^\s]+)");
84     return std::regex_match(content, pattern);
85 }
86 }; // namespace
87 
~TextPattern()88 TextPattern::~TextPattern()
89 {
90     // node destruct, need to stop text race animation
91     CHECK_NULL_VOID(contentMod_);
92     contentMod_->StopTextRace();
93 }
94 
OnWindowHide()95 void TextPattern::OnWindowHide()
96 {
97     if (magnifierController_) {
98         magnifierController_->RemoveMagnifierFrameNode();
99     }
100     CHECK_NULL_VOID(contentMod_);
101     contentMod_->PauseAnimation();
102     auto host = GetHost();
103     CHECK_NULL_VOID(host);
104     TAG_LOGD(AceLogTag::ACE_TEXT, "OnWindowHide [%{public}d]", host->GetId());
105     PauseSymbolAnimation();
106 }
107 
OnWindowShow()108 void TextPattern::OnWindowShow()
109 {
110     CHECK_NULL_VOID(contentMod_);
111     contentMod_->ResumeAnimation();
112     auto host = GetHost();
113     CHECK_NULL_VOID(host);
114     TAG_LOGD(AceLogTag::ACE_TEXT, "OnWindowShow [%{public}d]", host->GetId());
115     ResumeSymbolAnimation();
116 }
117 
OnAttachToFrameNode()118 void TextPattern::OnAttachToFrameNode()
119 {
120     auto host = GetHost();
121     THREAD_SAFE_NODE_CHECK(host, OnAttachToFrameNode);  // call OnAttachToFrameNodeMultiThread() by multi thread
122     CHECK_NULL_VOID(host);
123     auto pipeline = host->GetContext();
124     CHECK_NULL_VOID(pipeline);
125     pipeline_ = pipeline;
126     auto fontManager = pipeline->GetFontManager();
127     if (fontManager) {
128         fontManager->AddFontNodeNG(host);
129     }
130     if (host->LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
131         if (pipeline->GetMinPlatformVersion() > API_PROTEXTION_GREATER_NINE) {
132             host->GetRenderContext()->UpdateClipEdge(true);
133             host->GetRenderContext()->SetClipToFrame(true);
134         }
135     }
136     InitSurfaceChangedCallback();
137     InitSurfacePositionChangedCallback();
138     pipeline->AddWindowStateChangedCallback(host->GetId());
139     pipeline->AddWindowSizeChangeCallback(host->GetId());
140     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
141     CHECK_NULL_VOID(textLayoutProperty);
142     auto theme = pipeline->GetTheme<TextTheme>();
143     CHECK_NULL_VOID(theme);
144     textLayoutProperty->UpdateTextAlign(theme->GetTextStyle().GetTextAlign());
145     textLayoutProperty->UpdateAlignment(Alignment::CENTER_LEFT);
146 }
147 
OnDetachFromFrameNode(FrameNode * node)148 void TextPattern::OnDetachFromFrameNode(FrameNode* node)
149 {
150     // call OnDetachFromFrameNodeMultiThread() by multi thread
151     THREAD_SAFE_NODE_CHECK(node, OnDetachFromFrameNode, node);
152     if (dataDetectorAdapter_) {
153         dataDetectorAdapter_->aiDetectDelayTask_.Cancel();
154     }
155     selectOverlay_->CloseOverlay(false, CloseReason::CLOSE_REASON_NORMAL);
156     auto pipeline = pipeline_.Upgrade();
157     CHECK_NULL_VOID(pipeline);
158     if (HasSurfaceChangedCallback()) {
159         pipeline->UnregisterSurfaceChangedCallback(surfaceChangedCallbackId_.value_or(-1));
160     }
161     if (HasSurfacePositionChangedCallback()) {
162         pipeline->UnregisterSurfacePositionChangedCallback(surfacePositionChangedCallbackId_.value_or(-1));
163     }
164     auto frameNode = WeakClaim(node);
165     pipeline->RemoveFontNodeNG(frameNode);
166     auto fontManager = pipeline->GetFontManager();
167     if (fontManager) {
168         fontManager->UnRegisterCallbackNG(frameNode);
169         fontManager->RemoveVariationNodeNG(frameNode);
170 #ifdef ENABLE_ROSEN_BACKEND
171         if (Rosen::RSUIDirector::IsHybridRenderEnabled()) {
172             fontManager->RemoveHybridRenderNode(frameNode);
173         }
174 #endif
175     }
176     pipeline->RemoveOnAreaChangeNode(node->GetId());
177     pipeline->RemoveWindowStateChangedCallback(node->GetId());
178     pipeline->RemoveVisibleAreaChangeNode(node->GetId());
179     pipeline->RemoveWindowSizeChangeCallback(node->GetId());
180     RemoveFormVisibleChangeCallback(node->GetId());
181 }
182 
OnAttachToMainTree()183 void TextPattern::OnAttachToMainTree()
184 {
185     auto host = GetHost();
186     THREAD_SAFE_NODE_CHECK(host, OnAttachToMainTree);  // call OnAttachToMainTreeMultiThread() by multi thread
187     isDetachFromMainTree_ = false;
188 }
189 
OnDetachFromMainTree()190 void TextPattern::OnDetachFromMainTree()
191 {
192     auto host = GetHost();
193     THREAD_SAFE_NODE_CHECK(host, OnDetachFromMainTree);  // call OnDetachFromMainTreeMultiThread() by multi thread
194     isDetachFromMainTree_ = true;
195 }
196 
CloseSelectOverlay()197 void TextPattern::CloseSelectOverlay()
198 {
199     CloseSelectOverlay(false);
200 }
201 
CloseSelectOverlay(bool animation)202 void TextPattern::CloseSelectOverlay(bool animation)
203 {
204     auto host = GetHost();
205     FREE_NODE_CHECK(host, CloseSelectOverlay, animation);  // call CloseSelectOverlayMultiThread() by multi thread
206     // Deprecated use selectOverlay_ instead.
207     if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
208         selectOverlayProxy_->Close(animation);
209         RemoveAreaChangeInner();
210     }
211     selectOverlay_->CloseOverlay(animation, CloseReason::CLOSE_REASON_NORMAL);
212 }
213 
ResetSelection()214 void TextPattern::ResetSelection()
215 {
216     if (textSelector_.IsValid() && !shiftFlag_) {
217         HandleSelectionChange(-1, -1);
218         auto host = GetHost();
219         CHECK_NULL_VOID(host);
220         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
221     }
222 }
223 
GetIndexByOffset(const Offset & pos,int32_t & extend)224 void TextPattern::GetIndexByOffset(const Offset& pos, int32_t& extend)
225 {
226     auto selectionOffset = pos;
227     if (GreatNotEqual(selectionOffset.GetY(), pManager_->GetHeight())) {
228         selectionOffset.SetX(contentRect_.Width());
229         selectionOffset.SetY(pManager_->GetHeight());
230     }
231     extend = pManager_->GetGlyphIndexByCoordinate(selectionOffset, true);
232     if (pManager_->GetParagraphs().size() > 1) {
233         // paragraph may contain only newlines, look forward for non-newlines characters.
234         auto selectRects = pManager_->GetRects(extend, extend + 1);
235         if (selectRects.size() == 1 && NearZero(selectRects.back().Width())) {
236             auto selectStr = GetSelectedText(extend, extend + 1);
237             while (selectStr == u"\n" && extend > 0) {
238                 --extend;
239                 selectStr = GetSelectedText(extend, extend + 1);
240             }
241         }
242     }
243 }
244 
InitSelection(const Offset & pos)245 void TextPattern::InitSelection(const Offset& pos)
246 {
247     CHECK_NULL_VOID(pManager_);
248     int32_t extend;
249     GetIndexByOffset(pos, extend);
250     int32_t start = 0;
251     int32_t end = 0;
252     if (!pManager_->GetWordBoundary(extend, start, end)) {
253         start = extend;
254         end = std::min(static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_,
255             extend + GetGraphemeClusterLength(textForDisplay_, extend));
256     }
257     auto host = GetHost();
258     CHECK_NULL_VOID(host);
259     if (SystemProperties::GetTextTraceEnabled()) {
260         TAG_LOGI(AceLogTag::ACE_TEXT,
261             "InitSelection[id:%{public}d][extend:%{public}d][start:%{public}d][end:%{public}d]", host->GetId(), extend,
262             start, end);
263     }
264     HandleSelectionChange(start, end);
265 }
266 
ResetAISelected(AIResetSelectionReason reason)267 void TextPattern::ResetAISelected(AIResetSelectionReason reason)
268 {
269     textSelector_.ResetAiSelected();
270     if (SystemProperties::GetTextTraceEnabled()) {
271         auto host = GetHost();
272         CHECK_NULL_VOID(host);
273         TAG_LOGI(
274             AceLogTag::ACE_TEXT, "TextPattern::ResetAISelected id:%{public}d reason:%{public}d", host->GetId(), reason);
275     }
276 }
277 
GetOrCreatePreviewMenuController()278 RefPtr<PreviewMenuController> TextPattern::GetOrCreatePreviewMenuController()
279 {
280     if (!previewController_) {
281         previewController_ = AceType::MakeRefPtr<PreviewMenuController>(WeakClaim(this));
282     }
283     return previewController_;
284 }
285 
CanAIEntityDrag()286 bool TextPattern::CanAIEntityDrag()
287 {
288     CHECK_NULL_RETURN(GetDataDetectorAdapter(), false);
289     CHECK_NULL_RETURN(dataDetectorAdapter_->enablePreviewMenu_, false);
290     CHECK_NULL_RETURN(!IsSelected(), false);
291     // 如果已触发拉起预览菜单则,不允许做拖拽
292     if (previewController_ && previewController_->IsPreviewMenuShow()) {
293         return false;
294     }
295     return NeedShowAIDetect();
296 }
297 
CheckAIPreviewMenuEnable()298 bool TextPattern::CheckAIPreviewMenuEnable()
299 {
300     return GetDataDetectorAdapter() && dataDetectorAdapter_->enablePreviewMenu_
301         && NeedShowAIDetect()
302         && IsShowHandle();
303 }
304 
InitAiSelection(const Offset & globalOffset)305 void TextPattern::InitAiSelection(const Offset& globalOffset)
306 {
307     ResetAISelected(AIResetSelectionReason::INIT_SELECTION);
308     CHECK_NULL_VOID(pManager_ && selectOverlay_ && CheckAIPreviewMenuEnable());
309     int32_t extend = 0;
310     auto host = GetHost();
311     CHECK_NULL_VOID(host);
312     auto offset = host->GetPaintRectOffset(false, true);
313     auto localOffset = globalOffset - Offset(offset.GetX(), offset.GetY());
314     if (selectOverlay_->HasRenderTransform()) {
315         localOffset = ConvertGlobalToLocalOffset(globalOffset);
316     }
317     auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
318     Offset textOffset = { localOffset.GetX() - textPaintOffset.GetX(), localOffset.GetY() - textPaintOffset.GetY() };
319     GetIndexByOffset(textOffset, extend);
320     if (IsSelected() && LocalOffsetInRange(localOffset, textSelector_.GetTextStart(), textSelector_.GetTextEnd())) {
321         return;
322     }
323     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
324     CHECK_NULL_VOID(textLayoutProperty);
325     if (textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::ELLIPSIS) {
326         auto range = pManager_->GetEllipsisTextRange();
327         if (LocalOffsetInRange(localOffset, static_cast<int32_t>(range.first), static_cast<int32_t>(range.second))) {
328             return;
329         }
330     }
331     int32_t start = 0;
332     int32_t end = 0;
333     bool isAiSpan = false;
334     if (GetDataDetectorAdapter()) {
335         auto aiSpanIter = dataDetectorAdapter_->aiSpanMap_.upper_bound(extend);
336         if (aiSpanIter != dataDetectorAdapter_->aiSpanMap_.begin()) {
337             --aiSpanIter;
338         }
339         start = aiSpanIter->second.start;
340         end = aiSpanIter->second.end;
341         if (extend >= start && extend < end && LocalOffsetInRange(localOffset, start, end)) {
342             isAiSpan = true;
343         }
344     }
345     if (isAiSpan && start >= 0 && end >= 0 && start < end) {
346         textSelector_.aiStart = start;
347         textSelector_.aiEnd = end;
348     }
349     TAG_LOGI(AceLogTag::ACE_TEXT, "InitAiSelection[id:%{public}d][extend:%{public}d][start:%{public}d][end:%{public}d]",
350         host->GetId(), extend, textSelector_.aiStart.value_or(-1), textSelector_.aiEnd.value_or(-1));
351 }
352 
IsAiSelected()353 bool TextPattern::IsAiSelected()
354 {
355     return textSelector_.aiStart && textSelector_.aiEnd;
356 }
357 
IsPreviewMenuShow()358 bool TextPattern::IsPreviewMenuShow()
359 {
360     CHECK_NULL_RETURN(previewController_, false);
361     return previewController_->IsPreviewMenuShow();
362 }
363 
DragNodeDetachFromParent()364 void TextPattern::DragNodeDetachFromParent()
365 {
366     auto host = GetHost();
367     CHECK_NULL_VOID(host);
368     auto gestureHub = host->GetOrCreateGestureEventHub();
369     CHECK_NULL_VOID(gestureHub);
370     gestureHub->DragNodeDetachFromParent();
371 }
372 
ShowAIEntityMenuForCancel()373 void TextPattern::ShowAIEntityMenuForCancel()
374 {
375     auto host = GetHost();
376     CHECK_NULL_VOID(host);
377     CHECK_NULL_VOID(IsAiSelected() && GetDataDetectorAdapter() && previewController_);
378     auto [start, end] = GetSelectedStartAndEnd();
379     ResetAISelected(AIResetSelectionReason::SHOW_FOR_CANCEL);
380     if (SystemProperties::GetTextTraceEnabled()) {
381         TAG_LOGI(AceLogTag::ACE_TEXT,
382             "TextPattern::ShowAIEntityMenuForCancel id:%{public}d IsPreviewMenuShow:%{public}d start:%{public}d, "
383             "end:%{public}d",
384             host->GetId(), previewController_->IsPreviewMenuShow(), start, end);
385     }
386     // ai预览菜单已显示,长按回落无需再选中
387     if (previewController_->IsPreviewMenuShow()) {
388         return;
389     }
390     auto aiSpan = dataDetectorAdapter_->aiSpanMap_.find(start);
391     if (aiSpan == dataDetectorAdapter_->aiSpanMap_.end()) {
392         return;
393     }
394     HandleSelectionChange(start, end);
395     textResponseType_ = TextResponseType::LONG_PRESS;
396     UpdateSelectionSpanType(start, end);
397     CalculateHandleOffsetAndShowOverlay();
398     ShowSelectOverlay({ .animation = true });
399     TAG_LOGI(AceLogTag::ACE_TEXT,
400         "TextPattern::ShowAIEntityMenuForCancel id:%{public}d IsPreviewMenuShow:%{public}d start:%{public}d, "
401         "end:%{public}d",
402         host->GetId(), previewController_->IsPreviewMenuShow(), start, end);
403     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
404 }
405 
GetSelectedAIData()406 AISpan TextPattern::GetSelectedAIData()
407 {
408     CHECK_NULL_RETURN(IsAiSelected(), AISpan());
409     CHECK_NULL_RETURN(GetDataDetectorAdapter(), AISpan());
410     auto aiSpan = dataDetectorAdapter_->aiSpanMap_.find(textSelector_.aiStart.value());
411     if (aiSpan == dataDetectorAdapter_->aiSpanMap_.end()) {
412         return AISpan();
413     }
414     return aiSpan->second;
415 }
416 
GetPreviewMenuAISpanClickrCallback(const AISpan & aiSpan)417 std::function<void()> TextPattern::GetPreviewMenuAISpanClickrCallback(const AISpan& aiSpan)
418 {
419     return [weak = WeakClaim(this), aiSpan, mainId = Container::CurrentIdSafelyWithCheck()]() {
420         ContainerScope scope(mainId);
421         auto pattern = weak.Upgrade();
422         CHECK_NULL_VOID(pattern);
423         auto dataDetectorAdapter_ = pattern->GetDataDetectorAdapter();
424         CHECK_NULL_VOID(dataDetectorAdapter_);
425         dataDetectorAdapter_->ResponseBestMatchItem(aiSpan);
426     };
427 }
428 
CalcCaretMetricsByPosition(int32_t extent,CaretMetricsF & caretCaretMetric,TextAffinity textAffinity)429 void TextPattern::CalcCaretMetricsByPosition(int32_t extent, CaretMetricsF& caretCaretMetric, TextAffinity textAffinity)
430 {
431     auto host = GetHost();
432     CHECK_NULL_VOID(host);
433     auto rect = host->GetGeometryNode()->GetFrameRect();
434     CHECK_NULL_VOID(pManager_);
435     auto computeSuccess = pManager_->CalcCaretMetricsByPosition(extent, caretCaretMetric, textAffinity);
436     if (!computeSuccess) {
437         caretCaretMetric = CaretMetricsF(OffsetF(0.0f, rect.Height()), 0.0f);
438     }
439 }
440 
CalculateHandleOffsetAndShowOverlay(bool isUsingMouse)441 void TextPattern::CalculateHandleOffsetAndShowOverlay(bool isUsingMouse)
442 {
443     parentGlobalOffset_ = GetParentGlobalOffset();
444     auto textContentGlobalOffset = selectOverlay_->GetHandleGlobalOffset() + contentRect_.GetOffset();
445     auto paragraphPaintOffset = textContentGlobalOffset - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
446 
447     // calculate firstHandleOffset, secondHandleOffset and handlePaintSize
448     CaretMetricsF firstHandleMetrics;
449     CaretMetricsF secondHandleMetrics;
450     CalcCaretMetricsByPosition(textSelector_.baseOffset, firstHandleMetrics, TextAffinity::DOWNSTREAM);
451     CalcCaretMetricsByPosition(textSelector_.destinationOffset, secondHandleMetrics, TextAffinity::UPSTREAM);
452     OffsetF firstHandleOffset = firstHandleMetrics.offset + paragraphPaintOffset;
453     OffsetF secondHandleOffset = secondHandleMetrics.offset + paragraphPaintOffset;
454 
455     textSelector_.selectionBaseOffset = firstHandleOffset;
456     textSelector_.selectionDestinationOffset = secondHandleOffset;
457 
458     RectF firstHandle;
459     firstHandle.SetOffset(firstHandleOffset);
460     firstHandle.SetSize({ SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), firstHandleMetrics.height });
461     firstHandle.SetOffset(OffsetF(firstHandle.GetX() - firstHandle.Width() / 2.0f, firstHandle.GetY()));
462     textSelector_.firstHandle = firstHandle;
463 
464     RectF secondHandle;
465     secondHandle.SetOffset(secondHandleOffset);
466     secondHandle.SetSize({ SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), secondHandleMetrics.height });
467     secondHandle.SetHeight(secondHandleMetrics.height);
468     secondHandle.SetOffset(OffsetF(secondHandle.GetX() - secondHandle.Width() / 2.0f, secondHandle.GetY()));
469     textSelector_.secondHandle = secondHandle;
470 }
471 
GetSpansInfoInStyledString(int32_t start,int32_t end)472 std::list<ResultObject> TextPattern::GetSpansInfoInStyledString(int32_t start, int32_t end)
473 {
474     std::list<ResultObject> resultObjects;
475     int32_t imageIndex = 0;
476     for (const auto& item : spans_) {
477         auto obj = item->GetSpanResultObject(start, end);
478         if (obj.type == SelectSpanType::TYPEIMAGE) {
479             obj.spanPosition.spanIndex = imageIndex;
480             ++imageIndex;
481         }
482         if (obj.isInit) {
483             resultObjects.emplace_back(obj);
484         }
485     }
486     return resultObjects;
487 }
488 
GetSpansInfo(int32_t start,int32_t end,GetSpansMethod method)489 SelectionInfo TextPattern::GetSpansInfo(int32_t start, int32_t end, GetSpansMethod method)
490 {
491     int32_t index = 0;
492     std::int32_t realEnd = 0;
493     std::int32_t realStart = 0;
494     SelectionInfo selection;
495     std::list<ResultObject> resultObjects;
496     auto length = GetTextContentLength();
497     if (method == GetSpansMethod::GETSPANS) {
498         realStart = (start == -1) ? 0 : start;
499         realEnd = (end == -1) ? length : end;
500         if (realStart > realEnd) {
501             std::swap(realStart, realEnd);
502         }
503         realStart = std::max(0, realStart);
504         realEnd = std::min(length, realEnd);
505     } else if (method == GetSpansMethod::ONSELECT) {
506         realEnd = std::min(length, end);
507         realStart = std::min(length, start);
508     }
509     selection.SetSelectionEnd(realEnd);
510     selection.SetSelectionStart(realStart);
511     // Verify that realStart, realEnd, and spans_ are valid
512     if (realStart > length || realEnd < 0 || spans_.empty() || (start > length && end > length) ||
513         (method == GetSpansMethod::ONSELECT && realStart == realEnd)) {
514         selection.SetResultObjectList(resultObjects);
515         return selection;
516     }
517     if (isSpanStringMode_) {
518         auto result = GetSpansInfoInStyledString(realStart, realEnd);
519         selection.SetResultObjectList(result);
520         return selection;
521     }
522     const auto& children = GetAllChildren();
523     for (const auto& uinode : children) {
524         if (uinode->GetTag() == V2::IMAGE_ETS_TAG) {
525             ResultObject resultObject = GetImageResultObject(uinode, index, realStart, realEnd);
526             if (!resultObject.valueString.empty() || resultObject.valuePixelMap) {
527                 resultObjects.emplace_back(resultObject);
528             }
529         } else if (uinode->GetTag() == V2::SPAN_ETS_TAG) {
530             ResultObject resultObject = GetTextResultObject(uinode, index, realStart, realEnd);
531             if (!resultObject.valueString.empty()) {
532                 resultObjects.emplace_back(resultObject);
533             }
534         } else if (uinode->GetTag() == V2::SYMBOL_SPAN_ETS_TAG) {
535             ResultObject resultObject = GetSymbolSpanResultObject(uinode, index, realStart, realEnd);
536             if (!resultObject.valueString.empty()) {
537                 resultObjects.emplace_back(resultObject);
538             }
539         } else if (uinode->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG ||
540             uinode->GetTag() == V2::CUSTOM_SPAN_NODE_ETS_TAG) {
541             ResultObject resultObject = GetBuilderResultObject(uinode, index, realStart, realEnd);
542             if (!resultObject.valueString.empty()) {
543                 resultObjects.emplace_back(resultObject);
544             }
545         }
546         index++;
547     }
548     selection.SetResultObjectList(resultObjects);
549     return selection;
550 }
551 
GetTextContentLength()552 int32_t TextPattern::GetTextContentLength()
553 {
554     if (!spans_.empty()) {
555         return static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_;
556     }
557     return 0;
558 }
559 
StartVibratorByLongPress()560 void TextPattern::StartVibratorByLongPress()
561 {
562     CHECK_NULL_VOID(isEnableHapticFeedback_);
563     VibratorUtils::StartVibraFeedback("longPress.light");
564 }
565 
HandleLongPress(GestureEvent & info)566 void TextPattern::HandleLongPress(GestureEvent& info)
567 {
568     auto host = GetHost();
569     CHECK_NULL_VOID(host);
570     if (SystemProperties::GetTextTraceEnabled()) {
571         ACE_TEXT_SCOPED_TRACE("TextPattern::HandleLongPress[id:%d][isMousePressed:%d]", host->GetId(), isMousePressed_);
572         TAG_LOGI(AceLogTag::ACE_TEXT, "HandleLongPress[id:%{public}d]", host->GetId());
573     }
574     HandleSpanLongPressEvent(info);
575     if (!IsSelectableAndCopy() || isMousePressed_ || selectOverlay_->GetIsHandleDragging()) {
576         return;
577     }
578     auto hub = host->GetOrCreateEventHub<EventHub>();
579     CHECK_NULL_VOID(hub);
580     auto gestureHub = hub->GetOrCreateGestureEventHub();
581     CHECK_NULL_VOID(gestureHub);
582     auto localOffset = info.GetLocalLocation();
583     if (selectOverlay_->HasRenderTransform()) {
584         localOffset = ConvertGlobalToLocalOffset(info.GetGlobalLocation());
585     }
586 
587     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
588     if ((textLayoutProperty && textLayoutProperty->GetMaxLines() != 0) && textForDisplay_.length() != 0) {
589         StartVibratorByLongPress();
590     }
591 
592     if (IsDraggable(localOffset)) {
593         // prevent long press event from being triggered when dragging
594         if (IsAiSelected()) {
595             ResetSelection();
596             CloseSelectOverlay(true);
597             ShowAIEntityPreviewMenuTimer();
598         }
599         gestureHub->SetIsTextDraggable(true);
600         return;
601     }
602     ResetAISelected(AIResetSelectionReason::LONG_PRESS);
603     gestureHub->SetIsTextDraggable(false);
604     auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
605     Offset textOffset = { localOffset.GetX() - textPaintOffset.GetX(), localOffset.GetY() - textPaintOffset.GetY() };
606     InitSelection(textOffset);
607     textResponseType_ = TextResponseType::LONG_PRESS;
608     UpdateSelectionSpanType(std::min(textSelector_.baseOffset, textSelector_.destinationOffset),
609         std::max(textSelector_.baseOffset, textSelector_.destinationOffset));
610     oldSelectedType_ = selectedType_.value_or(TextSpanType::NONE);
611     CalculateHandleOffsetAndShowOverlay();
612     CloseSelectOverlay(true);
613     if (GetOrCreateMagnifier() && HasContent()) {
614         magnifierController_->SetLocalOffset({ localOffset.GetX(), localOffset.GetY() });
615     }
616     StartGestureSelection(textSelector_.GetStart(), textSelector_.GetEnd(), localOffset);
617     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
618 }
619 
ShowAIEntityPreviewMenuTimer()620 void TextPattern::ShowAIEntityPreviewMenuTimer()
621 {
622     CHECK_NULL_VOID(GetOrCreatePreviewMenuController());
623     CHECK_NULL_VOID(dragNode_);
624     auto host = GetHost();
625     CHECK_NULL_VOID(host);
626     auto&& task = [weakPtr = AceType::WeakClaim(this), node = WeakPtr<FrameNode>(host),
627                       mainId = Container::CurrentIdSafelyWithCheck()]() {
628         ContainerScope scope(mainId);
629         auto pattern = weakPtr.Upgrade();
630         CHECK_NULL_VOID(pattern);
631         CHECK_NULL_VOID(pattern->IsAiSelected());
632         auto previewController = pattern->GetOrCreatePreviewMenuController();
633         CHECK_NULL_VOID(previewController);
634         auto dragNode = pattern->MoveDragNode();
635         CHECK_NULL_VOID(dragNode);
636         auto parent = dragNode->GetParent();
637         CHECK_NULL_VOID(parent);
638         pattern->PreviewDragNodeHideAnimation();
639         previewController->BindContextMenu(dragNode);
640     };
641     auto context = host->GetContext();
642     CHECK_NULL_VOID(context);
643     auto taskExecutor = context->GetTaskExecutor();
644     CHECK_NULL_VOID(taskExecutor);
645     taskExecutor->PostDelayedTask(
646         task, TaskExecutor::TaskType::UI, PREVIEW_MENU_DELAY, "ArkShowAIEntityPreviewMenuTimer");
647 }
648 
PreviewDragNodeHideAnimation()649 void TextPattern::PreviewDragNodeHideAnimation()
650 {
651     CHECK_NULL_VOID(dragNode_);
652     auto renderContext = dragNode_->GetRenderContext();
653     CHECK_NULL_VOID(renderContext);
654     renderContext->UpdateOpacity(1.0f);
655     AnimationOption option;
656     option.SetDuration(DRAG_NODE_HIDE);
657     option.SetCurve(Curves::SHARP);
658     AnimationUtils::Animate(
659         option, [renderContext, mainId = Container::CurrentIdSafelyWithCheck()]() {
660             ContainerScope scope(mainId);
661             renderContext->UpdateOpacity(0.0);
662         });
663 }
664 
CreateAIEntityMenu()665 RefPtr<FrameNode> TextPattern::CreateAIEntityMenu()
666 {
667     CHECK_NULL_RETURN(IsAiSelected() && GetDataDetectorAdapter(), nullptr);
668     auto aiSpan = dataDetectorAdapter_->aiSpanMap_.find(textSelector_.aiStart.value());
669     if (aiSpan == dataDetectorAdapter_->aiSpanMap_.end()) {
670         return nullptr;
671     }
672     auto host = GetHost();
673     CHECK_NULL_RETURN(host, nullptr);
674     SetOnClickMenu(aiSpan->second, nullptr, nullptr);
675     auto [isShowCopy, isShowSelectText] = GetCopyAndSelectable();
676     return dataDetectorAdapter_->CreateAIEntityMenu(aiSpan->second, host, { isShowCopy, isShowSelectText });
677 }
678 
ShowShadow(const PointF & textOffset,const Color & color)679 bool TextPattern::ShowShadow(const PointF& textOffset, const Color& color)
680 {
681     CHECK_NULL_RETURN(overlayMod_, false);
682     CHECK_NULL_RETURN(hasUrlSpan_, false);
683     CHECK_NULL_RETURN(!spans_.empty() && pManager_, false);
684     int32_t start = 0;
685     for (const auto& item : spans_) {
686         if (!item) {
687             continue;
688         }
689         auto selectedRects = GetSelectedRects(start, item->position);
690         for (auto&& rect : selectedRects) {
691             if (!rect.IsInRegion(textOffset)) {
692                 continue;
693             }
694             if (!item->urlOnRelease) {
695                 overlayMod_->ClearSelectedForegroundColorAndRects();
696                 MarkDirtySelf();
697                 return false;
698             }
699             auto inter = GetStartAndEnd(start, item);
700             auto rects = GetSelectedRects(inter.first, inter.second);
701             overlayMod_->SetSelectedForegroundColorAndRects(rects, color.GetValue());
702             MarkDirtySelf();
703             return true;
704         }
705         start = item->position;
706     }
707     overlayMod_->ClearSelectedForegroundColorAndRects();
708     MarkDirtySelf();
709     return false;
710 }
711 
GetStartAndEnd(int32_t start,const RefPtr<SpanItem> & spanItem)712 std::pair<int32_t, int32_t> TextPattern::GetStartAndEnd(int32_t start, const RefPtr<SpanItem>& spanItem)
713 {
714     auto spanBases = styledString_->GetSpans(0, styledString_->GetLength(), SpanType::Url);
715     for (const auto& spanBase : spanBases) {
716         if (start >= spanBase->GetStartIndex() && start < spanBase->GetEndIndex()) {
717             return {spanBase->GetStartIndex(), spanBase->GetEndIndex()};
718         }
719     }
720     return {0, 0};
721 }
722 
HandleSpanLongPressEvent(GestureEvent & info)723 void TextPattern::HandleSpanLongPressEvent(GestureEvent& info)
724 {
725     RectF textContentRect = contentRect_;
726     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
727     textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
728 
729     auto localLocation = info.GetLocalLocation();
730     if (selectOverlay_->HasRenderTransform()) {
731         localLocation = ConvertGlobalToLocalOffset(info.GetGlobalLocation());
732     }
733 
734     auto host = GetHost();
735     CHECK_NULL_VOID(host);
736     auto renderContext = host->GetRenderContext();
737     CHECK_NULL_VOID(renderContext);
738     PointF textOffset = { static_cast<float>(localLocation.GetX()) - textContentRect.GetX(),
739         static_cast<float>(localLocation.GetY()) - textContentRect.GetY() };
740     if (renderContext->GetClipEdge().has_value() && !renderContext->GetClipEdge().value() && overlayMod_) {
741         textContentRect = overlayMod_->GetBoundsRect();
742         textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
743     }
744     auto longPressFunc = [](RefPtr<SpanItem> item, GestureEvent& info, const RectF& rect,
745                              const PointF& textOffset) -> bool {
746         if (rect.IsInRegion(textOffset)) {
747             if (item && item->onLongPress) {
748                 item->onLongPress(info);
749             }
750             return true;
751         }
752         return false;
753     };
754 
755     if (textContentRect.IsInRegion(
756         PointF(static_cast<float>(localLocation.GetX()), static_cast<float>(localLocation.GetY()))) &&
757         !spans_.empty() && pManager_) {
758         int32_t start = 0;
759         for (const auto& item : spans_) {
760             if (!item) {
761                 continue;
762             }
763             auto selectedRects = GetSelectedRects(start, item->position);
764             for (auto&& rect : selectedRects) {
765                 CHECK_NULL_VOID(!longPressFunc(item, info, rect, textOffset));
766             }
767             start = item->position;
768         }
769     }
770 }
771 
772 // Deprecated: Use the TextSelectOverlay::OnHandleMove() instead.
773 // It is currently used by RichEditorPattern.
OnHandleMove(const RectF & handleRect,bool isFirstHandle)774 void TextPattern::OnHandleMove(const RectF& handleRect, bool isFirstHandle)
775 {
776     auto host = GetHost();
777     CHECK_NULL_VOID(host);
778     auto textContentGlobalOffset = parentGlobalOffset_ + contentRect_.GetOffset();
779     auto textPaintOffset = textContentGlobalOffset - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
780 
781     auto localOffset = handleRect.GetOffset();
782 
783     auto renderContext = host->GetRenderContext();
784     CHECK_NULL_VOID(renderContext);
785     auto clip = false;
786     if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
787         clip = true;
788     }
789     if (renderContext->GetClipEdge().value_or(clip)) {
790         if (localOffset.GetX() < textContentGlobalOffset.GetX()) {
791             localOffset.SetX(textContentGlobalOffset.GetX());
792         } else if (GreatOrEqual(localOffset.GetX(), textContentGlobalOffset.GetX() + contentRect_.Width())) {
793             localOffset.SetX(textContentGlobalOffset.GetX() + contentRect_.Width());
794         }
795 
796         if (localOffset.GetY() < textContentGlobalOffset.GetY()) {
797             localOffset.SetY(textContentGlobalOffset.GetY());
798         } else if (GreatNotEqual(localOffset.GetY(), textContentGlobalOffset.GetY() + contentRect_.Height())) {
799             localOffset.SetY(textContentGlobalOffset.GetY() + contentRect_.Height());
800         }
801     }
802 
803     localOffset -= textPaintOffset;
804 
805     CHECK_NULL_VOID(pManager_);
806     // the handle position is calculated based on the middle of the handle height.
807     UpdateSelectorOnHandleMove(localOffset, handleRect.Height(), isFirstHandle);
808     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
809 
810     CHECK_NULL_VOID(selectOverlayProxy_);
811     auto start = textSelector_.GetTextStart();
812     auto end = textSelector_.GetTextEnd();
813     selectOverlayProxy_->SetSelectInfo(UtfUtils::Str16DebugToStr8(GetSelectedText(start, end)));
814 }
815 
UpdateSelectorOnHandleMove(const OffsetF & localOffset,float handleHeight,bool isFirstHandle)816 void TextPattern::UpdateSelectorOnHandleMove(const OffsetF& localOffset, float handleHeight, bool isFirstHandle)
817 {
818     if (isFirstHandle) {
819         auto start = GetHandleIndex(Offset(
820             localOffset.GetX(), localOffset.GetY() + (selectOverlayProxy_->IsHandleReverse() ? handleHeight : 0)));
821         HandleSelectionChange(start, textSelector_.destinationOffset);
822     } else {
823         auto end = GetHandleIndex(Offset(localOffset.GetX(),
824             localOffset.GetY() +
825                 (selectOverlayProxy_->IsHandleReverse() || NearEqual(localOffset.GetY(), 0) ? 0 : handleHeight)));
826         HandleSelectionChange(textSelector_.baseOffset, end);
827     }
828 }
829 
IsSelectAll()830 bool TextPattern::IsSelectAll()
831 {
832     return textSelector_.GetTextStart() == 0 &&
833            textSelector_.GetTextEnd() == static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_;
834 }
835 
GetSelectedText(int32_t start,int32_t end,bool includeStartHalf,bool includeEndHalf,bool getSubstrDirectly) const836 std::u16string TextPattern::GetSelectedText(int32_t start, int32_t end, bool includeStartHalf,
837     bool includeEndHalf, bool getSubstrDirectly) const
838 {
839     if (spans_.empty()) {
840         auto min = std::clamp(std::max(std::min(start, end), 0), 0, static_cast<int32_t>(textForDisplay_.length()));
841         auto max = std::clamp(std::min(std::max(start, end), static_cast<int32_t>(textForDisplay_.length())), 0,
842             static_cast<int32_t>(textForDisplay_.length()));
843         if (max - min < 0) {
844             return std::u16string();
845         }
846         if (getSubstrDirectly) {
847             return textForDisplay_.substr(min, max - min);
848         } else {
849             return TextEmojiProcessor::SubU16string(min, max - min, textForDisplay_, includeStartHalf, includeEndHalf);
850         }
851     }
852     std::u16string value;
853     int32_t tag = 0;
854     for (const auto& span : spans_) {
855         if (span->GetSymbolUnicode() != 0) {
856             tag = span->position == -1 ? tag + 1 : span->position;
857             continue;
858         }
859         if (span->position - 1 >= start && span->placeholderIndex == -1 && span->position != -1) {
860             auto wideString = span->GetSpanContent();
861             auto max = std::min(span->position, end);
862             auto min = std::max(start, tag);
863             if (getSubstrDirectly) {
864                 value += wideString.substr(std::clamp((min - tag), 0, static_cast<int32_t>(wideString.length())),
865                     std::clamp((max - min), 0, static_cast<int32_t>(wideString.length())));
866             } else {
867                 value += TextEmojiProcessor::SubU16string(
868                     std::clamp((min - tag), 0, static_cast<int32_t>(wideString.length())),
869                     std::clamp((max - min), 0, static_cast<int32_t>(wideString.length())),
870                     wideString, includeStartHalf, includeEndHalf);
871             }
872         } else if (span->position - 1 >= start && span->position != -1) {
873             // image span or custom span (span->placeholderIndex != -1)
874             value += u" ";
875         }
876         tag = span->position == -1 ? tag + 1 : span->position;
877         if (span->position >= end) {
878             break;
879         }
880     }
881     return value;
882 }
883 
GetSelectedStartAndEnd()884 std::pair<int32_t, int32_t> TextPattern::GetSelectedStartAndEnd()
885 {
886     auto start = IsAiSelected() ? textSelector_.aiStart.value() : textSelector_.GetTextStart();
887     auto end = IsAiSelected() ? textSelector_.aiEnd.value() : textSelector_.GetTextEnd();
888     return std::make_pair(start, end);
889 }
890 
HandleOnCopy()891 void TextPattern::HandleOnCopy()
892 {
893     CHECK_NULL_VOID(clipboard_);
894     CHECK_NULL_VOID(GetDataDetectorAdapter());
895     if (textSelector_.IsValid() && textSelector_.GetTextStart() == textSelector_.GetTextEnd() && !IsAiSelected()) {
896         HandleSelectionChange(-1, -1);
897         return;
898     }
899     auto [start, end] = GetSelectedStartAndEnd();
900     auto value = GetSelectedText(start, end, false, false, true);
901     if (IsSelectableAndCopy() || dataDetectorAdapter_->hasClickedMenuOption_) {
902         if (isSpanStringMode_ && !externalParagraph_) {
903             HandleOnCopySpanString();
904         } else if (!value.empty()) {
905             HandleOnCopyWithoutSpanString(UtfUtils::Str16DebugToStr8(value));
906         }
907     }
908     HiddenMenu();
909     CHECK_NULL_VOID(!value.empty());
910     auto host = GetHost();
911     CHECK_NULL_VOID(host);
912     auto eventHub = host->GetOrCreateEventHub<TextEventHub>();
913     CHECK_NULL_VOID(eventHub);
914     eventHub->FireOnCopy(value);
915 }
916 
HandleAIMenuOption(const std::string & labelInfo)917 void TextPattern::HandleAIMenuOption(const std::string& labelInfo)
918 {
919     // lableInfo can be used for further extension: multiple ai entity in selected range
920     // only support one ai entity's first function now, hence pick begin
921     CHECK_NE_VOID(isShowAIMenuOption_, true);
922     CHECK_NE_VOID(aiMenuOptions_.size(), 1);
923     auto aiSpan = aiMenuOptions_.begin()->second;
924     auto aiEntityType = aiSpan.type;
925     CHECK_NULL_VOID(GetDataDetectorAdapter());
926     auto menuOptionAndActions = dataDetectorAdapter_->textDetectResult_.
927                                 menuOptionAndAction[TEXT_DETECT_MAP.at(aiEntityType)];
928     CHECK_EQUAL_VOID(menuOptionAndActions.empty(), true);
929     HiddenMenu();
930     dataDetectorAdapter_->OnClickAIMenuOption(aiSpan, *menuOptionAndActions.begin(), nullptr);
931 }
932 
HandleOnAskCelia()933 void TextPattern::HandleOnAskCelia()
934 {
935     CHECK_NULL_VOID(IsSelected());
936     CHECK_NULL_VOID(GetDataDetectorAdapter());
937     auto baseOffset = std::min(textSelector_.baseOffset, textSelector_.destinationOffset);
938     auto destinationOffset = std::max(textSelector_.baseOffset, textSelector_.destinationOffset);
939     auto selectedContent = GetSelectedText(baseOffset, destinationOffset, false, false, true);
940     auto menuOptionAndActions = dataDetectorAdapter_->textDetectResult_.
941                                 menuOptionAndAction["askCelia"];
942     CHECK_EQUAL_VOID(menuOptionAndActions.empty(), true);
943     HiddenMenu();
944     AISpan aiSpan{
945         .start = baseOffset,
946         .end = destinationOffset,
947         .content = UtfUtils::Str16DebugToStr8(selectedContent),
948         .type = TextDataDetectType::ASK_CELIA
949     };
950     dataDetectorAdapter_->OnClickAIMenuOption(aiSpan, *menuOptionAndActions.begin(), nullptr);
951 }
952 
GetSpanItemAttributeUseForHtml(NG::FontStyle & fontStyle,NG::TextLineStyle & textLineStyle,const std::optional<TextStyle> & textStyle)953 void TextPattern::GetSpanItemAttributeUseForHtml(NG::FontStyle& fontStyle,
954     NG::TextLineStyle& textLineStyle, const std::optional<TextStyle>& textStyle)
955 {
956     if (!textStyle.has_value()) {
957         return;
958     }
959     fontStyle.UpdateFontSize(textStyle->GetFontSize());
960     fontStyle.UpdateTextColor(textStyle->GetTextColor());
961     fontStyle.UpdateTextShadow(textStyle->GetTextShadows());
962     fontStyle.UpdateItalicFontStyle(textStyle->GetFontStyle());
963     fontStyle.UpdateFontWeight(textStyle->GetFontWeight());
964     fontStyle.UpdateVariableFontWeight(textStyle->GetVariableFontWeight());
965     fontStyle.UpdateEnableVariableFontWeight(textStyle->GetEnableVariableFontWeight());
966     fontStyle.UpdateFontFamily(textStyle->GetFontFamilies());
967     fontStyle.UpdateFontFeature(textStyle->GetFontFeatures());
968     fontStyle.UpdateTextDecoration(textStyle->GetTextDecoration());
969     fontStyle.UpdateTextDecorationColor(textStyle->GetTextDecorationColor());
970     fontStyle.UpdateTextDecorationStyle(textStyle->GetTextDecorationStyle());
971     fontStyle.UpdateTextCase(textStyle->GetTextCase());
972     fontStyle.UpdateAdaptMinFontSize(textStyle->GetAdaptMinFontSize());
973     fontStyle.UpdateAdaptMaxFontSize(textStyle->GetAdaptMaxFontSize());
974     fontStyle.UpdateLetterSpacing(textStyle->GetLetterSpacing());
975     fontStyle.UpdateSymbolColorList(textStyle->GetSymbolColorList());
976     fontStyle.UpdateSymbolType(textStyle->GetSymbolType());
977     textLineStyle.UpdateLineHeight(textStyle->GetLineHeight());
978     textLineStyle.UpdateTextBaseline(textStyle->GetTextBaseline());
979     textLineStyle.UpdateBaselineOffset(textStyle->GetBaselineOffset());
980     textLineStyle.UpdateTextOverflow(textStyle->GetTextOverflow());
981     textLineStyle.UpdateTextAlign(textStyle->GetTextAlign());
982     textLineStyle.UpdateMaxLines(textStyle->GetMaxLines());
983     textLineStyle.UpdateTextIndent(textStyle->GetTextIndent());
984     textLineStyle.UpdateWordBreak(textStyle->GetWordBreak());
985     textLineStyle.UpdateEllipsisMode(textStyle->GetEllipsisMode());
986     textLineStyle.UpdateLineSpacing(textStyle->GetLineSpacing());
987     textLineStyle.UpdateLineBreakStrategy(textStyle->GetLineBreakStrategy());
988     textLineStyle.UpdateHalfLeading(textStyle->GetHalfLeading());
989     textLineStyle.UpdateAllowScale(textStyle->IsAllowScale());
990     textLineStyle.UpdateParagraphSpacing(textStyle->GetParagraphSpacing());
991 }
992 
GetTaskExecutorItem()993 RefPtr<TaskExecutor> TextPattern::GetTaskExecutorItem()
994 {
995     auto host = GetHost();
996     CHECK_NULL_RETURN(host, nullptr);
997     auto pipeline = host->GetContext();
998     CHECK_NULL_RETURN(pipeline, nullptr);
999     return pipeline->GetTaskExecutor();
1000 }
1001 
AsyncHandleOnCopySpanStringHtml(RefPtr<SpanString> & subSpanString)1002 void TextPattern::AsyncHandleOnCopySpanStringHtml(RefPtr<SpanString>& subSpanString)
1003 {
1004     auto taskExecutor = GetTaskExecutorItem();
1005     CHECK_NULL_VOID(taskExecutor);
1006     std::list<RefPtr<SpanItem>> spans = GetSpanSelectedContent();
1007     auto multiTypeRecordImpl = AceType::MakeRefPtr<MultiTypeRecordImpl>();
1008     subSpanString->EncodeTlv(multiTypeRecordImpl->GetSpanStringBuffer());
1009     multiTypeRecordImpl->SetPlainText(subSpanString->GetString());
1010     taskExecutor->PostTask(
1011         [spans, multiTypeRecordImpl, weak = WeakClaim(this), task = WeakClaim(RawPtr(taskExecutor))]() {
1012             CHECK_NULL_VOID(multiTypeRecordImpl);
1013             std::string htmlText = HtmlUtils::ToHtml(spans);
1014             multiTypeRecordImpl->SetHtmlText(htmlText);
1015 
1016             auto uiTaskExecutor = task.Upgrade();
1017             CHECK_NULL_VOID(uiTaskExecutor);
1018             uiTaskExecutor->PostTask(
1019                 [weak, multiTypeRecordImpl]() {
1020                     auto textPattern = weak.Upgrade();
1021                     CHECK_NULL_VOID(textPattern && textPattern->clipboard_);
1022                     RefPtr<PasteDataMix> pasteData = textPattern->clipboard_->CreatePasteDataMix();
1023                     textPattern->clipboard_->AddMultiTypeRecord(pasteData, multiTypeRecordImpl);
1024                     textPattern->clipboard_->SetData(pasteData, textPattern->copyOption_);
1025                 }, TaskExecutor::TaskType::UI, "AsyncHandleOnCopySpanStringHtmlSetClipboardData");
1026         }, TaskExecutor::TaskType::BACKGROUND, "AsyncHandleOnCopySpanStringHtml");
1027 }
1028 
HandleOnCopySpanString()1029 void TextPattern::HandleOnCopySpanString()
1030 {
1031     auto [start, end] = GetSelectedStartAndEnd();
1032     auto subSpanString = styledString_->GetSubSpanString(start, end - start);
1033     subSpanString->isFromStyledStringMode = true;
1034 #if defined(PREVIEW)
1035     clipboard_->SetData(subSpanString->GetString(), copyOption_);
1036     return;
1037 #endif
1038     AsyncHandleOnCopySpanStringHtml(subSpanString);
1039 }
1040 
GetSpanSelectedContent()1041 std::list<RefPtr<SpanItem>> TextPattern::GetSpanSelectedContent()
1042 {
1043     std::list<RefPtr<SpanItem>> spans;
1044     if (!textSelector_.IsValid()) {
1045         return spans;
1046     }
1047     auto selectStart = textSelector_.GetTextStart();
1048     auto selectEnd = textSelector_.GetTextEnd();
1049     int32_t tag = 0;
1050     for (const auto& item : spans_) {
1051         CHECK_NULL_CONTINUE(item);
1052         if (item->GetSymbolUnicode() != 0) {
1053             tag = item->position == -1 ? tag + 1 : item->position;
1054             continue;
1055         }
1056         std::u16string spanSelectedContent;
1057         if (item->position - 1 >= selectStart && item->placeholderIndex == -1 && item->position != -1) {
1058             auto wideString = item->GetSpanContent();
1059             auto max = std::min(item->position, selectEnd);
1060             auto min = std::max(selectStart, tag);
1061             spanSelectedContent = TextEmojiProcessor::SubU16string(
1062                 std::clamp((min - tag), 0, static_cast<int32_t>(wideString.length())),
1063                 std::clamp((max - min), 0, static_cast<int32_t>(wideString.length())),
1064                 wideString, false, false);
1065             auto spanItem = MakeRefPtr<SpanItem>();
1066             NG::FontStyle fontStyle;
1067             NG::TextLineStyle textLineStyle;
1068             GetSpanItemAttributeUseForHtml(fontStyle, textLineStyle, item->GetTextStyle());
1069             spanItem->fontStyle = std::make_unique<FontStyle>(fontStyle);
1070             spanItem->textLineStyle = std::make_unique<TextLineStyle>(textLineStyle);
1071             spanItem->content = spanSelectedContent;
1072             spanItem->spanItemType = item->spanItemType;
1073             spans.emplace_back(spanItem);
1074         } else if (item->position - 1 >= selectStart && item->position != -1) {
1075             spanSelectedContent = u" ";
1076             auto spanItem = item->GetSameStyleSpanItem(true);
1077             spanItem->content = spanSelectedContent;
1078             spanItem->spanItemType = item->spanItemType;
1079             spans.emplace_back(spanItem);
1080         }
1081         tag = item->position == -1 ? tag + 1 : item->position;
1082         if (item->position >= selectEnd) {
1083             break;
1084         }
1085     }
1086     return spans;
1087 }
1088 
AsyncHandleOnCopyWithoutSpanStringHtml(const std::string & pasteData)1089 void TextPattern::AsyncHandleOnCopyWithoutSpanStringHtml(const std::string& pasteData)
1090 {
1091     auto multiTypeRecordImpl = AceType::MakeRefPtr<MultiTypeRecordImpl>();
1092     std::list<RefPtr<SpanItem>> spans;
1093     NG::FontStyle fontStyle;
1094     NG::TextLineStyle textLineStyle;
1095     if (spans_.empty()) {
1096         EncodeTlvNoChild(pasteData, multiTypeRecordImpl->GetSpanStringBuffer());
1097         GetSpanItemAttributeUseForHtml(fontStyle, textLineStyle, textStyle_);
1098     } else {
1099         EncodeTlvSpanItems(pasteData, multiTypeRecordImpl->GetSpanStringBuffer());
1100         spans = GetSpanSelectedContent();
1101     }
1102     auto taskExecutor = GetTaskExecutorItem();
1103     CHECK_NULL_VOID(taskExecutor);
1104     taskExecutor->PostTask(
1105         [pasteData, multiTypeRecordImpl, fontStyle, textLineStyle, spans,
1106             weak = WeakClaim(this), task = WeakClaim(RawPtr(taskExecutor))]() {
1107             auto textPattern = weak.Upgrade();
1108             CHECK_NULL_VOID(textPattern);
1109             CHECK_NULL_VOID(multiTypeRecordImpl);
1110             multiTypeRecordImpl->SetPlainText(pasteData);
1111             std::string htmlText = "";
1112             if (!textPattern->spans_.empty()) {
1113                 htmlText = HtmlUtils::ToHtml(spans);
1114             } else {
1115                 std::u16string content = UtfUtils::Str8DebugToStr16(pasteData);
1116                 htmlText = HtmlUtils::ToHtmlForNormalType(fontStyle, textLineStyle, content);
1117             }
1118             multiTypeRecordImpl->SetHtmlText(htmlText);
1119             auto uiTaskExecutor = task.Upgrade();
1120             CHECK_NULL_VOID(uiTaskExecutor);
1121             uiTaskExecutor->PostTask(
1122                 [weak, multiTypeRecordImpl]() {
1123                     auto textPattern = weak.Upgrade();
1124                     CHECK_NULL_VOID(textPattern && textPattern->clipboard_);
1125                     RefPtr<PasteDataMix> pasteDataMix = textPattern->clipboard_->CreatePasteDataMix();
1126                     textPattern->clipboard_->AddMultiTypeRecord(pasteDataMix, multiTypeRecordImpl);
1127                     textPattern->clipboard_->SetData(pasteDataMix, textPattern->copyOption_);
1128                 }, TaskExecutor::TaskType::UI, "AsyncHandleOnCopyWithoutSpanStringSetClipboardData");
1129         }, TaskExecutor::TaskType::BACKGROUND, "AsyncHandleOnCopyWithoutSpanStringHtml");
1130 }
1131 
HandleOnCopyWithoutSpanString(const std::string & pasteData)1132 void TextPattern::HandleOnCopyWithoutSpanString(const std::string& pasteData)
1133 {
1134 #if defined(PREVIEW)
1135     clipboard_->SetData(pasteData, copyOption_);
1136     return;
1137 #endif
1138     AsyncHandleOnCopyWithoutSpanStringHtml(pasteData);
1139 }
1140 
1141 #define WRITE_TLV_INHERIT(group, name, tag, type, inheritName)   \
1142     if ((group)->Has##name()) {                                  \
1143         TLVUtil::WriteUint8(buff, (tag));                        \
1144         TLVUtil::Write##type(buff, (group)->prop##name.value()); \
1145     } else if (textStyle_.has_value()) {                         \
1146         auto temp##name = textStyle_->Get##inheritName();        \
1147         TLVUtil::WriteUint8(buff, (tag));                        \
1148         TLVUtil::Write##type(buff, temp##name);                  \
1149     }
1150 
1151 #define WRITE_TEXT_STYLE_TLV(group, name, tag, type)                 \
1152     do {                                                             \
1153         if ((group)->Has##name()) {                                  \
1154             TLVUtil::WriteUint8(buff, (tag));                        \
1155             TLVUtil::Write##type(buff, (group)->prop##name.value()); \
1156         }                                                            \
1157     } while (false)
1158 
EncodeTlvNoChild(const std::string & pasteData,std::vector<uint8_t> & buff)1159 void TextPattern::EncodeTlvNoChild(const std::string& pasteData, std::vector<uint8_t>& buff)
1160 {
1161     TLVUtil::WriteUint8(buff, TLV_SPAN_STRING_SPANS);
1162     TLVUtil::WriteInt32(buff, 1);
1163 
1164     TLVUtil::WriteInt32(buff, static_cast<int32_t>(SpanItemType::NORMAL));
1165     TLVUtil::WriteUint8(buff, TLV_SPANITEM_TAG);
1166     TLVUtil::WriteInt32(buff, 0);
1167     TLVUtil::WriteInt32(buff, pasteData.length());
1168     TLVUtil::WriteString(buff, pasteData);
1169     EncodeTlvFontStyleNoChild(buff);
1170     EncodeTlvTextLineStyleNoChild(buff);
1171     TLVUtil::WriteUint8(buff, TLV_SPANITEM_END_TAG);
1172 
1173     TLVUtil::WriteUint8(buff, TLV_SPAN_STRING_CONTENT);
1174     TLVUtil::WriteString(buff, pasteData);
1175     TLVUtil::WriteUint8(buff, TLV_END);
1176 }
1177 
EncodeTlvFontStyleNoChild(std::vector<uint8_t> & buff)1178 void TextPattern::EncodeTlvFontStyleNoChild(std::vector<uint8_t>& buff)
1179 {
1180     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1181     CHECK_NULL_VOID(textLayoutProperty);
1182     auto& fontStyle = textLayoutProperty->GetFontStyle();
1183     CHECK_NULL_VOID(fontStyle);
1184     WRITE_TLV_INHERIT(fontStyle, FontSize, TLV_SPAN_FONT_STYLE_FONTSIZE, Dimension, FontSize);
1185     WRITE_TLV_INHERIT(fontStyle, TextColor, TLV_SPAN_FONT_STYLE_TEXTCOLOR, Color, TextColor);
1186     WRITE_TLV_INHERIT(fontStyle, TextShadow, TLV_SPAN_FONT_STYLE_TEXTSHADOW, TextShadows, TextShadows);
1187     WRITE_TLV_INHERIT(fontStyle, ItalicFontStyle, TLV_SPAN_FONT_STYLE_ITALICFONTSTYLE, FontStyle, FontStyle);
1188     WRITE_TLV_INHERIT(fontStyle, FontWeight, TLV_SPAN_FONT_STYLE_FONTWEIGHT, FontWeight, FontWeight);
1189     WRITE_TLV_INHERIT(fontStyle, FontFamily, TLV_SPAN_FONT_STYLE_FONTFAMILY, FontFamily, FontFamilies);
1190     WRITE_TLV_INHERIT(fontStyle, FontFeature, TLV_SPAN_FONT_STYLE_FONTFEATURE, FontFeature, FontFeatures);
1191     WRITE_TLV_INHERIT(
1192         fontStyle, TextDecorationColor, TLV_SPAN_FONT_STYLE_TEXTDECORATIONCOLOR, Color, TextDecorationColor);
1193     WRITE_TLV_INHERIT(fontStyle, TextDecorationStyle, TLV_SPAN_FONT_STYLE_TEXTDECORATIONSTYLE, TextDecorationStyle,
1194         TextDecorationStyle);
1195     WRITE_TLV_INHERIT(fontStyle, TextCase, TLV_SPAN_FONT_STYLE_TEXTCASE, TextCase, TextCase);
1196     WRITE_TLV_INHERIT(fontStyle, AdaptMinFontSize, TLV_SPAN_FONT_STYLE_ADPATMINFONTSIZE, Dimension, AdaptMinFontSize);
1197     WRITE_TLV_INHERIT(fontStyle, AdaptMaxFontSize, TLV_SPAN_FONT_STYLE_ADPATMAXFONTSIZE, Dimension, AdaptMaxFontSize);
1198     WRITE_TLV_INHERIT(fontStyle, LetterSpacing, TLV_SPAN_FONT_STYLE_LETTERSPACING, Dimension, LetterSpacing);
1199     WRITE_TLV_INHERIT(fontStyle, LineThicknessScale, TLV_SPAN_FONT_STYLE_LineThicknessScale, Float,
1200         LineThicknessScale);
1201     if (fontStyle->HasTextDecoration()) {
1202         TLVUtil::WriteTextDecorations(buff, fontStyle->GetTextDecoration().value());
1203     } else if (textStyle_.has_value()) {
1204         TLVUtil::WriteTextDecorations(buff, textStyle_->GetTextDecoration());
1205     }
1206 }
1207 
EncodeTlvTextLineStyleNoChild(std::vector<uint8_t> & buff)1208 void TextPattern::EncodeTlvTextLineStyleNoChild(std::vector<uint8_t>& buff)
1209 {
1210     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1211     CHECK_NULL_VOID(textLayoutProperty);
1212     auto& textLineStyle = textLayoutProperty->GetTextLineStyle();
1213     CHECK_NULL_VOID(textLineStyle);
1214     WRITE_TLV_INHERIT(textLineStyle, LineHeight, TLV_SPAN_TEXT_LINE_STYLE_LINEHEIGHT, Dimension, LineHeight);
1215     WRITE_TLV_INHERIT(textLineStyle, LineSpacing, TLV_SPAN_TEXT_LINE_STYLE_LINESPACING, Dimension, LineSpacing);
1216     WRITE_TLV_INHERIT(textLineStyle, TextBaseline, TLV_SPAN_TEXT_LINE_STYLE_TEXTBASELINE, TextBaseline, TextBaseline);
1217     WRITE_TLV_INHERIT(textLineStyle, TextOverflow, TLV_SPAN_TEXT_LINE_STYLE_TEXTOVERFLOW, TextOverflow, TextOverflow);
1218     WRITE_TLV_INHERIT(textLineStyle, TextAlign, TLV_SPAN_TEXT_LINE_STYLE_TEXTALIGN, TextAlign, TextAlign);
1219     WRITE_TEXT_STYLE_TLV(textLineStyle, MaxLength, TLV_SPAN_TEXT_LINE_STYLE_MAXLENGTH, Int32);
1220     WRITE_TLV_INHERIT(textLineStyle, MaxLines, TLV_SPAN_TEXT_LINE_STYLE_MAXLINES, Int32, MaxLines);
1221     WRITE_TEXT_STYLE_TLV(
1222         textLineStyle, HeightAdaptivePolicy, TLV_SPAN_TEXT_LINE_STYLE_HEIGHTADAPTIVEPOLICY, TextHeightAdaptivePolicy);
1223     WRITE_TLV_INHERIT(textLineStyle, TextIndent, TLV_SPAN_TEXT_LINE_STYLE_TEXTINDENT, Dimension, TextIndent);
1224     WRITE_TEXT_STYLE_TLV(textLineStyle, LeadingMargin, TLV_SPAN_TEXT_LINE_STYLE_LEADINGMARGIN, LeadingMargin);
1225     WRITE_TLV_INHERIT(textLineStyle, WordBreak, TLV_SPAN_TEXT_LINE_STYLE_WORDBREAK, WordBreak, WordBreak);
1226     WRITE_TLV_INHERIT(textLineStyle, LineBreakStrategy, TLV_SPAN_TEXT_LINE_STYLE_LINEBREAKSTRATEGY, LineBreakStrategy,
1227         LineBreakStrategy);
1228     WRITE_TLV_INHERIT(textLineStyle, EllipsisMode, TLV_SPAN_TEXT_LINE_STYLE_ELLIPSISMODE, EllipsisMode, EllipsisMode);
1229 }
1230 
EncodeTlvSpanItems(const std::string & pasteData,std::vector<uint8_t> & buff)1231 void TextPattern::EncodeTlvSpanItems(const std::string& pasteData, std::vector<uint8_t>& buff)
1232 {
1233     auto [start, end] = GetSelectedStartAndEnd();
1234     std::list<RefPtr<NG::SpanItem>> selectSpanItems;
1235     int32_t ignoreLength = 0;
1236     for (const auto& spanItem : spans_) {
1237         int32_t oldStart = spanItem->position - static_cast<int32_t>(spanItem->length);
1238         int32_t oldEnd = spanItem->position;
1239         if (oldEnd <= start || end <= oldStart) {
1240             continue;
1241         }
1242         if (spanItem->spanItemType == SpanItemType::SYMBOL) {
1243             ignoreLength += static_cast<int32_t>(spanItem->length);
1244             continue;
1245         }
1246         auto spanStart = oldStart <= start ? 0 : oldStart - start;
1247         auto spanEnd = oldEnd < end ? oldEnd - start : end - start;
1248         auto newSpanItem = spanItem->GetSameStyleSpanItem(true);
1249         newSpanItem->interval = { spanStart - ignoreLength, spanEnd - ignoreLength };
1250         newSpanItem->content = spanItem->content
1251                 .substr(std::max(start - oldStart, 0), std::min(end, oldEnd) - std::max(start, oldStart));
1252         selectSpanItems.emplace_back(newSpanItem);
1253     }
1254 
1255     TLVUtil::WriteUint8(buff, TLV_SPAN_STRING_SPANS);
1256     TLVUtil::WriteInt32(buff, selectSpanItems.size());
1257     for (auto it = selectSpanItems.begin(); it != selectSpanItems.end(); ++it) {
1258         auto spanItem = (*it);
1259         if (spanItem->spanItemType == SpanItemType::CustomSpan) {
1260             TLVUtil::WriteInt32(buff, static_cast<int32_t>(SpanItemType::NORMAL));
1261             auto placeHolderSpan = AceType::MakeRefPtr<NG::SpanItem>();
1262             placeHolderSpan->content = u" ";
1263             placeHolderSpan->interval = spanItem->interval;
1264             placeHolderSpan->EncodeTlv(buff);
1265             continue;
1266         }
1267         TLVUtil::WriteInt32(buff, static_cast<int32_t>(spanItem->spanItemType));
1268         spanItem->EncodeTlv(buff);
1269     }
1270     TLVUtil::WriteUint8(buff, TLV_SPAN_STRING_CONTENT);
1271     TLVUtil::WriteString(buff, pasteData);
1272     TLVUtil::WriteUint8(buff, TLV_END);
1273 }
1274 
HiddenMenu()1275 void TextPattern::HiddenMenu()
1276 {
1277     if (IsUsingMouse()) {
1278         CloseSelectOverlay();
1279     } else {
1280         selectOverlay_->HideMenu();
1281     }
1282 }
1283 
SetTextSelection(int32_t selectionStart,int32_t selectionEnd)1284 void TextPattern::SetTextSelection(int32_t selectionStart, int32_t selectionEnd)
1285 {
1286     auto host = GetHost();
1287     // call SetTextSelectionMultiThread() by multi thread
1288     FREE_NODE_CHECK(host, SetTextSelection, selectionStart, selectionEnd);
1289     CHECK_NULL_VOID(host);
1290     if (SystemProperties::GetTextTraceEnabled()) {
1291         ACE_TEXT_SCOPED_TRACE("TextPattern::SetTextSelection[id:%d][selectionStart:%d][selectionStart:%d]",
1292             host->GetId(), selectionStart, selectionEnd);
1293     }
1294     auto eventHub = host->GetOrCreateEventHub<EventHub>();
1295     CHECK_NULL_VOID(eventHub);
1296     auto context = host->GetContext();
1297     if (context) {
1298         context->AddAfterLayoutTask([weak = WeakClaim(this), selectionStart, selectionEnd, eventHub]() {
1299             auto textPattern = weak.Upgrade();
1300             CHECK_NULL_VOID(textPattern);
1301             auto host = textPattern->GetHost();
1302             CHECK_NULL_VOID(host);
1303             auto geometryNode = host->GetGeometryNode();
1304             CHECK_NULL_VOID(geometryNode);
1305             auto frameRect = geometryNode->GetFrameRect();
1306             if (frameRect.IsEmpty()) {
1307                 return;
1308             }
1309             auto textLayoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
1310             CHECK_NULL_VOID(textLayoutProperty);
1311             auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
1312             if (mode == TextSelectableMode::UNSELECTABLE ||
1313                 textLayoutProperty->GetCopyOptionValue(CopyOptions::None) == CopyOptions::None ||
1314                 textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE ||
1315                 textPattern->GetTextEffect()) {
1316                 return;
1317             }
1318             if (!textPattern->IsSetObscured() && eventHub->IsEnabled()) {
1319                 textPattern->ActSetSelection(selectionStart, selectionEnd);
1320             }
1321         });
1322     }
1323     host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
1324 }
1325 
GetRenderContext()1326 RefPtr<RenderContext> TextPattern::GetRenderContext()
1327 {
1328     auto frameNode = GetHost();
1329     CHECK_NULL_RETURN(frameNode, nullptr);
1330     return frameNode->GetRenderContext();
1331 }
1332 
1333 // ret: whether show aiMenuOption
PrepareAIMenuOptions(std::unordered_map<TextDataDetectType,AISpan> & aiMenuOptions)1334 bool TextPattern::PrepareAIMenuOptions(
1335         std::unordered_map<TextDataDetectType, AISpan>& aiMenuOptions)
1336 {
1337     aiMenuOptions.clear();
1338     CHECK_NULL_RETURN(IsSelected(), false);
1339     CHECK_NULL_RETURN(GetDataDetectorAdapter(), false);
1340     int selectedAiEntityNum = 0;
1341     auto baseOffset = std::min(textSelector_.baseOffset, textSelector_.destinationOffset);
1342     auto destinationOffset = std::max(textSelector_.baseOffset, textSelector_.destinationOffset);
1343     auto spanIter = dataDetectorAdapter_->aiSpanMap_.lower_bound(baseOffset);
1344 
1345     for (;spanIter != dataDetectorAdapter_->aiSpanMap_.end(); spanIter++) {
1346         auto aiSpanStart = spanIter->first;
1347         auto aiSpanEnd = spanIter->second.end; // [start, end)
1348         if (aiSpanStart >= baseOffset && aiSpanEnd <= destinationOffset) {
1349             ++selectedAiEntityNum;
1350         } else {
1351             break;
1352         }
1353         if (selectedAiEntityNum > MAX_SELECTED_AI_ENTITY) {
1354             break;
1355         } else { // put ai span functions
1356             aiMenuOptions[spanIter->second.type] = spanIter->second;
1357         }
1358     }
1359     return selectedAiEntityNum == MAX_SELECTED_AI_ENTITY;
1360 }
1361 
UpdateAIMenuOptions()1362 void TextPattern::UpdateAIMenuOptions()
1363 {
1364     if ((copyOption_ == CopyOptions::Local || copyOption_ == CopyOptions::Distributed) &&
1365         NeedShowAIDetect()) {
1366         isShowAIMenuOption_ = PrepareAIMenuOptions(aiMenuOptions_);
1367     } else {
1368         isShowAIMenuOption_ = false;
1369     }
1370     if (copyOption_ == CopyOptions::Local || copyOption_ == CopyOptions::Distributed) {
1371         if (NeedShowAIDetect()) {
1372             SetIsAskCeliaEnabled(!isShowAIMenuOption_);
1373         } else {
1374             SetIsAskCeliaEnabled(true);
1375         }
1376     } else {
1377         SetIsAskCeliaEnabled(false);
1378     }
1379     if (!IsSupportAskCelia()) {
1380         SetIsAskCeliaEnabled(false);
1381     }
1382     CHECK_NULL_VOID(GetDataDetectorAdapter());
1383     if (isAskCeliaEnabled_ && !NeedShowAIDetect() &&
1384         dataDetectorAdapter_->textDetectResult_.menuOptionAndAction.empty()) {
1385         dataDetectorAdapter_->GetAIEntityMenu();
1386     }
1387 }
1388 
ProcessOverlay(const OverlayRequest & request)1389 void TextPattern::ProcessOverlay(const OverlayRequest& request)
1390 {
1391     selectOverlay_->ProcessOverlay(request);
1392 }
1393 
ShowSelectOverlay(const OverlayRequest & request)1394 void TextPattern::ShowSelectOverlay(const OverlayRequest& request)
1395 {
1396     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1397     CHECK_NULL_VOID(textLayoutProperty);
1398     if (textLayoutProperty->GetMaxLines() == 0) {
1399         CloseSelectOverlay();
1400         ResetSelection();
1401         return;
1402     }
1403     UpdateAIMenuOptions();
1404     ProcessOverlay(request);
1405 }
1406 
HandleOnSelectAll()1407 void TextPattern::HandleOnSelectAll()
1408 {
1409     auto textSize = static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_;
1410     HandleSelectionChange(0, textSize);
1411     CalculateHandleOffsetAndShowOverlay();
1412     CloseSelectOverlay(true);
1413     if (IsUsingMouse()) {
1414         if (IsSelected()) {
1415             selectOverlay_->SetSelectionHoldCallback();
1416         }
1417     } else {
1418         ShowSelectOverlay({ .animation = true });
1419     }
1420     auto host = GetHost();
1421     CHECK_NULL_VOID(host);
1422     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1423     ResetOriginCaretPosition();
1424 }
1425 
IsShowTranslate()1426 bool TextPattern::IsShowTranslate()
1427 {
1428     auto host = GetHost();
1429     CHECK_NULL_RETURN(host, false);
1430     auto context = host->GetContext();
1431     CHECK_NULL_RETURN(context, false);
1432     auto textTheme = context->GetTheme<TextTheme>();
1433     CHECK_NULL_RETURN(textTheme, false);
1434     return textTheme->IsShowTranslate();
1435 }
1436 
IsShowSearch()1437 bool TextPattern::IsShowSearch()
1438 {
1439     auto container = Container::Current();
1440     if (container && container->IsSceneBoardWindow()) {
1441         return false;
1442     }
1443     auto host = GetHost();
1444     CHECK_NULL_RETURN(host, false);
1445     auto context = host->GetContext();
1446     CHECK_NULL_RETURN(context, false);
1447     auto textTheme = context->GetTheme<TextTheme>();
1448     CHECK_NULL_RETURN(textTheme, false);
1449     return textTheme->IsShowSearch();
1450 }
1451 
IsSupportAskCelia()1452 bool TextPattern::IsSupportAskCelia()
1453 {
1454     auto host = GetHost();
1455     CHECK_NULL_RETURN(host, false);
1456     auto context = host->GetContext();
1457     CHECK_NULL_RETURN(context, false);
1458     auto textTheme = context->GetTheme<TextTheme>();
1459     CHECK_NULL_RETURN(textTheme, false);
1460     return textTheme->IsSupportAskCelia();
1461 }
1462 
InitLongPressEvent(const RefPtr<GestureEventHub> & gestureHub)1463 void TextPattern::InitLongPressEvent(const RefPtr<GestureEventHub>& gestureHub)
1464 {
1465     constexpr int32_t longPressDelay = 600;
1466     if (longPressEvent_) {
1467         gestureHub->SetLongPressEvent(longPressEvent_, false, false, longPressDelay);
1468         return;
1469     }
1470     auto longPressCallback = [weak = WeakClaim(this)](GestureEvent& info) {
1471         auto pattern = weak.Upgrade();
1472         CHECK_NULL_VOID(pattern);
1473         pattern->sourceType_ = info.GetSourceDevice();
1474         pattern->HandleLongPress(info);
1475     };
1476     longPressEvent_ = MakeRefPtr<LongPressEvent>(std::move(longPressCallback));
1477 
1478     // Default time is 500, used by drag event. Drag event would trigger if text is selected, but we want
1479     // it to only trigger on the second long press, after selection. Therefore, long press delay of Selection needs to
1480     // be slightly longer to ensure that order.
1481     gestureHub->SetLongPressEvent(longPressEvent_, false, false, longPressDelay);
1482 
1483     auto onTextSelectorChange = [weak = WeakClaim(this)]() {
1484         auto pattern = weak.Upgrade();
1485         CHECK_NULL_VOID(pattern);
1486         auto frameNode = pattern->GetHost();
1487         CHECK_NULL_VOID(frameNode);
1488         frameNode->OnAccessibilityEvent(AccessibilityEventType::TEXT_SELECTION_UPDATE);
1489     };
1490     textSelector_.SetOnAccessibility(std::move(onTextSelectorChange));
1491 }
1492 
OnHandleTouchUp()1493 void TextPattern::OnHandleTouchUp()
1494 {
1495     CloseSelectOverlay();
1496     ResetSelection();
1497 }
1498 
HandleClickEvent(GestureEvent & info)1499 void TextPattern::HandleClickEvent(GestureEvent& info)
1500 {
1501     CreateMultipleClickRecognizer();
1502     CHECK_NULL_VOID(GetDataDetectorAdapter());
1503     ResetAISelected(AIResetSelectionReason::CLICK);
1504     if ((selectOverlay_->IsClickAtHandle(info) && !multipleClickRecognizer_->IsRunning()) ||
1505         selectOverlay_->GetIsHandleDragging()) {
1506         return;
1507     }
1508     if (dataDetectorAdapter_->hasClickedAISpan_) {
1509         dataDetectorAdapter_->hasClickedAISpan_ = false;
1510     }
1511     multipleClickRecognizer_->Start(info);
1512     if (multipleClickRecognizer_->IsDoubleClick()) {
1513         HandleDoubleClickEvent(info);
1514     } else {
1515         HandleSingleClickEvent(info);
1516     }
1517 }
1518 
HandleUrlClick()1519 bool TextPattern::HandleUrlClick()
1520 {
1521     if (LessNotEqual(clickedSpanPosition_, 0)) {
1522         return false;
1523     }
1524     auto iter = spans_.begin();
1525     std::advance(iter, clickedSpanPosition_);
1526     RefPtr<SpanItem> span;
1527     if (iter == spans_.end()) {
1528         span = spans_.back();
1529     } else {
1530         span = *iter;
1531     }
1532     if (span && span->urlOnRelease) {
1533         span->urlOnRelease();
1534         return true;
1535     }
1536     return false;
1537 }
1538 
HandleSingleClickEvent(GestureEvent & info)1539 void TextPattern::HandleSingleClickEvent(GestureEvent& info)
1540 {
1541     auto host = GetHost();
1542     CHECK_NULL_VOID(host);
1543     RectF textContentRect = contentRect_;
1544     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
1545     textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
1546     PointF textOffset = { info.GetLocalLocation().GetX() - textContentRect.GetX(),
1547         info.GetLocalLocation().GetY() - textContentRect.GetY() };
1548 
1549     if (IsUsingMouse() && isMousePressed_ && leftMousePressed_ && moveOverClickThreshold_) {
1550         TAG_LOGI(ACE_TEXT, "not trigger click after mouse move");
1551         moveOverClickThreshold_ = false;
1552         return;
1553     }
1554 
1555     CheckClickedOnSpanOrText(textContentRect, info.GetLocalLocation());
1556     if (SystemProperties::GetTextTraceEnabled()) {
1557         ACE_TEXT_SCOPED_TRACE(
1558             "TextPattern::HandleSingleClickEvent id:%d clickedSpanPosition:%d", host->GetId(), clickedSpanPosition_);
1559     }
1560     if (HandleUrlClick()) {
1561         return;
1562     }
1563     if (selectOverlay_->SelectOverlayIsOn() && !selectOverlay_->IsUsingMouse() &&
1564         GlobalOffsetInSelectedArea(info.GetGlobalLocation())) {
1565         if (!IsLocationInFrameRegion(info.GetLocalLocation())) {
1566             return;
1567         }
1568         selectOverlay_->SwitchToOverlayMode();
1569         selectOverlay_->ToggleMenu();
1570         return;
1571     }
1572     if (!isMousePressed_ && !isTryEntityDragging_) {
1573         HandleClickAISpanEvent(textOffset);
1574     }
1575     if (GetDataDetectorAdapter()->hasClickedAISpan_) {
1576         selectOverlay_->HideMenu();
1577         return;
1578     }
1579     HandleClickOnTextAndSpan(info);
1580 }
1581 
HandleClickOnTextAndSpan(GestureEvent & info)1582 void TextPattern::HandleClickOnTextAndSpan(GestureEvent& info)
1583 {
1584     if (textSelector_.IsValid() && mouseStatus_ != MouseStatus::MOVE && !isMousePressed_) {
1585         CloseSelectOverlay(true);
1586         ResetSelection();
1587     }
1588     if (clickedSpanPosition_ == -1) {
1589         ActTextOnClick(info);
1590         return;
1591     }
1592     auto iter = spans_.begin();
1593     std::advance(iter, clickedSpanPosition_);
1594     RefPtr<SpanItem> span;
1595     if (iter == spans_.end()) {
1596         span = spans_.back();
1597     } else {
1598         span = *iter;
1599     }
1600     if (span && span->onClick) {
1601         GestureEvent spanClickinfo = info;
1602         EventTarget target = info.GetTarget();
1603         target.area.SetWidth(Dimension(0.0f));
1604         target.area.SetHeight(Dimension(0.0f));
1605         spanClickinfo.SetTarget(target);
1606         if (!TryLinkJump(span)) {
1607             span->onClick(spanClickinfo);
1608             // todo: RecordSpanClickEvent
1609         }
1610     } else {
1611         ActTextOnClick(info);
1612     }
1613 }
1614 
1615 // return: whether execute link jump callback
TryLinkJump(const RefPtr<SpanItem> & span)1616 bool TextPattern::TryLinkJump(const RefPtr<SpanItem>& span)
1617 {
1618     auto host = GetHost();
1619     CHECK_NULL_RETURN(host, false);
1620     auto pipelineContext = host->GetContext();
1621     CHECK_NULL_RETURN(pipelineContext, false);
1622 
1623     bool isCloudConfOpen = pipelineContext->GetIsLinkJumpOpen();
1624     if (isCloudConfOpen) {
1625         std::string spanContent = UtfUtils::Str16DebugToStr8(span->GetSpanContent()); // change for u16string
1626         auto isJumpLink = IsJumpLink(spanContent);
1627         TAG_LOGI(AceLogTag::ACE_TEXT, "TextPattern::TryLinkJump, spanContentLen: %{public}zu, isJumpLink: %{public}d",
1628             spanContent.size(), isJumpLink);
1629         if (isJumpLink) {
1630             pipelineContext->ExecuteLinkJumpCallback(spanContent);
1631             // todo: RecordSpanClickEvent
1632             return true;
1633         }
1634     }
1635     return false;
1636 }
1637 
ActTextOnClick(GestureEvent & info)1638 void TextPattern::ActTextOnClick(GestureEvent& info)
1639 {
1640     auto host = GetHost();
1641     CHECK_NULL_VOID(host);
1642     if (SystemProperties::GetTextTraceEnabled()) {
1643         ACE_TEXT_SCOPED_TRACE("TextPattern::ActTextOnClick id:%d", host->GetId());
1644     }
1645     if (onClick_) {
1646         auto onClick = onClick_;
1647         onClick(info);
1648     }
1649 }
1650 
GlobalOffsetInSelectedArea(const Offset & globalOffset)1651 bool TextPattern::GlobalOffsetInSelectedArea(const Offset& globalOffset)
1652 {
1653     auto host = GetHost();
1654     CHECK_NULL_RETURN(host, false);
1655     auto offset = host->GetPaintRectOffset(false, true);
1656     auto localOffset = globalOffset - Offset(offset.GetX(), offset.GetY());
1657     if (selectOverlay_->HasRenderTransform()) {
1658         localOffset = ConvertGlobalToLocalOffset(globalOffset);
1659     }
1660     return LocalOffsetInSelectedArea(localOffset);
1661 }
1662 
LocalOffsetInSelectedArea(const Offset & localOffset)1663 bool TextPattern::LocalOffsetInSelectedArea(const Offset& localOffset)
1664 {
1665     auto [start, end] = GetSelectedStartAndEnd();
1666     if (IsSelectableAndCopy() && GreatNotEqual(end, start)) {
1667         // Determine if the pan location is in the selected area
1668         return LocalOffsetInRange(localOffset, start, end);
1669     }
1670     return false;
1671 }
1672 
LocalOffsetInRange(const Offset & localOffset,int32_t start,int32_t end)1673 bool TextPattern::LocalOffsetInRange(const Offset& localOffset, int32_t start, int32_t end)
1674 {
1675     auto selectedRects = pManager_->GetRects(start, end);
1676     TextBase::CalculateSelectedRect(selectedRects, contentRect_.Width());
1677     auto panOffset = OffsetF(localOffset.GetX(), localOffset.GetY()) - contentRect_.GetOffset() +
1678                         OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
1679     for (const auto& selectedRect : selectedRects) {
1680         if (selectedRect.IsInRegion(PointF(panOffset.GetX(), panOffset.GetY()))) {
1681             return true;
1682         }
1683     }
1684     return false;
1685 }
1686 
HandleClickAISpanEvent(const PointF & textOffset)1687 void TextPattern::HandleClickAISpanEvent(const PointF& textOffset)
1688 {
1689     CHECK_NULL_VOID(GetDataDetectorAdapter());
1690     dataDetectorAdapter_->hasClickedAISpan_ = false;
1691     if (!NeedShowAIDetect() || mouseStatus_ == MouseStatus::MOVE || IsDragging()) {
1692         return;
1693     }
1694 
1695     for (const auto& kv : dataDetectorAdapter_->aiSpanMap_) {
1696         auto& aiSpan = kv.second;
1697         ClickAISpan(textOffset, aiSpan);
1698         if (dataDetectorAdapter_->hasClickedAISpan_) {
1699             return;
1700         }
1701     }
1702 }
1703 
CheckClickedOnSpanOrText(RectF textContentRect,const Offset & localLocation)1704 bool TextPattern::CheckClickedOnSpanOrText(RectF textContentRect, const Offset& localLocation)
1705 {
1706     clickedSpanPosition_ = -1;
1707     auto host = GetHost();
1708     CHECK_NULL_RETURN(host, false);
1709     auto renderContext = host->GetRenderContext();
1710     CHECK_NULL_RETURN(host, false);
1711     PointF textOffset = GetTextOffset(localLocation, textContentRect);
1712     auto clip = false;
1713     if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1714         clip = true;
1715     }
1716     if (renderContext->GetClipEdge().has_value() && !renderContext->GetClipEdge().value_or(clip) && overlayMod_) {
1717         textContentRect = overlayMod_->GetBoundsRect();
1718         textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
1719     }
1720     if (textContentRect.IsInRegion(
1721         PointF(static_cast<float>(localLocation.GetX()), static_cast<float>(localLocation.GetY()))) &&
1722         !spans_.empty() && pManager_) {
1723         if (CalculateClickedSpanPosition(textOffset)) {
1724             return true;
1725         }
1726     }
1727     if (onClick_) {
1728         return true;
1729     }
1730     return false;
1731 }
1732 
GetTextOffset(const Offset & localLocation,const RectF & contentRect)1733 PointF TextPattern::GetTextOffset(const Offset &localLocation, const RectF &contentRect)
1734 {
1735     PointF textOffset = {static_cast<float>(localLocation.GetX()) - contentRect.GetX(),
1736                          static_cast<float>(localLocation.GetY()) - contentRect.GetY()};
1737     return textOffset;
1738 }
1739 
CalculateClickedSpanPosition(const PointF & textOffset)1740 bool TextPattern::CalculateClickedSpanPosition(const PointF& textOffset)
1741 {
1742     int32_t start = 0;
1743     for (const auto& item : spans_) {
1744         clickedSpanPosition_++;
1745         if (!item) {
1746             continue;
1747         }
1748         auto end = isSpanStringMode_ && item->position == -1 ? item->interval.second : item->position;
1749         auto selectedRects = GetSelectedRects(start, end);
1750         start = end;
1751         for (auto&& rect : selectedRects) {
1752             if (!rect.IsInRegion(textOffset)) {
1753                 continue;
1754             }
1755             return CheckAndClick(item);
1756         }
1757     }
1758     clickedSpanPosition_ = -1;
1759     return false;
1760 }
1761 
CheckAndClick(const RefPtr<SpanItem> & item)1762 bool TextPattern::CheckAndClick(const RefPtr<SpanItem>& item)
1763 {
1764     if (item->onClick || item->urlOnRelease) {
1765         return true;
1766     }
1767     clickedSpanPosition_ = -1;
1768     return false;
1769 }
1770 
GetSelectedRects(int32_t start,int32_t end)1771 std::vector<RectF> TextPattern::GetSelectedRects(int32_t start, int32_t end)
1772 {
1773     return pManager_->GetRects(start, end);
1774 }
1775 
ClickAISpan(const PointF & textOffset,const AISpan & aiSpan)1776 bool TextPattern::ClickAISpan(const PointF& textOffset, const AISpan& aiSpan)
1777 {
1778     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1779     CHECK_NULL_RETURN(textLayoutProperty, false);
1780     CHECK_NULL_RETURN(GetDataDetectorAdapter(), false);
1781     int32_t start = aiSpan.start;
1782     int32_t end = aiSpan.end;
1783     if (textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::ELLIPSIS) {
1784         auto range = pManager_->GetEllipsisTextRange();
1785         int32_t ellipsisStart = static_cast<int32_t>(range.first);
1786         int32_t ellipsisEnd = static_cast<int32_t>(range.second);
1787         if (ellipsisStart != -1 && ellipsisEnd > 0 && ellipsisStart < ellipsisEnd) {
1788             if (ellipsisStart <= aiSpan.start && ellipsisEnd >= aiSpan.end) {
1789                 // ellipsisTextRange contains [aispan.start, aispan.end)
1790                 return false;
1791             } else if (ellipsisStart <= aiSpan.start && ellipsisEnd >= aiSpan.start) {
1792                 // ellipsisTextRange covers [aispan.start, ellipsisEnd)
1793                 start = ellipsisEnd;
1794             } else if (ellipsisStart <= aiSpan.end && ellipsisEnd >= aiSpan.end) {
1795                 // ellipsisTextRange covers [ellipsisStart, aiSpan.end);
1796                 end = ellipsisStart;
1797             }
1798         }
1799     }
1800 
1801     auto aiRects = pManager_->GetRects(start, end);
1802     for (auto&& rect : aiRects) {
1803         if (rect.IsInRegion(textOffset)) {
1804             dataDetectorAdapter_->hasClickedAISpan_ = true;
1805             if (leftMousePressed_) {
1806                 dataDetectorAdapter_->ResponseBestMatchItem(aiSpan);
1807                 return true;
1808             }
1809             return ShowAIEntityMenu(aiSpan);
1810         }
1811     }
1812     return false;
1813 }
1814 
InitUrlMouseEvent()1815 void TextPattern::InitUrlMouseEvent()
1816 {
1817     CHECK_NULL_VOID(!urlMouseEventInitialized_);
1818     auto host = GetHost();
1819     CHECK_NULL_VOID(host);
1820     auto eventHub = host->GetOrCreateEventHub<EventHub>();
1821     CHECK_NULL_VOID(eventHub);
1822     auto inputHub = eventHub->GetOrCreateInputEventHub();
1823     CHECK_NULL_VOID(inputHub);
1824     auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
1825         auto pattern = weak.Upgrade();
1826         if (pattern) {
1827             pattern->HandleUrlMouseEvent(info);
1828         }
1829     };
1830     auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
1831     inputHub->AddOnMouseEvent(mouseEvent);
1832     auto mouseHoverTask = [weak = WeakClaim(this)](bool isHover) {
1833         auto pattern = weak.Upgrade();
1834         CHECK_NULL_VOID(pattern);
1835         pattern->URLOnHover(isHover);
1836     };
1837     auto mouseHoverEvent = MakeRefPtr<InputEvent>(std::move(mouseHoverTask));
1838     inputHub->AddOnHoverEvent(mouseHoverEvent);
1839     urlMouseEventInitialized_ = true;
1840 }
1841 
URLOnHover(bool isHover)1842 void TextPattern::URLOnHover(bool isHover)
1843 {
1844     CHECK_NULL_VOID(!isHover);
1845     auto host = GetHost();
1846     CHECK_NULL_VOID(host);
1847     auto nodeId = host->GetId();
1848     auto pipelineContext = host->GetContext();
1849     CHECK_NULL_VOID(pipelineContext);
1850     pipelineContext->ChangeMouseStyle(nodeId, MouseFormat::DEFAULT);
1851     pipelineContext->FreeMouseStyleHoldNode(nodeId);
1852     CHECK_NULL_VOID(overlayMod_);
1853     overlayMod_->ClearSelectedForegroundColorAndRects();
1854     MarkDirtySelf();
1855 }
1856 
HandleUrlMouseEvent(const MouseInfo & info)1857 void TextPattern::HandleUrlMouseEvent(const MouseInfo& info)
1858 {
1859     if (isMousePressed_) {
1860         return;
1861     }
1862     RectF textContentRect = contentRect_;
1863     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
1864     textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
1865     auto localLocation = info.GetLocalLocation();
1866     if (selectOverlay_->HasRenderTransform()) {
1867         localLocation = ConvertGlobalToLocalOffset(info.GetGlobalLocation());
1868     }
1869     auto host = GetHost();
1870     CHECK_NULL_VOID(host);
1871     auto hostId = host->GetId();
1872     auto renderContext = host->GetRenderContext();
1873     CHECK_NULL_VOID(renderContext);
1874     PointF textOffset = { static_cast<float>(localLocation.GetX()) - textContentRect.GetX(),
1875         static_cast<float>(localLocation.GetY()) - textContentRect.GetY() };
1876     auto show = ShowShadow(textOffset, GetUrlHoverColor());
1877     auto pipelineContext = host->GetContext();
1878     CHECK_NULL_VOID(pipelineContext);
1879     if (show) {
1880         pipelineContext->SetMouseStyleHoldNode(hostId);
1881         pipelineContext->ChangeMouseStyle(hostId, MouseFormat::HAND_POINTING);
1882     } else {
1883         pipelineContext->ChangeMouseStyle(hostId, MouseFormat::DEFAULT);
1884         pipelineContext->FreeMouseStyleHoldNode(hostId);
1885     }
1886 }
1887 
HandleUrlTouchEvent(const TouchEventInfo & info)1888 void TextPattern::HandleUrlTouchEvent(const TouchEventInfo& info)
1889 {
1890     CHECK_NULL_VOID(overlayMod_);
1891     CHECK_NULL_VOID(!IsDragging());
1892     if (selectOverlay_->IsTouchAtHandle(info)) {
1893         return;
1894     }
1895     auto touchType = info.GetTouches().front().GetTouchType();
1896     if (touchType != TouchType::DOWN && touchType != TouchType::UP) {
1897         return;
1898     }
1899     if (touchType == TouchType::DOWN) {
1900         RectF textContentRect = contentRect_;
1901         auto touchOffset = info.GetTouches().front().GetLocalLocation();
1902         PointF textOffset = { static_cast<float>(touchOffset.GetX()) - textContentRect.GetX(),
1903             static_cast<float>(touchOffset.GetY()) - textContentRect.GetY() };
1904         ShowShadow(textOffset, GetUrlPressColor());
1905     } else {
1906         overlayMod_->ClearSelectedForegroundColorAndRects();
1907         MarkDirtySelf();
1908     }
1909 }
SetOnClickMenu(const AISpan & aiSpan,const CalculateHandleFunc & calculateHandleFunc,const ShowSelectOverlayFunc & showSelectOverlayFunc)1910 void TextPattern::SetOnClickMenu(const AISpan& aiSpan, const CalculateHandleFunc& calculateHandleFunc,
1911     const ShowSelectOverlayFunc& showSelectOverlayFunc)
1912 
1913 {
1914     CHECK_NULL_VOID(GetDataDetectorAdapter());
1915     dataDetectorAdapter_->onClickMenu_ = [aiSpan, weak = WeakClaim(this), calculateHandleFunc, showSelectOverlayFunc,
1916         mainId = Container::CurrentIdSafelyWithCheck()](
1917                                              const std::string& action) {
1918         ContainerScope scope(mainId);
1919         auto pattern = weak.Upgrade();
1920         CHECK_NULL_VOID(pattern);
1921         pattern->CloseSelectOverlay();
1922         pattern->HandleSelectionChange(aiSpan.start, aiSpan.end);
1923         if (action == COPY) {
1924             pattern->HandleOnCopy();
1925             pattern->ResetSelection();
1926         } else if (action == SELECT_TEXT) {
1927             if (calculateHandleFunc == nullptr) {
1928                 pattern->CalculateHandleOffsetAndShowOverlay();
1929             } else {
1930                 calculateHandleFunc();
1931             }
1932             if (showSelectOverlayFunc == nullptr) {
1933                 pattern->ShowSelectOverlay({ .animation = true });
1934             } else {
1935                 showSelectOverlayFunc(pattern->textSelector_.firstHandle, pattern->textSelector_.secondHandle);
1936             }
1937             auto frameNode = pattern->GetHost();
1938             CHECK_NULL_VOID(frameNode);
1939             frameNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1940         }
1941     };
1942 }
1943 
CalcAIMenuPosition(const AISpan & aiSpan,const CalculateHandleFunc & calculateHandleFunc)1944 RectF TextPattern::CalcAIMenuPosition(const AISpan& aiSpan, const CalculateHandleFunc& calculateHandleFunc)
1945 {
1946     RectF aiRect;
1947     auto host = GetHost();
1948     CHECK_NULL_RETURN(host, aiRect);
1949     auto context = host->GetRenderContext();
1950     CHECK_NULL_RETURN(context, aiRect);
1951     // save information
1952     auto baseOffset = textSelector_.baseOffset;
1953     auto destinationOffset = textSelector_.destinationOffset;
1954     // calculate result
1955     textSelector_.Update(aiSpan.start, aiSpan.end);
1956     if (calculateHandleFunc == nullptr) {
1957         CalculateHandleOffsetAndShowOverlay();
1958     } else {
1959         parentGlobalOffset_ = GetParentGlobalOffset();
1960         calculateHandleFunc();
1961     }
1962     if (textSelector_.firstHandle.Top() != textSelector_.secondHandle.Top()) {
1963         auto top = std::min(textSelector_.firstHandle.Top(), textSelector_.secondHandle.Top());
1964         auto bottom = std::max(textSelector_.firstHandle.Bottom(), textSelector_.secondHandle.Bottom());
1965         auto textContentGlobalOffset = parentGlobalOffset_ + contentRect_.GetOffset();
1966         auto left = textContentGlobalOffset.GetX();
1967         auto right = textContentGlobalOffset.GetX() + contentRect_.Width();
1968         aiRect = RectT(left, top, right - left, bottom - top);
1969         AdjustAIEntityRect(aiRect);
1970     } else {
1971         aiRect = textSelector_.firstHandle.CombineRectT(textSelector_.secondHandle);
1972     }
1973     RectF viewPort;
1974     if (selectOverlay_->GetClipHandleViewPort(viewPort) &&
1975         GreatNotEqual(aiRect.GetY() + aiRect.Height(), viewPort.GetY() + viewPort.Height()) &&
1976         context->GetClipEdge().value_or(false)) {
1977         aiRect = viewPort;
1978     }
1979     // restore textSelector_
1980     textSelector_.Update(baseOffset, destinationOffset);
1981     if (calculateHandleFunc == nullptr) {
1982         CalculateHandleOffsetAndShowOverlay();
1983     } else {
1984         calculateHandleFunc();
1985     }
1986     return aiRect;
1987 }
GetCopyAndSelectable()1988 std::pair<bool, bool> TextPattern::GetCopyAndSelectable()
1989 {
1990     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1991     CHECK_NULL_RETURN(textLayoutProperty, std::make_pair(false, false));
1992     auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
1993     bool isShowCopy = true;
1994     bool isShowSelectText = true;
1995     if (copyOption_ == CopyOptions::None || textEffect_) {
1996         isShowCopy = false;
1997         isShowSelectText = false;
1998     } else if (mode == TextSelectableMode::UNSELECTABLE) {
1999         isShowSelectText = false;
2000     }
2001     return std::make_pair(isShowCopy, isShowSelectText);
2002 }
2003 
ShowAIEntityMenu(const AISpan & aiSpan,const CalculateHandleFunc & calculateHandleFunc,const ShowSelectOverlayFunc & showSelectOverlayFunc)2004 bool TextPattern::ShowAIEntityMenu(const AISpan& aiSpan, const CalculateHandleFunc& calculateHandleFunc,
2005     const ShowSelectOverlayFunc& showSelectOverlayFunc)
2006 {
2007     auto host = GetHost();
2008     CHECK_NULL_RETURN(host, false);
2009     auto context = host->GetContext();
2010     CHECK_NULL_RETURN(context, false);
2011     auto safeAreaManager = context->GetSafeAreaManager();
2012     CHECK_NULL_RETURN(safeAreaManager, false);
2013     SetOnClickMenu(aiSpan, calculateHandleFunc, showSelectOverlayFunc);
2014     RectF aiRect = CalcAIMenuPosition(aiSpan, calculateHandleFunc);
2015     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
2016     CHECK_NULL_RETURN(textLayoutProperty, false);
2017     auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
2018     if (!NearEqual(safeAreaManager->GetKeyboardInset().Length(), 0)
2019         && mode == TextSelectableMode::SELECTABLE_FOCUSABLE) {
2020         aiRect.SetTop(aiRect.GetY() - safeAreaManager->GetKeyboardOffset());
2021     }
2022     auto [isShowCopy, isShowSelectText] = GetCopyAndSelectable();
2023     CHECK_NULL_RETURN(GetDataDetectorAdapter(), false);
2024     return dataDetectorAdapter_->ShowAIEntityMenu(aiSpan, aiRect, host, {isShowCopy, isShowSelectText});
2025 }
2026 
HandleDoubleClickEvent(GestureEvent & info)2027 void TextPattern::HandleDoubleClickEvent(GestureEvent& info)
2028 {
2029     CheckOnClickEvent(info);
2030     auto textSize = static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_;
2031     if (!IsSelectableAndCopy() || (textSize == 0)) {
2032         return;
2033     }
2034     auto host = GetHost();
2035     CHECK_NULL_VOID(host);
2036     auto hub = host->GetOrCreateEventHub<EventHub>();
2037     CHECK_NULL_VOID(hub);
2038     auto gestureHub = hub->GetOrCreateGestureEventHub();
2039     CHECK_NULL_VOID(gestureHub);
2040     isDoubleClick_ = true;
2041     auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
2042     Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
2043         info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
2044     InitSelection(textOffset);
2045     textResponseType_ = TextResponseType::NONE;
2046     UpdateSelectionSpanType(std::min(textSelector_.baseOffset, textSelector_.destinationOffset),
2047         std::max(textSelector_.baseOffset, textSelector_.destinationOffset));
2048     CalculateHandleOffsetAndShowOverlay();
2049     if (!isMousePressed_) {
2050         ShowSelectOverlay({ .animation = true });
2051     }
2052     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2053 }
2054 
CheckOnClickEvent(GestureEvent & info)2055 void TextPattern::CheckOnClickEvent(GestureEvent& info)
2056 {
2057     RectF textContentRect = contentRect_;
2058     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
2059     textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
2060     PointF textOffset = { info.GetLocalLocation().GetX() - textContentRect.GetX(),
2061         info.GetLocalLocation().GetY() - textContentRect.GetY() };
2062     CheckClickedOnSpanOrText(textContentRect, info.GetLocalLocation());
2063     HandleClickOnTextAndSpan(info);
2064 }
2065 
InitClickEvent(const RefPtr<GestureEventHub> & gestureHub)2066 void TextPattern::InitClickEvent(const RefPtr<GestureEventHub>& gestureHub)
2067 {
2068     gestureHub->SetNodeClickDistance(distanceThreshold_);
2069     CHECK_NULL_VOID(!clickEventInitialized_);
2070     CreateMultipleClickRecognizer();
2071     auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
2072         auto pattern = weak.Upgrade();
2073         CHECK_NULL_VOID(pattern);
2074         pattern->sourceType_ = info.GetSourceDevice();
2075         pattern->HandleClickEvent(info);
2076     };
2077     auto clickListener = MakeRefPtr<ClickEvent>(std::move(clickCallback));
2078     clickListener->SetSysJudge([weak = WeakClaim(this)](const RefPtr<GestureInfo>& gestureInfo,
2079                                    const std::shared_ptr<BaseGestureEvent>& info) -> GestureJudgeResult {
2080         auto textPattern = weak.Upgrade();
2081         CHECK_NULL_RETURN(textPattern, GestureJudgeResult::CONTINUE);
2082         if (info->GetFingerList().empty()) {
2083             return GestureJudgeResult::CONTINUE;
2084         }
2085         auto localLocation = info->GetFingerList().begin()->localLocation_;
2086         auto contentRect = textPattern->GetTextContentRect();
2087         auto baselineOffset = textPattern->GetBaselineOffset();
2088 
2089         RectF textContentRect = contentRect;
2090         textContentRect.SetTop(contentRect.GetY() - std::min(baselineOffset, 0.0f));
2091         textContentRect.SetHeight(contentRect.Height() - std::max(baselineOffset, 0.0f));
2092         if (textPattern->GetCopyOptions() == CopyOptions::None && !textPattern->NeedShowAIDetect() &&
2093             !textPattern->CheckClickedOnSpanOrText(textContentRect, localLocation)) {
2094             return GestureJudgeResult::REJECT;
2095         }
2096         return GestureJudgeResult::CONTINUE;
2097     });
2098     gestureHub->AddClickEvent(clickListener);
2099     clickEventInitialized_ = true;
2100 }
2101 
InitAISpanHoverEvent()2102 void TextPattern::InitAISpanHoverEvent()
2103 {
2104     CHECK_NULL_VOID(!aiSpanHoverEventInitialized_);
2105     auto host = GetHost();
2106     CHECK_NULL_VOID(host);
2107     auto eventHub = host->GetOrCreateEventHub<EventHub>();
2108     CHECK_NULL_VOID(eventHub);
2109     auto inputHub = eventHub->GetOrCreateInputEventHub();
2110     CHECK_NULL_VOID(inputHub);
2111 
2112     auto aiSpanHoverTask = [weak = WeakClaim(this)](MouseInfo& info) {
2113         auto pattern = weak.Upgrade();
2114         CHECK_NULL_VOID(pattern);
2115         pattern->HandleAISpanHoverEvent(info);
2116     };
2117     auto aiSpanHoverEvent = MakeRefPtr<InputEvent>(std::move(aiSpanHoverTask));
2118     inputHub->AddOnMouseEvent(aiSpanHoverEvent);
2119     aiSpanHoverEventInitialized_ = true;
2120 }
2121 
HandleAISpanHoverEvent(const MouseInfo & info)2122 void TextPattern::HandleAISpanHoverEvent(const MouseInfo& info)
2123 {
2124     CHECK_NULL_VOID(GetDataDetectorAdapter());
2125     if (info.GetAction() != MouseAction::MOVE || !NeedShowAIDetect() || !isHover_) {
2126         return;
2127     }
2128     if (dataDetectorAdapter_->aiSpanRects_.empty()) {
2129         for (const auto& kv : dataDetectorAdapter_->aiSpanMap_) {
2130             auto& aiSpan = kv.second;
2131             const auto& aiRects = pManager_->GetRects(aiSpan.start, aiSpan.end);
2132             dataDetectorAdapter_->aiSpanRects_.insert(
2133                 dataDetectorAdapter_->aiSpanRects_.end(), aiRects.begin(), aiRects.end());
2134         }
2135     }
2136 
2137     auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
2138     PointF textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
2139         info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
2140     auto host = GetHost();
2141     CHECK_NULL_VOID(host);
2142     auto pipeline = GetContext();
2143     CHECK_NULL_VOID(pipeline);
2144     auto nodeId = host->GetId();
2145     pipeline->SetMouseStyleHoldNode(nodeId);
2146     for (auto&& rect : dataDetectorAdapter_->aiSpanRects_) {
2147         if (!rect.IsInRegion(textOffset)) {
2148             continue;
2149         }
2150         if (currentMouseStyle_ != MouseFormat::HAND_POINTING) {
2151             bool changeSuccess = pipeline->ChangeMouseStyle(nodeId, MouseFormat::HAND_POINTING);
2152             CHECK_NULL_VOID(changeSuccess);
2153             currentMouseStyle_ = MouseFormat::HAND_POINTING;
2154         }
2155         return;
2156     }
2157     if (currentMouseStyle_ != MouseFormat::DEFAULT) {
2158         bool changeSuccess = pipeline->ChangeMouseStyle(nodeId, MouseFormat::DEFAULT);
2159         CHECK_NULL_VOID(changeSuccess);
2160         currentMouseStyle_ = MouseFormat::DEFAULT;
2161     }
2162 }
2163 
OnHover(bool isHover)2164 void TextPattern::OnHover(bool isHover)
2165 {
2166     isHover_ = isHover;
2167     TAG_LOGI(AceLogTag::ACE_TEXT, "isHover=%{public}d", isHover);
2168     auto host = GetHost();
2169     CHECK_NULL_VOID(host);
2170     auto pipeline = GetContext();
2171     CHECK_NULL_VOID(pipeline);
2172     auto nodeId = host->GetId();
2173     if (isHover) {
2174         pipeline->SetMouseStyleHoldNode(nodeId);
2175         pipeline->ChangeMouseStyle(nodeId, MouseFormat::DEFAULT);
2176         currentMouseStyle_ = MouseFormat::DEFAULT;
2177     } else {
2178         pipeline->ChangeMouseStyle(nodeId, MouseFormat::DEFAULT);
2179         currentMouseStyle_ = MouseFormat::DEFAULT;
2180         pipeline->FreeMouseStyleHoldNode(nodeId);
2181     }
2182 }
2183 
InitSpanMouseEvent()2184 void TextPattern::InitSpanMouseEvent()
2185 {
2186     CHECK_NULL_VOID(!spanMouseEventInitialized_);
2187     auto host = GetHost();
2188     CHECK_NULL_VOID(host);
2189     auto eventHub = host->GetEventHub<EventHub>();
2190     CHECK_NULL_VOID(eventHub);
2191     auto inputHub = eventHub->GetOrCreateInputEventHub();
2192     CHECK_NULL_VOID(inputHub);
2193 
2194     auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
2195         auto pattern = weak.Upgrade();
2196         CHECK_NULL_VOID(pattern);
2197         pattern->HandleSpanMouseEvent(info);
2198     };
2199     auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
2200     inputHub->AddOnMouseEvent(mouseEvent);
2201 
2202     auto hoverTask = [weak = WeakClaim(this)](bool isHover, HoverInfo& info) {
2203         TAG_LOGI(AceLogTag::ACE_TEXT, "on hover event isHover=%{public}d", isHover);
2204         auto pattern = weak.Upgrade();
2205         if (pattern) {
2206             if (!isHover) {
2207                 pattern->ExitSpansForOnHoverEvent(info);
2208             }
2209         }
2210     };
2211     auto hoverEvent = MakeRefPtr<InputEvent>(std::move(hoverTask));
2212     inputHub->AddOnHoverEvent(hoverEvent);
2213     spanMouseEventInitialized_ = true;
2214 }
2215 
ConvertHoverInfoFromMouseInfo(const MouseInfo & info) const2216 HoverInfo TextPattern::ConvertHoverInfoFromMouseInfo(const MouseInfo& info) const
2217 {
2218     HoverInfo result;
2219     result.SetGlobalLocation(info.GetGlobalLocation());
2220     result.SetScreenLocation(info.GetScreenLocation());
2221     result.SetLocalLocation(info.GetLocalLocation());
2222     result.SetGlobalDisplayLocation(info.GetGlobalDisplayLocation());
2223     result.SetTimeStamp(info.GetTimeStamp());
2224     result.SetTarget(info.GetTarget());
2225     result.SetDeviceId(info.GetDeviceId());
2226     result.SetTargetDisplayId(info.GetTargetDisplayId());
2227     result.SetSourceDevice(info.GetSourceDevice());
2228     if (info.GetTiltX().has_value()) {
2229         result.SetTiltX(info.GetTiltX().value());
2230     }
2231     if (info.GetTiltY().has_value()) {
2232         result.SetTiltY(info.GetTiltY().value());
2233     }
2234     if (info.GetRollAngle().has_value()) {
2235         result.SetRollAngle(info.GetRollAngle().value());
2236     }
2237     result.SetStopPropagation(info.IsStopPropagation());
2238     result.SetPreventDefault(info.IsPreventDefault());
2239     return result;
2240 }
2241 
HandleSpanMouseEvent(const MouseInfo & info)2242 void TextPattern::HandleSpanMouseEvent(const MouseInfo& info)
2243 {
2244     RectF textContentRect = contentRect_;
2245     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
2246     textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
2247     auto localLocation = info.GetLocalLocation();
2248     if (selectOverlay_->HasRenderTransform()) {
2249         localLocation = ConvertGlobalToLocalOffset(info.GetGlobalLocation());
2250     }
2251     PointF textOffset = { static_cast<float>(localLocation.GetX()) - textContentRect.GetX(),
2252         static_cast<float>(localLocation.GetY()) - textContentRect.GetY() };
2253     TriggerSpansOnHover(ConvertHoverInfoFromMouseInfo(info), textOffset);
2254 }
2255 
TriggerSpanOnHoverEvent(const HoverInfo & info,const RefPtr<SpanItem> & item,bool isOnHover)2256 void TextPattern::TriggerSpanOnHoverEvent(const HoverInfo& info, const RefPtr<SpanItem>& item, bool isOnHover)
2257 {
2258     TAG_LOGI(AceLogTag::ACE_TEXT, "on span hover event isHover=%{public}d", isOnHover);
2259     item->isOnHover = isOnHover;
2260     if (item->onHover) {
2261         item->onHover(isOnHover, const_cast<HoverInfo&>(info));
2262     }
2263 }
2264 
TriggerSpansOnHover(const HoverInfo & info,const PointF & textOffset)2265 void TextPattern::TriggerSpansOnHover(const HoverInfo& info, const PointF& textOffset)
2266 {
2267     CHECK_NULL_VOID(!spans_.empty());
2268     // check exit
2269     RefPtr<SpanItem> exitItem;
2270     RefPtr<SpanItem> enterItem;
2271     for (const auto& item : spans_) {
2272         if (!item || !item->onHover) {
2273             continue;
2274         }
2275         int32_t end = isSpanStringMode_ && item->position == -1 ? item->interval.second : item->position;
2276         int32_t start = end - static_cast<int32_t>(item->content.length());
2277         auto selectedRects = GetSelectedRects(start, end);
2278         bool isOnHover = false;
2279         for (auto&& rect : selectedRects) {
2280             isOnHover = rect.IsInRegion(textOffset);
2281             if (isOnHover) {
2282                 break;
2283             }
2284         }
2285         if (!isOnHover && item->isOnHover != isOnHover) {
2286             exitItem = item;
2287         } else if (isOnHover && item->isOnHover != isOnHover) {
2288             enterItem = item;
2289         }
2290         if (exitItem && enterItem) {
2291             break;
2292         }
2293     }
2294     if (exitItem) {
2295         TriggerSpanOnHoverEvent(info, exitItem, false);
2296     }
2297     if (enterItem) {
2298         TriggerSpanOnHoverEvent(info, enterItem, true);
2299     }
2300 }
2301 
ExitSpansForOnHoverEvent(const HoverInfo & info)2302 void TextPattern::ExitSpansForOnHoverEvent(const HoverInfo& info)
2303 {
2304     CHECK_NULL_VOID(!spans_.empty());
2305     for (const auto& item : spans_) {
2306         if (!item || !item->onHover) {
2307             continue;
2308         }
2309         bool isOnHover = false;
2310         if (item->isOnHover == isOnHover) {
2311             continue;
2312         }
2313         TriggerSpanOnHoverEvent(info, item, isOnHover);
2314         return;
2315     }
2316 }
2317 
HasSpanOnHoverEvent()2318 bool TextPattern::HasSpanOnHoverEvent()
2319 {
2320     CHECK_NULL_RETURN(!spanMouseEventInitialized_, false);
2321     CHECK_NULL_RETURN(!spans_.empty(), false);
2322     for (const auto& item : spans_) {
2323         if (item && item->onHover) {
2324             return true;
2325         }
2326     }
2327     return false;
2328 }
2329 
InitMouseEvent()2330 void TextPattern::InitMouseEvent()
2331 {
2332     CHECK_NULL_VOID(!mouseEventInitialized_);
2333     auto host = GetHost();
2334     CHECK_NULL_VOID(host);
2335     auto eventHub = host->GetOrCreateEventHub<EventHub>();
2336     CHECK_NULL_VOID(eventHub);
2337     auto inputHub = eventHub->GetOrCreateInputEventHub();
2338     CHECK_NULL_VOID(inputHub);
2339 
2340     auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
2341         auto pattern = weak.Upgrade();
2342         CHECK_NULL_VOID(pattern);
2343         pattern->HandleMouseEvent(info);
2344     };
2345     auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
2346     inputHub->AddOnMouseEvent(mouseEvent);
2347 
2348     auto hoverTask = [weak = WeakClaim(this)](bool isHover) {
2349         TAG_LOGI(AceLogTag::ACE_TEXT, "on hover event isHover=%{public}d", isHover);
2350         auto pattern = weak.Upgrade();
2351         if (pattern) {
2352             pattern->OnHover(isHover);
2353         }
2354     };
2355     auto hoverEvent = MakeRefPtr<InputEvent>(std::move(hoverTask));
2356     inputHub->AddOnHoverEvent(hoverEvent);
2357     mouseEventInitialized_ = true;
2358 }
2359 
InitFocusEvent()2360 void TextPattern::InitFocusEvent()
2361 {
2362     CHECK_NULL_VOID(!focusInitialized_);
2363     auto host = GetHost();
2364     auto focusHub = host->GetFocusHub();
2365     CHECK_NULL_VOID(focusHub);
2366     auto focusTask = [weak = WeakClaim(this)](FocusReason reason) {
2367         auto pattern = weak.Upgrade();
2368         CHECK_NULL_VOID(pattern);
2369         auto contentModifier = pattern->GetContentModifier();
2370         CHECK_NULL_VOID(contentModifier);
2371         contentModifier->SetIsFocused(true);
2372         pattern->AddIsFocusActiveUpdateEvent();
2373     };
2374     focusHub->SetOnFocusInternal(focusTask);
2375 
2376     auto blurTask = [weak = WeakClaim(this)]() {
2377         auto pattern = weak.Upgrade();
2378         CHECK_NULL_VOID(pattern);
2379         auto contentModifier = pattern->GetContentModifier();
2380         CHECK_NULL_VOID(contentModifier);
2381         contentModifier->SetIsFocused(false);
2382         pattern->RemoveIsFocusActiveUpdateEvent();
2383         pattern->ResetOriginCaretPosition();
2384     };
2385     focusHub->SetOnBlurInternal(blurTask);
2386 
2387     focusInitialized_ = true;
2388 }
2389 
AddIsFocusActiveUpdateEvent()2390 void TextPattern::AddIsFocusActiveUpdateEvent()
2391 {
2392     if (!isFocusActiveUpdateEvent_) {
2393         isFocusActiveUpdateEvent_ = [weak = WeakClaim(this)](bool isFocusAcitve) {
2394             auto pattern = weak.Upgrade();
2395             CHECK_NULL_VOID(pattern);
2396             pattern->OnIsFocusActiveUpdate(isFocusAcitve);
2397         };
2398     }
2399 
2400     auto pipline = PipelineContext::GetCurrentContextSafelyWithCheck();
2401     CHECK_NULL_VOID(pipline);
2402     pipline->AddIsFocusActiveUpdateEvent(GetHost(), isFocusActiveUpdateEvent_);
2403 }
2404 
RemoveIsFocusActiveUpdateEvent()2405 void TextPattern::RemoveIsFocusActiveUpdateEvent()
2406 {
2407     auto pipline = PipelineContext::GetCurrentContextSafelyWithCheck();
2408     CHECK_NULL_VOID(pipline);
2409     pipline->RemoveIsFocusActiveUpdateEvent(GetHost());
2410 }
2411 
OnIsFocusActiveUpdate(bool isFocusAcitve)2412 void TextPattern::OnIsFocusActiveUpdate(bool isFocusAcitve)
2413 {
2414     auto host = GetHost();
2415     CHECK_NULL_VOID(host);
2416     auto pattern = host->GetPattern<TextPattern>();
2417     CHECK_NULL_VOID(pattern);
2418     auto contentModifier = pattern->GetContentModifier();
2419     CHECK_NULL_VOID(contentModifier);
2420     contentModifier->SetIsFocused(isFocusAcitve);
2421 }
2422 
InitHoverEvent()2423 void TextPattern::InitHoverEvent()
2424 {
2425     CHECK_NULL_VOID(!hoverInitialized_);
2426     auto host = GetHost();
2427     CHECK_NULL_VOID(host);
2428     auto eventHub = host->GetOrCreateEventHub<EventHub>();
2429     CHECK_NULL_VOID(eventHub);
2430     auto inputHub = eventHub->GetOrCreateInputEventHub();
2431     CHECK_NULL_VOID(inputHub);
2432 
2433     auto mouseTask = [weak = WeakClaim(this)](bool isHover) {
2434         auto pattern = weak.Upgrade();
2435         CHECK_NULL_VOID(pattern);
2436         auto contentModifier = pattern->GetContentModifier();
2437         CHECK_NULL_VOID(contentModifier);
2438         contentModifier->SetIsHovered(isHover);
2439     };
2440     auto mouseEvent_ = MakeRefPtr<InputEvent>(std::move(mouseTask));
2441     inputHub->AddOnHoverEvent(mouseEvent_);
2442 
2443     hoverInitialized_ = true;
2444 }
2445 
RecoverCopyOption()2446 void TextPattern::RecoverCopyOption()
2447 {
2448     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
2449     CHECK_NULL_VOID(textLayoutProperty);
2450     auto host = GetHost();
2451     auto contentHost = GetContentHost();
2452     CHECK_NULL_VOID(host && contentHost);
2453 
2454     copyOption_ = textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE
2455                       ? CopyOptions::None
2456                       : textLayoutProperty->GetCopyOption().value_or(CopyOptions::None);
2457 
2458     const auto& children = contentHost->GetChildren();
2459     if (children.empty()) {
2460         if (IsSetObscured() && !isSpanStringMode_) {
2461             copyOption_ = CopyOptions::None;
2462         }
2463     }
2464     auto gestureEventHub = host->GetOrCreateGestureEventHub();
2465     CHECK_NULL_VOID(gestureEventHub);
2466     auto eventHub = host->GetOrCreateEventHub<EventHub>();
2467     CHECK_NULL_VOID(eventHub);
2468     if (copyOption_ == CopyOptions::None && !textDetectEnable_ && !textLayoutProperty->GetTextOverflow() &&
2469         !onClick_ && !longPressEvent_) { // performance prune
2470         if (host->IsDraggable() || gestureEventHub->GetTextDraggable()) {
2471             gestureEventHub->SetTextDraggable(false);
2472             eventHub->SetDefaultOnDragStart(nullptr);
2473             if (!eventHub->HasOnDragStart() && IsTextNode()) {
2474                 gestureEventHub->RemoveDragEvent();
2475             }
2476         }
2477         return;
2478     }
2479     if (copyOption_ == CopyOptions::None) {
2480         CloseSelectOverlay();
2481         ResetSelection();
2482     }
2483     if ((children.empty() || isSpanStringMode_) &&
2484         CanStartAITask() && !GetDataDetectorAdapter()->aiDetectInitialized_) {
2485         dataDetectorAdapter_->textForAI_ = textForDisplay_;
2486         dataDetectorAdapter_->StartAITask();
2487     }
2488     ProcessMarqueeVisibleAreaCallback();
2489     InitCopyOption(gestureEventHub, eventHub);
2490     bool enabledCache = eventHub->IsEnabled();
2491     selectOverlay_->SetMenuTranslateIsSupport(IsShowTranslate());
2492     selectOverlay_->SetIsSupportMenuSearch(IsShowSearch());
2493     selectOverlay_->UpdateHandleColor();
2494     if (textDetectEnable_ && enabledCache != enabled_) {
2495         enabled_ = enabledCache;
2496         host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE);
2497     }
2498 }
2499 
InitCopyOption(const RefPtr<GestureEventHub> & gestureEventHub,const RefPtr<EventHub> & eventHub)2500 void TextPattern::InitCopyOption(const RefPtr<GestureEventHub>& gestureEventHub, const RefPtr<EventHub>& eventHub)
2501 {
2502     CHECK_NULL_VOID(gestureEventHub);
2503     CHECK_NULL_VOID(eventHub);
2504     auto host = GetHost();
2505     CHECK_NULL_VOID(host);
2506     if (IsSelectableAndCopy()) {
2507         auto context = host->GetContext();
2508         CHECK_NULL_VOID(context);
2509         if (!clipboard_ && context) {
2510             clipboard_ = ClipboardProxy::GetInstance()->GetClipboard(context->GetTaskExecutor());
2511         }
2512         InitLongPressEvent(gestureEventHub);
2513         if (host->IsDraggable() && !shiftFlag_) {
2514             InitDragEvent();
2515         }
2516         InitKeyEvent();
2517         InitMouseEvent();
2518         InitTouchEvent();
2519         SetAccessibilityAction();
2520     } else {
2521         if (host->IsDraggable() || gestureEventHub->GetTextDraggable()) {
2522             gestureEventHub->SetTextDraggable(false);
2523             eventHub->SetDefaultOnDragStart(nullptr);
2524             if (!eventHub->HasOnDragStart() && IsTextNode()) {
2525                 gestureEventHub->RemoveDragEvent();
2526             }
2527         }
2528         if (longPressEvent_ && !hasSpanStringLongPressEvent_) {
2529             gestureEventHub->SetLongPressEvent(nullptr);
2530             longPressEvent_ = nullptr;
2531         }
2532     }
2533     if (onClick_ || IsSelectableAndCopy() || CanStartAITask()) {
2534         InitClickEvent(gestureEventHub);
2535         if (CanStartAITask()) {
2536             auto context = host->GetContext();
2537             CHECK_NULL_VOID(context);
2538             if (!clipboard_ && context) {
2539                 clipboard_ = ClipboardProxy::GetInstance()->GetClipboard(context->GetTaskExecutor());
2540             }
2541             InitMouseEvent();
2542             InitAISpanHoverEvent();
2543         }
2544     }
2545 }
2546 
HandleMouseEvent(const MouseInfo & info)2547 void TextPattern::HandleMouseEvent(const MouseInfo& info)
2548 {
2549     auto localLocation = info.GetLocalLocation();
2550     if (isAutoScrollByMouse_ && GetHost()) {
2551         NG::PointF localPoint(info.GetGlobalLocation().GetX(), info.GetGlobalLocation().GetY());
2552         NG::NGGestureRecognizer::Transform(localPoint, GetHost(), true);
2553         localLocation.SetX(localPoint.GetX());
2554         localLocation.SetY(localPoint.GetY());
2555     }
2556     auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
2557     Offset textOffset = { localLocation.GetX() - textPaintOffset.GetX(),
2558         localLocation.GetY() - textPaintOffset.GetY() };
2559     if (info.GetButton() == MouseButton::LEFT_BUTTON) {
2560         lastLeftMouseMoveLocation_ = info.GetGlobalLocation();
2561         HandleMouseLeftButton(info, textOffset);
2562         if (IsSelected()) {
2563             selectOverlay_->SetSelectionHoldCallback();
2564         }
2565         sourceType_ = info.GetSourceDevice();
2566     } else if (info.GetButton() == MouseButton::RIGHT_BUTTON) {
2567         HandleMouseRightButton(info, textOffset);
2568         sourceType_ = info.GetSourceDevice();
2569     }
2570     if (!IsSelected()) {
2571         ResetOriginCaretPosition();
2572     }
2573 }
2574 
HandleMouseLeftButton(const MouseInfo & info,const Offset & textOffset)2575 void TextPattern::HandleMouseLeftButton(const MouseInfo& info, const Offset& textOffset)
2576 {
2577     if (info.GetAction() == MouseAction::PRESS) {
2578         HandleMouseLeftPressAction(info, textOffset);
2579     } else if (info.GetAction() == MouseAction::MOVE) {
2580         HandleMouseLeftMoveAction(info, textOffset);
2581     } else if (info.GetAction() == MouseAction::RELEASE) {
2582         HandleMouseLeftReleaseAction(info, textOffset);
2583     }
2584 
2585     auto host = GetHost();
2586     CHECK_NULL_VOID(host);
2587     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2588 }
2589 
HandleMouseLeftPressAction(const MouseInfo & info,const Offset & textOffset)2590 void TextPattern::HandleMouseLeftPressAction(const MouseInfo& info, const Offset& textOffset)
2591 {
2592     isMousePressed_ = true;
2593     CheckPressedSpanPosition(textOffset);
2594     leftMousePressed_ = true;
2595     ShowShadow({ textOffset.GetX(), textOffset.GetY() }, GetUrlPressColor());
2596     if (BetweenSelectedPosition(info.GetGlobalLocation())) {
2597         blockPress_ = true;
2598         return;
2599     }
2600     mouseStatus_ = MouseStatus::PRESSED;
2601     lastLeftMouseClickStyle_ = currentMouseStyle_;
2602     CHECK_NULL_VOID(pManager_);
2603     if (shiftFlag_) {
2604         auto end = pManager_->GetGlyphIndexByCoordinate(textOffset);
2605         HandleSelectionChange(textSelector_.lastValidStart, end);
2606     } else {
2607         auto start = pManager_->GetGlyphIndexByCoordinate(textOffset);
2608         textSelector_.Update(start, start);
2609     }
2610     // auto scroll.
2611     scrollableParent_ = selectOverlay_->FindScrollableParent();
2612     auto host = GetHost();
2613     if (scrollableParent_.Upgrade() && host) {
2614         host->RegisterNodeChangeListener();
2615     }
2616 }
2617 
CheckPressedSpanPosition(const Offset & textOffset)2618 void TextPattern::CheckPressedSpanPosition(const Offset& textOffset)
2619 {
2620     leftMousePressedOffset_ = textOffset;
2621 }
2622 
ResetMouseLeftPressedState()2623 void TextPattern::ResetMouseLeftPressedState()
2624 {
2625     isMousePressed_ = false;
2626     leftMousePressed_ = false;
2627 }
2628 
HandleMouseLeftReleaseAction(const MouseInfo & info,const Offset & textOffset)2629 void TextPattern::HandleMouseLeftReleaseAction(const MouseInfo& info, const Offset& textOffset)
2630 {
2631     bool pressBetweenSelectedPosition = blockPress_;
2632     blockPress_ = false;
2633     auto oldMouseStatus = mouseStatus_;
2634     mouseStatus_ = MouseStatus::RELEASED;
2635     auto oldEntityDragging = isTryEntityDragging_;
2636     isTryEntityDragging_ = false;
2637     lastLeftMouseClickStyle_ = MouseFormat::DEFAULT;
2638     ShowShadow({ textOffset.GetX(), textOffset.GetY() }, GetUrlHoverColor());
2639     if (isDoubleClick_) {
2640         isDoubleClick_ = false;
2641         ResetMouseLeftPressedState();
2642         return;
2643     }
2644     if (oldMouseStatus != MouseStatus::MOVE && oldMouseStatus == MouseStatus::PRESSED &&
2645         !IsDragging() && !oldEntityDragging) {
2646         HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY()));
2647         if (GetDataDetectorAdapter()->hasClickedAISpan_) {
2648             selectOverlay_->DisableMenu();
2649             ResetMouseLeftPressedState();
2650             return;
2651         }
2652     }
2653 
2654     CHECK_NULL_VOID(pManager_);
2655     auto start = textSelector_.baseOffset;
2656     auto end = pManager_->GetGlyphIndexByCoordinate(textOffset);
2657     if (!IsSelected() || (pressBetweenSelectedPosition && !mouseUpAndDownPointChange_)) {
2658         start = -1;
2659         end = -1;
2660     }
2661     if (isMousePressed_ || oldMouseStatus == MouseStatus::MOVE || shiftFlag_) {
2662         HandleSelectionChange(start, end);
2663     }
2664 
2665     if (IsSelected() && oldMouseStatus == MouseStatus::MOVE && IsSelectedBindSelectionMenu()) {
2666         selectOverlay_->SetMouseMenuOffset(OffsetF(
2667             static_cast<float>(info.GetGlobalLocation().GetX()), static_cast<float>(info.GetGlobalLocation().GetY())));
2668         textResponseType_ = TextResponseType::SELECTED_BY_MOUSE;
2669         ShowSelectOverlay({ .animation = true });
2670     }
2671     ResetMouseLeftPressedState();
2672     moveOverClickThreshold_ = false;
2673     mouseUpAndDownPointChange_ = false;
2674     // stop auto scroll.
2675     auto host = GetHost();
2676     if (host && scrollableParent_.Upgrade() && !selectOverlay_->SelectOverlayIsOn()) {
2677         host->UnregisterNodeChangeListener();
2678     }
2679     selectOverlay_->TriggerScrollableParentToScroll(scrollableParent_.Upgrade(), info.GetGlobalLocation(), true);
2680     isAutoScrollByMouse_ = false;
2681 }
2682 
HandleMouseLeftMoveAction(const MouseInfo & info,const Offset & textOffset)2683 void TextPattern::HandleMouseLeftMoveAction(const MouseInfo& info, const Offset& textOffset)
2684 {
2685     if (!IsSelectableAndCopy()) {
2686         isTryEntityDragging_ = lastLeftMouseClickStyle_ == MouseFormat::HAND_POINTING;
2687         isMousePressed_ = false;
2688         leftMousePressed_ = false;
2689         return;
2690     }
2691     if (isMousePressed_) {
2692         mouseStatus_ = MouseStatus::MOVE;
2693         CHECK_NULL_VOID(pManager_);
2694         auto end = pManager_->GetGlyphIndexByCoordinate(textOffset);
2695         HandleSelectionChange(textSelector_.baseOffset, end);
2696         selectOverlay_->TriggerScrollableParentToScroll(scrollableParent_.Upgrade(), info.GetGlobalLocation(), false);
2697         auto distance = (textOffset - leftMousePressedOffset_).GetDistance();
2698         if (distance >= CLICK_THRESHOLD.ConvertToPx()) {
2699             moveOverClickThreshold_ = true;
2700             mouseUpAndDownPointChange_ = true;
2701         }
2702     }
2703 }
2704 
HandleMouseRightButton(const MouseInfo & info,const Offset & textOffset)2705 void TextPattern::HandleMouseRightButton(const MouseInfo& info, const Offset& textOffset)
2706 {
2707     if (info.GetAction() == MouseAction::RELEASE) {
2708         selectOverlay_->SetMouseMenuOffset(OffsetF(
2709             static_cast<float>(info.GetGlobalLocation().GetX()), static_cast<float>(info.GetGlobalLocation().GetY())));
2710         if (!BetweenSelectedPosition(info.GetGlobalLocation())) {
2711             HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY()));
2712             if (GetDataDetectorAdapter()->hasClickedAISpan_) {
2713                 isMousePressed_ = false;
2714                 return;
2715             }
2716         }
2717         if (!IsSelectableAndCopy()) {
2718             return;
2719         }
2720 
2721         CalculateHandleOffsetAndShowOverlay(true);
2722         if (selectOverlay_->SelectOverlayIsOn()) {
2723             CloseSelectOverlay(true);
2724         }
2725         textResponseType_ = TextResponseType::RIGHT_CLICK;
2726         if (!IsSelected()) {
2727             auto spanNode = DynamicCast<FrameNode>(GetChildByIndex(GetSelectionSpanItemIndex(info)));
2728             if (spanNode && spanNode->GetTag() == V2::IMAGE_ETS_TAG) {
2729                 selectedType_ = TextSpanType::IMAGE;
2730             } else {
2731                 selectedType_ = TextSpanType::TEXT;
2732             }
2733         }
2734         ShowSelectOverlay({ .animation = true });
2735         isMousePressed_ = false;
2736         auto host = GetHost();
2737         CHECK_NULL_VOID(host);
2738         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2739     } else if (info.GetAction() == MouseAction::PRESS) {
2740         isMousePressed_ = true;
2741         CloseSelectOverlay(true);
2742     }
2743 }
2744 
InitTouchEvent()2745 void TextPattern::InitTouchEvent()
2746 {
2747     CHECK_NULL_VOID(!touchEventInitialized_);
2748     auto host = GetHost();
2749     CHECK_NULL_VOID(host);
2750     auto gesture = host->GetOrCreateGestureEventHub();
2751     CHECK_NULL_VOID(gesture);
2752 
2753     auto touchTask = [weak = WeakClaim(this)](TouchEventInfo& info) {
2754         auto pattern = weak.Upgrade();
2755         CHECK_NULL_VOID(pattern);
2756         pattern->sourceType_ = info.GetSourceDevice();
2757         pattern->HandleTouchEvent(info);
2758     };
2759     auto touchListener = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
2760     gesture->AddTouchEvent(touchListener);
2761     touchEventInitialized_ = true;
2762 }
2763 
InitUrlTouchEvent()2764 void TextPattern::InitUrlTouchEvent()
2765 {
2766     CHECK_NULL_VOID(!urlTouchEventInitialized_);
2767     auto host = GetHost();
2768     CHECK_NULL_VOID(host);
2769     auto gesture = host->GetOrCreateGestureEventHub();
2770     CHECK_NULL_VOID(gesture);
2771 
2772     auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
2773         auto pattern = weak.Upgrade();
2774         CHECK_NULL_VOID(pattern);
2775         pattern->HandleUrlTouchEvent(info);
2776     };
2777     auto touchListener = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
2778     gesture->AddTouchEvent(touchListener);
2779     urlTouchEventInitialized_ = true;
2780 }
2781 
InitSpanStringTouchEvent()2782 void TextPattern::InitSpanStringTouchEvent()
2783 {
2784     CHECK_NULL_VOID(!spanStringTouchInitialized_);
2785     auto host = GetHost();
2786     CHECK_NULL_VOID(host);
2787     auto gesture = host->GetOrCreateGestureEventHub();
2788     CHECK_NULL_VOID(gesture);
2789 
2790     auto touchTask = [weak = WeakClaim(this)](TouchEventInfo& info) {
2791         auto pattern = weak.Upgrade();
2792         CHECK_NULL_VOID(pattern);
2793         pattern->HandleSpanStringTouchEvent(info);
2794     };
2795     auto touchListener = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
2796     gesture->AddTouchEvent(touchListener);
2797     spanStringTouchInitialized_ = true;
2798 }
2799 
MarkDirtySelf()2800 void TextPattern::MarkDirtySelf()
2801 {
2802     auto host = GetHost();
2803     CHECK_NULL_VOID(host);
2804     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2805 }
2806 
HandleTouchEvent(const TouchEventInfo & info)2807 void TextPattern::HandleTouchEvent(const TouchEventInfo& info)
2808 {
2809     DoGestureSelection(info);
2810     ResetOriginCaretPosition();
2811 }
2812 
HandleSpanStringTouchEvent(TouchEventInfo & info)2813 void TextPattern::HandleSpanStringTouchEvent(TouchEventInfo& info)
2814 {
2815     CHECK_NULL_VOID(!info.GetTouches().empty());
2816     auto touchOffset = info.GetTouches().front().GetLocalLocation();
2817     auto contentRect = GetTextRect();
2818     PointF textOffset = { static_cast<float>(touchOffset.GetX()) - contentRect.GetX(),
2819         static_cast<float>(touchOffset.GetY()) - contentRect.GetY() };
2820     auto touchedSpan = FindSpanItemByOffset(textOffset);
2821     if (touchedSpan && touchedSpan->onTouch) {
2822         touchedSpan->onTouch(info);
2823     }
2824 }
2825 
FindSpanItemByOffset(const PointF & textOffset)2826 RefPtr<SpanItem> TextPattern::FindSpanItemByOffset(const PointF& textOffset)
2827 {
2828     int32_t start = 0;
2829     for (const auto& item : spans_) {
2830         if (!item) {
2831             continue;
2832         }
2833         auto end = isSpanStringMode_ && item->position == -1 ? item->interval.second : item->position;
2834         auto selectedRects = GetSelectedRects(start, end);
2835         start = end;
2836         for (auto&& rect : selectedRects) {
2837             if (rect.IsInRegion(textOffset)) {
2838                 return item;
2839             }
2840         }
2841     }
2842     return nullptr;
2843 }
2844 
InitKeyEvent()2845 void TextPattern::InitKeyEvent()
2846 {
2847     CHECK_NULL_VOID(!keyEventInitialized_);
2848     auto host = GetHost();
2849     CHECK_NULL_VOID(host);
2850     auto focusHub = host->GetOrCreateFocusHub();
2851     CHECK_NULL_VOID(focusHub);
2852 
2853     auto keyTask = [weak = WeakClaim(this)](const KeyEvent& event) -> bool {
2854         auto pattern = weak.Upgrade();
2855         CHECK_NULL_RETURN(pattern, false);
2856         return pattern->HandleKeyEvent(event);
2857     };
2858     focusHub->SetOnKeyEventInternal(std::move(keyTask));
2859     keyEventInitialized_ = true;
2860 }
2861 
UpdateShiftFlag(const KeyEvent & keyEvent)2862 void TextPattern::UpdateShiftFlag(const KeyEvent& keyEvent)
2863 {
2864     bool flag = false;
2865     if (keyEvent.action == KeyAction::DOWN) {
2866         if (keyEvent.HasKey(KeyCode::KEY_SHIFT_LEFT) || keyEvent.HasKey(KeyCode::KEY_SHIFT_RIGHT)) {
2867             flag = true;
2868         }
2869     }
2870     if (flag != shiftFlag_) {
2871         shiftFlag_ = flag;
2872         if (!shiftFlag_) {
2873             // open drag
2874             InitDragEvent();
2875         } else  {
2876             // close drag
2877             ClearDragEvent();
2878         }
2879     }
2880 }
2881 
HandleKeyEvent(const KeyEvent & keyEvent)2882 bool TextPattern::HandleKeyEvent(const KeyEvent& keyEvent)
2883 {
2884     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
2885     CHECK_NULL_RETURN(textLayoutProperty, false);
2886     if (textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE) {
2887         return false;
2888     }
2889     CHECK_NULL_RETURN(!textEffect_, false);
2890     UpdateShiftFlag(keyEvent);
2891     auto host = GetHost();
2892     CHECK_NULL_RETURN(host, false);
2893     if (SystemProperties::GetTextTraceEnabled()) {
2894         ACE_TEXT_SCOPED_TRACE("TextPattern::HandleKeyEvent[id:%d][action:%d]", host->GetId(), keyEvent.action);
2895     }
2896     if (keyEvent.action != KeyAction::DOWN) {
2897         return false;
2898     }
2899 
2900     if (keyEvent.IsCtrlWith(KeyCode::KEY_C)) {
2901         HandleOnCopy();
2902         return true;
2903     }
2904 
2905     if (keyEvent.IsCtrlWith(KeyCode::KEY_A)) {
2906         auto textSize = static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_;
2907         HandleSelectionChange(0, textSize);
2908         CloseSelectOverlay();
2909         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2910         return true;
2911     }
2912 
2913     if (keyEvent.IsShiftWith(keyEvent.code)) {
2914         HandleOnSelect(keyEvent.code);
2915         return true;
2916     }
2917     return false;
2918 }
2919 
HandleOnSelect(KeyCode code)2920 void TextPattern::HandleOnSelect(KeyCode code)
2921 {
2922     auto end = textSelector_.GetEnd();
2923     switch (code) {
2924         case KeyCode::KEY_DPAD_LEFT: {
2925             HandleSelection(true, end - 1);
2926             break;
2927         }
2928         case KeyCode::KEY_DPAD_RIGHT: {
2929             HandleSelection(false, end + 1);
2930             break;
2931         }
2932         case KeyCode::KEY_DPAD_UP: {
2933             HandleSelectionUp();
2934             break;
2935         }
2936         case KeyCode::KEY_DPAD_DOWN: {
2937             HandleSelectionDown();
2938             break;
2939         }
2940         default:
2941             break;
2942     }
2943     if (!(shiftFlag_ && (code == KeyCode::KEY_DPAD_UP ||
2944                          code == KeyCode::KEY_DPAD_DOWN))) {
2945         ResetOriginCaretPosition();
2946     }
2947 }
2948 
HandleSelectionUp()2949 void TextPattern::HandleSelectionUp()
2950 {
2951     auto end = textSelector_.GetEnd();
2952     auto line = pManager_->GetLineCount();
2953     if (line == 1) {
2954         HandleSelection(true, 0);
2955         return;
2956     }
2957     CaretMetricsF secondHandleMetrics;
2958     CalcCaretMetricsByPosition(textSelector_.destinationOffset, secondHandleMetrics, TextAffinity::UPSTREAM);
2959     auto secondOffsetX = secondHandleMetrics.offset.GetX();
2960     auto secondOffsetY = secondHandleMetrics.offset.GetY();
2961     RecordOriginCaretPosition({ secondOffsetX, secondOffsetY });
2962     OffsetF originCaretPosition;
2963     auto caretXPosition = GetOriginCaretPosition(originCaretPosition) ? // recorded offset x
2964         originCaretPosition.GetX() : secondOffsetX;
2965     double height = GetTextHeight(end, false);
2966     Offset offset = { caretXPosition, secondOffsetY - height * 0.5 };
2967     auto caculateIndex = GetHandleIndex(offset);
2968     if (end == caculateIndex) {
2969         caculateIndex = 0;
2970     }
2971     HandleSelection(true, caculateIndex);
2972 }
2973 
HandleSelectionDown()2974 void TextPattern::HandleSelectionDown()
2975 {
2976     auto end = textSelector_.GetEnd();
2977     auto line = pManager_->GetLineCount();
2978     auto lastIndex = GetActualTextLength();
2979     if (line == 1) {
2980         HandleSelection(true, lastIndex);
2981         return;
2982     }
2983     CaretMetricsF secondHandleMetrics;
2984     CalcCaretMetricsByPosition(textSelector_.destinationOffset, secondHandleMetrics, TextAffinity::UPSTREAM);
2985     auto secondOffsetX = secondHandleMetrics.offset.GetX();
2986     RecordOriginCaretPosition({ secondOffsetX, secondHandleMetrics.offset.GetY() });
2987     OffsetF originCaretPosition;
2988     auto caretXPosition = GetOriginCaretPosition(originCaretPosition) ? // recorded offset x
2989         originCaretPosition.GetX() : secondOffsetX;
2990     double height = GetTextHeight(end, true);
2991     auto caculateIndex = GetHandleIndex({ caretXPosition, height });
2992     if (NearZero(height) || caculateIndex == end || caculateIndex > lastIndex) {
2993         caculateIndex = lastIndex;
2994     }
2995     HandleSelection(true, caculateIndex);
2996 }
2997 
HandleSelection(bool isEmojiStart,int32_t end)2998 void TextPattern::HandleSelection(bool isEmojiStart, int32_t end)
2999 {
3000     auto host = GetHost();
3001     CHECK_NULL_VOID(host);
3002     auto start = textSelector_.GetStart();
3003     auto lastIndex = GetActualTextLength();
3004     if (SystemProperties::GetTextTraceEnabled()) {
3005         TAG_LOGI(AceLogTag::ACE_TEXT,
3006             "HandleSelection[id:%{public}d][start:%{public}d][end:%{public}d][isEmojiStart:%{public}d]", host->GetId(),
3007             start, end, isEmojiStart);
3008     }
3009     if (start < 0 || start > lastIndex || end < 0 || end > lastIndex) {
3010         return;
3011     }
3012     int32_t emojiStartIndex;
3013     int32_t emojiEndIndex;
3014     bool isIndexInEmoji = TextEmojiProcessor::IsIndexInEmoji(end, GetSelectedText(0, lastIndex),
3015         emojiStartIndex, emojiEndIndex);
3016     if (isIndexInEmoji) {
3017         end = isEmojiStart ? emojiStartIndex : emojiEndIndex;
3018     }
3019     HandleSelectionChange(start, end);
3020     CalculateHandleOffsetAndShowOverlay();
3021     CloseSelectOverlay(true);
3022     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3023 }
3024 
GetTextHeight(int32_t index,bool isNextLine)3025 double TextPattern::GetTextHeight(int32_t index, bool isNextLine)
3026 {
3027     double lineHeight = 0.0;
3028     auto lineCount = static_cast<int32_t>(pManager_->GetLineCount());
3029     for (auto lineNumber = 0; lineNumber < lineCount; lineNumber++) {
3030         auto lineMetrics = GetLineMetrics(lineNumber);
3031         auto startIndex = static_cast<int32_t>(lineMetrics.startIndex);
3032         auto endIndex = static_cast<int32_t>(lineMetrics.endIndex);
3033         lineHeight += lineMetrics.height;
3034         if (isNextLine) {
3035             if (index <= endIndex && endIndex != GetActualTextLength()) {
3036                 return lineHeight;
3037             }
3038         } else {
3039             auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
3040             CHECK_NULL_RETURN(textLayoutProperty, 0);
3041             auto maxLines = textLayoutProperty->GetMaxLinesValue(Infinity<uint32_t>());
3042             if ((index <= endIndex && startIndex != 0) ||
3043                 ((lineNumber + 1) == static_cast<int32_t>(maxLines) && lineNumber != 0)) {
3044                 return GetLineMetrics(lineNumber - 1).height;
3045             }
3046         }
3047     }
3048     return 0.0;
3049 }
3050 
GetActualTextLength()3051 int32_t TextPattern::GetActualTextLength()
3052 {
3053     auto lineCount = static_cast<int32_t>(pManager_->GetLineCount());
3054     return GetLineMetrics(lineCount - 1).endIndex;
3055 }
3056 
SetTextSelectableMode(TextSelectableMode value)3057 void TextPattern::SetTextSelectableMode(TextSelectableMode value)
3058 {
3059     auto host = GetHost();
3060     CHECK_NULL_VOID(host);
3061     auto focusHub = host->GetOrCreateFocusHub();
3062     CHECK_NULL_VOID(focusHub);
3063     if (value == TextSelectableMode::SELECTABLE_FOCUSABLE) {
3064         focusHub->SetFocusable(true);
3065         focusHub->SetIsFocusOnTouch(true);
3066     } else {
3067         focusHub->SetFocusable(false);
3068         focusHub->SetIsFocusOnTouch(false);
3069     }
3070 }
3071 
IsSelectableAndCopy()3072 bool TextPattern::IsSelectableAndCopy()
3073 {
3074     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
3075     CHECK_NULL_RETURN(textLayoutProperty, false);
3076     auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
3077     return mode != TextSelectableMode::UNSELECTABLE && copyOption_ != CopyOptions::None && !textEffect_;
3078 }
3079 
IsDraggable(const Offset & offset)3080 bool TextPattern::IsDraggable(const Offset& offset)
3081 {
3082     auto host = GetHost();
3083     CHECK_NULL_RETURN(host, false);
3084     auto eventHub = host->GetOrCreateEventHub<EventHub>();
3085     bool draggable = eventHub->HasOnDragStart();
3086     return draggable && LocalOffsetInSelectedArea(offset);
3087 }
3088 
OnDragStart(const RefPtr<Ace::DragEvent> & event,const std::string & extraParams)3089 NG::DragDropInfo TextPattern::OnDragStart(const RefPtr<Ace::DragEvent>& event, const std::string& extraParams)
3090 {
3091     DragDropInfo itemInfo;
3092     auto host = GetHost();
3093     CHECK_NULL_RETURN(host, itemInfo);
3094     if (overlayMod_) {
3095         overlayMod_->ClearSelectedForegroundColorAndRects();
3096     }
3097     auto hub = host->GetOrCreateEventHub<EventHub>();
3098     auto gestureHub = hub->GetOrCreateGestureEventHub();
3099     auto [start, end] = GetSelectedStartAndEnd();
3100     recoverStart_ = start;
3101     recoverEnd_ = end;
3102     auto textSelectInfo = GetSpansInfo(start, end, GetSpansMethod::ONSELECT);
3103     dragResultObjects_ = textSelectInfo.GetSelection().resultObjects;
3104     ResetDragRecordSize(dragResultObjects_.empty() ? -1 : 1);
3105     dragBoxes_ = GetTextBoxes();
3106     ResetAISelected(AIResetSelectionReason::DRAG_START);
3107     status_ = Status::DRAGGING;
3108     if (dragResultObjects_.empty() || !gestureHub->GetIsTextDraggable()) {
3109         return itemInfo;
3110     }
3111     auto data = event->GetData();
3112     if (!data) {
3113         AddUdmfData(event);
3114     }
3115     CloseOperate();
3116     host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
3117     return itemInfo;
3118 }
3119 
AddUdmfTxtPreProcessor(const ResultObject src,ResultObject & result,bool isAppend)3120 void TextPattern::AddUdmfTxtPreProcessor(const ResultObject src, ResultObject& result, bool isAppend)
3121 {
3122     auto valueString = GetSelectedSpanText(src.valueString,
3123         src.offsetInSpan[RichEditorSpanRange::RANGESTART], src.offsetInSpan[RichEditorSpanRange::RANGEEND],
3124         false, true, false);
3125     if (isAppend) {
3126         result.valueString = result.valueString + valueString;
3127     } else {
3128         result.valueString = valueString;
3129     }
3130 }
3131 
AddUdmfData(const RefPtr<Ace::DragEvent> & event)3132 void TextPattern::AddUdmfData(const RefPtr<Ace::DragEvent>& event)
3133 {
3134     RefPtr<UnifiedData> unifiedData = UdmfClient::GetInstance()->CreateUnifiedData();
3135     if (isSpanStringMode_) {
3136         std::vector<uint8_t> arr;
3137         auto dragSpanString = styledString_->GetSubSpanString(recoverStart_, recoverEnd_ - recoverStart_,
3138             false, true, false);
3139         dragSpanString->EncodeTlv(arr);
3140         UdmfClient::GetInstance()->AddSpanStringRecord(unifiedData, arr);
3141     } else {
3142         ProcessNormalUdmfData(unifiedData);
3143     }
3144     event->SetData(unifiedData);
3145 }
3146 
ProcessNormalUdmfData(const RefPtr<UnifiedData> & unifiedData)3147 void TextPattern::ProcessNormalUdmfData(const RefPtr<UnifiedData>& unifiedData)
3148 {
3149     std::list<ResultObject> finalResult;
3150     auto type = SelectSpanType::TYPESPAN;
3151     for (const auto& resultObj : dragResultObjects_) {
3152         if (finalResult.empty() || resultObj.type != SelectSpanType::TYPESPAN || type != SelectSpanType::TYPESPAN) {
3153             type = resultObj.type;
3154             finalResult.emplace_back(resultObj);
3155             if (resultObj.type == SelectSpanType::TYPESPAN) {
3156                 AddUdmfTxtPreProcessor(resultObj, finalResult.back(), false);
3157             }
3158         } else {
3159             AddUdmfTxtPreProcessor(resultObj, finalResult.back(), true);
3160         }
3161     }
3162     auto resultProcessor = [unifiedData, weak = WeakClaim(this)](const ResultObject& result) {
3163         auto pattern = weak.Upgrade();
3164         CHECK_NULL_VOID(pattern);
3165         std::string u8ValueString = UtfUtils::Str16DebugToStr8(result.valueString);
3166         if (result.type == SelectSpanType::TYPESPAN) {
3167             UdmfClient::GetInstance()->AddPlainTextRecord(unifiedData, u8ValueString);
3168             return;
3169         }
3170         if (result.type == SelectSpanType::TYPEIMAGE) {
3171             if (result.valuePixelMap) {
3172                 pattern->AddPixelMapToUdmfData(result.valuePixelMap, unifiedData);
3173             } else if (u8ValueString.size() > 1) {
3174                 UdmfClient::GetInstance()->AddImageRecord(unifiedData, u8ValueString);
3175             } else {
3176                 // builder span, fill pixelmap data
3177                 auto builderNode = DynamicCast<FrameNode>(pattern->GetChildByIndex(result.spanPosition.spanIndex));
3178                 CHECK_NULL_VOID(builderNode);
3179                 pattern->AddPixelMapToUdmfData(builderNode->GetDragPixelMap(), unifiedData);
3180             }
3181         }
3182     };
3183     for (const auto& resultObj : finalResult) {
3184         resultProcessor(resultObj);
3185     }
3186 }
3187 
AddPixelMapToUdmfData(const RefPtr<PixelMap> & pixelMap,const RefPtr<UnifiedData> & unifiedData)3188 void TextPattern::AddPixelMapToUdmfData(const RefPtr<PixelMap>& pixelMap, const RefPtr<UnifiedData>& unifiedData)
3189 {
3190     CHECK_NULL_VOID(pixelMap && unifiedData);
3191     const uint8_t* pixels = pixelMap->GetPixels();
3192     CHECK_NULL_VOID(pixels);
3193     int32_t length = pixelMap->GetByteCount();
3194     std::vector<uint8_t> data(pixels, pixels + length);
3195     PixelMapRecordDetails details = { pixelMap->GetWidth(), pixelMap->GetHeight(),
3196         pixelMap->GetPixelFormat(), pixelMap->GetAlphaType() };
3197     UdmfClient::GetInstance()->AddPixelMapRecord(unifiedData, data, details);
3198 }
3199 
CloseOperate()3200 void TextPattern::CloseOperate()
3201 {
3202     UpdateSpanItemDragStatus(dragResultObjects_, true);
3203     recoverDragResultObjects_ = dragResultObjects_;
3204     AceEngineExt::GetInstance().DragStartExt();
3205     CloseKeyboard(true);
3206     CloseSelectOverlay();
3207     ResetSelection();
3208 }
3209 
OnDragStartNoChild(const RefPtr<Ace::DragEvent> & event,const std::string & extraParams)3210 DragDropInfo TextPattern::OnDragStartNoChild(const RefPtr<Ace::DragEvent>& event, const std::string& extraParams)
3211 {
3212     auto weakPtr = WeakClaim(this);
3213     DragDropInfo itemInfo;
3214     auto pattern = weakPtr.Upgrade();
3215     auto host = pattern->GetHost();
3216     auto hub = host->GetOrCreateEventHub<EventHub>();
3217     auto gestureHub = hub->GetOrCreateGestureEventHub();
3218     CHECK_NULL_RETURN(gestureHub, itemInfo);
3219     if (!gestureHub->GetIsTextDraggable()) {
3220         return itemInfo;
3221     }
3222     auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
3223     dragBoxes_ = GetTextBoxes();
3224     pattern->status_ = Status::DRAGGING;
3225     pattern->contentMod_->ChangeDragStatus();
3226     pattern->showSelect_ = false;
3227     auto [start, end] = GetSelectedStartAndEnd();
3228     pattern->recoverStart_ = start;
3229     pattern->recoverEnd_ = end;
3230     auto beforeStr = GetSelectedText(0, start, false, true);
3231     auto selectedStr = GetSelectedText(start, end, false, true);
3232     auto afterStr = GetSelectedText(end, textForDisplay_.length(), false, true);
3233     pattern->dragContents_ = { beforeStr, selectedStr, afterStr };
3234     auto selectedUtf8Str = UtfUtils::Str16DebugToStr8(selectedStr);
3235     itemInfo.extraInfo = selectedUtf8Str;
3236     RefPtr<UnifiedData> unifiedData = UdmfClient::GetInstance()->CreateUnifiedData();
3237     UdmfClient::GetInstance()->AddPlainTextRecord(unifiedData, selectedUtf8Str);
3238     event->SetData(unifiedData);
3239     host->MarkDirtyWithOnProChange(layoutProperty->GetMaxLinesValue(Infinity<float>()) <= 1
3240                                        ? PROPERTY_UPDATE_MEASURE_SELF
3241                                        : PROPERTY_UPDATE_MEASURE);
3242 
3243     CloseSelectOverlay();
3244     ResetSelection();
3245     ResetAISelected(AIResetSelectionReason::DRAG_START_ON_CHILDREN);
3246     return itemInfo;
3247 }
3248 
UpdateSpanItemDragStatus(const std::list<ResultObject> & resultObjects,bool isDragging)3249 void TextPattern::UpdateSpanItemDragStatus(const std::list<ResultObject>& resultObjects, bool isDragging)
3250 {
3251     if (resultObjects.empty()) {
3252         return;
3253     }
3254     auto dragStatusUpdateAction = [weakPtr = WeakClaim(this), isDragging](const ResultObject& resultObj) {
3255         auto pattern = weakPtr.Upgrade();
3256         CHECK_NULL_VOID(pattern && !pattern->spans_.empty());
3257         auto it = pattern->spans_.begin();
3258         if (resultObj.spanPosition.spanIndex >= static_cast<int32_t>(pattern->spans_.size())) {
3259             std::advance(it, !pattern->spans_.empty() ? static_cast<int32_t>(pattern->spans_.size()) - 1 : 0);
3260             TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "resultObj.spanPosition.spanIndex is larger than spans size.");
3261         } else {
3262             std::advance(it, resultObj.spanPosition.spanIndex);
3263         }
3264         auto spanItem = *it;
3265         CHECK_NULL_VOID(spanItem);
3266         if (resultObj.type == SelectSpanType::TYPESPAN) {
3267             if (pattern->isSpanStringMode_) {
3268                 spanItem = resultObj.span.Upgrade();
3269                 CHECK_NULL_VOID(spanItem);
3270             }
3271             spanItem->MarkDirty();
3272             if (isDragging) {
3273                 spanItem->StartDrag(resultObj.offsetInSpan[RichEditorSpanRange::RANGESTART],
3274                     resultObj.offsetInSpan[RichEditorSpanRange::RANGEEND]);
3275                 pattern->dragSpanItems_.emplace_back(spanItem);
3276             } else {
3277                 spanItem->EndDrag();
3278             }
3279             return;
3280         }
3281         spanItem->MarkDirty();
3282         if (resultObj.type == SelectSpanType::TYPEIMAGE) {
3283             if (isDragging) {
3284                 pattern->dragSpanItems_.emplace_back(spanItem);
3285             }
3286             auto imageNode = DynamicCast<FrameNode>(pattern->GetChildByIndex(resultObj.spanPosition.spanIndex));
3287             CHECK_NULL_VOID(imageNode);
3288             auto renderContext = imageNode->GetRenderContext();
3289             CHECK_NULL_VOID(renderContext);
3290             renderContext->UpdateOpacity(isDragging ? (double)DRAGGED_TEXT_OPACITY / 255 : 1);
3291             imageNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3292         }
3293     };
3294     for (const auto& resultObj : resultObjects) {
3295         dragStatusUpdateAction(resultObj);
3296     }
3297 }
3298 
OnDragEnd(const RefPtr<Ace::DragEvent> & event)3299 void TextPattern::OnDragEnd(const RefPtr<Ace::DragEvent>& event)
3300 {
3301     ResetDragRecordSize(-1);
3302     auto wk = WeakClaim(this);
3303     auto pattern = wk.Upgrade();
3304     CHECK_NULL_VOID(pattern);
3305     auto host = GetHost();
3306     CHECK_NULL_VOID(host);
3307     isMousePressed_ = false;
3308     if (status_ == Status::DRAGGING) {
3309         status_ = Status::NONE;
3310     }
3311     dragSpanItems_.clear();
3312     if (dragResultObjects_.empty()) {
3313         return;
3314     }
3315     UpdateSpanItemDragStatus(dragResultObjects_, false);
3316     dragResultObjects_.clear();
3317     if (event && event->GetResult() != DragRet::DRAG_SUCCESS && IsSelectableAndCopy()) {
3318         HandleSelectionChange(recoverStart_, recoverEnd_);
3319         isShowMenu_ = false;
3320         if (GetCurrentDragTool() == SourceTool::FINGER) {
3321             CalculateHandleOffsetAndShowOverlay();
3322             ShowSelectOverlay({ .menuIsShow = false });
3323         }
3324     }
3325     host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
3326 }
3327 
OnDragEndNoChild(const RefPtr<Ace::DragEvent> & event)3328 void TextPattern::OnDragEndNoChild(const RefPtr<Ace::DragEvent>& event)
3329 {
3330     auto wk = WeakClaim(this);
3331     auto pattern = wk.Upgrade();
3332     CHECK_NULL_VOID(pattern);
3333     auto host = pattern->GetHost();
3334     CHECK_NULL_VOID(host);
3335     isMousePressed_ = false;
3336     if (pattern->status_ == Status::DRAGGING) {
3337         pattern->status_ = Status::NONE;
3338         pattern->MarkContentChange();
3339         pattern->contentMod_->ChangeDragStatus();
3340         if (event && event->GetResult() != DragRet::DRAG_SUCCESS && IsSelectableAndCopy()) {
3341             HandleSelectionChange(recoverStart_, recoverEnd_);
3342             isShowMenu_ = false;
3343             if (GetCurrentDragTool() == SourceTool::FINGER) {
3344                 CalculateHandleOffsetAndShowOverlay();
3345                 ShowSelectOverlay({ .menuIsShow = false });
3346             }
3347         }
3348         auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
3349         host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
3350     }
3351 }
3352 
OnDragMove(const RefPtr<Ace::DragEvent> & event)3353 void TextPattern::OnDragMove(const RefPtr<Ace::DragEvent>& event)
3354 {
3355     auto weakPtr = WeakClaim(this);
3356     auto pattern = weakPtr.Upgrade();
3357     if (pattern->status_ == Status::DRAGGING) {
3358         CloseSelectOverlay();
3359         pattern->showSelect_ = false;
3360     }
3361 }
3362 
InitDragEvent()3363 void TextPattern::InitDragEvent()
3364 {
3365     auto host = GetHost();
3366     CHECK_NULL_VOID(host);
3367     auto eventHub = host->GetOrCreateEventHub<EventHub>();
3368     CHECK_NULL_VOID(eventHub);
3369     auto gestureHub = host->GetOrCreateGestureEventHub();
3370     CHECK_NULL_VOID(gestureHub);
3371     gestureHub->InitDragDropEvent();
3372     gestureHub->SetTextDraggable(true);
3373     gestureHub->SetThumbnailCallback(GetThumbnailCallback());
3374     auto onDragStart = [weakPtr = WeakClaim(this)](
3375                            const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& extraParams) -> DragDropInfo {
3376         NG::DragDropInfo itemInfo;
3377         auto pattern = weakPtr.Upgrade();
3378         CHECK_NULL_RETURN(pattern, itemInfo);
3379         auto eventHub = pattern->GetOrCreateEventHub<EventHub>();
3380         CHECK_NULL_RETURN(eventHub, itemInfo);
3381         pattern->SetCurrentDragTool(event->GetSourceTool());
3382         if (pattern->spans_.empty() && !pattern->isSpanStringMode_) {
3383             return pattern->OnDragStartNoChild(event, extraParams);
3384         }
3385         return pattern->OnDragStart(event, extraParams);
3386     };
3387     eventHub->SetDefaultOnDragStart(std::move(onDragStart));
3388     auto onDragMove = [weakPtr = WeakClaim(this)](
3389                           const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& extraParams) {
3390         auto pattern = weakPtr.Upgrade();
3391         CHECK_NULL_VOID(pattern);
3392         pattern->OnDragMove(event);
3393     };
3394     eventHub->SetOnDragMove(std::move(onDragMove));
3395     auto onDragEnd = [weakPtr = WeakClaim(this)](const RefPtr<OHOS::Ace::DragEvent>& event) {
3396         auto pattern = weakPtr.Upgrade();
3397         CHECK_NULL_VOID(pattern);
3398         // 拖拽框架强引用导致退出页面后还能够运行到这里
3399         if (pattern->isDetachFromMainTree_) {
3400             return;
3401         }
3402         ContainerScope scope(pattern->GetHostInstanceId());
3403         pattern->showSelect_ = true;
3404         if (pattern->spans_.empty()) {
3405             pattern->OnDragEndNoChild(event);
3406         } else {
3407             pattern->OnDragEnd(event);
3408         }
3409     };
3410     eventHub->SetOnDragEnd(std::move(onDragEnd));
3411 }
3412 
ClearDragEvent()3413 void TextPattern::ClearDragEvent()
3414 {
3415     auto host = GetHost();
3416     CHECK_NULL_VOID(host);
3417     auto eventHub = host->GetOrCreateEventHub<EventHub>();
3418     CHECK_NULL_VOID(eventHub);
3419     auto gestureHub = host->GetOrCreateGestureEventHub();
3420     CHECK_NULL_VOID(gestureHub);
3421     gestureHub->SetTextDraggable(false);
3422     gestureHub->SetIsTextDraggable(false);
3423     gestureHub->SetThumbnailCallback(nullptr);
3424     eventHub->SetDefaultOnDragStart(nullptr);
3425     eventHub->SetOnDragMove(nullptr);
3426     eventHub->SetOnDragEnd(nullptr);
3427 }
3428 
GetThumbnailCallback()3429 std::function<void(Offset)> TextPattern::GetThumbnailCallback()
3430 {
3431     return [wk = WeakClaim(this)](const Offset& point) {
3432         auto pattern = wk.Upgrade();
3433         CHECK_NULL_VOID(pattern);
3434         pattern->InitAiSelection(point);
3435         if (pattern->BetweenSelectedPosition(point) || pattern->IsAiSelected()) {
3436             const auto& children = pattern->GetChildNodes();
3437             std::list<RefPtr<FrameNode>> imageChildren;
3438             for (const auto& child : children) {
3439                 auto node = DynamicCast<FrameNode>(child);
3440                 if (!node) {
3441                     continue;
3442                 }
3443                 auto image = node->GetPattern<ImagePattern>();
3444                 if (image) {
3445                     imageChildren.emplace_back(node);
3446                 }
3447             }
3448             auto info = pattern->CreateTextDragInfo();
3449             pattern->dragNode_ = RichEditorDragPattern::CreateDragNode(pattern->GetHost(), imageChildren, info);
3450             auto textDragPattern = pattern->dragNode_->GetPattern<TextDragPattern>();
3451             if (textDragPattern) {
3452                 auto option = pattern->GetHost()->GetDragPreviewOption();
3453                 option.options.shadowPath = textDragPattern->GetBackgroundPath()->ConvertToSVGString();
3454                 option.options.shadow = Shadow(RICH_DEFAULT_ELEVATION, {0.0, 0.0}, Color(RICH_DEFAULT_SHADOW_COLOR),
3455                 ShadowStyle::OuterFloatingSM);
3456                 pattern->GetHost()->SetDragPreviewOptions(option);
3457             }
3458             FrameNode::ProcessOffscreenNode(pattern->dragNode_);
3459         }
3460     };
3461 }
3462 
CreateTextDragInfo()3463 TextDragInfo TextPattern::CreateTextDragInfo()
3464 {
3465     TextDragInfo info;
3466     auto context = PipelineContext::GetCurrentContextSafelyWithCheck();
3467     CHECK_NULL_RETURN(context, info);
3468     auto theme = context->GetTheme<TextTheme>();
3469     CHECK_NULL_RETURN(theme, info);
3470     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
3471     CHECK_NULL_RETURN(textLayoutProperty, info);
3472     info.handleColor = theme->GetCaretColor();
3473     info.selectedBackgroundColor = theme->GetSelectedColor();
3474     selectOverlay_->GetVisibleDragViewHandles(info.firstHandle, info.secondHandle);
3475     if (IsAiSelected()) {
3476         info.isFirstHandleAnimation = false;
3477         info.isSecondHandleAnimation = false;
3478     }
3479     return info;
3480 }
3481 
GetAllChildren() const3482 const std::list<RefPtr<UINode>>& TextPattern::GetAllChildren() const
3483 {
3484     return childNodes_;
3485 }
3486 
GetSelectedSpanText(std::u16string value,int32_t start,int32_t end,bool includeStartHalf,bool includeEndHalf,bool getSubstrDirectly) const3487 std::u16string TextPattern::GetSelectedSpanText(std::u16string value, int32_t start, int32_t end, bool includeStartHalf,
3488     bool includeEndHalf, bool getSubstrDirectly) const
3489 {
3490     if (start < 0 || end > static_cast<int32_t>(value.length()) || start >= end) {
3491         return u"";
3492     }
3493     auto min = std::min(start, end);
3494     auto max = std::max(start, end);
3495     if (getSubstrDirectly) {
3496         min = std::clamp(min, 0, static_cast<int32_t>(value.length()));
3497         return value.substr(min, max - min);
3498     } else {
3499         return TextEmojiProcessor::SubU16string(min, max - min, value, includeStartHalf, includeEndHalf);
3500     }
3501 }
3502 
GetTextStyleObject(const RefPtr<SpanNode> & node)3503 TextStyleResult TextPattern::GetTextStyleObject(const RefPtr<SpanNode>& node)
3504 {
3505     TextStyleResult textStyle;
3506     textStyle.fontColor = node->GetTextColorValue(Color::BLACK).ColorToString();
3507     textStyle.fontStyle = static_cast<int32_t>(node->GetItalicFontStyleValue(OHOS::Ace::FontStyle::NORMAL));
3508     textStyle.fontWeight = static_cast<int32_t>(node->GetFontWeightValue(FontWeight::NORMAL));
3509     std::string fontFamilyValue;
3510     const std::vector<std::string> defaultFontFamily = { "HarmonyOS Sans" };
3511     auto fontFamily = node->GetFontFamilyValue(defaultFontFamily);
3512     for (const auto& str : fontFamily) {
3513         fontFamilyValue += str;
3514         fontFamilyValue += ",";
3515     }
3516     fontFamilyValue =
3517         fontFamilyValue.substr(0, !fontFamilyValue.empty() ? static_cast<int32_t>(fontFamilyValue.size()) - 1 : 0);
3518     textStyle.fontFamily = !fontFamilyValue.empty() ? fontFamilyValue : defaultFontFamily.front();
3519     textStyle.decorationType = static_cast<int32_t>(node->GetTextDecorationFirst());
3520     textStyle.decorationColor = node->GetTextDecorationColorValue(Color::BLACK).ColorToString();
3521     textStyle.decorationStyle = static_cast<int32_t>(node->GetTextDecorationStyleValue(TextDecorationStyle::SOLID));
3522     textStyle.lineThicknessScale = node->GetLineThicknessScaleValue(1.0f);
3523     textStyle.textAlign = static_cast<int32_t>(node->GetTextAlignValue(TextAlign::START));
3524     auto lm = node->GetLeadingMarginValue({});
3525     if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
3526         textStyle.fontSize = node->GetFontSizeValue(Dimension(16.0f, DimensionUnit::VP)).ConvertToFp();
3527         textStyle.lineHeight = node->GetLineHeightValue(Dimension()).ConvertToFp();
3528         textStyle.letterSpacing = node->GetLetterSpacingValue(Dimension()).ConvertToFp();
3529         textStyle.lineSpacing = node->GetLineSpacingValue(Dimension()).ConvertToFp();
3530     } else {
3531         textStyle.fontSize = node->GetFontSizeValue(Dimension(16.0f, DimensionUnit::VP)).ConvertToVp();
3532         textStyle.lineHeight = node->GetLineHeightValue(Dimension()).ConvertToVp();
3533         textStyle.letterSpacing = node->GetLetterSpacingValue(Dimension()).ConvertToVp();
3534         textStyle.lineSpacing = node->GetLineSpacingValue(Dimension()).ConvertToVp();
3535     }
3536     textStyle.optimizeTrailingSpace = node->GetOptimizeTrailingSpaceValue(false);
3537     textStyle.halfLeading = node->GetHalfLeadingValue(false);
3538     textStyle.fontFeature = node->GetFontFeatureValue(ParseFontFeatureSettings("\"pnum\" 1"));
3539     textStyle.leadingMarginSize[RichEditorLeadingRange::LEADING_START] = lm.size.Width().ToString();
3540     textStyle.leadingMarginSize[RichEditorLeadingRange::LEADING_END] = lm.size.Height().ToString();
3541     textStyle.wordBreak = static_cast<int32_t>(node->GetWordBreakValue(WordBreak::BREAK_WORD));
3542     textStyle.lineBreakStrategy = static_cast<int32_t>(node->GetLineBreakStrategyValue(LineBreakStrategy::GREEDY));
3543     textStyle.textShadows = node->GetTextShadowValue({});
3544     textStyle.textBackgroundStyle = node->GetTextBackgroundStyle();
3545     textStyle.paragraphSpacing = node->GetParagraphSpacing();
3546     auto textVerticalAlign = node->GetTextVerticalAlign();
3547     if (textVerticalAlign.has_value()) {
3548         textStyle.textVerticalAlign =static_cast<int32_t>(textVerticalAlign.value());
3549     }
3550     return textStyle;
3551 }
3552 
GetChildByIndex(int32_t index) const3553 RefPtr<UINode> TextPattern::GetChildByIndex(int32_t index) const
3554 {
3555     const auto& children = childNodes_;
3556     int32_t size = static_cast<int32_t>(children.size());
3557     if (index < 0 || index >= size) {
3558         return nullptr;
3559     }
3560     auto pos = children.begin();
3561     std::advance(pos, index);
3562     return *pos;
3563 }
3564 
GetSpanItemByIndex(int32_t index) const3565 RefPtr<SpanItem> TextPattern::GetSpanItemByIndex(int32_t index) const
3566 {
3567     int32_t size = static_cast<int32_t>(spans_.size());
3568     if (index < 0 || index >= size) {
3569         return nullptr;
3570     }
3571     auto pos = spans_.begin();
3572     std::advance(pos, index);
3573     return *pos;
3574 }
3575 
GetTextResultObject(RefPtr<UINode> uinode,int32_t index,int32_t start,int32_t end)3576 ResultObject TextPattern::GetTextResultObject(RefPtr<UINode> uinode, int32_t index, int32_t start, int32_t end)
3577 {
3578     bool selectFlag = false;
3579     ResultObject resultObject;
3580     if (!DynamicCast<SpanNode>(uinode)) {
3581         return resultObject;
3582     }
3583     auto spanItem = DynamicCast<SpanNode>(uinode)->GetSpanItem();
3584     int32_t itemLength = static_cast<int32_t>(spanItem->content.length());
3585     int32_t endPosition = std::min(GetTextContentLength(), spanItem->position);
3586     int32_t startPosition = endPosition - itemLength;
3587 
3588     if (startPosition >= start && endPosition <= end) {
3589         selectFlag = true;
3590         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
3591         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
3592     } else if (startPosition < start && endPosition <= end && endPosition > start) {
3593         selectFlag = true;
3594         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
3595         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
3596     } else if (startPosition >= start && startPosition < end && endPosition >= end) {
3597         selectFlag = true;
3598         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
3599         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
3600     } else if (startPosition <= start && endPosition >= end) {
3601         selectFlag = true;
3602         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
3603         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
3604     }
3605     if (selectFlag) {
3606         resultObject.spanPosition.spanIndex = index;
3607         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
3608         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
3609         resultObject.type = SelectSpanType::TYPESPAN;
3610         SetResultObjectText(resultObject, spanItem);
3611         auto spanNode = DynamicCast<SpanNode>(uinode);
3612         resultObject.textStyle = GetTextStyleObject(spanNode);
3613     }
3614     return resultObject;
3615 }
3616 
SetResultObjectText(ResultObject & resultObject,const RefPtr<SpanItem> & spanItem)3617 void TextPattern::SetResultObjectText(ResultObject& resultObject, const RefPtr<SpanItem>& spanItem)
3618 {
3619     CHECK_NULL_VOID(spanItem);
3620     resultObject.valueString = spanItem->content;
3621 }
3622 
GetSymbolSpanResultObject(RefPtr<UINode> uinode,int32_t index,int32_t start,int32_t end)3623 ResultObject TextPattern::GetSymbolSpanResultObject(RefPtr<UINode> uinode, int32_t index, int32_t start, int32_t end)
3624 {
3625     bool selectFlag = false;
3626     ResultObject resultObject;
3627     resultObject.isDraggable = false;
3628     if (!DynamicCast<SpanNode>(uinode)) {
3629         return resultObject;
3630     }
3631     auto spanItem = DynamicCast<SpanNode>(uinode)->GetSpanItem();
3632     int32_t itemLength = static_cast<int32_t>(spanItem->content.length());
3633     int32_t endPosition = std::min(GetTextContentLength(), spanItem->position);
3634     int32_t startPosition = endPosition - itemLength;
3635 
3636     if (startPosition >= start && endPosition <= end) {
3637         selectFlag = true;
3638         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
3639         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
3640     } else if (startPosition < start && endPosition <= end && endPosition > start) {
3641         selectFlag = true;
3642         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
3643         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
3644     } else if (startPosition >= start && startPosition < end && endPosition >= end) {
3645         selectFlag = true;
3646         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
3647         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
3648     } else if (startPosition <= start && endPosition >= end) {
3649         selectFlag = true;
3650         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
3651         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
3652     }
3653     if (selectFlag) {
3654         resultObject.valueResource = spanItem->GetResourceObject();
3655         resultObject.spanPosition.spanIndex = index;
3656         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
3657         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
3658         resultObject.type = SelectSpanType::TYPESYMBOLSPAN;
3659         resultObject.valueString = UtfUtils::Str8ToStr16(std::to_string(spanItem->unicode));
3660         auto spanNode = DynamicCast<SpanNode>(uinode);
3661         resultObject.symbolSpanStyle = GetSymbolSpanStyleObject(spanNode);
3662     }
3663     return resultObject;
3664 }
3665 
GetSymbolSpanStyleObject(const RefPtr<SpanNode> & node)3666 SymbolSpanStyle TextPattern::GetSymbolSpanStyleObject(const RefPtr<SpanNode>& node)
3667 {
3668     SymbolSpanStyle symbolSpanStyle;
3669     std::string symbolColorValue;
3670     auto symbolColors = node->GetSymbolColorList();
3671     if (symbolColors.has_value()) {
3672         for (const auto& color : *symbolColors) {
3673             symbolColorValue += color.ColorToString() + ",";
3674         }
3675     }
3676     symbolColorValue =
3677         symbolColorValue.substr(0, !symbolColorValue.empty() ? static_cast<int32_t>(symbolColorValue.size()) - 1 : 0);
3678     symbolSpanStyle.symbolColor = !symbolColorValue.empty() ? symbolColorValue : SYMBOL_COLOR;
3679     if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
3680         symbolSpanStyle.fontSize = node->GetFontSizeValue(Dimension(DIMENSION_VALUE, DimensionUnit::VP)).ConvertToFp();
3681     } else {
3682         symbolSpanStyle.fontSize = node->GetFontSizeValue(Dimension(DIMENSION_VALUE, DimensionUnit::VP)).ConvertToVp();
3683     }
3684     symbolSpanStyle.fontWeight = static_cast<int32_t>(node->GetFontWeightValue(FontWeight::NORMAL));
3685     symbolSpanStyle.renderingStrategy = static_cast<int32_t>(node->GetSymbolRenderingStrategyValue(0));
3686     symbolSpanStyle.effectStrategy = static_cast<int32_t>(node->GetSymbolEffectStrategyValue(0));
3687     return symbolSpanStyle;
3688 }
3689 
GetImageResultObject(RefPtr<UINode> uinode,int32_t index,int32_t start,int32_t end)3690 ResultObject TextPattern::GetImageResultObject(RefPtr<UINode> uinode, int32_t index, int32_t start, int32_t end)
3691 {
3692     int32_t itemLength = 1;
3693     ResultObject resultObject;
3694     if (!DynamicCast<FrameNode>(uinode) || !GetSpanItemByIndex(index)) {
3695         return resultObject;
3696     }
3697     int32_t endPosition = std::min(GetTextContentLength(), GetSpanItemByIndex(index)->position);
3698     int32_t startPosition = endPosition - itemLength;
3699     if ((start <= startPosition) && (end >= endPosition)) {
3700         auto imageNode = DynamicCast<FrameNode>(uinode);
3701         CHECK_NULL_RETURN(imageNode, resultObject);
3702         auto imageLayoutProperty = DynamicCast<ImageLayoutProperty>(imageNode->GetLayoutProperty());
3703         CHECK_NULL_RETURN(imageLayoutProperty, resultObject);
3704         resultObject.spanPosition.spanIndex = index;
3705         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
3706         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
3707         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
3708         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
3709         resultObject.type = SelectSpanType::TYPEIMAGE;
3710         if (imageLayoutProperty->GetImageSourceInfo() && !imageLayoutProperty->GetImageSourceInfo()->GetPixmap()) {
3711             resultObject.valueString = UtfUtils::Str8DebugToStr16(imageLayoutProperty->GetImageSourceInfo()->GetSrc());
3712         } else {
3713             resultObject.valuePixelMap = imageLayoutProperty->GetImageSourceInfo()->GetPixmap();
3714         }
3715         auto geometryNode = imageNode->GetGeometryNode();
3716         resultObject.imageStyle.size[RichEditorImageSize::SIZEWIDTH] = geometryNode->GetMarginFrameSize().Width();
3717         resultObject.imageStyle.size[RichEditorImageSize::SIZEHEIGHT] = geometryNode->GetMarginFrameSize().Height();
3718         if (imageLayoutProperty->HasImageFit()) {
3719             resultObject.imageStyle.objectFit = static_cast<int32_t>(imageLayoutProperty->GetImageFitValue());
3720         }
3721         if (imageLayoutProperty->HasVerticalAlign()) {
3722             resultObject.imageStyle.verticalAlign = static_cast<int32_t>(imageLayoutProperty->GetVerticalAlignValue());
3723         }
3724         if (imageLayoutProperty->GetMarginProperty()) {
3725             resultObject.imageStyle.margin = imageLayoutProperty->GetMarginProperty()->ToString();
3726         }
3727         auto imageRenderCtx = imageNode->GetRenderContext();
3728         if (imageRenderCtx->GetBorderRadius()) {
3729             BorderRadiusProperty brp;
3730             auto jsonObject = JsonUtil::Create(true);
3731             auto jsonBorder = JsonUtil::Create(true);
3732             InspectorFilter emptyFilter;
3733             imageRenderCtx->GetBorderRadiusValue(brp).ToJsonValue(jsonObject, jsonBorder, emptyFilter);
3734             resultObject.imageStyle.borderRadius = jsonObject->GetValue("borderRadius")->IsObject()
3735                                                        ? jsonObject->GetValue("borderRadius")->ToString()
3736                                                        : jsonObject->GetString("borderRadius");
3737         }
3738     }
3739     return resultObject;
3740 }
3741 
3742 // ===========================================================
3743 // TextDragBase implementations
GetLineHeight() const3744 float TextPattern::GetLineHeight() const
3745 {
3746     auto selectedRects = pManager_->GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
3747     CHECK_NULL_RETURN(selectedRects.size(), {});
3748     return selectedRects.front().Height();
3749 }
3750 
GetTextBoxes()3751 std::vector<RectF> TextPattern::GetTextBoxes()
3752 {
3753     if (IsAiSelected()) {
3754         return pManager_->GetRects(textSelector_.aiStart.value(), textSelector_.aiEnd.value());
3755     }
3756     return pManager_->GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
3757 }
3758 
GetParentGlobalOffset() const3759 OffsetF TextPattern::GetParentGlobalOffset() const
3760 {
3761     selectOverlay_->UpdateHandleGlobalOffset();
3762     auto host = GetHost();
3763     CHECK_NULL_RETURN(host, {});
3764     auto pipeline = host->GetContext();
3765     CHECK_NULL_RETURN(pipeline, {});
3766     auto rootOffset = pipeline->GetRootRect().GetOffset();
3767     return host->GetPaintRectOffsetNG(false, true) - rootOffset;
3768 }
3769 
CreateHandles()3770 void TextPattern::CreateHandles()
3771 {
3772     if (IsDragging() || IsAiSelected()) {
3773         TAG_LOGI(AceLogTag::ACE_TEXT, "do not show handles when dragging or AISpan selected");
3774         return;
3775     }
3776     ShowSelectOverlay({ .menuIsShow = false });
3777 }
3778 
BetweenSelectedPosition(const Offset & globalOffset)3779 bool TextPattern::BetweenSelectedPosition(const Offset& globalOffset)
3780 {
3781     auto host = GetHost();
3782     CHECK_NULL_RETURN(host, false);
3783     auto offset = host->GetPaintRectOffset(false, true);
3784     auto localOffset = globalOffset - Offset(offset.GetX(), offset.GetY());
3785     if (selectOverlay_->HasRenderTransform()) {
3786         localOffset = ConvertGlobalToLocalOffset(globalOffset);
3787     }
3788     auto result = IsDraggable(localOffset);
3789     return result;
3790 }
3791 
LogForFormRender(const std::string & logTag)3792 void TextPattern::LogForFormRender(const std::string& logTag)
3793 {
3794     auto host = GetHost();
3795     CHECK_NULL_VOID(host);
3796     auto pipeline = host->GetContext();
3797     CHECK_NULL_VOID(pipeline);
3798     if (pipeline->IsFormRender() && !IsSetObscured() && !IsSensitiveEnable()) {
3799         auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
3800         CHECK_NULL_VOID(textLayoutProperty);
3801         auto content = textLayoutProperty->GetContent().value_or(u"");
3802         if (content.length() == 1) {
3803             TAG_LOGI(AceLogTag::ACE_TEXT, "%{public}s, content:%{public}s id:%{public}d", logTag.c_str(),
3804                 UtfUtils::Str16ToStr8(content).c_str(), host->GetId());
3805         }
3806     }
3807 }
3808 
3809 // end of TextDragBase implementations
3810 // ===========================================================
OnModifyDone()3811 void TextPattern::OnModifyDone()
3812 {
3813     auto host = GetHost();
3814     Pattern::OnModifyDone();
3815     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
3816     CHECK_NULL_VOID(textLayoutProperty);
3817     auto contentHost = GetContentHost();
3818     CHECK_NULL_VOID(host && contentHost);
3819     auto renderContext = host->GetRenderContext();
3820     CHECK_NULL_VOID(renderContext);
3821     auto nowTime = static_cast<unsigned long long>(GetSystemTimestamp());
3822     ACE_TEXT_SCOPED_TRACE("OnModifyDone[Text][id:%d][time:%llu]", host->GetId(), nowTime);
3823     auto pipeline = host->GetContext();
3824     if (!(pipeline && pipeline->GetMinPlatformVersion() > API_PROTEXTION_GREATER_NINE)) {
3825         bool shouldClipToContent =
3826             textLayoutProperty->GetTextOverflow().value_or(TextOverflow::CLIP) == TextOverflow::CLIP;
3827         host->GetRenderContext()->SetClipToFrame(shouldClipToContent);
3828     }
3829     if (textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE) {
3830         if (!renderContext->GetClipEdge().has_value()) {
3831             renderContext->UpdateClipEdge(true);
3832             renderContext->SetClipToFrame(true);
3833         }
3834         UpdateMarqueeStartPolicy();
3835     }
3836     const auto& children = contentHost->GetChildren();
3837     if (children.empty()) {
3838         std::u16string textCache = textForDisplay_;
3839         if (!isSpanStringMode_) {
3840             textForDisplay_ = textLayoutProperty->GetContent().value_or(u"");
3841         }
3842         if (textCache != textForDisplay_) {
3843             host->OnAccessibilityEvent(AccessibilityEventType::TEXT_CHANGE, UtfUtils::Str16DebugToStr8(textCache),
3844                 UtfUtils::Str16DebugToStr8(textForDisplay_));
3845             if (dataDetectorAdapter_) {
3846                 dataDetectorAdapter_->aiDetectInitialized_ = false;
3847             }
3848         }
3849         if (CanStartAITask() && !GetDataDetectorAdapter()->aiDetectInitialized_) {
3850             ParseOriText(textForDisplay_);
3851         }
3852         if (textCache != textForDisplay_) { // textForDisplay_ is updated by ParseOriText
3853             CloseSelectOverlay();
3854             ResetSelection();
3855         }
3856     }
3857     ResetTextEffectBeforeLayout();
3858     RecoverCopyOption();
3859     RegisterFormVisibleChangeCallback();
3860     RegisterVisibleAreaChangeCallback();
3861 }
3862 
UpdateMarqueeStartPolicy()3863 void TextPattern::UpdateMarqueeStartPolicy()
3864 {
3865     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
3866     CHECK_NULL_VOID(textLayoutProperty);
3867     if (!textLayoutProperty->HasTextMarqueeStartPolicy()) {
3868         auto host = GetHost();
3869         CHECK_NULL_VOID(host);
3870         auto context = host->GetContext();
3871         CHECK_NULL_VOID(context);
3872         auto theme = context->GetTheme<TextTheme>();
3873         CHECK_NULL_VOID(theme);
3874         textLayoutProperty->UpdateTextMarqueeStartPolicy(theme->GetMarqueeStartPolicy());
3875     }
3876     if (textLayoutProperty->GetTextMarqueeStartPolicyValue(MarqueeStartPolicy::DEFAULT) ==
3877         MarqueeStartPolicy::ON_FOCUS) {
3878         InitFocusEvent();
3879         InitHoverEvent();
3880     }
3881 }
3882 
SetActionExecSubComponent()3883 bool TextPattern::SetActionExecSubComponent()
3884 {
3885     auto host = GetHost();
3886     CHECK_NULL_RETURN(host, false);
3887     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
3888     CHECK_NULL_RETURN(accessibilityProperty, false);
3889     accessibilityProperty->SetActionExecSubComponent([weakPtr = WeakClaim(this)](int32_t spanId) -> bool {
3890             const auto& pattern = weakPtr.Upgrade();
3891             CHECK_NULL_RETURN(pattern, false);
3892             return pattern->ExecSubComponent(spanId);
3893         });
3894     return true;
3895 }
3896 
GetSubComponentInfos(std::vector<SubComponentInfo> & subComponentInfos)3897 size_t TextPattern::GetSubComponentInfos(std::vector<SubComponentInfo>& subComponentInfos)
3898 {
3899     subComponentInfos.clear();
3900     subComponentInfos_.clear();
3901     if (spans_.empty()) {
3902         GetSubComponentInfosForAISpans(subComponentInfos);
3903     } else {
3904         GetSubComponentInfosForSpans(subComponentInfos);
3905     }
3906     SetActionExecSubComponent();
3907     return subComponentInfos.size();
3908 }
3909 
GetSubComponentInfosForAISpans(std::vector<SubComponentInfo> & subComponentInfos)3910 void TextPattern::GetSubComponentInfosForAISpans(std::vector<SubComponentInfo>& subComponentInfos)
3911 {
3912     CHECK_NULL_VOID(dataDetectorAdapter_);
3913     for (const auto& kv : dataDetectorAdapter_->aiSpanMap_) {
3914         auto& aiSpan = kv.second;
3915         AddSubComponentInfoForAISpan(subComponentInfos, aiSpan.content, aiSpan);
3916     }
3917 }
3918 
GetSubComponentInfosForSpans(std::vector<SubComponentInfo> & subComponentInfos)3919 void TextPattern::GetSubComponentInfosForSpans(std::vector<SubComponentInfo>& subComponentInfos)
3920 {
3921     for (const auto& span : spans_) {
3922         if (span == nullptr) {
3923             continue; // skip null
3924         }
3925         if ((span->spanItemType == SpanItemType::IMAGE) || (span->unicode > 0)) {
3926             continue;  // skip ImageSpan and SymbolSpan
3927         }
3928         if (span->spanItemType == SpanItemType::CustomSpan) {
3929             continue; // skip CustomSpan
3930         }
3931         auto placeholderSpan = DynamicCast<PlaceholderSpanItem>(span);
3932         if ((placeholderSpan != nullptr) && (placeholderSpan->placeholderSpanNodeId >=0)) {
3933             continue; // skip PlaceholderSpan
3934         }
3935         if (span->content.empty()) {
3936             continue; // skip empty text
3937         }
3938         AddSubComponentInfoForSpan(subComponentInfos, UtfUtils::Str16DebugToStr8(span->content), span);
3939         AddSubComponentInfosByDataDetectorForSpan(subComponentInfos, span);
3940     }
3941 }
3942 
AddSubComponentInfosByDataDetectorForSpan(std::vector<SubComponentInfo> & subComponentInfos,const RefPtr<SpanItem> & span)3943 void TextPattern::AddSubComponentInfosByDataDetectorForSpan(std::vector<SubComponentInfo>& subComponentInfos,
3944     const RefPtr<SpanItem>& span)
3945 {
3946     CHECK_NULL_VOID(span);
3947     CHECK_NULL_VOID(dataDetectorAdapter_);
3948     int32_t wSpanContentLength = static_cast<int32_t>(span->content.length());
3949     int32_t spanStart = span->position - wSpanContentLength;
3950     if (span->needRemoveNewLine) {
3951         spanStart -= 1;
3952     }
3953     int32_t preEnd = spanStart;
3954     auto aiSpanMap = dataDetectorAdapter_->aiSpanMap_;
3955     while (!aiSpanMap.empty()) {
3956         auto aiSpan = aiSpanMap.begin()->second;
3957         if (aiSpan.start >= span->position || preEnd >= span->position) {
3958             break;
3959         }
3960         int32_t aiSpanStartInSpan = std::max(spanStart, aiSpan.start);
3961         int32_t aiSpanEndInSpan = std::min(span->position, aiSpan.end);
3962         if (aiSpan.end <= spanStart || aiSpanStartInSpan < preEnd) {
3963             TAG_LOGI(AceLogTag::ACE_TEXT, "Error prediction");
3964             aiSpanMap.erase(aiSpanMap.begin());
3965             continue;
3966         }
3967         AddSubComponentInfoForAISpan(subComponentInfos, aiSpan.content, aiSpan);
3968         preEnd = aiSpanEndInSpan;
3969         if (aiSpan.end > span->position) {
3970             return;
3971         } else {
3972             aiSpanMap.erase(aiSpanMap.begin());
3973         }
3974     }
3975 }
3976 
ExecSubComponent(int32_t spanId)3977 bool TextPattern::ExecSubComponent(int32_t spanId)
3978 {
3979     if ((spanId < 0) || (spanId >= static_cast<int32_t>(subComponentInfos_.size()))) {
3980         return false;
3981     }
3982     auto subComponentInfo = subComponentInfos_[spanId];
3983     if (subComponentInfo.aiSpan.has_value()) {
3984         CHECK_NULL_RETURN(GetDataDetectorAdapter(), false);
3985         dataDetectorAdapter_->ResponseBestMatchItem(subComponentInfo.aiSpan.value());
3986         return true;
3987     }
3988     const auto& span = subComponentInfo.span.Upgrade();
3989     CHECK_NULL_RETURN(span, false);
3990     CHECK_NULL_RETURN(span->onClick, false);
3991     GestureEvent info;
3992     std::chrono::microseconds microseconds(GetMicroTickCount());
3993     TimeStamp time(microseconds);
3994     info.SetTimeStamp(time);
3995     span->onClick(info);
3996     return true;
3997 }
3998 
AddSubComponentInfoForSpan(std::vector<SubComponentInfo> & subComponentInfos,const std::string & content,const RefPtr<SpanItem> & span)3999 void TextPattern::AddSubComponentInfoForSpan(std::vector<SubComponentInfo>& subComponentInfos,
4000     const std::string& content, const RefPtr<SpanItem>& span)
4001 {
4002     CHECK_NULL_VOID(span);
4003     CHECK_NULL_VOID(span->onClick); // skip null onClick
4004     SubComponentInfo subComponentInfo;
4005     subComponentInfo.spanId = static_cast<int32_t>(subComponentInfos.size());
4006     subComponentInfo.spanText = content;
4007     if (span->accessibilityProperty == nullptr) {
4008         subComponentInfo.accessibilityLevel = AccessibilityProperty::Level::AUTO;
4009     } else {
4010         subComponentInfo.accessibilityText = span->accessibilityProperty->GetAccessibilityText();
4011         subComponentInfo.accessibilityDescription =
4012             span->accessibilityProperty->GetAccessibilityDescription();
4013         subComponentInfo.accessibilityLevel = span->accessibilityProperty->GetAccessibilityLevel();
4014     }
4015     subComponentInfos.emplace_back(subComponentInfo);
4016 
4017     SubComponentInfoEx subComponentInfoEx;
4018     subComponentInfoEx.span = span;
4019     subComponentInfos_.emplace_back(subComponentInfoEx);
4020 }
4021 
AddSubComponentInfoForAISpan(std::vector<SubComponentInfo> & subComponentInfos,const std::string & content,const AISpan & aiSpan)4022 void TextPattern::AddSubComponentInfoForAISpan(std::vector<SubComponentInfo>& subComponentInfos,
4023     const std::string& content, const AISpan& aiSpan)
4024 {
4025     SubComponentInfo subComponentInfo;
4026     subComponentInfo.spanId = static_cast<int32_t>(subComponentInfos.size());
4027     subComponentInfo.spanText = content;
4028     subComponentInfo.accessibilityLevel = AccessibilityProperty::Level::AUTO;
4029     subComponentInfos.emplace_back(subComponentInfo);
4030 
4031     SubComponentInfoEx subComponentInfoEx;
4032     subComponentInfoEx.aiSpan = aiSpan;
4033     subComponentInfos_.emplace_back(subComponentInfoEx);
4034 }
4035 
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const4036 void TextPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
4037 {
4038     json->PutFixedAttr("content", UtfUtils::Str16ToStr8(textForDisplay_).c_str(), filter, FIXED_ATTR_CONTENT);
4039     /* no fixed attr below, just return */
4040     if (filter.IsFastFilter()) {
4041         return;
4042     }
4043     json->PutExtAttr("enableDataDetector", textDetectEnable_ ? "true" : "false", filter);
4044     json->PutExtAttr("dataDetectorConfig",
4045         dataDetectorAdapter_ ? dataDetectorAdapter_->textDetectConfigStr_.c_str() : "", filter);
4046     const auto& selector = GetTextSelector();
4047     auto result = "[" + std::to_string(selector.GetTextStart()) + "," + std::to_string(selector.GetTextEnd()) + "]";
4048     json->PutExtAttr("selection", result.c_str(), filter);
4049     auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
4050     CHECK_NULL_VOID(textLayoutProp);
4051     json->PutExtAttr("fontSize", GetFontSizeWithThemeInJson(textLayoutProp->GetFontSize()).c_str(), filter);
4052     if (textStyle_.has_value() && textStyle_->GetAdaptTextSize()) {
4053         auto adaptedFontSize = textStyle_->GetFontSize();
4054         json->PutExtAttr("actualFontSize", adaptedFontSize.ToString().c_str(), filter);
4055     } else {
4056         json->PutExtAttr("actualFontSize", GetFontSizeWithThemeInJson(textLayoutProp->GetFontSize()).c_str(), filter);
4057     }
4058     json->PutExtAttr("font", GetFontInJson().c_str(), filter);
4059     json->PutExtAttr("bindSelectionMenu", GetBindSelectionMenuInJson().c_str(), filter);
4060     json->PutExtAttr("caretColor", GetCaretColor().c_str(), filter);
4061     json->PutExtAttr("selectedBackgroundColor", GetSelectedBackgroundColor().c_str(), filter);
4062     json->PutExtAttr("enableHapticFeedback", isEnableHapticFeedback_ ? "true" : "false", filter);
4063     json->PutExtAttr("shaderStyle", GetShaderStyleInJson(), filter);
4064 }
4065 
GetShaderStyleInJson() const4066 std::unique_ptr<JsonValue> TextPattern::GetShaderStyleInJson() const
4067 {
4068     auto resultJson = JsonUtil::Create(true);
4069     auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
4070     CHECK_NULL_RETURN(layoutProperty, resultJson);
4071     auto shaderStyle = layoutProperty->GetShaderStyle();
4072     if (shaderStyle.has_value() && shaderStyle->size() > 0) {
4073         resultJson = ::OHOS::Ace::NG::GetShaderStyleInJson(shaderStyle);
4074         return resultJson;
4075     }
4076     if (layoutProperty->HasGradientShaderStyle()) {
4077         auto propGradient = layoutProperty->GetGradientShaderStyle().value_or(Gradient());
4078         auto type = propGradient.GetType();
4079         if (type == GradientType::LINEAR) {
4080             return GradientJsonUtils::LinearGradientToJson(propGradient);
4081         } else if (type == GradientType::RADIAL) {
4082             return GradientJsonUtils::RadialGradientToJson(propGradient);
4083         }
4084     } else if (layoutProperty->HasColorShaderStyle()) {
4085         resultJson->Put(
4086             "color", layoutProperty->GetColorShaderStyle().value_or(Color::TRANSPARENT).ColorToString().c_str());
4087         return resultJson;
4088     }
4089     return resultJson;
4090 }
4091 
GetBindSelectionMenuInJson() const4092 std::string TextPattern::GetBindSelectionMenuInJson() const
4093 {
4094     auto jsonArray = JsonUtil::CreateArray(true);
4095     for (auto& [spanResponsePair, params] : selectionMenuMap_) {
4096         auto& [spanType, responseType] = spanResponsePair;
4097         auto jsonItem = JsonUtil::Create(true);
4098         jsonItem->Put("spanType", TextSpanTypeMapper::GetJsSpanType(spanType, params->isValid));
4099         jsonItem->Put("responseType", static_cast<int32_t>(responseType));
4100         jsonItem->Put("menuType", static_cast<int32_t>(SelectionMenuType::SELECTION_MENU));
4101         jsonArray->Put(jsonItem);
4102     }
4103     FillPreviewMenuInJson(jsonArray);
4104     return StringUtils::RestoreBackslash(jsonArray->ToString());
4105 }
4106 
GetFontInJson() const4107 std::string TextPattern::GetFontInJson() const
4108 {
4109     auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
4110     CHECK_NULL_RETURN(textLayoutProp, "");
4111     auto jsonValue = JsonUtil::Create(true);
4112     jsonValue->Put("style", GetFontStyleInJson(textLayoutProp->GetItalicFontStyle()).c_str());
4113     jsonValue->Put("size", GetFontSizeWithThemeInJson(textLayoutProp->GetFontSize()).c_str());
4114     jsonValue->Put("weight", GetFontWeightInJson(textLayoutProp->GetFontWeight()).c_str());
4115     jsonValue->Put("variableFontWeight", std::to_string(textLayoutProp->GetVariableFontWeight().value_or(0)).c_str());
4116     jsonValue->Put("enableVariableFontWeight",
4117                    textLayoutProp->GetEnableVariableFontWeight().value_or(false) ? "true" : "false");
4118     jsonValue->Put("family", GetFontFamilyInJson(textLayoutProp->GetFontFamily()).c_str());
4119     return jsonValue->ToString();
4120 }
4121 
GetFontSizeWithThemeInJson(const std::optional<Dimension> & value) const4122 std::string TextPattern::GetFontSizeWithThemeInJson(const std::optional<Dimension>& value) const
4123 {
4124     auto host = GetHost();
4125     CHECK_NULL_RETURN(host, "");
4126     auto pipeline = host->GetContext();
4127     CHECK_NULL_RETURN(pipeline, "");
4128     auto theme = pipeline->GetTheme<TextTheme>();
4129     CHECK_NULL_RETURN(theme, "");
4130     return value.value_or(theme->GetTextStyle().GetFontSize()).ToString();
4131 }
4132 
ToTreeJson(std::unique_ptr<JsonValue> & json,const InspectorConfig & config) const4133 void TextPattern::ToTreeJson(std::unique_ptr<JsonValue>& json, const InspectorConfig& config) const
4134 {
4135     Pattern::ToTreeJson(json, config);
4136     if (!textForDisplay_.empty()) {
4137         json->Put(TreeKey::CONTENT, UtfUtils::Str16DebugToStr8(textForDisplay_).c_str());
4138     }
4139 }
4140 
OnAfterModifyDone()4141 void TextPattern::OnAfterModifyDone()
4142 {
4143     auto host = GetHost();
4144     CHECK_NULL_VOID(host);
4145     auto inspectorId = host->GetInspectorId().value_or("");
4146     if (!inspectorId.empty()) {
4147         auto prop = host->GetAccessibilityProperty<NG::AccessibilityProperty>();
4148         Recorder::NodeDataCache::Get().PutString(host, inspectorId, prop->GetText());
4149     }
4150 }
4151 
ActSetSelection(int32_t start,int32_t end)4152 void TextPattern::ActSetSelection(int32_t start, int32_t end)
4153 {
4154     auto host = GetHost();
4155     CHECK_NULL_VOID(host);
4156     int32_t min = 0;
4157     int32_t textSize = static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_;
4158     start = start < min ? min : start;
4159     end = end < min ? min : end;
4160     start = start > textSize ? textSize : start;
4161     end = end > textSize ? textSize : end;
4162     if (SystemProperties::GetTextTraceEnabled()) {
4163         ACE_TEXT_SCOPED_TRACE("TextPattern::ActSetSelection[id:%d][start:%d][end:%d][textSize:%d][placeholderCount:%d]",
4164             host->GetId(), start, end, textSize, placeholderCount_);
4165     }
4166     if (start >= end) {
4167         ResetSelection();
4168         CloseSelectOverlay();
4169         return;
4170     }
4171     HandleSelectionChange(start, end);
4172     CalculateHandleOffsetAndShowOverlay();
4173     if (textSelector_.firstHandle == textSelector_.secondHandle && pManager_) {
4174         ResetSelection();
4175         CloseSelectOverlay();
4176         return;
4177     }
4178     if (IsShowHandle()) {
4179         ShowSelectOverlay();
4180     } else {
4181         CloseSelectOverlay();
4182         if (IsSelected()) {
4183             selectOverlay_->SetSelectionHoldCallback();
4184         }
4185     }
4186     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
4187 }
4188 
IsShowHandle()4189 bool TextPattern::IsShowHandle()
4190 {
4191     auto host = GetHost();
4192     CHECK_NULL_RETURN(host, false);
4193     auto pipeline = host->GetContext();
4194     CHECK_NULL_RETURN(pipeline, false);
4195     auto theme = pipeline->GetTheme<TextTheme>(GetThemeScopeId());
4196     CHECK_NULL_RETURN(theme, false);
4197     return !theme->IsShowHandle();
4198 }
4199 
GetUrlHoverColor()4200 Color TextPattern::GetUrlHoverColor()
4201 {
4202     auto host = GetHost();
4203     CHECK_NULL_RETURN(host, Color());
4204     auto pipeline = host->GetContext();
4205     CHECK_NULL_RETURN(pipeline, Color());
4206     auto theme = pipeline->GetTheme<TextTheme>();
4207     CHECK_NULL_RETURN(theme, Color());
4208     return theme->GetUrlHoverColor();
4209 }
4210 
GetUrlPressColor()4211 Color TextPattern::GetUrlPressColor()
4212 {
4213     auto host = GetHost();
4214     CHECK_NULL_RETURN(host, Color());
4215     auto pipeline = host->GetContext();
4216     CHECK_NULL_RETURN(pipeline, Color());
4217     auto theme = pipeline->GetTheme<TextTheme>();
4218     CHECK_NULL_RETURN(theme, Color());
4219     return theme->GetUrlPressColor();
4220 }
4221 
GetUrlSpanColor()4222 Color TextPattern::GetUrlSpanColor()
4223 {
4224     auto host = GetHost();
4225     CHECK_NULL_RETURN(host, Color());
4226     auto pipeline = host->GetContext();
4227     CHECK_NULL_RETURN(pipeline, Color());
4228     auto theme = pipeline->GetTheme<TextTheme>();
4229     CHECK_NULL_RETURN(theme, Color());
4230 
4231     auto eventHub = host->GetOrCreateEventHub<EventHub>();
4232     CHECK_NULL_RETURN(eventHub, Color());
4233 
4234     if (eventHub && !eventHub->IsEnabled()) {
4235         return theme->GetUrlDisabledColor();
4236     } else {
4237         return theme->GetUrlDefaultColor();
4238     }
4239 }
4240 
4241 // Deprecated: Use the TextSelectOverlay::ProcessOverlay() instead.
4242 // It is currently used by RichEditorPattern.
UpdateSelectOverlayOrCreate(SelectOverlayInfo & selectInfo,bool animation)4243 void TextPattern::UpdateSelectOverlayOrCreate(SelectOverlayInfo& selectInfo, bool animation)
4244 {
4245     if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
4246         SelectHandleInfo firstHandleInfo;
4247         firstHandleInfo.paintRect = textSelector_.firstHandle;
4248         CheckHandles(firstHandleInfo);
4249 
4250         SelectHandleInfo secondHandleInfo;
4251         secondHandleInfo.paintRect = textSelector_.secondHandle;
4252         CheckHandles(secondHandleInfo);
4253 
4254         auto start = textSelector_.GetTextStart();
4255         auto end = textSelector_.GetTextEnd();
4256         selectOverlayProxy_->SetSelectInfo(UtfUtils::Str16DebugToStr8(GetSelectedText(start, end)));
4257         if (selectInfo.isNewAvoid) {
4258             selectOverlayProxy_->UpdateSelectArea(selectInfo.selectArea);
4259         }
4260         selectOverlayProxy_->UpdateFirstAndSecondHandleInfo(firstHandleInfo, secondHandleInfo);
4261         selectOverlayProxy_->ShowOrHiddenMenu(!firstHandleInfo.isShow && !secondHandleInfo.isShow);
4262     } else {
4263         auto host = GetHost();
4264         CHECK_NULL_VOID(host);
4265         auto pipeline = host->GetContext();
4266         CHECK_NULL_VOID(pipeline);
4267         pipeline->AddOnAreaChangeNode(host->GetId());
4268         selectInfo.callerFrameNode = GetHost();
4269         selectInfo.hitTestMode = HitTestMode::HTMDEFAULT;
4270         if (!selectInfo.isUsingMouse) {
4271             CheckHandles(selectInfo.firstHandle);
4272             CheckHandles(selectInfo.secondHandle);
4273         }
4274         selectOverlayProxy_ =
4275             pipeline->GetSelectOverlayManager()->CreateAndShowSelectOverlay(selectInfo, WeakClaim(this), animation);
4276         CHECK_NULL_VOID(selectOverlayProxy_);
4277         auto start = textSelector_.GetTextStart();
4278         auto end = textSelector_.GetTextEnd();
4279         selectOverlayProxy_->SetSelectInfo(UtfUtils::Str16DebugToStr8(GetSelectedText(start, end)));
4280     }
4281 }
4282 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)4283 bool TextPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
4284 {
4285     if (config.skipMeasure || dirty->SkipMeasureContent()) {
4286         return false;
4287     }
4288 
4289     contentRect_ = dirty->GetGeometryNode()->GetContentRect();
4290     if (dataDetectorAdapter_) {
4291         dataDetectorAdapter_->aiSpanRects_.clear();
4292     }
4293 
4294     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
4295     CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
4296     auto textLayoutAlgorithm = DynamicCast<TextLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
4297     CHECK_NULL_RETURN(textLayoutAlgorithm, false);
4298     baselineOffset_ = textLayoutAlgorithm->GetBaselineOffset();
4299     contentOffset_ = dirty->GetGeometryNode()->GetContentOffset();
4300     textStyle_ = textLayoutAlgorithm->GetTextStyle();
4301     ProcessOverlayAfterLayout();
4302     return true;
4303 }
4304 
ProcessOverlayAfterLayout()4305 void TextPattern::ProcessOverlayAfterLayout()
4306 {
4307     if (selectOverlay_->SelectOverlayIsOn()) {
4308         CalculateHandleOffsetAndShowOverlay();
4309         selectOverlay_->UpdateAllHandlesOffset();
4310         selectOverlay_->UpdateViewPort();
4311     }
4312 }
4313 
PreCreateLayoutWrapper()4314 void TextPattern::PreCreateLayoutWrapper()
4315 {
4316     auto host = GetContentHost();
4317     CHECK_NULL_VOID(host);
4318 
4319     auto paintProperty = GetPaintProperty<PaintProperty>();
4320     CHECK_NULL_VOID(paintProperty);
4321     auto flag = paintProperty->GetPropertyChangeFlag();
4322     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4323     CHECK_NULL_VOID(textLayoutProperty);
4324     auto layoutFlag = textLayoutProperty->GetPropertyChangeFlag();
4325     if (!CheckNeedMeasure(flag) && !CheckNeedMeasure(layoutFlag)) {
4326         return;
4327     }
4328     auto beforeSpanSize = spans_.size();
4329     spans_.clear();
4330     childNodes_.clear();
4331 
4332     // When dirty areas are marked because of child node changes, the text rendering node tree is reset.
4333     const auto& children = host->GetChildren();
4334     if (children.empty()) {
4335         placeholderCount_ = 0;
4336         return;
4337     }
4338 
4339     // Depth-first iterates through all host's child nodes to collect the SpanNode object, building a text rendering
4340     // tree.
4341     std::stack<SpanNodeInfo> nodes;
4342     for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
4343         nodes.push({ .node = *iter });
4344     }
4345 
4346     InitSpanItem(nodes);
4347     CHECK_NULL_VOID(beforeSpanSize != spans_.size());
4348     textLayoutProperty->OnPropertyChangeMeasure();
4349 }
4350 
InitSpanItemEvent(bool & isSpanHasClick,bool & isSpanHasLongPress)4351 void TextPattern::InitSpanItemEvent(bool& isSpanHasClick, bool& isSpanHasLongPress)
4352 {
4353     auto host = GetHost();
4354     CHECK_NULL_VOID(host);
4355     auto gestureEventHub = host->GetOrCreateGestureEventHub();
4356     if (isSpanHasClick) {
4357         InitClickEvent(gestureEventHub);
4358     }
4359     if (isSpanHasLongPress) {
4360         InitLongPressEvent(gestureEventHub);
4361     }
4362 }
4363 
InitSpanItem(std::stack<SpanNodeInfo> nodes)4364 void TextPattern::InitSpanItem(std::stack<SpanNodeInfo> nodes)
4365 {
4366     auto host = GetHost();
4367     CHECK_NULL_VOID(host);
4368     // span perf can be optimize via create as requirement
4369     CHECK_NULL_VOID(GetDataDetectorAdapter());
4370     std::u16string textCache;
4371     std::u16string textForAICache;
4372     int32_t oldPlaceholderCount = placeholderCount_;
4373     placeholderCount_ = 0;
4374     if (!nodes.empty()) {
4375         textCache = textForDisplay_;
4376         textForAICache = dataDetectorAdapter_->textForAI_;
4377         textForDisplay_.clear();
4378         dataDetectorAdapter_->textForAI_.clear();
4379     }
4380 
4381     bool isSpanHasClick = false;
4382     bool isSpanHasLongPress = false;
4383     CollectSpanNodes(nodes, isSpanHasClick, isSpanHasLongPress);
4384     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4385     CHECK_NULL_VOID(textLayoutProperty);
4386     if (childNodes_.empty()) {
4387         textForDisplay_ = textLayoutProperty->GetContent().value_or(u"");
4388     }
4389     if (oldPlaceholderCount != placeholderCount_) {
4390         CloseSelectOverlay();
4391         ResetSelection();
4392     }
4393 
4394     if (textCache != textForDisplay_) {
4395         host->OnAccessibilityEvent(AccessibilityEventType::TEXT_CHANGE, UtfUtils::Str16DebugToStr8(textCache),
4396             UtfUtils::Str16DebugToStr8(textForDisplay_));
4397         OnAfterModifyDone();
4398         for (const auto& item : spans_) {
4399             if (item->inspectId.empty()) {
4400                 continue;
4401             }
4402             Recorder::NodeDataCache::Get().PutString(host, item->inspectId, UtfUtils::Str16DebugToStr8(item->content));
4403         }
4404         ResetAfterTextChange();
4405     }
4406     InitSpanItemEvent(isSpanHasClick, isSpanHasLongPress);
4407     if (textForAICache != dataDetectorAdapter_->textForAI_) {
4408         dataDetectorAdapter_->aiDetectInitialized_ = false;
4409     }
4410     if (CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
4411         ParseOriText(textLayoutProperty->GetContent().value_or(u""));
4412         if (!dataDetectorAdapter_->aiDetectInitialized_) {
4413             dataDetectorAdapter_->StartAITask();
4414         }
4415     }
4416 }
4417 
ResetAfterTextChange()4418 void TextPattern::ResetAfterTextChange()
4419 {
4420     CloseSelectOverlay();
4421     ResetSelection();
4422     ResetOriginCaretPosition();
4423 }
4424 
ParseOriText(const std::u16string & currentText)4425 void TextPattern::ParseOriText(const std::u16string& currentText)
4426 {
4427     CHECK_NULL_VOID(GetDataDetectorAdapter());
4428     auto entityJson = JsonUtil::ParseJsonString(UtfUtils::Str16DebugToStr8(currentText));
4429     bool entityIsJson = !entityJson->IsNull();
4430     TAG_LOGI(AceLogTag::ACE_TEXT, "text content is the json format: %{public}d", entityIsJson);
4431     if (entityIsJson && !entityJson->GetValue("bundleName")->IsNull() &&
4432         dataDetectorAdapter_->ParseOriText(entityJson, textForDisplay_)) {
4433         if (childNodes_.empty()) {
4434             auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4435             CHECK_NULL_VOID(textLayoutProperty);
4436             textLayoutProperty->UpdateContent(textForDisplay_);
4437         }
4438     }
4439 }
4440 
BeforeCreateLayoutWrapper()4441 void TextPattern::BeforeCreateLayoutWrapper()
4442 {
4443     auto host = GetHost();
4444     CHECK_NULL_VOID(host);
4445     CHECK_NULL_VOID(host->GetTag() != V2::SYMBOL_ETS_TAG);
4446     if (!isSpanStringMode_) {
4447         PreCreateLayoutWrapper();
4448     }
4449     selectOverlay_->MarkOverlayDirty();
4450     if (HasSpanOnHoverEvent()) {
4451         InitSpanMouseEvent();
4452     }
4453 }
4454 
ResetTextEffectBeforeLayout(bool onlyReset)4455 bool TextPattern::ResetTextEffectBeforeLayout(bool onlyReset)
4456 {
4457     if (onlyReset && !textEffect_) {
4458         return true;
4459     }
4460     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4461     CHECK_NULL_RETURN(textLayoutProperty, true);
4462     if (textLayoutProperty->GetTextEffectStrategyValue(TextEffectStrategy::NONE) == TextEffectStrategy::NONE ||
4463         textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE || !spans_.empty() ||
4464         isSpanStringMode_ || externalParagraph_ || IsSetObscured() || IsSensitiveEnable()) {
4465         ResetTextEffect();
4466         return true;
4467     }
4468     return false;
4469 }
4470 
RelayoutResetOrUpdateTextEffect()4471 void TextPattern::RelayoutResetOrUpdateTextEffect()
4472 {
4473     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4474     CHECK_NULL_VOID(textLayoutProperty);
4475     ResetTextEffectBeforeLayout();
4476     // 重排版动效config切换
4477     if (textEffect_) {
4478         textEffect_->UpdateEffectConfig(textLayoutProperty->GetTextFlipDirectionValue(TextFlipDirection::DOWN),
4479             textLayoutProperty->GetTextFlipEnableBlurValue(false));
4480     }
4481 }
4482 
ResetTextEffect()4483 void TextPattern::ResetTextEffect()
4484 {
4485     CHECK_NULL_VOID(textEffect_);
4486     textEffect_->StopEffect();
4487     std::vector<RefPtr<Paragraph>> paragraphs;
4488     textEffect_->RemoveTypography(paragraphs);
4489     textEffect_ = nullptr;
4490 }
4491 
GetOrCreateTextEffect(const std::u16string & content,bool & needUpdateTypography)4492 RefPtr<TextEffect> TextPattern::GetOrCreateTextEffect(const std::u16string& content, bool& needUpdateTypography)
4493 {
4494     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4495     CHECK_NULL_RETURN(textLayoutProperty, nullptr);
4496     if (textLayoutProperty->GetTextEffectStrategyValue(TextEffectStrategy::NONE) == TextEffectStrategy::NONE) {
4497         ResetTextEffect();
4498         return nullptr;
4499     }
4500     if (ResetTextEffectBeforeLayout(false)) {
4501         return nullptr;
4502     }
4503     auto isNumber = RegularMatchNumbers(content);
4504     if (!isNumber) {
4505         ResetTextEffect();
4506         return nullptr;
4507     }
4508     if (!textEffect_) {
4509         auto host = GetHost();
4510         CHECK_NULL_RETURN(host, textEffect_);
4511         textEffect_ = TextEffect::CreateTextEffect();
4512         if (textSelector_.IsValid()) {
4513             CloseSelectOverlay();
4514             ResetSelection();
4515         }
4516         TAG_LOGI(AceLogTag::ACE_TEXT, "TextPattern::GetOrCreateTextEffect create textEffeect [id:%{public}d]",
4517             host->GetId());
4518     } else {
4519         // 上一次与此次的paragraph都满足翻牌要求需要重新更新textEffect中的paragraph
4520         needUpdateTypography = true;
4521     }
4522     if (textEffect_) {
4523         textEffect_->UpdateEffectConfig(textLayoutProperty->GetTextFlipDirectionValue(TextFlipDirection::DOWN),
4524             textLayoutProperty->GetTextFlipEnableBlurValue(false));
4525     }
4526     return textEffect_;
4527 }
4528 
RegularMatchNumbers(const std::u16string & content)4529 bool TextPattern::RegularMatchNumbers(const std::u16string& content)
4530 {
4531     if (content.empty()) {
4532         return false;
4533     }
4534     for (const auto& c : content) {
4535         if (c < u'0' || c > u'9') {
4536             return false;
4537         }
4538     }
4539     return true;
4540 }
4541 
SetSpanEventFlagValue(const RefPtr<UINode> & node,bool & isSpanHasClick,bool & isSpanHasLongPress)4542 void TextPattern::SetSpanEventFlagValue(
4543     const RefPtr<UINode>& node, bool& isSpanHasClick, bool& isSpanHasLongPress)
4544 {
4545     auto spanNode = DynamicCast<FrameNode>(node);
4546     CHECK_NULL_VOID(spanNode);
4547     auto focus_hub = spanNode->GetOrCreateFocusHub();
4548     if (focus_hub && focus_hub->GetOnClickCallback()) {
4549         isSpanHasClick = true;
4550     }
4551     if (focus_hub && focus_hub->GetOnLongPressCallback()) {
4552         isSpanHasLongPress = true;
4553     }
4554 }
4555 
CollectSymbolSpanNodes(const RefPtr<SpanNode> & spanNode,const RefPtr<UINode> & node)4556 void TextPattern::CollectSymbolSpanNodes(const RefPtr<SpanNode>& spanNode, const RefPtr<UINode>& node)
4557 {
4558     CHECK_NULL_VOID(GetDataDetectorAdapter());
4559     spanNode->CleanSpanItemChildren();
4560     spanNode->MountToParagraph();
4561     textForDisplay_.append(u"  ");
4562     dataDetectorAdapter_->textForAI_.append(SYMBOL_TRANS);
4563     childNodes_.push_back(node);
4564 }
4565 
CollectSpanNodes(std::stack<SpanNodeInfo> nodes,bool & isSpanHasClick,bool & isSpanHasLongPress)4566 void TextPattern::CollectSpanNodes(std::stack<SpanNodeInfo> nodes, bool& isSpanHasClick, bool& isSpanHasLongPress)
4567 {
4568     while (!nodes.empty()) {
4569         auto current = nodes.top();
4570         nodes.pop();
4571         if (!current.node) {
4572             continue;
4573         }
4574         UpdateContainerChildren(current.containerSpanNode, current.node);
4575         auto spanNode = DynamicCast<SpanNode>(current.node);
4576         auto tag = current.node->GetTag();
4577         if (spanNode && tag == V2::SYMBOL_SPAN_ETS_TAG && spanNode->GetSpanItem()->GetSymbolUnicode() != 0) {
4578             CollectSymbolSpanNodes(spanNode, current.node);
4579         } else if (spanNode && tag != V2::PLACEHOLDER_SPAN_ETS_TAG) {
4580             CollectTextSpanNodes(spanNode, isSpanHasClick, isSpanHasLongPress);
4581             childNodes_.push_back(current.node);
4582         } else if (tag == V2::IMAGE_ETS_TAG || tag == V2::PLACEHOLDER_SPAN_ETS_TAG) {
4583             placeholderCount_++;
4584             AddChildSpanItem(current.node);
4585             GetDataDetectorAdapter()->textForAI_.append(u"\n");
4586             auto imageNode = DynamicCast<FrameNode>(current.node);
4587             if (!imageNode) {
4588                 continue;
4589             }
4590             SetSpanEventFlagValue(imageNode, isSpanHasClick, isSpanHasLongPress);
4591             childNodes_.push_back(current.node);
4592         } else if (tag == V2::CUSTOM_SPAN_NODE_ETS_TAG) {
4593             placeholderCount_++;
4594             AddChildSpanItem(current.node);
4595             GetDataDetectorAdapter()->textForAI_.append(u"\n");
4596             childNodes_.emplace_back(current.node);
4597             auto customNode = DynamicCast<FrameNode>(current.node);
4598             if (!customNode) {
4599                 continue;
4600             }
4601             SetSpanEventFlagValue(customNode, isSpanHasClick, isSpanHasLongPress);
4602         }
4603         if (tag == V2::PLACEHOLDER_SPAN_ETS_TAG) {
4604             continue;
4605         }
4606         const auto& nextChildren = current.node->GetChildren();
4607         if (nextChildren.empty()) {
4608             continue;
4609         }
4610         auto containerSpanNode = tag == V2::CONTAINER_SPAN_ETS_TAG ? current.node : current.containerSpanNode;
4611         for (auto iter = nextChildren.rbegin(); iter != nextChildren.rend(); ++iter) {
4612             nodes.push({ .node = *iter, .containerSpanNode = containerSpanNode });
4613         }
4614     }
4615 }
4616 
CollectTextSpanNodes(const RefPtr<SpanNode> & spanNode,bool & isSpanHasClick,bool & isSpanHasLongPress)4617 void TextPattern::CollectTextSpanNodes(const RefPtr<SpanNode>& spanNode, bool& isSpanHasClick, bool& isSpanHasLongPress)
4618 {
4619     spanNode->CleanSpanItemChildren();
4620     spanNode->MountToParagraph();
4621     textForDisplay_.append(spanNode->GetSpanItem()->content);
4622     GetDataDetectorAdapter()->textForAI_.append(spanNode->GetSpanItem()->content);
4623     if (spanNode->GetSpanItem()->onClick) {
4624         isSpanHasClick = true;
4625     }
4626     if (spanNode->GetSpanItem()->onLongPress) {
4627         isSpanHasLongPress = true;
4628     }
4629 }
4630 
UpdateContainerChildren(const RefPtr<UINode> & parentNode,const RefPtr<UINode> & child)4631 void TextPattern::UpdateContainerChildren(const RefPtr<UINode>& parentNode, const RefPtr<UINode>& child)
4632 {
4633     CHECK_NULL_VOID(child);
4634     auto parent = DynamicCast<ContainerSpanNode>(parentNode);
4635     CHECK_NULL_VOID(parent);
4636     auto baseSpan = DynamicCast<BaseSpan>(child);
4637     if (baseSpan) {
4638         if (baseSpan->HasTextBackgroundStyle()) {
4639             return;
4640         }
4641         baseSpan->UpdateTextBackgroundFromParent(parent->GetTextBackgroundStyle());
4642         return;
4643     }
4644     if (child->GetTag() == V2::IMAGE_ETS_TAG) {
4645         auto imageNode = DynamicCast<FrameNode>(child);
4646         CHECK_NULL_VOID(imageNode);
4647         auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
4648         CHECK_NULL_VOID(imageLayoutProperty);
4649         if (imageLayoutProperty->GetHasPlaceHolderStyleValue(false)) {
4650             return;
4651         }
4652         if (parent->GetTextBackgroundStyle().has_value()) {
4653             imageLayoutProperty->UpdatePlaceHolderStyle(parent->GetTextBackgroundStyle().value());
4654         }
4655     }
4656 }
4657 
GetGlobalOffset(Offset & offset)4658 void TextPattern::GetGlobalOffset(Offset& offset)
4659 {
4660     auto host = GetHost();
4661     CHECK_NULL_VOID(host);
4662     auto pipeline = host->GetContext();
4663     CHECK_NULL_VOID(pipeline);
4664     auto rootOffset = pipeline->GetRootRect().GetOffset();
4665     auto globalOffset = host->GetPaintRectOffsetNG(false, true) - rootOffset;
4666     offset = Offset(globalOffset.GetX(), globalOffset.GetY());
4667 }
4668 
OnVisibleChange(bool isVisible)4669 void TextPattern::OnVisibleChange(bool isVisible)
4670 {
4671     if (!isVisible) {
4672         if (textSelector_.IsValid()) {
4673             CloseSelectOverlay();
4674             ResetSelection();
4675         }
4676         if (textDetectEnable_) {
4677             GetDataDetectorAdapter()->aiDetectDelayTask_.Cancel();
4678         }
4679         PauseSymbolAnimation();
4680     } else {
4681         if (CanStartAITask()) {
4682             GetDataDetectorAdapter()->StartAITask();
4683         }
4684         ResumeSymbolAnimation();
4685     }
4686 }
4687 
PauseSymbolAnimation()4688 void TextPattern::PauseSymbolAnimation()
4689 {
4690     auto host = GetHost();
4691     CHECK_NULL_VOID(host);
4692     if (host->GetTag() != V2::SYMBOL_ETS_TAG) {
4693         return;
4694     }
4695     auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
4696     CHECK_NULL_VOID(layoutProperty);
4697     if (!layoutProperty->GetIsLoopAnimation()) {
4698         return;
4699     }
4700     auto symbolEffectOptions = layoutProperty->GetSymbolEffectOptionsValue(SymbolEffectOptions());
4701     if (!symbolEffectOptions.GetIsTxtActive()) {
4702         return;
4703     }
4704     symbolEffectOptions.SetIsTxtActive(false);
4705     layoutProperty->UpdateSymbolEffectOptions(symbolEffectOptions);
4706     host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
4707 }
4708 
ResumeSymbolAnimation()4709 void TextPattern::ResumeSymbolAnimation()
4710 {
4711     auto host = GetHost();
4712     CHECK_NULL_VOID(host);
4713     if (host->GetTag() != V2::SYMBOL_ETS_TAG) {
4714         return;
4715     }
4716     auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
4717     CHECK_NULL_VOID(layoutProperty);
4718     if (!layoutProperty->GetIsLoopAnimation()) {
4719         return;
4720     }
4721     auto symbolEffectOptions = layoutProperty->GetSymbolEffectOptionsValue(SymbolEffectOptions());
4722     if (symbolEffectOptions.GetIsTxtActive()) {
4723         return;
4724     }
4725     symbolEffectOptions.SetIsTxtActive(true);
4726     layoutProperty->UpdateSymbolEffectOptions(symbolEffectOptions);
4727     host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
4728 }
4729 
InitSurfaceChangedCallback()4730 void TextPattern::InitSurfaceChangedCallback()
4731 {
4732     auto host = GetHost();
4733     CHECK_NULL_VOID(host);
4734     auto pipeline = host->GetContext();
4735     CHECK_NULL_VOID(pipeline);
4736     if (!HasSurfaceChangedCallback()) {
4737         auto callbackId = pipeline->RegisterSurfaceChangedCallback(
4738             [weak = WeakClaim(this)](int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight,
4739                 WindowSizeChangeReason type) {
4740                 auto pattern = weak.Upgrade();
4741                 if (pattern) {
4742                     pattern->HandleSurfaceChanged(newWidth, newHeight, prevWidth, prevHeight, type);
4743                 }
4744             });
4745         UpdateSurfaceChangedCallbackId(callbackId);
4746     }
4747 }
4748 
HandleSurfaceChanged(int32_t newWidth,int32_t newHeight,int32_t prevWidth,int32_t prevHeight,WindowSizeChangeReason type)4749 void TextPattern::HandleSurfaceChanged(
4750     int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight, WindowSizeChangeReason type)
4751 {
4752     if (newWidth == prevWidth && newHeight == prevHeight) {
4753         return;
4754     }
4755     if (type != WindowSizeChangeReason::DRAG) {
4756         auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4757         CHECK_NULL_VOID(textLayoutProperty);
4758         textLayoutProperty->OnPropertyChangeMeasure();
4759     }
4760     CHECK_NULL_VOID(selectOverlay_->SelectOverlayIsOn());
4761     if (selectOverlay_->IsShowMouseMenu()) {
4762         CloseSelectOverlay();
4763     } else {
4764         auto host = GetHost();
4765         CHECK_NULL_VOID(host);
4766         auto context = host->GetContext();
4767         if (context) {
4768             context->AddAfterLayoutTask([weak = WeakClaim(this)]() {
4769                 auto pattern = weak.Upgrade();
4770                 CHECK_NULL_VOID(pattern);
4771                 pattern->CalculateHandleOffsetAndShowOverlay();
4772                 pattern->ShowSelectOverlay({ .menuIsShow = false });
4773             });
4774         }
4775     }
4776 }
4777 
InitSurfacePositionChangedCallback()4778 void TextPattern::InitSurfacePositionChangedCallback()
4779 {
4780     auto host = GetHost();
4781     CHECK_NULL_VOID(host);
4782     auto pipeline = host->GetContext();
4783     CHECK_NULL_VOID(pipeline);
4784     if (!HasSurfacePositionChangedCallback()) {
4785         auto callbackId =
4786             pipeline->RegisterSurfacePositionChangedCallback([weak = WeakClaim(this)](int32_t posX, int32_t posY) {
4787                 auto pattern = weak.Upgrade();
4788                 if (pattern) {
4789                     pattern->HandleSurfacePositionChanged(posX, posY);
4790                 }
4791             });
4792         UpdateSurfacePositionChangedCallbackId(callbackId);
4793     }
4794 }
4795 
AddChildSpanItem(const RefPtr<UINode> & child)4796 void TextPattern::AddChildSpanItem(const RefPtr<UINode>& child)
4797 {
4798     CHECK_NULL_VOID(child);
4799     auto chidNode = DynamicCast<FrameNode>(child);
4800     if (chidNode && chidNode->GetLayoutProperty() && chidNode->GetLayoutProperty()->IsOverlayNode()) {
4801         return;
4802     }
4803     if (child->GetTag() == V2::SPAN_ETS_TAG || child->GetTag() == V2::SYMBOL_SPAN_ETS_TAG) {
4804         auto spanNode = DynamicCast<SpanNode>(child);
4805         if (spanNode) {
4806             spans_.emplace_back(spanNode->GetSpanItem());
4807         }
4808     } else if (child->GetTag() == V2::IMAGE_ETS_TAG) {
4809         AddImageToSpanItem(child);
4810     } else if (child->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG) {
4811         auto placeholderSpanNode = DynamicCast<PlaceholderSpanNode>(child);
4812         if (placeholderSpanNode) {
4813             auto placeholderSpan = placeholderSpanNode->GetSpanItem();
4814             placeholderSpan->placeholderSpanNodeId = placeholderSpanNode->GetId();
4815             spans_.emplace_back(placeholderSpan);
4816         }
4817     } else if (child->GetTag() == V2::CUSTOM_SPAN_NODE_ETS_TAG) {
4818         auto customSpanNode = DynamicCast<CustomSpanNode>(child);
4819         if (customSpanNode) {
4820             auto customSpan = customSpanNode->GetSpanItem();
4821             customSpan->placeholderSpanNodeId = customSpanNode->GetId();
4822             auto focus_hub = chidNode->GetOrCreateFocusHub();
4823             CHECK_NULL_VOID(focus_hub);
4824             SetSpanItemEvent(customSpan, focus_hub);
4825             spans_.emplace_back(customSpan);
4826         }
4827     }
4828 }
4829 
SetSpanItemEvent(const RefPtr<SpanItem> & spanItem,RefPtr<FocusHub> & focusHub)4830 void TextPattern::SetSpanItemEvent(const RefPtr<SpanItem>& spanItem, RefPtr<FocusHub>& focusHub)
4831 {
4832     CHECK_NULL_VOID(focusHub);
4833     auto clickCall = focusHub->GetOnClickCallback();
4834     if (clickCall) {
4835         spanItem->SetOnClickEvent(std::move(clickCall));
4836     }
4837     auto longPressCallback = focusHub->GetOnLongPressCallback();
4838     if (longPressCallback) {
4839         spanItem->SetLongPressEvent(std::move(longPressCallback));
4840     }
4841 }
4842 
AddImageToSpanItem(const RefPtr<UINode> & child)4843 void TextPattern::AddImageToSpanItem(const RefPtr<UINode>& child)
4844 {
4845     auto imageSpanNode = DynamicCast<ImageSpanNode>(child);
4846     if (imageSpanNode) {
4847         auto host = GetHost();
4848         CHECK_NULL_VOID(host);
4849         auto imageSpanItem = imageSpanNode->GetSpanItem();
4850         if (host->GetTag() != V2::RICH_EDITOR_ETS_TAG) {
4851             auto focus_hub = imageSpanNode->GetOrCreateFocusHub();
4852             SetSpanItemEvent(imageSpanItem, focus_hub);
4853             auto gesture = imageSpanNode->GetOrCreateGestureEventHub();
4854             CHECK_NULL_VOID(gesture);
4855             gesture->SetHitTestMode(HitTestMode::HTMNONE);
4856         }
4857         imageSpanItem->UpdatePlaceholderBackgroundStyle(imageSpanNode);
4858         spans_.emplace_back(imageSpanItem);
4859         spans_.back()->nodeId_ = imageSpanNode->GetId();
4860         return;
4861     }
4862     auto imageNode = DynamicCast<FrameNode>(child);
4863     if (imageNode) {
4864         auto imageSpanItem = MakeRefPtr<ImageSpanItem>();
4865         imageSpanItem->nodeId_ = imageNode->GetId();
4866         imageSpanItem->UpdatePlaceholderBackgroundStyle(imageNode);
4867         auto focus_hub = imageNode->GetOrCreateFocusHub();
4868         SetSpanItemEvent(imageSpanItem, focus_hub);
4869         spans_.emplace_back(imageSpanItem);
4870         auto gesture = imageNode->GetOrCreateGestureEventHub();
4871         CHECK_NULL_VOID(gesture);
4872         gesture->SetHitTestMode(HitTestMode::HTMNONE);
4873         return;
4874     }
4875 }
4876 
DumpSimplifyInfo(std::shared_ptr<JsonValue> & json)4877 void TextPattern::DumpSimplifyInfo(std::shared_ptr<JsonValue>& json)
4878 {
4879     auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
4880     CHECK_NULL_VOID(textLayoutProp);
4881     if (IsSetObscured()) {
4882         json->Put("content", "");
4883         return;
4884     }
4885     auto textValue = UtfUtils::Str16DebugToStr8(textLayoutProp->GetContent().value_or(u""));
4886     if (!textValue.empty()) {
4887         json->Put("content", textValue.c_str());
4888     } else {
4889         CHECK_NULL_VOID(pManager_);
4890         auto paragraphs = pManager_->GetParagraphs();
4891         if (paragraphs.empty()) {
4892             return;
4893         }
4894 
4895         std::string text;
4896         for (auto&& info : paragraphs) {
4897             auto paragraph = info.paragraph;
4898             if (paragraph) {
4899                 text += StringUtils::Str16ToStr8(paragraph->GetParagraphText());
4900             }
4901         }
4902         json->Put("content", text.c_str());
4903     }
4904 }
4905 
DumpAdvanceInfo()4906 void TextPattern::DumpAdvanceInfo()
4907 {
4908     DumpLog::GetInstance().AddDesc(std::string("-----DumpAdvanceInfo-----"));
4909     DumpLog::GetInstance().AddDesc(
4910         std::string("BindSelectionMenu: ").append(std::to_string(selectionMenuMap_.empty())));
4911     DumpLog::GetInstance().AddDesc(std::string("Selection: ").append("(").append(textSelector_.ToString()).append(")"));
4912 }
4913 
DumpInfo()4914 void TextPattern::DumpInfo()
4915 {
4916     auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
4917     CHECK_NULL_VOID(textLayoutProp);
4918     auto& dumpLog = DumpLog::GetInstance();
4919     auto nowTime = GetSystemTimestamp();
4920     dumpLog.AddDesc(std::string("frameRecord: ").append(frameRecord_));
4921     dumpLog.AddDesc(std::string("time: ").append(std::to_string(nowTime)));
4922     if (!IsSetObscured() && !IsSensitiveEnable()) {
4923         dumpLog.AddDesc(std::string("Content: ").append(
4924             UtfUtils::Str16DebugToStr8(textLayoutProp->GetContent().value_or(u" "))));
4925     }
4926     dumpLog.AddDesc(std::string("isSpanStringMode: ").append(std::to_string(isSpanStringMode_)));
4927     dumpLog.AddDesc(std::string("externalParagraph: ").append(std::to_string(externalParagraph_.has_value())));
4928     DumpTextStyleInfo();
4929     if (contentMod_) {
4930         contentMod_->ContentModifierDump();
4931     }
4932     dumpLog.AddDesc(
4933         std::string("HeightAdaptivePolicy: ")
4934             .append(V2::ConvertWrapTextHeightAdaptivePolicyToString(
4935                 textLayoutProp->GetHeightAdaptivePolicy().value_or(TextHeightAdaptivePolicy::MAX_LINES_FIRST))));
4936     if (pManager_) {
4937         auto num = static_cast<int32_t>(pManager_->GetParagraphs().size());
4938         dumpLog.AddDesc(std::string("Paragraphs num: ").append(std::to_string(num)));
4939         dumpLog.AddDesc(std::string("PaintInfo: ").append(paintInfo_));
4940     }
4941     DumpScaleInfo();
4942     DumpTextEngineInfo();
4943     if (SystemProperties::GetDebugEnabled()) {
4944         DumpAdvanceInfo();
4945     }
4946     DumpSpanItem();
4947 }
4948 
DumpSpanItem()4949 void TextPattern::DumpSpanItem()
4950 {
4951     CHECK_NULL_VOID(isSpanStringMode_);
4952     auto& dumpLog = DumpLog::GetInstance();
4953     dumpLog.AddDesc(std::string("-----SpanDumpInfo-----"));
4954     for (const auto& item : spans_) {
4955         if (!item) {
4956             continue;
4957         }
4958         item->SpanDumpInfo();
4959     }
4960 }
4961 
DumpTextStyleInfo()4962 void TextPattern::DumpTextStyleInfo()
4963 {
4964     auto& dumpLog = DumpLog::GetInstance();
4965     auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
4966     CHECK_NULL_VOID(textLayoutProp);
4967     auto host = GetHost();
4968     CHECK_NULL_VOID(host);
4969     auto renderContext = host->GetRenderContext();
4970     CHECK_NULL_VOID(renderContext);
4971     dumpLog.AddDesc(
4972         std::string("FontColor: ")
4973             .append((textStyle_.has_value() ? textStyle_->GetTextColor() : Color::BLACK).ColorToString())
4974             .append(" pro: ")
4975             .append(
4976                 textLayoutProp->HasTextColor() ? textLayoutProp->GetTextColorValue(Color::BLACK).ColorToString() : "Na")
4977             .append(" ForegroundColor: ")
4978             .append(
4979                 renderContext->HasForegroundColor() ? renderContext->GetForegroundColorValue().ColorToString() : "Na"));
4980     if (renderContext->HasForegroundColorStrategy()) {
4981         auto strategy = static_cast<int32_t>(renderContext->GetForegroundColorStrategyValue());
4982         DumpLog::GetInstance().AddDesc(std::string("ForegroundColorStrategy: ").append(std::to_string(strategy)));
4983     }
4984     dumpLog.AddDesc(
4985         std::string("FontSize: ")
4986             .append((textStyle_.has_value() ? textStyle_->GetFontSize() : Dimension(DIMENSION_VALUE, DimensionUnit::FP))
4987                         .ToString())
4988             .append(" pro: ")
4989             .append(textLayoutProp->HasFontSize()
4990                         ? textLayoutProp->GetFontSizeValue(Dimension(0.0, DimensionUnit::FP)).ToString()
4991                         : "Na")
4992             .append(" actual: ")
4993             .append(std::to_string(textStyle_.has_value() ? textStyle_->GetFontSizeActual() : 0.0f)));
4994     if (textStyle_.has_value()) {
4995         dumpLog.AddDesc(
4996             std::string("MaxFontSize: ")
4997                 .append(textStyle_->GetAdaptMaxFontSize().ToString())
4998                 .append(" pro: ")
4999                 .append(textLayoutProp->HasAdaptMaxFontSize()
5000                             ? textLayoutProp->GetAdaptMaxFontSizeValue(Dimension(0.0, DimensionUnit::FP)).ToString()
5001                             : "Na")
5002                 .append(std::string(" MinFontSize: "))
5003                 .append(textStyle_->GetAdaptMinFontSize().ToString())
5004                 .append(" pro: ")
5005                 .append(textLayoutProp->HasAdaptMinFontSize()
5006                             ? textLayoutProp->GetAdaptMinFontSizeValue(Dimension(0.0, DimensionUnit::FP)).ToString()
5007                             : "Na"));
5008     }
5009     DumpTextStyleInfo2();
5010     DumpTextStyleInfo3();
5011     DumpTextStyleInfo4();
5012 }
5013 
DumpTextStyleInfo2()5014 void TextPattern::DumpTextStyleInfo2()
5015 {
5016     auto& dumpLog = DumpLog::GetInstance();
5017     auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
5018     CHECK_NULL_VOID(textLayoutProp);
5019     if (textStyle_.has_value()) {
5020         dumpLog.AddDesc(std::string("FontWeight: ")
5021                             .append(StringUtils::ToString(textStyle_->GetFontWeight()))
5022                             .append(" pro: ")
5023                             .append(textLayoutProp->HasFontWeight()
5024                                         ? StringUtils::ToString(textLayoutProp->GetFontWeightValue(FontWeight::NORMAL))
5025                                         : "Na")
5026                             .append(" EnableVariableFontWeight: ")
5027                             .append(std::to_string(textStyle_->GetEnableVariableFontWeight()))
5028                             .append(" pro: ")
5029                             .append(textLayoutProp->HasEnableVariableFontWeight()
5030                                         ? std::to_string(textLayoutProp->GetEnableVariableFontWeightValue(false))
5031                                         : "Na")
5032                             .append(" VariableFontWeight: ")
5033                             .append(std::to_string(textStyle_->GetVariableFontWeight()))
5034                             .append(" pro: ")
5035                             .append(textLayoutProp->HasVariableFontWeight()
5036                                         ? std::to_string(textLayoutProp->GetVariableFontWeightValue(0))
5037                                         : "Na")
5038                             .append(std::string(" FontStyle: "))
5039                             .append(StringUtils::ToString(textStyle_->GetFontStyle())));
5040         auto hasIsOnlyBetweenLines = (textLayoutProp->GetIsOnlyBetweenLinesValue(false)) ? "true" : "false";
5041         dumpLog.AddDesc(
5042             std::string("LineHeight: ")
5043                 .append(textStyle_->GetLineHeight().ToString())
5044                 .append(" pro: ")
5045                 .append(textLayoutProp->HasLineHeight()
5046                             ? textLayoutProp->GetLineHeightValue(Dimension(0.0, DimensionUnit::FP)).ToString()
5047                             : "Na")
5048                 .append(std::string(" LineSpacing: "))
5049                 .append(textStyle_->GetLineSpacing().ToString())
5050                 .append(" pro: ")
5051                 .append(textLayoutProp->HasLineSpacing()
5052                             ? textLayoutProp->GetLineSpacingValue(Dimension(0.0, DimensionUnit::FP)).ToString()
5053                             : "Na")
5054                 .append(" isOnlyBetweenLines: ")
5055                 .append((textStyle_->GetIsOnlyBetweenLines()) ? "true" : "false")
5056                 .append(" pro: ")
5057                 .append(textLayoutProp->HasIsOnlyBetweenLines() ? hasIsOnlyBetweenLines : "Na"));
5058 
5059     }
5060 }
5061 
DumpTextStyleInfo3()5062 void TextPattern::DumpTextStyleInfo3()
5063 {
5064     auto& dumpLog = DumpLog::GetInstance();
5065     auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
5066     CHECK_NULL_VOID(textLayoutProp);
5067     if (textStyle_.has_value()) {
5068         dumpLog.AddDesc(
5069             std::string("maxLines: ")
5070                 .append(std::to_string(textStyle_->GetMaxLines()))
5071                 .append(" pro: ")
5072                 .append(
5073                     textLayoutProp->HasMaxLines() ? std::to_string(textLayoutProp->GetMaxLinesValue(UINT32_MAX)) : "Na")
5074                 .append(std::string(" BaselineOffset: "))
5075                 .append(textStyle_->GetBaselineOffset().ToString())
5076                 .append(" pro: ")
5077                 .append(textLayoutProp->HasBaselineOffset()
5078                             ? textLayoutProp->GetBaselineOffsetValue(Dimension(0.0, DimensionUnit::FP)).ToString()
5079                             : "Na"));
5080         dumpLog.AddDesc(
5081             std::string("TextIndent: ")
5082                 .append(textStyle_->GetTextIndent().ToString())
5083                 .append(" pro: ")
5084                 .append(textLayoutProp->HasTextIndent()
5085                             ? textLayoutProp->GetTextIndentValue(Dimension(0.0, DimensionUnit::FP)).ToString()
5086                             : "Na"));
5087         dumpLog.AddDesc(
5088             std::string("fontFamily: ")
5089                 .append(GetFontFamilyInJson(textStyle_->GetFontFamilies()))
5090                 .append(" pro: ")
5091                 .append(textLayoutProp->HasFontFamily() ? GetFontFamilyInJson(textLayoutProp->GetFontFamily().value())
5092                                                         : "Na"));
5093         dumpLog.AddDesc(
5094             std::string("LetterSpacing: ")
5095                 .append(textStyle_->GetLetterSpacing().ToString())
5096                 .append(" pro: ")
5097                 .append(textLayoutProp->HasLetterSpacing()
5098                             ? textLayoutProp->GetLetterSpacingValue(Dimension(0.0, DimensionUnit::FP)).ToString()
5099                             : "Na"));
5100         dumpLog.AddDesc(
5101             std::string("TextOverflow: ")
5102                 .append(StringUtils::ToString(textStyle_->GetTextOverflow()))
5103                 .append(" pro: ")
5104                 .append(textLayoutProp->HasTextOverflow()
5105                             ? StringUtils::ToString(textLayoutProp->GetTextOverflowValue(TextOverflow::NONE))
5106                             : "Na"));
5107         dumpLog.AddDesc(std::string("TextAlign: ")
5108                             .append(StringUtils::ToString(textStyle_->GetTextAlign()))
5109                             .append(" pro: ")
5110                             .append(textLayoutProp->HasTextAlign()
5111                                         ? StringUtils::ToString(textLayoutProp->GetTextAlignValue(TextAlign::START))
5112                                         : "Na"));
5113     }
5114 }
5115 
DumpTextStyleInfo4()5116 void TextPattern::DumpTextStyleInfo4()
5117 {
5118     auto& dumpLog = DumpLog::GetInstance();
5119     auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
5120     CHECK_NULL_VOID(textLayoutProp);
5121     if (textStyle_.has_value()) {
5122         dumpLog.AddDesc(std::string("WordBreak: ")
5123                             .append(StringUtils::ToString(textStyle_->GetWordBreak()))
5124                             .append(std::string(" TextCase: "))
5125                             .append(StringUtils::ToString(textStyle_->GetTextCase()))
5126                             .append(std::string(" EllipsisMode: "))
5127                             .append(StringUtils::ToString(textStyle_->GetEllipsisMode())));
5128         dumpLog.AddDesc(std::string("LineBreakStrategy: ")
5129                             .append(GetLineBreakStrategyInJson(textStyle_->GetLineBreakStrategy()))
5130                             .append(" pro: ")
5131                             .append(textLayoutProp->HasLineBreakStrategy()
5132                                         ? GetLineBreakStrategyInJson(
5133                                               textLayoutProp->GetLineBreakStrategyValue(LineBreakStrategy::GREEDY))
5134                                         : "Na"));
5135         dumpLog.AddDesc(std::string("SymbolColorList: ")
5136                             .append(StringUtils::SymbolColorListToString(textStyle_->GetSymbolColorList())));
5137         dumpLog.AddDesc(std::string("CopyOption: ")
5138                             .append(StringUtils::ToString(copyOption_))
5139                             .append(" pro: ")
5140                             .append(textLayoutProp->HasCopyOption() ? textLayoutProp->GetCopyOptionString() : "Na")
5141                             .append(" SelectableMode:")
5142                             .append(StringUtils::ToString(textLayoutProp->GetTextSelectableModeValue(
5143                                 TextSelectableMode::SELECTABLE_UNFOCUSABLE))));
5144         dumpLog.AddDesc(
5145             std::string("Decoration: ")
5146                 .append(StringUtils::ToString(textStyle_->GetTextDecorationStyle()))
5147                 .append(" ")
5148                 .append(StringUtils::ToString(textStyle_->GetTextDecorationFirst()))
5149                 .append(" ")
5150                 .append(textStyle_->GetTextDecorationColor().ColorToString())
5151                 .append(" self: ")
5152                 .append(
5153                     textLayoutProp->HasTextDecorationStyle()
5154                         ? StringUtils::ToString(textLayoutProp->GetTextDecorationStyleValue(TextDecorationStyle::SOLID))
5155                         : "Na")
5156                 .append(" ")
5157                 .append(textLayoutProp->HasTextDecoration()
5158                             ? StringUtils::ToString(textLayoutProp->GetTextDecorationFirst())
5159                             : "Na")
5160                 .append(" ")
5161                 .append(textLayoutProp->HasTextDecorationColor()
5162                             ? textLayoutProp->GetTextDecorationColorValue(Color::BLACK).ColorToString()
5163                             : "Na"));
5164     }
5165 }
5166 
DumpScaleInfo()5167 void TextPattern::DumpScaleInfo()
5168 {
5169     auto& dumpLog = DumpLog::GetInstance();
5170     dumpLog.AddDesc(std::string("-----DumpScaleInfo-----"));
5171     auto host = GetHost();
5172     CHECK_NULL_VOID(host);
5173     auto pipeline = host->GetContext();
5174     CHECK_NULL_VOID(pipeline);
5175     auto fontScale = pipeline->GetFontScale();
5176     auto fontWeightScale = pipeline->GetFontWeightScale();
5177     auto followSystem = pipeline->IsFollowSystem();
5178     float maxFontScale = pipeline->GetMaxAppFontScale();
5179     auto halfLeading = pipeline->GetHalfLeading();
5180     dumpLog.AddDesc(std::string("fontScale: ").append(std::to_string(fontScale))
5181         .append(std::string(", fontWeightScale: ")).append(std::to_string(fontWeightScale))
5182         .append(std::string(", IsFollowSystem: ")).append(std::to_string(followSystem))
5183         .append(std::string(", maxFontScale: ")).append(std::to_string(maxFontScale))
5184         .append(std::string(", ConfigHalfLeading: ")).append(std::to_string(halfLeading)));
5185     auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
5186     CHECK_NULL_VOID(textLayoutProp);
5187     auto minFontScale = textLayoutProp->GetMinFontScale().value_or(0.0f);
5188     auto maxfontScale = textLayoutProp->GetMaxFontScale().value_or(static_cast<float>(INT32_MAX));
5189     dumpLog.AddDesc(std::string("minFontScale: ").append(std::to_string(minFontScale))
5190         .append(std::string(", maxFontScale: ")).append(std::to_string(maxfontScale)));
5191     auto flag = textLayoutProp->HasHalfLeading();
5192     dumpLog.AddDesc(
5193         std::string("HalfLeading: ").append(flag ? std::to_string(textLayoutProp->GetHalfLeadingValue(false)) : "NA"));
5194 }
5195 
DumpTextEngineInfo()5196 void TextPattern::DumpTextEngineInfo()
5197 {
5198     auto& dumpLog = DumpLog::GetInstance();
5199     dumpLog.AddDesc(std::string("-----TextEngine paragraphs_ info-----"));
5200     dumpLog.AddDesc(std::string("contentRect :").append(contentRect_.ToString()));
5201     if (pManager_) {
5202         dumpLog.AddDesc(std::string("from TextEngine paragraphs_ info :").append(pManager_->GetDumpInfo()));
5203         auto paragraphs = pManager_->GetParagraphs();
5204         if (paragraphs.empty()) {
5205             dumpLog.AddDesc(std::string("paragraphs is empty!"));
5206             return;
5207         }
5208         dumpLog.AddDesc(std::string("DidExceedMaxLines:").append(std::to_string(pManager_->DidExceedMaxLines()))
5209                         .append(" DidExceedMaxLinesInner:")
5210                         .append(std::to_string(pManager_->DidExceedMaxLinesInner())));
5211         dumpLog.AddDesc(std::string("GetTextWidth:")
5212                             .append(std::to_string(pManager_->GetTextWidth()))
5213                             .append(" GetHeight:")
5214                             .append(std::to_string(pManager_->GetHeight()))
5215                             .append(" GetMaxWidth:")
5216                             .append(std::to_string(pManager_->GetMaxWidth()))
5217                             .append(" GetMaxIntrinsicWidth:")
5218                             .append(std::to_string(pManager_->GetMaxIntrinsicWidth())));
5219         dumpLog.AddDesc(std::string("GetLineCount:")
5220                             .append(std::to_string(pManager_->GetLineCount()))
5221                             .append(" GetLongestLine:")
5222                             .append(std::to_string(pManager_->GetLongestLine()))
5223                             .append(std::to_string(pManager_->GetLongestLineWithIndent())));
5224     }
5225     dumpLog.AddDesc(std::string("spans size :").append(std::to_string(spans_.size())));
5226     if (!IsSetObscured() && !IsSensitiveEnable()) {
5227         DumpParagraphsInfo();
5228     }
5229 }
5230 
DumpParagraphsInfo()5231 void TextPattern::DumpParagraphsInfo()
5232 {
5233     CHECK_NULL_VOID(pManager_);
5234     auto& dumpLog = DumpLog::GetInstance();
5235     auto paragraphs = pManager_->GetParagraphs();
5236     if (paragraphs.empty()) {
5237         dumpLog.AddDesc(std::string("paragraphs is empty!"));
5238         return;
5239     }
5240     dumpLog.AddDesc(std::string("paragraphs size:").append(std::to_string(paragraphs.size())));
5241     for (auto&& info : paragraphs) {
5242         auto paragraph = info.paragraph;
5243         if (paragraph) {
5244             auto text = StringUtils::Str16ToStr8(paragraph->GetParagraphText());
5245             auto paraStyle = paragraph->GetParagraphStyle();
5246             auto direction = V2::ConvertTextDirectionToString(paraStyle.direction);
5247             dumpLog.AddDesc(std::string("paragraph: ")
5248                                 .append(text)
5249                                 .append("; direction:")
5250                                 .append(direction)
5251                                 .append(";fontlocate:")
5252                                 .append(paraStyle.fontLocale)
5253                                 .append(";fontSize:")
5254                                 .append(std::to_string(paraStyle.fontSize))
5255                                 .append(";maxLines:")
5256                                 .append(std::to_string(paraStyle.maxLines))
5257                                 .append(";align:")
5258                                 .append(StringUtils::ToString(paraStyle.align))
5259                                 .append(";isEndAddParagraphSpacing:")
5260                                 .append(std::to_string(paraStyle.isEndAddParagraphSpacing)));
5261         }
5262     }
5263 }
5264 
SetAccessibilityAction()5265 void TextPattern::SetAccessibilityAction()
5266 {
5267     auto host = GetHost();
5268     CHECK_NULL_VOID(host);
5269     auto textAccessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
5270     CHECK_NULL_VOID(textAccessibilityProperty);
5271     textAccessibilityProperty->SetActionSetSelection(
5272         [weakPtr = WeakClaim(this)](int32_t start, int32_t end, bool isForward) {
5273             const auto& pattern = weakPtr.Upgrade();
5274             CHECK_NULL_VOID(pattern);
5275             auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
5276             CHECK_NULL_VOID(textLayoutProperty);
5277             auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
5278             if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None &&
5279                 mode != TextSelectableMode::UNSELECTABLE) {
5280                 pattern->ActSetSelection(start, end);
5281             }
5282         });
5283 
5284     textAccessibilityProperty->SetActionClearSelection([weakPtr = WeakClaim(this)]() {
5285         const auto& pattern = weakPtr.Upgrade();
5286         CHECK_NULL_VOID(pattern);
5287         auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
5288         CHECK_NULL_VOID(textLayoutProperty);
5289         auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
5290         if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None &&
5291             mode != TextSelectableMode::UNSELECTABLE) {
5292             pattern->CloseSelectOverlay(true);
5293             pattern->ResetSelection();
5294         }
5295     });
5296 
5297     textAccessibilityProperty->SetActionCopy([weakPtr = WeakClaim(this)]() {
5298         const auto& pattern = weakPtr.Upgrade();
5299         CHECK_NULL_VOID(pattern);
5300         auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
5301         CHECK_NULL_VOID(textLayoutProperty);
5302         auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
5303         if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None &&
5304             mode != TextSelectableMode::UNSELECTABLE) {
5305             pattern->HandleOnCopy();
5306             pattern->CloseSelectOverlay(true);
5307             pattern->ResetSelection();
5308         }
5309     });
5310 }
5311 
OnColorConfigurationUpdate()5312 void TextPattern::OnColorConfigurationUpdate()
5313 {
5314     auto host = GetHost();
5315     CHECK_NULL_VOID(host);
5316     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
5317     CHECK_NULL_VOID(textLayoutProperty);
5318     if (!textLayoutProperty->HasTextColor()) {
5319         host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
5320     }
5321     if (GetOrCreateMagnifier()) {
5322         magnifierController_->SetColorModeChange(true);
5323     }
5324     if (isSpanStringMode_) {
5325         for (const auto& item : spans_) {
5326             if (!item) {
5327                 continue;
5328             }
5329             item->fontStyle->UpdateColorByResourceId();
5330             if (item->backgroundStyle) {
5331                 item->backgroundStyle->UpdateColorByResourceId();
5332             }
5333         }
5334         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
5335     }
5336     ACE_TEXT_SCOPED_TRACE("OnColorConfigurationUpdate[Text][self:%d]", host->GetId());
5337 }
5338 
OnThemeScopeUpdate(int32_t themeScopeId)5339 bool TextPattern::OnThemeScopeUpdate(int32_t themeScopeId)
5340 {
5341     auto host = GetHost();
5342     CHECK_NULL_RETURN(host, false);
5343     auto contex = host->GetRenderContext();
5344     CHECK_NULL_RETURN(contex, false);
5345     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
5346     CHECK_NULL_RETURN(textLayoutProperty, false);
5347 
5348     if (!textLayoutProperty->HasTextColor() && !contex->HasForegroundColor()) {
5349         auto pipeline = host->GetContext();
5350         CHECK_NULL_RETURN(pipeline, false);
5351         auto textTheme = pipeline->GetTheme<TextTheme>(themeScopeId);
5352         CHECK_NULL_RETURN(textTheme, false);
5353         UpdateFontColor(textTheme->GetTextStyle().GetTextColor());
5354     }
5355     return false;
5356 }
5357 
5358 // return: whether the offset is valid, return false if invalid
GetOriginCaretPosition(OffsetF & offset) const5359 bool TextPattern::GetOriginCaretPosition(OffsetF& offset) const
5360 {
5361     if (!originCaretPosition_.NonNegative()) {
5362         return false;
5363     }
5364     offset = originCaretPosition_;
5365     return true;
5366 }
5367 
ResetOriginCaretPosition()5368 void TextPattern::ResetOriginCaretPosition()
5369 {
5370     originCaretPosition_ = DEFAULT_NEGATIVE_CARET_OFFSET;
5371 }
5372 
5373 // Record current caret position if originCaretPosition_ is invalid
5374 // return: whether the current offset is recorded and valid
RecordOriginCaretPosition(const OffsetF & offset)5375 bool TextPattern::RecordOriginCaretPosition(const OffsetF& offset)
5376 {
5377     if (originCaretPosition_.NonNegative() || !offset.NonNegative()) {
5378         return false;
5379     }
5380     originCaretPosition_ = offset;
5381     return true;
5382 }
5383 
ResetCustomFontColor()5384 void TextPattern::ResetCustomFontColor()
5385 {
5386     auto host = GetHost();
5387     CHECK_NULL_VOID(host);
5388     auto pipeline = host->GetContext();
5389     CHECK_NULL_VOID(pipeline);
5390     auto textTheme = pipeline->GetTheme<TextTheme>(host->GetThemeScopeId());
5391     CHECK_NULL_VOID(textTheme);
5392     auto color = textTheme->GetTextStyle().GetTextColor();
5393     UpdateFontColor(color);
5394 }
5395 
GetDragUpperLeftCoordinates()5396 OffsetF TextPattern::GetDragUpperLeftCoordinates()
5397 {
5398     auto dragBoxes = GetTextBoxes();
5399     if (dragBoxes.empty()) {
5400         return { 0.0f, 0.0f };
5401     }
5402     auto startY = dragBoxes.front().Top();
5403     auto startX = dragBoxes.front().Left();
5404 
5405     auto endY = dragBoxes.back().Top();
5406     OffsetF offset;
5407     if (NearEqual(startY, endY)) {
5408         offset = { contentRect_.GetX() + startX, startY + contentRect_.GetY() };
5409     } else {
5410         offset = { contentRect_.GetX(), startY + contentRect_.GetY() };
5411     }
5412 
5413     return GetParentGlobalOffset() + offset;
5414 }
5415 
UpdateRectForSymbolShadow(RectF & rect,float offsetX,float offsetY,float blurRadius) const5416 void TextPattern::UpdateRectForSymbolShadow(RectF& rect, float offsetX, float offsetY, float blurRadius) const
5417 {
5418     float blur = blurRadius * 2.0f;
5419     float leftOffsetX = 0.0f;
5420     float rightOffsetX = 0.0f;
5421     float upOffsetY = 0.0f;
5422     float downOffsetY = 0.0f;
5423     if (LessNotEqual(offsetX - blurRadius, leftOffsetX)) {
5424         leftOffsetX = offsetX - blur;
5425     }
5426     if (GreatNotEqual(offsetX + blur, rightOffsetX)) {
5427         rightOffsetX = offsetX + blur;
5428     }
5429     if (GreatNotEqual(offsetY - blur, upOffsetY)) {
5430         upOffsetY = offsetY - blur;
5431     }
5432     if (GreatNotEqual(offsetY + blur, downOffsetY)) {
5433         downOffsetY = offsetY + blur;
5434     }
5435 
5436     rect.SetRect(
5437         leftOffsetX, upOffsetY, rect.Width() + rightOffsetX - leftOffsetX, rect.Height() + downOffsetY - upOffsetY);
5438 }
5439 
ProcessBoundRectByTextShadow(RectF & rect)5440 void TextPattern::ProcessBoundRectByTextShadow(RectF& rect)
5441 {
5442     auto property = GetHost()->GetLayoutProperty<TextLayoutProperty>();
5443     auto shadowOpt  = property->GetSymbolShadow();
5444     if (shadowOpt.has_value()) {
5445         const auto& symbolShadow = shadowOpt.value();
5446         UpdateRectForSymbolShadow(rect, symbolShadow.offset.first, symbolShadow.offset.second, symbolShadow.radius);
5447         return;
5448     }
5449     auto shadows = property->GetTextShadow();
5450     if (!shadows.has_value()) {
5451         return;
5452     }
5453     float leftOffsetX = 0.0f;
5454     float rightOffsetX = 0.0f;
5455     float upOffsetY = 0.0f;
5456     float downOffsetY = 0.0f;
5457     for (const auto& shadow : shadows.value()) {
5458         auto shadowBlurRadius = shadow.GetBlurRadius() * 2.0f;
5459         if (LessNotEqual(shadow.GetOffset().GetX() - shadowBlurRadius, leftOffsetX)) {
5460             leftOffsetX = shadow.GetOffset().GetX() - shadowBlurRadius;
5461         }
5462 
5463         if (GreatNotEqual(shadow.GetOffset().GetX() + shadowBlurRadius, rightOffsetX)) {
5464             rightOffsetX = shadow.GetOffset().GetX() + shadowBlurRadius;
5465         }
5466 
5467         if (LessNotEqual(shadow.GetOffset().GetY() - shadowBlurRadius, upOffsetY)) {
5468             upOffsetY = shadow.GetOffset().GetY() - shadowBlurRadius;
5469         }
5470 
5471         if (GreatNotEqual(shadow.GetOffset().GetY() + shadowBlurRadius, downOffsetY)) {
5472             downOffsetY = shadow.GetOffset().GetY() + shadowBlurRadius;
5473         }
5474     }
5475     rect.SetRect(
5476         leftOffsetX, upOffsetY, rect.Width() + rightOffsetX - leftOffsetX, rect.Height() + downOffsetY - upOffsetY);
5477 }
5478 
ProcessBoundRectByTextMarquee(RectF & rect)5479 void TextPattern::ProcessBoundRectByTextMarquee(RectF& rect)
5480 {
5481     auto host = GetHost();
5482     CHECK_NULL_VOID(host);
5483     auto textLayoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
5484     CHECK_NULL_VOID(textLayoutProperty);
5485     if (textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) != TextOverflow::MARQUEE) {
5486         return;
5487     }
5488     auto geometryNode = host->GetGeometryNode();
5489     CHECK_NULL_VOID(geometryNode);
5490     auto contentSize = geometryNode->GetContentSize();
5491     CHECK_NULL_VOID(pManager_);
5492     if (pManager_->GetTextWidth() < contentSize.Width()) {
5493         return;
5494     }
5495     auto frameSize = geometryNode->GetFrameSize();
5496     auto relativeSelfLeftOffsetX =
5497         std::max(-1 * host->GetOffsetRelativeToWindow().GetX(), rect.GetOffset().GetX() - pManager_->GetTextWidth());
5498     rect.SetLeft(relativeSelfLeftOffsetX);
5499     rect.SetWidth(frameSize.Width() + pManager_->GetTextWidth() - relativeSelfLeftOffsetX);
5500 }
5501 
CreateNodePaintMethod()5502 RefPtr<NodePaintMethod> TextPattern::CreateNodePaintMethod()
5503 {
5504     CreateModifier();
5505     auto paintMethod = MakeRefPtr<TextPaintMethod>(WeakClaim(this), baselineOffset_, contentMod_, overlayMod_);
5506     auto host = GetHost();
5507     CHECK_NULL_RETURN(host, paintMethod);
5508     auto context = host->GetRenderContext();
5509     CHECK_NULL_RETURN(context, paintMethod);
5510     auto geometryNode = host->GetGeometryNode();
5511     CHECK_NULL_RETURN(geometryNode, paintMethod);
5512     auto frameSize = geometryNode->GetFrameSize();
5513     if (context->GetClipEdge().value_or(host->LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE))) {
5514         SetResponseRegion(frameSize, frameSize);
5515         return paintMethod;
5516     }
5517     CHECK_NULL_RETURN(pManager_, paintMethod);
5518     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
5519     auto thickness = textLayoutProperty->GetLineThicknessScale().value_or(1.0f);
5520     RectF boundsRect = overlayMod_->GetBoundsRect();
5521     auto boundsWidth = contentRect_.GetX() + std::ceil(pManager_->GetLongestLineWithIndent());
5522     auto boundsHeight = contentRect_.GetY() + static_cast<float>(pManager_->GetHeight() + std::fabs(baselineOffset_));
5523     if (GreatNotEqual(thickness, 1.0f)) {
5524         boundsHeight += thickness;
5525     }
5526     boundsRect.SetWidth(boundsWidth);
5527     boundsRect.SetHeight(boundsHeight);
5528     SetResponseRegion(frameSize, boundsRect.GetSize());
5529     ProcessBoundRectByTextShadow(boundsRect);
5530     ProcessBoundRectByTextMarquee(boundsRect);
5531     boundsRect.SetWidth(std::max(frameSize.Width(), boundsRect.Width()));
5532     boundsRect.SetHeight(std::max(frameSize.Height(), boundsRect.Height()));
5533     auto baselineOffset = LessOrEqual(baselineOffset_, 0) ? std::fabs(baselineOffset_) : 0;
5534     pManager_->GetPaintRegion(boundsRect, contentRect_.GetX(), contentRect_.GetY() + baselineOffset);
5535     overlayMod_->SetBoundsRect(boundsRect);
5536     return paintMethod;
5537 }
5538 
SetResponseRegion(const SizeF & frameSize,const SizeF & boundsSize)5539 void TextPattern::SetResponseRegion(const SizeF& frameSize, const SizeF& boundsSize)
5540 {
5541     auto host = GetHost();
5542     CHECK_NULL_VOID(host);
5543     auto gestureHub = host->GetOrCreateGestureEventHub();
5544     CHECK_NULL_VOID(gestureHub);
5545     if (isUserSetResponseRegion_) {
5546         return;
5547     }
5548     std::vector<DimensionRect> hotZoneRegions;
5549     DimensionRect hotZoneRegion;
5550     hotZoneRegion.SetSize(DimensionSize(Dimension(std::max(boundsSize.Width(), frameSize.Width())),
5551         Dimension(std::max(frameSize.Height(), boundsSize.Height()))));
5552     hotZoneRegions.emplace_back(hotZoneRegion);
5553     gestureHub->SetResponseRegion(hotZoneRegions);
5554     host->UpdateAccessibilityNodeRect();
5555 }
5556 
CreateModifier()5557 void TextPattern::CreateModifier()
5558 {
5559     if (!contentMod_) {
5560         contentMod_ = MakeRefPtr<TextContentModifier>(textStyle_, WeakClaim(this));
5561     }
5562     if (!overlayMod_) {
5563         overlayMod_ = MakeRefPtr<TextOverlayModifier>();
5564     }
5565     if (isCustomFont_) {
5566         contentMod_->SetIsCustomFont(true);
5567     }
5568 }
5569 
GetHandleIndex(const Offset & offset) const5570 int32_t TextPattern::GetHandleIndex(const Offset& offset) const
5571 {
5572     return pManager_->GetGlyphIndexByCoordinate(offset);
5573 }
5574 
OnHandleAreaChanged()5575 void TextPattern::OnHandleAreaChanged()
5576 {
5577     if (selectOverlay_->SelectOverlayIsOn()) {
5578         auto parentGlobalOffset = GetParentGlobalOffset();
5579         if (parentGlobalOffset != parentGlobalOffset_) {
5580             parentGlobalOffset_ = parentGlobalOffset;
5581             CalculateHandleOffsetAndShowOverlay();
5582             ShowSelectOverlay({ .menuIsShow = false, .animation = true });
5583         }
5584     }
5585 }
5586 
RemoveAreaChangeInner()5587 void TextPattern::RemoveAreaChangeInner()
5588 {
5589     auto host = GetHost();
5590     CHECK_NULL_VOID(host);
5591     auto pipeline = host->GetContext();
5592     CHECK_NULL_VOID(pipeline);
5593     auto eventHub = host->GetOrCreateEventHub<TextEventHub>();
5594     CHECK_NULL_VOID(eventHub);
5595     if (eventHub->HasOnAreaChanged()) {
5596         return;
5597     }
5598     pipeline->RemoveOnAreaChangeNode(host->GetId());
5599 }
5600 
SetTextDetectEnable(bool enable)5601 void TextPattern::SetTextDetectEnable(bool enable)
5602 {
5603     auto host = GetHost();
5604     // call SetTextDetectEnableMultiThread() by multi thread
5605     FREE_NODE_CHECK(host, SetTextDetectEnable, enable);
5606     CHECK_NULL_VOID(host);
5607     CHECK_NULL_VOID(GetDataDetectorAdapter());
5608     dataDetectorAdapter_->frameNode_ = host;
5609     if (enable == textDetectEnable_) {
5610         return;
5611     }
5612     textDetectEnable_ = enable;
5613     if (textDetectEnable_) {
5614         auto pipeline = host->GetContext();
5615         CHECK_NULL_VOID(pipeline);
5616         auto callback = [weak = WeakClaim(this)]() {
5617             auto pattern = weak.Upgrade();
5618             CHECK_NULL_VOID(pattern);
5619             pattern->dataDetectorAdapter_->GetAIEntityMenu();
5620         };
5621         pipeline->SetConfigChangedCallback(host->GetId(), callback);
5622     } else {
5623         dataDetectorAdapter_->CancelAITask();
5624     }
5625     host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE);
5626 }
5627 
CanStartAITask() const5628 bool TextPattern::CanStartAITask() const
5629 {
5630     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
5631     if (textLayoutProperty) {
5632         return textDetectEnable_ && enabled_ && !IsSetObscured() &&
5633                textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) != TextOverflow::MARQUEE;
5634     } else {
5635         return textDetectEnable_ && enabled_;
5636     }
5637 }
5638 
NeedShowAIDetect()5639 bool TextPattern::NeedShowAIDetect()
5640 {
5641     return CanStartAITask() && !GetDataDetectorAdapter()->aiSpanMap_.empty();
5642 }
5643 
BindSelectionMenu(TextSpanType spanType,TextResponseType responseType,std::function<void ()> & menuBuilder,const SelectMenuParam & menuParam)5644 void TextPattern::BindSelectionMenu(TextSpanType spanType, TextResponseType responseType,
5645     std::function<void()>& menuBuilder, const SelectMenuParam& menuParam)
5646 {
5647     auto key = std::make_pair(spanType, responseType);
5648     auto it = selectionMenuMap_.find(key);
5649     if (it != selectionMenuMap_.end()) {
5650         if (menuBuilder == nullptr) {
5651             selectionMenuMap_.erase(it);
5652             return;
5653         }
5654         it->second->buildFunc = menuBuilder;
5655         it->second->onAppear = menuParam.onAppear;
5656         it->second->onDisappear = menuParam.onDisappear;
5657         it->second->onMenuShow = menuParam.onMenuShow;
5658         it->second->onMenuHide = menuParam.onMenuHide;
5659         it->second->isValid = menuParam.isValid;
5660         return;
5661     }
5662 
5663     auto selectionMenuParams = std::make_shared<SelectionMenuParams>(
5664         spanType, menuBuilder, menuParam.onAppear, menuParam.onDisappear, responseType);
5665     selectionMenuParams->onMenuShow = menuParam.onMenuShow;
5666     selectionMenuParams->onMenuHide = menuParam.onMenuHide;
5667     selectionMenuParams->isValid = menuParam.isValid;
5668     selectionMenuMap_[key] = selectionMenuParams;
5669     auto host = GetHost();
5670     CHECK_NULL_VOID(host);
5671     host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
5672 }
5673 
CloseSelectionMenu()5674 void TextPattern::CloseSelectionMenu()
5675 {
5676     textResponseType_ = TextResponseType::NONE;
5677     CloseSelectOverlay(true);
5678 }
5679 
GetMenuParams(TextSpanType spanType,TextResponseType responseType)5680 std::shared_ptr<SelectionMenuParams> TextPattern::GetMenuParams(TextSpanType spanType, TextResponseType responseType)
5681 {
5682     // JS TextSpanType.DEFAULT = TextSpanType::NONE
5683     // JS TextResponseType.DEFAULT = TextResponseType::NONE
5684     std::vector<std::pair<TextSpanType, TextResponseType>> searchPairs = {
5685         { spanType, responseType },
5686         { spanType, TextResponseType::NONE },
5687     };
5688     if (spanType != TextSpanType::NONE) {
5689         searchPairs.push_back({ TextSpanType::NONE, responseType });
5690         searchPairs.push_back({ TextSpanType::NONE, TextResponseType::NONE });
5691     }
5692     for (const auto& key : searchPairs) {
5693         auto it = selectionMenuMap_.find(key);
5694         if (it != selectionMenuMap_.end()) {
5695             return it->second;
5696         }
5697     }
5698     TAG_LOGD(AceLogTag::ACE_TEXT, "The key not in selectionMenuMap_");
5699     return nullptr;
5700 }
5701 
CopySelectionMenuParams(SelectOverlayInfo & selectInfo,TextResponseType responseType)5702 void TextPattern::CopySelectionMenuParams(SelectOverlayInfo& selectInfo, TextResponseType responseType)
5703 {
5704     auto currentSpanType = selectedType_.value_or(TextSpanType::NONE);
5705     std::shared_ptr<SelectionMenuParams> menuParams = nullptr;
5706     menuParams = GetMenuParams(currentSpanType, responseType);
5707     if (menuParams == nullptr || !menuParams->isValid) {
5708         return;
5709     }
5710     CopyBindSelectionMenuParams(selectInfo, menuParams);
5711 }
5712 
CopyBindSelectionMenuParams(SelectOverlayInfo & selectInfo,std::shared_ptr<SelectionMenuParams> menuParams)5713 void TextPattern::CopyBindSelectionMenuParams(
5714     SelectOverlayInfo& selectInfo, std::shared_ptr<SelectionMenuParams> menuParams)
5715 {
5716     selectInfo.menuInfo.menuBuilder = menuParams->buildFunc;
5717     auto weak = AceType::WeakClaim(this);
5718     selectInfo.menuCallback.onAppear = [weak, menuParams]() {
5719         auto pattern = weak.Upgrade();
5720         CHECK_NULL_VOID(pattern);
5721         pattern->OnHandleSelectionMenuCallback(SelectionMenuCalblackId::MENU_APPEAR, menuParams);
5722     };
5723     selectInfo.menuCallback.onDisappear = menuParams->onDisappear;
5724     selectInfo.menuCallback.onMenuShow = [weak, menuParams]() {
5725         auto pattern = weak.Upgrade();
5726         CHECK_NULL_VOID(pattern);
5727         pattern->OnHandleSelectionMenuCallback(SelectionMenuCalblackId::MENU_SHOW, menuParams);
5728     };
5729     selectInfo.menuCallback.onMenuHide = [weak, menuParams]() {
5730         auto pattern = weak.Upgrade();
5731         CHECK_NULL_VOID(pattern);
5732         pattern->OnHandleSelectionMenuCallback(SelectionMenuCalblackId::MENU_HIDE, menuParams);
5733     };
5734 }
5735 
OnHandleSelectionMenuCallback(SelectionMenuCalblackId callbackId,std::shared_ptr<SelectionMenuParams> menuParams)5736 void TextPattern::OnHandleSelectionMenuCallback(
5737     SelectionMenuCalblackId callbackId, std::shared_ptr<SelectionMenuParams> menuParams)
5738 {
5739     std::function<void(int32_t, int32_t)> callback;
5740     switch (callbackId) {
5741         case SelectionMenuCalblackId::MENU_SHOW:
5742             callback = menuParams->onMenuShow;
5743             break;
5744         case SelectionMenuCalblackId::MENU_HIDE:
5745             callback = menuParams->onMenuHide;
5746             break;
5747         case SelectionMenuCalblackId::MENU_APPEAR:
5748             callback = menuParams->onAppear;
5749             break;
5750         default:
5751             callback = nullptr;
5752     }
5753     CHECK_NULL_VOID(callback);
5754     auto selectStart = std::min(textSelector_.baseOffset, textSelector_.destinationOffset);
5755     auto selectEnd = std::max(textSelector_.baseOffset, textSelector_.destinationOffset);
5756     callback(selectStart, selectEnd);
5757 }
5758 
FireOnSelectionChange(int32_t start,int32_t end)5759 void TextPattern::FireOnSelectionChange(int32_t start, int32_t end)
5760 {
5761     auto host = GetHost();
5762     CHECK_NULL_VOID(host);
5763     auto eventHub = host->GetOrCreateEventHub<TextEventHub>();
5764     CHECK_NULL_VOID(eventHub);
5765     eventHub->FireOnSelectionChange(start, end);
5766 }
5767 
FireOnMarqueeStateChange(const TextMarqueeState & state)5768 void TextPattern::FireOnMarqueeStateChange(const TextMarqueeState& state)
5769 {
5770     auto host = GetHost();
5771     CHECK_NULL_VOID(host);
5772     auto eventHub = host->GetOrCreateEventHub<TextEventHub>();
5773     CHECK_NULL_VOID(eventHub);
5774     eventHub->FireOnMarqueeStateChange(static_cast<int32_t>(state));
5775 
5776     if (TextMarqueeState::START == state) {
5777         CloseSelectOverlay();
5778         ResetSelection();
5779         isMarqueeRunning_ = true;
5780     } else if (TextMarqueeState::FINISH == state) {
5781         isMarqueeRunning_ = false;
5782     }
5783 
5784     RecoverCopyOption();
5785 }
5786 
OnSelectionMenuOptionsUpdate(const NG::OnCreateMenuCallback && onCreateMenuCallback,const NG::OnMenuItemClickCallback && onMenuItemClick,const NG::OnPrepareMenuCallback && onPrepareMenuCallback)5787 void TextPattern::OnSelectionMenuOptionsUpdate(const NG::OnCreateMenuCallback&& onCreateMenuCallback,
5788     const NG::OnMenuItemClickCallback&& onMenuItemClick, const NG::OnPrepareMenuCallback&& onPrepareMenuCallback)
5789 {
5790     selectOverlay_->OnSelectionMenuOptionsUpdate(
5791         std::move(onCreateMenuCallback), std::move(onMenuItemClick), std::move(onPrepareMenuCallback));
5792 }
5793 
StartVibratorByIndexChange(int32_t currentIndex,int32_t preIndex)5794 void TextPattern::StartVibratorByIndexChange(int32_t currentIndex, int32_t preIndex)
5795 {
5796     CHECK_NULL_VOID(isEnableHapticFeedback_ && (currentIndex != preIndex));
5797     VibratorUtils::StartVibraFeedback("slide");
5798 }
5799 
HandleSelectionChange(int32_t start,int32_t end)5800 void TextPattern::HandleSelectionChange(int32_t start, int32_t end)
5801 {
5802     auto host = GetHost();
5803     CHECK_NULL_VOID(host);
5804     if (SystemProperties::GetTextTraceEnabled()) {
5805         TAG_LOGI(AceLogTag::ACE_TEXT, "HandleSelectionChange[id:%{public}d][start:%{public}d][end:%{public}d]",
5806             host->GetId(), start, end);
5807     }
5808     if (textSelector_.GetStart() == start && textSelector_.GetEnd() == end) {
5809         return;
5810     }
5811 
5812     bool changeSymbolEffect = false;
5813     for (auto& span: spans_) {
5814         if (span->GetSymbolUnicode() == 0) {
5815             continue;
5816         }
5817         bool nextEffectSwitch = start != -1 && end != -1 ? false : true;
5818         if (span->GetSymbolEffectSwitch() != nextEffectSwitch) {
5819             span->SetSymbolEffectSwitch(nextEffectSwitch);
5820             changeSymbolEffect = true;
5821         }
5822     }
5823     textSelector_.Update(start, end);
5824     UpdateSelectionSpanType(std::min(start, end), std::max(start, end));
5825     FireOnSelectionChange(std::min(start, end), std::max(start, end));
5826     if (changeSymbolEffect) {
5827         host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
5828     }
5829 }
5830 
IsSelectedBindSelectionMenu()5831 bool TextPattern::IsSelectedBindSelectionMenu()
5832 {
5833     auto currentSpanType = selectedType_.value_or(TextSpanType::TEXT);
5834     return GetMenuParams(currentSpanType, TextResponseType::SELECTED_BY_MOUSE) != nullptr;
5835 }
5836 
UpdateSelectionSpanType(int32_t selectStart,int32_t selectEnd)5837 void TextPattern::UpdateSelectionSpanType(int32_t selectStart, int32_t selectEnd)
5838 {
5839     UpdateSelectionType(GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT));
5840     if ((selectedType_ == TextSpanType::NONE && !textSelector_.StartEqualToDest()) ||
5841         textSelector_.StartEqualToDest()) {
5842         selectedType_ = TextSpanType::TEXT;
5843     }
5844 }
5845 
UpdateSelectionType(const SelectionInfo & selection)5846 void TextPattern::UpdateSelectionType(const SelectionInfo& selection)
5847 {
5848     selectedType_ = TextSpanType::NONE;
5849     auto list = selection.GetSelection().resultObjects;
5850     bool imageSelected = false;
5851     bool textSelected = false;
5852     bool builderSelected = false;
5853     for (const auto& obj : list) {
5854         if (obj.type == SelectSpanType::TYPEIMAGE) {
5855             imageSelected = true;
5856         } else if (obj.type == SelectSpanType::TYPESPAN) {
5857             textSelected = true;
5858         } else if (obj.type == SelectSpanType::TYPEBUILDERSPAN) {
5859             builderSelected = true;
5860         }
5861         if ((imageSelected && textSelected) || (builderSelected && textSelected) ||
5862             (imageSelected && builderSelected)) {
5863             selectedType_ = TextSpanType::MIXED;
5864             return;
5865         }
5866     }
5867     if (imageSelected) {
5868         selectedType_ = TextSpanType::IMAGE;
5869     } else if (textSelected) {
5870         selectedType_ = TextSpanType::TEXT;
5871     } else if (builderSelected) {
5872         selectedType_ = TextSpanType::BUILDER;
5873     }
5874 
5875     TAG_LOGD(AceLogTag::ACE_TEXT, "UpdateSelectionSpanType, selectedType_: %{public}d", selectedType_.value());
5876 }
5877 
GetSelectionSpanItemIndex(const MouseInfo & info)5878 int32_t TextPattern::GetSelectionSpanItemIndex(const MouseInfo& info)
5879 {
5880     RectF textContentRect = contentRect_;
5881     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
5882     textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
5883     PointF textOffset = { info.GetLocalLocation().GetX() - textContentRect.GetX(),
5884         info.GetLocalLocation().GetY() - textContentRect.GetY() };
5885     if (!textContentRect.IsInRegion(PointF(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY())) ||
5886         spans_.empty() || pManager_->GetParagraphs().empty()) {
5887         return -1;
5888     }
5889     int32_t start = 0;
5890     bool isFind = false;
5891     int32_t index = -1;
5892     for (const auto& item : spans_) {
5893         index++;
5894         if (!item) {
5895             continue;
5896         }
5897         auto selectedRects = pManager_->GetRects(start, item->position);
5898         start = item->position;
5899         for (auto&& rect : selectedRects) {
5900             if (rect.IsInRegion(textOffset)) {
5901                 isFind = true;
5902                 break;
5903             }
5904         }
5905         if (isFind) {
5906             TAG_LOGD(AceLogTag::ACE_TEXT, "GetSelectionSpanItemIndex index: %{public}d", index);
5907             return index;
5908         }
5909     }
5910     return -1;
5911 }
5912 
GetBuilderResultObject(RefPtr<UINode> uiNode,int32_t index,int32_t start,int32_t end)5913 ResultObject TextPattern::GetBuilderResultObject(RefPtr<UINode> uiNode, int32_t index, int32_t start, int32_t end)
5914 {
5915     int32_t itemLength = 1;
5916     ResultObject resultObject;
5917     resultObject.isDraggable = true;
5918     if (!DynamicCast<FrameNode>(uiNode) || !GetSpanItemByIndex(index)) {
5919         return resultObject;
5920     }
5921     int32_t endPosition = std::min(GetTextContentLength(), GetSpanItemByIndex(index)->position);
5922     int32_t startPosition = endPosition - itemLength;
5923     if ((start <= startPosition) && (end >= endPosition)) {
5924         auto builderNode = DynamicCast<FrameNode>(uiNode);
5925         CHECK_NULL_RETURN(builderNode, resultObject);
5926         resultObject.spanPosition.spanIndex = index;
5927         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
5928         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
5929         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
5930         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
5931         resultObject.type = SelectSpanType::TYPEIMAGE;
5932         auto geometryNode = builderNode->GetGeometryNode();
5933         CHECK_NULL_RETURN(geometryNode, resultObject);
5934         resultObject.imageStyle.size[RichEditorImageSize::SIZEWIDTH] = geometryNode->GetMarginFrameSize().Width();
5935         resultObject.imageStyle.size[RichEditorImageSize::SIZEHEIGHT] = geometryNode->GetMarginFrameSize().Height();
5936         resultObject.valueString = u" ";
5937     }
5938     return resultObject;
5939 }
5940 
SetStyledString(const RefPtr<SpanString> & value,bool closeSelectOverlay)5941 void TextPattern::SetStyledString(const RefPtr<SpanString>& value, bool closeSelectOverlay)
5942 {
5943     auto host = GetHost();
5944     FREE_NODE_CHECK(host, SetStyledString, value,
5945         closeSelectOverlay);  // call SetStyledStringMultiThread() by multi thread
5946     AllocStyledString();
5947     isSpanStringMode_ = true;
5948     CHECK_NULL_VOID(host);
5949     if (closeSelectOverlay) {
5950         CloseSelectOverlay();
5951     }
5952     auto length = styledString_->GetLength();
5953     styledString_->RemoveCustomSpan();
5954     styledString_->ReplaceSpanString(0, length, value);
5955     spans_ = styledString_->GetSpanItems();
5956     if (SystemProperties::GetTextTraceEnabled()) {
5957         ACE_TEXT_SCOPED_TRACE(
5958             "TextPattern::SetStyledString[id:%d][size:%d]", host->GetId(), static_cast<int32_t>(spans_.size()));
5959     }
5960     ProcessSpanString();
5961     styledString_->AddCustomSpan();
5962     styledString_->SetFramNode(WeakClaim(Referenced::RawPtr(host)));
5963     host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE);
5964 }
5965 
MountImageNode(const RefPtr<ImageSpanItem> & imageItem)5966 void TextPattern::MountImageNode(const RefPtr<ImageSpanItem>& imageItem)
5967 {
5968     auto host = GetContentHost();
5969     CHECK_NULL_VOID(host);
5970     auto imageNode = ImageSpanNode::GetOrCreateSpanNode(V2::IMAGE_ETS_TAG,
5971         ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<ImagePattern>(); });
5972     auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
5973     auto options = imageItem->options;
5974     imageLayoutProperty->UpdateImageSourceInfo(ParagraphUtil::CreateImageSourceInfo(options));
5975     imageNode->MountToParent(host, host->GetChildren().size());
5976     SetImageNodeGesture(imageNode);
5977     if (options.imageAttribute.has_value()) {
5978         auto imgAttr = options.imageAttribute.value();
5979         auto imagePattern = imageNode->GetPattern<ImagePattern>();
5980         CHECK_NULL_VOID(imagePattern);
5981         imagePattern->SetSyncLoad(imgAttr.syncLoad);
5982         if (imgAttr.size.has_value()) {
5983             imageLayoutProperty->UpdateUserDefinedIdealSize(imgAttr.size->GetSize());
5984         }
5985         if (imgAttr.verticalAlign.has_value()) {
5986             imageLayoutProperty->UpdateVerticalAlign(imgAttr.verticalAlign.value());
5987         }
5988         if (imgAttr.objectFit.has_value()) {
5989             imageLayoutProperty->UpdateImageFit(imgAttr.objectFit.value());
5990         }
5991         if (imgAttr.marginProp.has_value()) {
5992             imageLayoutProperty->UpdateMargin(imgAttr.marginProp.value());
5993         }
5994         if (imgAttr.paddingProp.has_value()) {
5995             imageLayoutProperty->UpdatePadding(imgAttr.paddingProp.value());
5996         }
5997         if (imgAttr.borderRadius.has_value()) {
5998             auto imageRenderCtx = imageNode->GetRenderContext();
5999             imageRenderCtx->UpdateBorderRadius(imgAttr.borderRadius.value());
6000             imageRenderCtx->SetClipToBounds(true);
6001         }
6002         auto paintProperty = imageNode->GetPaintProperty<ImageRenderProperty>();
6003         if (imgAttr.colorFilterMatrix.has_value() && paintProperty) {
6004             paintProperty->UpdateColorFilter(imgAttr.colorFilterMatrix.value());
6005             paintProperty->ResetDrawingColorFilter();
6006         } else if (imgAttr.drawingColorFilter.has_value() && paintProperty) {
6007             paintProperty->UpdateDrawingColorFilter(imgAttr.drawingColorFilter.value());
6008             paintProperty->ResetColorFilter();
6009         }
6010     }
6011     imageNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
6012     imageNode->MarkModifyDone();
6013     imageItem->nodeId_ = imageNode->GetId();
6014     imageNode->SetImageItem(imageItem);
6015     childNodes_.emplace_back(imageNode);
6016 }
6017 
SetImageNodeGesture(RefPtr<ImageSpanNode> imageNode)6018 void TextPattern::SetImageNodeGesture(RefPtr<ImageSpanNode> imageNode)
6019 {
6020     auto gesture = imageNode->GetOrCreateGestureEventHub();
6021     CHECK_NULL_VOID(gesture);
6022     gesture->SetHitTestMode(HitTestMode::HTMNONE);
6023 }
6024 
ProcessSpanString()6025 void TextPattern::ProcessSpanString()
6026 {
6027     auto host = GetHost();
6028     CHECK_NULL_VOID(host);
6029     textForDisplay_.clear();
6030     childNodes_.clear();
6031     // styled string perf can be optimized via create as requirement
6032     CHECK_NULL_VOID(GetDataDetectorAdapter());
6033     dataDetectorAdapter_->textForAI_.clear();
6034     host->Clean();
6035     hasSpanStringLongPressEvent_ = false;
6036     hasUrlSpan_ = false;
6037 
6038     // 适配AI&&挂载image节点
6039     for (const auto& span : spans_) {
6040         auto imageSpan = DynamicCast<ImageSpanItem>(span);
6041         if (imageSpan) {
6042             dataDetectorAdapter_->textForAI_ += u'\n';
6043             MountImageNode(imageSpan);
6044         } else {
6045             dataDetectorAdapter_->textForAI_ += span->content;
6046         }
6047         if (span->onClick || span->urlOnRelease) {
6048             auto gestureEventHub = host->GetOrCreateGestureEventHub();
6049             InitClickEvent(gestureEventHub);
6050         }
6051         if (span->onLongPress) {
6052             auto gestureEventHub = host->GetOrCreateGestureEventHub();
6053             InitLongPressEvent(gestureEventHub);
6054             hasSpanStringLongPressEvent_ = true;
6055         }
6056         if (span->urlOnRelease) {
6057             hasUrlSpan_ = true;
6058             InitUrlMouseEvent();
6059             InitUrlTouchEvent();
6060         }
6061         if (span->onTouch) {
6062             InitSpanStringTouchEvent();
6063         }
6064         textForDisplay_ += span->content;
6065     }
6066     if (dataDetectorAdapter_->textForAI_ != textForDisplay_) {
6067         dataDetectorAdapter_->aiDetectInitialized_ = false;
6068     }
6069     if (CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
6070         dataDetectorAdapter_->StartAITask();
6071     }
6072 
6073     auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
6074     CHECK_NULL_VOID(layoutProperty);
6075     layoutProperty->UpdateContent(textForDisplay_);
6076 }
6077 
OnSensitiveStyleChange(bool isSensitive)6078 void TextPattern::OnSensitiveStyleChange(bool isSensitive)
6079 {
6080     auto host = GetHost();
6081     CHECK_NULL_VOID(host);
6082     isSensitive_ = isSensitive;
6083     host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE);
6084 }
6085 
IsSensitiveEnable()6086 bool TextPattern::IsSensitiveEnable()
6087 {
6088     auto host = GetHost();
6089     CHECK_NULL_RETURN(host, false);
6090     return isSensitive_ && host->IsPrivacySensitive();
6091 }
6092 
ConvertGlobalToLocalOffset(const Offset & globalOffset)6093 Offset TextPattern::ConvertGlobalToLocalOffset(const Offset& globalOffset)
6094 {
6095     auto localPoint = OffsetF(globalOffset.GetX(), globalOffset.GetY());
6096     selectOverlay_->RevertLocalPointWithTransform(localPoint);
6097     return Offset(localPoint.GetX(), localPoint.GetY());
6098 }
6099 
SetExternalSpanItem(const std::list<RefPtr<SpanItem>> & spans)6100 void TextPattern::SetExternalSpanItem(const std::list<RefPtr<SpanItem>>& spans)
6101 {
6102     auto host = GetHost();
6103     FREE_NODE_CHECK(host, SetExternalSpanItem, spans);  // call SetExternalSpanItemMultiThread() by multi thread
6104     isSpanStringMode_ = !spans.empty();
6105     if (isSpanStringMode_) {
6106         AllocStyledString();
6107     }
6108     spans_ = spans;
6109     ProcessSpanString();
6110     auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
6111     CHECK_NULL_VOID(layoutProperty);
6112     layoutProperty->UpdateContent(textForDisplay_);
6113 }
6114 
GetTextContentRect(bool isActualText) const6115 RectF TextPattern::GetTextContentRect(bool isActualText) const
6116 {
6117     auto textRect = contentRect_;
6118     auto host = GetHost();
6119     CHECK_NULL_RETURN(host, textRect);
6120     auto renderContext = host->GetRenderContext();
6121     CHECK_NULL_RETURN(renderContext, textRect);
6122     CHECK_NULL_RETURN(pManager_, textRect);
6123     if (!renderContext->GetClipEdge().value_or(false) &&
6124         LessNotEqual(textRect.Width(), pManager_->GetLongestLine())) {
6125         textRect.SetWidth(pManager_->GetLongestLine());
6126     }
6127     if (isActualText && !renderContext->GetClipEdge().value_or(false) &&
6128         LessNotEqual(textRect.Height(), pManager_->GetHeight())) {
6129         textRect.SetHeight(pManager_->GetHeight());
6130     }
6131     return textRect;
6132 }
6133 
GetLineCount() const6134 size_t TextPattern::GetLineCount() const
6135 {
6136     CHECK_NULL_RETURN(pManager_, 0);
6137     return pManager_->GetLineCount();
6138 }
6139 
DidExceedMaxLines() const6140 bool TextPattern::DidExceedMaxLines() const
6141 {
6142     CHECK_NULL_RETURN(pManager_, false);
6143     return pManager_->DidExceedMaxLines();
6144 }
6145 
IsSetObscured() const6146 bool TextPattern::IsSetObscured() const
6147 {
6148     auto host = GetHost();
6149     CHECK_NULL_RETURN(host, false);
6150     auto renderContext = host->GetRenderContext();
6151     CHECK_NULL_RETURN(renderContext, false);
6152     auto obscuredReasons = renderContext->GetObscured().value_or(std::vector<ObscuredReasons>());
6153     bool ifHaveObscured = spans_.empty() && std::any_of(obscuredReasons.begin(), obscuredReasons.end(),
6154         [](const auto& reason) { return reason == ObscuredReasons::PLACEHOLDER; });
6155     return ifHaveObscured;
6156 }
6157 
GetLineMetrics(int32_t lineNumber)6158 TextLineMetrics TextPattern::GetLineMetrics(int32_t lineNumber)
6159 {
6160     CHECK_NULL_RETURN(pManager_, TextLineMetrics());
6161     if (lineNumber < 0 || GetLineCount() == 0 || lineNumber > static_cast<int32_t>(GetLineCount()) - 1) {
6162         TAG_LOGI(AceLogTag::ACE_TEXT, "GetLineMetrics failed, lineNumber not between 0 and max lines:%{public}d",
6163             lineNumber);
6164         return TextLineMetrics();
6165     }
6166     auto lineMetrics = pManager_->GetLineMetrics(lineNumber);
6167     RectF textContentRect = contentRect_;
6168     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
6169     lineMetrics.x += textContentRect.GetX();
6170     lineMetrics.y += textContentRect.GetY();
6171     lineMetrics.baseline += textContentRect.GetY();
6172     return lineMetrics;
6173 }
6174 
GetRectsForRange(int32_t start,int32_t end,RectHeightStyle heightStyle,RectWidthStyle widthStyle)6175 std::vector<ParagraphManager::TextBox> TextPattern::GetRectsForRange(
6176     int32_t start, int32_t end, RectHeightStyle heightStyle, RectWidthStyle widthStyle)
6177 {
6178     if (start < 0 || end < 0 || start > end) {
6179         return {};
6180     }
6181     std::vector<ParagraphManager::TextBox> textBoxes = pManager_->GetRectsForRange(start, end, heightStyle, widthStyle);
6182     RectF textContentRect = contentRect_;
6183     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
6184     std::vector<ParagraphManager::TextBox> adjustedTextBoxes;
6185     for (auto& textBox : textBoxes) {
6186         ParagraphManager::TextBox adjustedTextBox = textBox;
6187         adjustedTextBox.rect_.SetLeft(textBox.rect_.Left() + textContentRect.Left());
6188         adjustedTextBox.rect_.SetTop(textBox.rect_.Top() + textContentRect.Top());
6189         adjustedTextBoxes.push_back(adjustedTextBox);
6190     }
6191     return adjustedTextBoxes;
6192 }
6193 
ConvertLocalOffsetToParagraphOffset(const Offset & offset)6194 Offset TextPattern::ConvertLocalOffsetToParagraphOffset(const Offset& offset)
6195 {
6196     RectF textContentRect = contentRect_;
6197     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
6198     Offset paragraphOffset = { offset.GetX() - textContentRect.GetX(), offset.GetY() - textContentRect.GetY() };
6199     return paragraphOffset;
6200 }
6201 
GetGlyphPositionAtCoordinate(int32_t x,int32_t y)6202 PositionWithAffinity TextPattern::GetGlyphPositionAtCoordinate(int32_t x, int32_t y)
6203 {
6204     Offset offset(x, y);
6205     return pManager_->GetGlyphPositionAtCoordinate(ConvertLocalOffsetToParagraphOffset(offset));
6206 }
6207 
ProcessMarqueeVisibleAreaCallback()6208 void TextPattern::ProcessMarqueeVisibleAreaCallback()
6209 {
6210     OnTextOverflowChanged();
6211     if (!IsMarqueeOverflow()) {
6212         return;
6213     }
6214     auto host = GetHost();
6215     CHECK_NULL_VOID(host);
6216     auto pipeline = GetContext();
6217     CHECK_NULL_VOID(pipeline);
6218     auto callback = [weak = WeakClaim(this)](bool visible, double ratio) {
6219         auto pattern = weak.Upgrade();
6220         CHECK_NULL_VOID(pattern);
6221         CHECK_NULL_VOID(pattern->contentMod_);
6222         if (!pattern->IsMarqueeOverflow()) {
6223             return;
6224         }
6225         if (visible && Positive(ratio)) {
6226             pattern->contentMod_->ResumeAnimation();
6227         }
6228         if (!visible && NonPositive(ratio)) {
6229             pattern->contentMod_->PauseAnimation();
6230         }
6231     };
6232     std::vector<double> ratioList = { 0.0 };
6233     pipeline->AddVisibleAreaChangeNode(host, ratioList, callback, false, true);
6234 }
6235 
OnTextOverflowChanged()6236 void TextPattern::OnTextOverflowChanged()
6237 {
6238     auto host = GetHost();
6239     CHECK_NULL_VOID(host);
6240     if (host->GetTag() == V2::SYMBOL_ETS_TAG) {
6241         return;
6242     }
6243     auto pipeline = GetContext();
6244     CHECK_NULL_VOID(pipeline);
6245     auto eventHub = host->GetOrCreateEventHub<TextEventHub>();
6246     CHECK_NULL_VOID(eventHub);
6247     auto hasInnerCallabck = eventHub->HasVisibleAreaCallback(false);
6248     if (!hasInnerCallabck) {
6249         return;
6250     }
6251     auto hasUserCallback = eventHub->HasVisibleAreaCallback(true);
6252     if (!hasUserCallback) {
6253         pipeline->RemoveVisibleAreaChangeNode(host->GetId());
6254     }
6255     eventHub->CleanVisibleAreaCallback(false);
6256 }
6257 
OnFrameNodeChanged(FrameNodeChangeInfoFlag flag)6258 void TextPattern::OnFrameNodeChanged(FrameNodeChangeInfoFlag flag)
6259 {
6260     if (selectOverlay_->SelectOverlayIsOn()) {
6261         selectOverlay_->OnAncestorNodeChanged(flag);
6262     }
6263     if (leftMousePressed_ && mouseStatus_ == MouseStatus::MOVE && scrollableParent_.Upgrade()) {
6264         auto host = GetHost();
6265         CHECK_NULL_VOID(host);
6266         auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
6267         NG::PointF localPoint(lastLeftMouseMoveLocation_.GetX(), lastLeftMouseMoveLocation_.GetY());
6268         NG::NGGestureRecognizer::Transform(localPoint, WeakClaim(Referenced::RawPtr(host)), true);
6269         Offset textOffset = { localPoint.GetX() - textPaintOffset.GetX(), localPoint.GetY() - textPaintOffset.GetY() };
6270         CHECK_NULL_VOID(pManager_);
6271         auto end = pManager_->GetGlyphIndexByCoordinate(textOffset);
6272         HandleSelectionChange(textSelector_.baseOffset, end);
6273         isAutoScrollByMouse_ = true;
6274         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6275     }
6276 }
6277 
IsMarqueeOverflow() const6278 bool TextPattern::IsMarqueeOverflow() const
6279 {
6280     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
6281     CHECK_NULL_RETURN(textLayoutProperty, false);
6282     return textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE;
6283 }
6284 
UnRegisterResource(const std::string & key)6285 void TextPattern::UnRegisterResource(const std::string& key)
6286 {
6287     if (key == "symbolColor") {
6288         for (auto index : symbolFontColorResObjIndexArr) {
6289             auto storeKey = key + "_" + std::to_string(index);
6290             RemoveResObj(storeKey);
6291         }
6292         symbolFontColorResObjIndexArr.clear();
6293         return;
6294     }
6295     Pattern::UnRegisterResource(key);
6296 }
6297 
UpdateFontColor(const Color & value)6298 void TextPattern::UpdateFontColor(const Color& value)
6299 {
6300     auto host = GetHost();
6301     CHECK_NULL_VOID(host);
6302     const auto& children = host->GetChildren();
6303     if (children.empty() && spans_.empty() && !NeedShowAIDetect() && !textEffect_) {
6304         if (textStyle_.has_value()) {
6305             textStyle_->SetTextColor(value);
6306         }
6307         if (contentMod_) {
6308             contentMod_->TextColorModifier(value);
6309         } else if (pManager_) {
6310             for (auto&& info : pManager_->GetParagraphs()) {
6311                 auto paragraph = info.paragraph;
6312                 CHECK_NULL_VOID(paragraph);
6313                 auto length = paragraph->GetParagraphText().length();
6314                 paragraph->UpdateColor(0, length, value);
6315             }
6316         }
6317     } else {
6318         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
6319     }
6320 }
6321 
MarkDirtyNodeRender()6322 void TextPattern::MarkDirtyNodeRender()
6323 {
6324     auto host = GetHost();
6325     CHECK_NULL_VOID(host);
6326     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6327 }
6328 
MarkDirtyNodeMeasure()6329 void TextPattern::MarkDirtyNodeMeasure()
6330 {
6331     auto host = GetHost();
6332     CHECK_NULL_VOID(host);
6333     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
6334 }
6335 
BeforeCreatePaintWrapper()6336 void TextPattern::BeforeCreatePaintWrapper()
6337 {
6338     // mark content dirty
6339     if (contentMod_) {
6340         contentMod_->ContentChange();
6341     }
6342 }
6343 
StartGestureSelection(int32_t start,int32_t end,const Offset & startOffset)6344 void TextPattern::StartGestureSelection(int32_t start, int32_t end, const Offset& startOffset)
6345 {
6346     scrollableParent_ = selectOverlay_->FindScrollableParent();
6347     SetupMagnifier();
6348     TextGestureSelector::StartGestureSelection(start, end, startOffset);
6349 }
6350 
GetTouchIndex(const OffsetF & offset)6351 int32_t TextPattern::GetTouchIndex(const OffsetF& offset)
6352 {
6353     OffsetF deltaOffset;
6354     if (scrollableParent_.Upgrade()) {
6355         auto parentGlobalOffset = GetParentGlobalOffset();
6356         deltaOffset = parentGlobalOffset - parentGlobalOffset_;
6357     }
6358     auto paragraphOffset =
6359         offset - deltaOffset - GetTextContentRect().GetOffset() + OffsetF(0.0f, std::min(GetBaselineOffset(), 0.0f));
6360     return GetHandleIndex({ paragraphOffset.GetX(), paragraphOffset.GetY() });
6361 }
6362 
OnTextGestureSelectionUpdate(int32_t start,int32_t end,const TouchEventInfo & info)6363 void TextPattern::OnTextGestureSelectionUpdate(int32_t start, int32_t end, const TouchEventInfo& info)
6364 {
6365     if (!HasContent()) {
6366         return;
6367     }
6368     selectOverlay_->TriggerScrollableParentToScroll(
6369         scrollableParent_.Upgrade(), info.GetTouches().front().GetGlobalLocation(), false);
6370     auto localOffset = info.GetTouches().front().GetLocalLocation();
6371     if (GetOrCreateMagnifier()) {
6372         magnifierController_->SetLocalOffset({ localOffset.GetX(), localOffset.GetY() });
6373     }
6374     if (start != textSelector_.GetStart()) {
6375         StartVibratorByIndexChange(start, textSelector_.GetStart());
6376     } else if (end != textSelector_.GetEnd()) {
6377         StartVibratorByIndexChange(end, textSelector_.GetEnd());
6378     }
6379     auto host = GetHost();
6380     CHECK_NULL_VOID(host);
6381     HandleSelectionChange(start, end);
6382     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6383 }
6384 
OnTextGestureSelectionEnd(const TouchLocationInfo & locationInfo)6385 void TextPattern::OnTextGestureSelectionEnd(const TouchLocationInfo& locationInfo)
6386 {
6387     selectOverlay_->TriggerScrollableParentToScroll(scrollableParent_.Upgrade(), Offset(), true);
6388     if (magnifierController_) {
6389         magnifierController_->RemoveMagnifierFrameNode();
6390     }
6391     if (HasContent()) {
6392         CalculateHandleOffsetAndShowOverlay();
6393         oldSelectedType_ = selectedType_.value_or(TextSpanType::NONE);
6394         ShowSelectOverlay({ .animation = true });
6395     }
6396 }
6397 
ChangeHandleHeight(const GestureEvent & event,bool isFirst,bool isOverlayMode)6398 void TextPattern::ChangeHandleHeight(const GestureEvent& event, bool isFirst, bool isOverlayMode)
6399 {
6400     auto touchOffset = event.GetGlobalLocation();
6401     auto& currentHandle = isFirst ? textSelector_.firstHandle : textSelector_.secondHandle;
6402     bool isChangeFirstHandle = isFirst ? (!textSelector_.StartGreaterDest()) : textSelector_.StartGreaterDest();
6403     if (isChangeFirstHandle) {
6404         ChangeFirstHandleHeight(touchOffset, currentHandle);
6405     } else {
6406         if (!selectOverlay_->ChangeSecondHandleHeight(event, isOverlayMode)) {
6407             ChangeSecondHandleHeight(touchOffset, currentHandle);
6408         }
6409     }
6410 }
6411 
ChangeFirstHandleHeight(const Offset & touchOffset,RectF & handleRect)6412 void TextPattern::ChangeFirstHandleHeight(const Offset& touchOffset, RectF& handleRect)
6413 {
6414     auto height = handleRect.Height();
6415     CalculateDefaultHandleHeight(height);
6416     bool isTouchHandleCircle = LessNotEqual(touchOffset.GetY(), handleRect.Top());
6417     if (!isTouchHandleCircle) {
6418         handleRect.SetTop(static_cast<float>(touchOffset.GetY()) - height / 2.0f);
6419     }
6420     handleRect.SetHeight(height);
6421 }
6422 
ChangeSecondHandleHeight(const Offset & touchOffset,RectF & handleRect)6423 void TextPattern::ChangeSecondHandleHeight(const Offset& touchOffset, RectF& handleRect)
6424 {
6425     auto height = handleRect.Height();
6426     CalculateDefaultHandleHeight(height);
6427     bool isTouchHandleCircle = GreatNotEqual(touchOffset.GetY(), handleRect.Bottom());
6428     auto handleOffsetY = isTouchHandleCircle
6429                             ? handleRect.Bottom() - height
6430                             : static_cast<float>(touchOffset.GetY()) - height / 2.0f;
6431     handleRect.SetTop(handleOffsetY);
6432     handleRect.SetHeight(height);
6433 }
6434 
CalculateDefaultHandleHeight(float & height)6435 void TextPattern::CalculateDefaultHandleHeight(float& height)
6436 {
6437     CHECK_NULL_VOID(textStyle_.has_value());
6438 #ifdef ENABLE_ROSEN_BACKEND
6439     MeasureContext content;
6440     content.textContent = "a";
6441     content.fontSize = textStyle_.value().GetFontSize();
6442     auto fontweight = StringUtils::FontWeightToString(textStyle_.value().GetFontWeight());
6443     content.fontWeight = fontweight;
6444     height = std::max(static_cast<float>(RosenRenderCustomPaint::MeasureTextSizeInner(content).Height()), 0.0f);
6445 #endif
6446 }
6447 
DumpAdvanceInfo(std::unique_ptr<JsonValue> & json)6448 void TextPattern::DumpAdvanceInfo(std::unique_ptr<JsonValue>& json)
6449 {
6450     json->Put("contentRect", contentRect_.ToString().c_str());
6451     if (SystemProperties::GetDebugEnabled() && pManager_) {
6452         std::unique_ptr<JsonValue> children = JsonUtil::Create(true);
6453         children->Put("DidExceedMaxLines", std::to_string(pManager_->DidExceedMaxLines()).c_str());
6454         children->Put("GetTextWidth", std::to_string(pManager_->GetTextWidth()).c_str());
6455         children->Put("GetHeight", std::to_string(pManager_->GetHeight()).c_str());
6456         children->Put("GetMaxWidth", std::to_string(pManager_->GetMaxWidth()).c_str());
6457         children->Put("GetMaxIntrinsicWidth", std::to_string(pManager_->GetMaxIntrinsicWidth()).c_str());
6458         children->Put("GetLineCount", std::to_string(pManager_->GetLineCount()).c_str());
6459         children->Put("GetLongestLine", std::to_string(pManager_->GetLongestLine()).c_str());
6460         children->Put("GetLongestLineWithIndent", std::to_string(pManager_->GetLongestLineWithIndent()).c_str());
6461         json->Put("from TextEngine paragraphs_ info", children);
6462     }
6463     json->Put("BindSelectionMenu", std::to_string(selectionMenuMap_.empty()).c_str());
6464     auto host = GetHost();
6465     CHECK_NULL_VOID(host);
6466     auto pipeline = host->GetContext();
6467     CHECK_NULL_VOID(pipeline);
6468     auto fontScale = pipeline->GetFontScale();
6469     auto fontWeightScale = pipeline->GetFontWeightScale();
6470     json->Put("fontScale", std::to_string(fontScale).c_str());
6471     json->Put("fontWeightScale", std::to_string(fontWeightScale).c_str());
6472     auto renderContext = host->GetRenderContext();
6473     CHECK_NULL_VOID(renderContext);
6474     if (renderContext->HasForegroundColor()) {
6475         json->Put("ForegroundColor", renderContext->GetForegroundColorValue().ColorToString().c_str());
6476     }
6477     if (renderContext->GetForegroundColorStrategy().has_value()) {
6478         auto strategy = static_cast<int32_t>(renderContext->GetForegroundColorStrategyValue());
6479         json->Put("ForegroundColorStrategy", strategy);
6480     }
6481 }
6482 
SetTextStyleDumpInfo(std::unique_ptr<JsonValue> & json)6483 void TextPattern::SetTextStyleDumpInfo(std::unique_ptr<JsonValue>& json)
6484 {
6485     if (textStyle_.has_value()) {
6486         json->Put("MaxFontSize", textStyle_->GetAdaptMaxFontSize().ToString().c_str());
6487         json->Put("MinFontSize", textStyle_->GetAdaptMinFontSize().ToString().c_str());
6488         json->Put("FontWeight", StringUtils::ToString(textStyle_->GetFontWeight()).c_str());
6489         json->Put("FontStyle", StringUtils::ToString(textStyle_->GetFontStyle()).c_str());
6490         json->Put("LineHeight", textStyle_->GetLineHeight().ToString().c_str());
6491         json->Put("LineSpacing", textStyle_->GetLineSpacing().ToString().c_str());
6492         json->Put("isOnlyBetweenLines", (textStyle_->GetIsOnlyBetweenLines()) ? "true" : "false");
6493         json->Put("BaselineOffset", textStyle_->GetBaselineOffset().ToString().c_str());
6494         json->Put("TextIndent", textStyle_->GetTextIndent().ToString().c_str());
6495         json->Put("LetterSpacing", textStyle_->GetLetterSpacing().ToString().c_str());
6496         json->Put("TextOverflow", StringUtils::ToString(textStyle_->GetTextOverflow()).c_str());
6497         json->Put("TextAlign", StringUtils::ToString(textStyle_->GetTextAlign()).c_str());
6498         json->Put("WordBreak", StringUtils::ToString(textStyle_->GetWordBreak()).c_str());
6499         json->Put("TextCase", StringUtils::ToString(textStyle_->GetTextCase()).c_str());
6500         json->Put("EllipsisMode", StringUtils::ToString(textStyle_->GetEllipsisMode()).c_str());
6501         json->Put("LineBreakStrategy", GetLineBreakStrategyInJson(textStyle_->GetLineBreakStrategy()).c_str());
6502     }
6503 }
6504 
DumpInfo(std::unique_ptr<JsonValue> & json)6505 void TextPattern::DumpInfo(std::unique_ptr<JsonValue>& json)
6506 {
6507     auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
6508     CHECK_NULL_VOID(textLayoutProp);
6509     auto nowTime = GetSystemTimestamp();
6510     json->Put("time", std::to_string(nowTime).c_str());
6511     if (!IsSetObscured() && !IsSensitiveEnable()) {
6512         json->Put("Content", UtfUtils::Str16DebugToStr8(textLayoutProp->GetContent().value_or(u" ")).c_str());
6513     }
6514     json->Put("ConteFontColornt",
6515         (textStyle_.has_value() ? textStyle_->GetTextColor() : Color::BLACK).ColorToString().c_str());
6516     json->Put(
6517         "FontSize", (textStyle_.has_value() ? textStyle_->GetFontSize() : Dimension(DIMENSION_VALUE, DimensionUnit::FP))
6518                         .ToString()
6519                         .c_str());
6520     SetTextStyleDumpInfo(json);
6521     json->Put("HeightAdaptivePolicy",
6522         V2::ConvertWrapTextHeightAdaptivePolicyToString(
6523             textLayoutProp->GetHeightAdaptivePolicy().value_or(TextHeightAdaptivePolicy::MAX_LINES_FIRST))
6524             .c_str());
6525 
6526     json->Put("Selection", textSelector_.ToString().c_str());
6527 
6528     if (pManager_ && !pManager_->GetParagraphs().empty()) {
6529         auto num = static_cast<int32_t>(pManager_->GetParagraphs().size());
6530         json->Put("Paragraphs num", std::to_string(num).c_str());
6531         json->Put("PaintInfo", paintInfo_.c_str());
6532     }
6533     if (SystemProperties::GetDebugEnabled()) {
6534         DumpAdvanceInfo(json);
6535     }
6536 }
6537 
HasContent()6538 bool TextPattern::HasContent()
6539 {
6540     if (GetTextForDisplay().empty()) {
6541         for (const auto& span : spans_) {
6542             if (span->spanItemType != SpanItemType::NORMAL) {
6543                 return true;
6544             }
6545         }
6546         return false;
6547     }
6548     return true;
6549 }
6550 
SetupMagnifier()6551 void TextPattern::SetupMagnifier()
6552 {
6553     GetOrCreateMagnifier();
6554     CHECK_NULL_VOID(magnifierController_);
6555     auto host = GetHost();
6556     CHECK_NULL_VOID(host);
6557     auto renderContext = host->GetRenderContext();
6558     CHECK_NULL_VOID(renderContext);
6559     auto geometryNode = host->GetGeometryNode();
6560     CHECK_NULL_VOID(geometryNode);
6561     if (renderContext->GetClipEdge().value_or(false)) {
6562         return;
6563     }
6564     RectF viewPort;
6565     if (selectOverlay_->GetClipHandleViewPort(viewPort)) {
6566         viewPort.SetHeight(std::min(pManager_->GetHeight(), viewPort.Height()));
6567         viewPort.SetWidth(std::min(pManager_->GetLongestLine(), viewPort.Width()));
6568         auto globalFrameRect = geometryNode->GetFrameRect();
6569         globalFrameRect.SetOffset(parentGlobalOffset_);
6570         auto maxRight = std::max(viewPort.Right(), globalFrameRect.Right());
6571         auto maxBottom = std::max(viewPort.Bottom(), globalFrameRect.Bottom());
6572         viewPort = geometryNode->GetFrameRect();
6573         viewPort.SetWidth(maxRight - globalFrameRect.Left());
6574         viewPort.SetHeight(maxBottom - globalFrameRect.Top());
6575         magnifierController_->SetHostViewPort(viewPort);
6576     }
6577 }
6578 
DoTextSelectionTouchCancel()6579 void TextPattern::DoTextSelectionTouchCancel()
6580 {
6581     CHECK_NULL_VOID(magnifierController_);
6582     magnifierController_->RemoveMagnifierFrameNode();
6583     ResetSelection();
6584 }
6585 
BeforeSyncGeometryProperties(const DirtySwapConfig & config)6586 void TextPattern::BeforeSyncGeometryProperties(const DirtySwapConfig& config)
6587 {
6588     if (afterLayoutCallback_.has_value()) {
6589         (*afterLayoutCallback_)();
6590     }
6591 }
6592 
GetCaretColor() const6593 std::string TextPattern::GetCaretColor() const
6594 {
6595     auto context = PipelineContext::GetCurrentContextSafelyWithCheck();
6596     CHECK_NULL_RETURN(context, "");
6597     auto theme = context->GetTheme<TextTheme>();
6598     CHECK_NULL_RETURN(theme, "");
6599     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
6600     CHECK_NULL_RETURN(textLayoutProperty, "");
6601     return textLayoutProperty->GetCursorColorValue(theme->GetCaretColor()).ColorToString();
6602 }
6603 
GetSelectedBackgroundColor() const6604 std::string TextPattern::GetSelectedBackgroundColor() const
6605 {
6606     auto context = PipelineContext::GetCurrentContextSafelyWithCheck();
6607     CHECK_NULL_RETURN(context, "");
6608     auto theme = context->GetTheme<TextTheme>();
6609     CHECK_NULL_RETURN(theme, "");
6610     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
6611     CHECK_NULL_RETURN(textLayoutProperty, "");
6612     return textLayoutProperty->GetSelectedBackgroundColorValue(theme->GetSelectedColor()).ColorToString();
6613 }
6614 
OnWindowSizeChanged(int32_t width,int32_t height,WindowSizeChangeReason type)6615 void TextPattern::OnWindowSizeChanged(int32_t width, int32_t height, WindowSizeChangeReason type)
6616 {
6617     CHECK_NULL_VOID(selectOverlay_);
6618     selectOverlay_->UpdateMenuOnWindowSizeChanged(type);
6619 }
6620 
IsLocationInFrameRegion(const Offset & localOffset) const6621 bool TextPattern::IsLocationInFrameRegion(const Offset& localOffset) const
6622 {
6623     auto host = GetHost();
6624     CHECK_NULL_RETURN(host, false);
6625     auto geometryNode = host->GetGeometryNode();
6626     CHECK_NULL_RETURN(geometryNode, false);
6627     auto frameSize = geometryNode->GetFrameSize();
6628     auto frameRect = RectF(OffsetF(0.0f, 0.0f), frameSize);
6629     return frameRect.IsInRegion(PointF(localOffset.GetX(), localOffset.GetY()));
6630 }
6631 
RegisterFormVisibleChangeCallback()6632 void TextPattern::RegisterFormVisibleChangeCallback()
6633 {
6634     if (hasRegisterFormVisibleCallback_ || !IsMarqueeOverflow()) {
6635         return;
6636     }
6637     auto host = GetHost();
6638     CHECK_NULL_VOID(host);
6639     auto pipeline = host->GetContext();
6640     CHECK_NULL_VOID(pipeline);
6641     auto isFormRender = pipeline->IsFormRender();
6642     auto formMgr = pipeline->GetFormVisibleManager();
6643     if (!isFormRender || !formMgr) {
6644         return;
6645     }
6646     auto formCallback = [weak = WeakClaim(this)](bool visible) {
6647         auto textPattern = weak.Upgrade();
6648         CHECK_NULL_VOID(textPattern);
6649         textPattern->HandleFormVisibleChange(visible);
6650     };
6651     formMgr->AddFormVisibleChangeNode(host, formCallback);
6652     hasRegisterFormVisibleCallback_ = true;
6653 }
6654 
RegisterVisibleAreaChangeCallback()6655 void TextPattern::RegisterVisibleAreaChangeCallback()
6656 {
6657     auto host = GetHost();
6658     CHECK_NULL_VOID(host);
6659     if (host->GetTag() != V2::SYMBOL_ETS_TAG) {
6660         return;
6661     }
6662     auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
6663     CHECK_NULL_VOID(layoutProperty);
6664     bool isLoopAnimation = layoutProperty->GetIsLoopAnimation();
6665     auto pipeline = GetContext();
6666     CHECK_NULL_VOID(pipeline);
6667     if (isLoopAnimation && !isRegisteredAreaCallback_) {
6668         isRegisteredAreaCallback_ = true;
6669         auto callback = [weak = WeakClaim(this)](bool visible, double ratio) {
6670             auto pattern = weak.Upgrade();
6671             CHECK_NULL_VOID(pattern);
6672             pattern->OnVisibleChange(visible);
6673         };
6674         std::vector<double> ratioList = {0.0};
6675         pipeline->AddVisibleAreaChangeNode(host, ratioList, callback, false, true);
6676     } else if (!isLoopAnimation && isRegisteredAreaCallback_) {
6677         isRegisteredAreaCallback_ = false;
6678         pipeline->RemoveVisibleAreaChangeNode(host->GetId());
6679         host->CleanVisibleAreaInnerCallback();
6680     } else {
6681         return;
6682     }
6683 }
6684 
RemoveFormVisibleChangeCallback(int32_t id)6685 void TextPattern::RemoveFormVisibleChangeCallback(int32_t id)
6686 {
6687     if (!hasRegisterFormVisibleCallback_) {
6688         return;
6689     }
6690     auto pipeline = pipeline_.Upgrade();
6691     CHECK_NULL_VOID(pipeline);
6692     auto formMgr = pipeline->GetFormVisibleManager();
6693     CHECK_NULL_VOID(formMgr);
6694     formMgr->RemoveFormVisibleChangeNode(id);
6695 }
6696 
HandleFormVisibleChange(bool visible)6697 void TextPattern::HandleFormVisibleChange(bool visible)
6698 {
6699     if (!IsMarqueeOverflow() || !contentMod_) {
6700         return;
6701     }
6702     if (visible) {
6703         contentMod_->ResumeAnimation();
6704     } else {
6705         contentMod_->PauseAnimation();
6706     }
6707 }
6708 
6709 #define DEFINE_PROP_HANDLER(KEY_TYPE, VALUE_TYPE, UPDATE_METHOD)                        \
6710     {                                                                                   \
6711         #KEY_TYPE, [](TextLayoutProperty* prop, RefPtr<PropertyValueBase> value) {      \
6712             if (auto realValue = std::get_if<VALUE_TYPE>(&(value->GetValue()))) {       \
6713                 prop->UPDATE_METHOD(*realValue);                                        \
6714             }                                                                           \
6715                                                                                         \
6716         }                                                                               \
6717     }                                                                                   \
6718 
UpdatePropertyImpl(const std::string & key,RefPtr<PropertyValueBase> value)6719 void TextPattern::UpdatePropertyImpl(const std::string& key, RefPtr<PropertyValueBase> value)
6720 {
6721     auto frameNode = GetHost();
6722     CHECK_NULL_VOID(frameNode);
6723     auto property = frameNode->GetLayoutPropertyPtr<TextLayoutProperty>();
6724     CHECK_NULL_VOID(property);
6725     CHECK_NULL_VOID(value);
6726     using Handler = std::function<void(TextLayoutProperty*, RefPtr<PropertyValueBase>)>;
6727     const std::unordered_map<std::string, Handler> handlers = {
6728         DEFINE_PROP_HANDLER(FontSize, CalcDimension, UpdateFontSize),
6729         DEFINE_PROP_HANDLER(TextIndent, CalcDimension, UpdateTextIndent),
6730         DEFINE_PROP_HANDLER(MinFontScale, float, UpdateMinFontScale),
6731         DEFINE_PROP_HANDLER(MaxFontScale, float, UpdateMaxFontScale),
6732         DEFINE_PROP_HANDLER(LineHeight, CalcDimension, UpdateLineHeight),
6733         DEFINE_PROP_HANDLER(LineSpacing, CalcDimension, UpdateLineSpacing),
6734         DEFINE_PROP_HANDLER(LetterSpacing, CalcDimension, UpdateLetterSpacing),
6735         DEFINE_PROP_HANDLER(AdaptMaxFontSize, CalcDimension, UpdateAdaptMaxFontSize),
6736         DEFINE_PROP_HANDLER(AdaptMinFontSize, CalcDimension, UpdateAdaptMinFontSize),
6737         DEFINE_PROP_HANDLER(BaselineOffset, CalcDimension, UpdateBaselineOffset),
6738         DEFINE_PROP_HANDLER(TextCaretColor, Color, UpdateCursorColor),
6739         DEFINE_PROP_HANDLER(TextDecorationColor, Color, UpdateTextDecorationColor),
6740         DEFINE_PROP_HANDLER(Content, std::u16string, UpdateContent),
6741         DEFINE_PROP_HANDLER(FontFamily, std::vector<std::string>, UpdateFontFamily),
6742 
6743         {"SelectedBackgroundColor", [wp = WeakClaim(RawPtr(frameNode))](
6744             TextLayoutProperty* prop, RefPtr<PropertyValueBase> value) {
6745                 if (auto realValue = std::get_if<Color>(&(value->GetValue()))) {
6746                         if (realValue->GetAlpha() == 255) {
6747                         *realValue = realValue->ChangeOpacity(0.2);
6748                     }
6749                     prop->UpdateSelectedBackgroundColor(*realValue);
6750                 }
6751             }
6752         },
6753 
6754         { "TextColor",
6755             [node = WeakClaim(RawPtr((frameNode))), weak = WeakClaim(this)](
6756                 TextLayoutProperty* prop, RefPtr<PropertyValueBase> value) {
6757                 if (auto realValue = std::get_if<Color>(&(value->GetValue()))) {
6758                     prop->UpdateTextColorByRender(*realValue);
6759                     auto frameNode = node.Upgrade();
6760                     CHECK_NULL_VOID(frameNode);
6761                     auto pattern = weak.Upgrade();
6762                     CHECK_NULL_VOID(pattern);
6763                     ACE_UPDATE_NODE_RENDER_CONTEXT(ForegroundColor, *realValue, frameNode);
6764                     ACE_RESET_NODE_RENDER_CONTEXT(RenderContext, ForegroundColorStrategy, frameNode);
6765                     ACE_UPDATE_NODE_RENDER_CONTEXT(ForegroundColorFlag, true, frameNode);
6766                     pattern->UpdateFontColor(*realValue);
6767                 }
6768             }
6769         },
6770         DEFINE_PROP_HANDLER(LetterSpacing, CalcDimension, UpdateLetterSpacing),
6771     };
6772     auto it = handlers.find(key);
6773     if (it != handlers.end()) {
6774         it->second(property, value);
6775     }
6776     if (frameNode->GetRerenderable()) {
6777         frameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
6778     }
6779 }
6780 } // namespace OHOS::Ace::NG
6781