• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/text/text_pattern.h"
17 
18 #include <cstdint>
19 #include <stack>
20 #include <string>
21 
22 #include "base/geometry/ng/offset_t.h"
23 #include "base/geometry/ng/rect_t.h"
24 #include "base/geometry/offset.h"
25 #include "base/log/dump_log.h"
26 #include "base/log/log_wrapper.h"
27 #include "base/utils/string_utils.h"
28 #include "base/utils/utils.h"
29 #include "base/window/drag_window.h"
30 #include "core/common/ace_engine_ext.h"
31 #include "core/common/ai/data_detector_mgr.h"
32 #include "core/common/container.h"
33 #include "core/common/container_scope.h"
34 #include "core/common/font_manager.h"
35 #include "core/common/recorder/event_recorder.h"
36 #include "core/common/recorder/node_data_cache.h"
37 #include "core/common/udmf/udmf_client.h"
38 #include "core/components/text_overlay/text_overlay_theme.h"
39 #include "core/components_ng/base/ui_node.h"
40 #include "core/components_ng/base/view_stack_processor.h"
41 #include "core/components_ng/event/gesture_event_hub.h"
42 #include "core/components_ng/event/long_press_event.h"
43 #include "core/components_ng/manager/select_overlay/select_overlay_manager.h"
44 #include "core/components_ng/pattern/image/image_layout_property.h"
45 #include "core/components_ng/pattern/rich_editor_drag/rich_editor_drag_pattern.h"
46 #include "core/components_ng/pattern/select_overlay/select_overlay_property.h"
47 #include "core/components_ng/pattern/text/text_event_hub.h"
48 #include "core/components_ng/pattern/text/text_layout_algorithm.h"
49 #include "core/components_ng/pattern/text/text_layout_property.h"
50 #include "core/components_ng/pattern/text_drag/text_drag_pattern.h"
51 #include "core/components_ng/property/property.h"
52 
53 namespace OHOS::Ace::NG {
54 namespace {
55 constexpr double DIMENSION_VALUE = 16.0;
56 constexpr const char COPY_ACTION[] = "copy";
57 constexpr const char SELECT_ACTION[] = "select";
58 constexpr const char SYMBOL_COLOR[] = "BLACK";
59 constexpr int32_t API_PROTEXTION_GREATER_NINE = 9;
60 // uncertainty range when comparing selectedTextBox to contentRect
61 constexpr float BOX_EPSILON = 0.5f;
62 constexpr float DOUBLECLICK_INTERVAL_MS = 300.0f;
63 constexpr uint32_t SECONDS_TO_MILLISECONDS = 1000;
64 const std::u16string SYMBOL_TRANS = u"\uF0001";
65 const std::string NEWLINE = "\n";
66 const std::wstring WIDE_NEWLINE = StringUtils::ToWstring(NEWLINE);
67 }; // namespace
68 
OnAttachToFrameNode()69 void TextPattern::OnAttachToFrameNode()
70 {
71     auto pipeline = PipelineContext::GetCurrentContext();
72     CHECK_NULL_VOID(pipeline);
73     auto host = GetHost();
74     CHECK_NULL_VOID(host);
75     if (pipeline->GetMinPlatformVersion() > API_PROTEXTION_GREATER_NINE) {
76         host->GetRenderContext()->UpdateClipEdge(true);
77     }
78     InitSurfaceChangedCallback();
79     InitSurfacePositionChangedCallback();
80     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
81     textLayoutProperty->UpdateTextAlign(TextAlign::START);
82     textLayoutProperty->UpdateAlignment(Alignment::CENTER_LEFT);
83 }
84 
OnDetachFromFrameNode(FrameNode * node)85 void TextPattern::OnDetachFromFrameNode(FrameNode* node)
86 {
87     dataDetectorAdapter_->aiDetectDelayTask_.Cancel();
88     CloseSelectOverlay();
89     auto pipeline = PipelineContext::GetCurrentContext();
90     CHECK_NULL_VOID(pipeline);
91     if (HasSurfaceChangedCallback()) {
92         pipeline->UnregisterSurfaceChangedCallback(surfaceChangedCallbackId_.value_or(-1));
93     }
94     if (HasSurfacePositionChangedCallback()) {
95         pipeline->UnregisterSurfacePositionChangedCallback(surfacePositionChangedCallbackId_.value_or(-1));
96     }
97     auto frameNode = WeakClaim(node);
98     pipeline->RemoveFontNodeNG(frameNode);
99     auto fontManager = pipeline->GetFontManager();
100     if (fontManager) {
101         fontManager->UnRegisterCallbackNG(frameNode);
102         fontManager->RemoveVariationNodeNG(frameNode);
103     }
104     pipeline->RemoveOnAreaChangeNode(node->GetId());
105 }
106 
CloseSelectOverlay()107 void TextPattern::CloseSelectOverlay()
108 {
109     CloseSelectOverlay(false);
110 }
111 
CloseSelectOverlay(bool animation)112 void TextPattern::CloseSelectOverlay(bool animation)
113 {
114     if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
115         selectOverlayProxy_->Close(animation);
116         RemoveAreaChangeInner();
117     }
118 }
119 
ResetSelection()120 void TextPattern::ResetSelection()
121 {
122     if (textSelector_.IsValid()) {
123         HandleSelectionChange(-1, -1);
124         auto host = GetHost();
125         CHECK_NULL_VOID(host);
126         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
127     }
128 }
129 
InitSelection(const Offset & pos)130 void TextPattern::InitSelection(const Offset& pos)
131 {
132     CHECK_NULL_VOID(paragraph_);
133     int32_t extend = paragraph_->GetGlyphIndexByCoordinate(pos, true);
134     if (IsLineBreakOrEndOfParagraph(extend)) {
135         extend--;
136     }
137     int32_t start = 0;
138     int32_t end = 0;
139     if (!paragraph_->GetWordBoundary(extend, start, end)) {
140         start = extend;
141         end = std::min(static_cast<int32_t>(GetWideText().length()) + placeholderCount_,
142             extend + GetGraphemeClusterLength(GetWideText(), extend));
143     }
144     HandleSelectionChange(start, end);
145 }
146 
IsLineBreakOrEndOfParagraph(int32_t pos) const147 bool TextPattern::IsLineBreakOrEndOfParagraph(int32_t pos) const
148 {
149     CHECK_NULL_RETURN(pos < static_cast<int32_t>(GetWideText().length() + placeholderCount_), true);
150     auto data = GetWideText();
151     CHECK_NULL_RETURN(data[pos] == WIDE_NEWLINE[0], false);
152     return true;
153 }
154 
CalcCaretMetricsByPosition(int32_t extent,CaretMetricsF & caretCaretMetric,TextAffinity textAffinity)155 void TextPattern::CalcCaretMetricsByPosition(int32_t extent, CaretMetricsF& caretCaretMetric, TextAffinity textAffinity)
156 {
157     auto host = GetHost();
158     CHECK_NULL_VOID(host);
159     auto rect = host->GetGeometryNode()->GetFrameRect();
160     CHECK_NULL_VOID(paragraph_);
161     auto computeSuccess = paragraph_->CalcCaretMetricsByPosition(extent, caretCaretMetric, textAffinity);
162     if (!computeSuccess) {
163         caretCaretMetric = CaretMetricsF(OffsetF(0.0f, rect.Width()), 0.0f);
164     }
165 }
166 
CalculateHandleOffsetAndShowOverlay(bool isUsingMouse)167 void TextPattern::CalculateHandleOffsetAndShowOverlay(bool isUsingMouse)
168 {
169     auto textContentGlobalOffset = parentGlobalOffset_ + contentRect_.GetOffset();
170     auto paragraphPaintOffset = textContentGlobalOffset - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
171 
172     // calculate firstHandleOffset, secondHandleOffset and handlePaintSize
173     CaretMetricsF firstHandleMetrics;
174     CaretMetricsF secondHandleMetrics;
175     CalcCaretMetricsByPosition(textSelector_.baseOffset, firstHandleMetrics, TextAffinity::DOWNSTREAM);
176     CalcCaretMetricsByPosition(textSelector_.destinationOffset, secondHandleMetrics, TextAffinity::UPSTREAM);
177     OffsetF firstHandleOffset = firstHandleMetrics.offset + paragraphPaintOffset;
178     OffsetF secondHandleOffset = secondHandleMetrics.offset + paragraphPaintOffset;
179 
180     textSelector_.selectionBaseOffset = firstHandleOffset;
181     textSelector_.selectionDestinationOffset = secondHandleOffset;
182 
183     RectF firstHandle;
184     firstHandle.SetOffset(firstHandleOffset);
185     firstHandle.SetSize({ SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), firstHandleMetrics.height });
186     textSelector_.firstHandle = firstHandle;
187 
188     RectF secondHandle;
189     secondHandle.SetOffset(secondHandleOffset);
190     secondHandle.SetSize({ SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), secondHandleMetrics.height });
191     secondHandle.SetHeight(secondHandleMetrics.height);
192     textSelector_.secondHandle = secondHandle;
193 }
194 
GetSpansInfo(int32_t start,int32_t end,GetSpansMethod method)195 SelectionInfo TextPattern::GetSpansInfo(int32_t start, int32_t end, GetSpansMethod method)
196 {
197     int32_t index = 0;
198     std::int32_t realEnd = 0;
199     std::int32_t realStart = 0;
200     SelectionInfo selection;
201     std::list<ResultObject> resultObjects;
202     auto length = GetTextContentLength();
203     if (method == GetSpansMethod::GETSPANS) {
204         realStart = (start == -1) ? 0 : start;
205         realEnd = (end == -1) ? length : end;
206         if (realStart > realEnd) {
207             std::swap(realStart, realEnd);
208         }
209         realStart = std::max(0, realStart);
210         realEnd = std::min(length, realEnd);
211     } else if (method == GetSpansMethod::ONSELECT) {
212         realEnd = std::min(length, end);
213         realStart = std::min(length, start);
214     }
215     selection.SetSelectionEnd(realEnd);
216     selection.SetSelectionStart(realStart);
217     // Verify that realStart, realEnd, and spans_ are valid
218     if (realStart > length || realEnd < 0 || spans_.empty() || (start > length && end > length) ||
219         (method == GetSpansMethod::ONSELECT && realStart == realEnd)) {
220         selection.SetResultObjectList(resultObjects);
221         return selection;
222     }
223     auto children = GetAllChildren();
224     for (const auto& uinode : children) {
225         if (uinode->GetTag() == V2::IMAGE_ETS_TAG) {
226             ResultObject resultObject = GetImageResultObject(uinode, index, realStart, realEnd);
227             if (!resultObject.valueString.empty() || resultObject.valuePixelMap) {
228                 resultObjects.emplace_back(resultObject);
229             }
230         } else if (uinode->GetTag() == V2::SPAN_ETS_TAG) {
231             ResultObject resultObject = GetTextResultObject(uinode, index, realStart, realEnd);
232             if (!resultObject.valueString.empty()) {
233                 resultObjects.emplace_back(resultObject);
234             }
235         } else if (uinode->GetTag() == V2::SYMBOL_SPAN_ETS_TAG) {
236             ResultObject resultObject = GetSymbolSpanResultObject(uinode, index, realStart, realEnd);
237             if (!resultObject.valueString.empty()) {
238                 resultObjects.emplace_back(resultObject);
239             }
240         } else if (uinode->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG) {
241             ResultObject resultObject = GetBuilderResultObject(uinode, index, realStart, realEnd);
242             if (!resultObject.valueString.empty()) {
243                 resultObjects.emplace_back(resultObject);
244             }
245         }
246         index++;
247     }
248     selection.SetResultObjectList(resultObjects);
249     return selection;
250 }
251 
GetTextContentLength()252 int32_t TextPattern::GetTextContentLength()
253 {
254     if (!spans_.empty()) {
255         return static_cast<int32_t>(GetWideText().length()) + placeholderCount_;
256     }
257     return 0;
258 }
259 
HandleLongPress(GestureEvent & info)260 void TextPattern::HandleLongPress(GestureEvent& info)
261 {
262     if (copyOption_ == CopyOptions::None || isMousePressed_) {
263         return;
264     }
265     auto host = GetHost();
266     CHECK_NULL_VOID(host);
267     auto hub = host->GetEventHub<EventHub>();
268     CHECK_NULL_VOID(hub);
269     auto gestureHub = hub->GetOrCreateGestureEventHub();
270     CHECK_NULL_VOID(gestureHub);
271     if (IsDraggable(info.GetLocalLocation())) {
272         dragBoxes_ = GetTextBoxes();
273         // prevent long press event from being triggered when dragging
274         gestureHub->SetIsTextDraggable(true);
275         return;
276     }
277     gestureHub->SetIsTextDraggable(false);
278     auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
279     Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
280         info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
281     InitSelection(textOffset);
282     textResponseType_ = TextResponseType::LONG_PRESS;
283     UpdateSelectionSpanType(std::min(textSelector_.baseOffset, textSelector_.destinationOffset),
284         std::max(textSelector_.baseOffset, textSelector_.destinationOffset));
285     oldSelectedType_ = selectedType_.value_or(TextSpanType::NONE);
286     parentGlobalOffset_ = GetParentGlobalOffset();
287     CalculateHandleOffsetAndShowOverlay();
288     CloseSelectOverlay(true);
289     ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true);
290     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
291 }
292 
OnHandleMove(const RectF & handleRect,bool isFirstHandle)293 void TextPattern::OnHandleMove(const RectF& handleRect, bool isFirstHandle)
294 {
295     auto host = GetHost();
296     CHECK_NULL_VOID(host);
297     auto textContentGlobalOffset = parentGlobalOffset_ + contentRect_.GetOffset();
298     auto textPaintOffset = textContentGlobalOffset - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
299 
300     auto localOffset = handleRect.GetOffset();
301 
302     if (localOffset.GetX() < textContentGlobalOffset.GetX()) {
303         localOffset.SetX(textContentGlobalOffset.GetX());
304     } else if (GreatOrEqual(localOffset.GetX(), textContentGlobalOffset.GetX() + contentRect_.Width())) {
305         localOffset.SetX(textContentGlobalOffset.GetX() + contentRect_.Width());
306     }
307 
308     if (localOffset.GetY() < textContentGlobalOffset.GetY()) {
309         localOffset.SetY(textContentGlobalOffset.GetY());
310     } else if (GreatNotEqual(localOffset.GetY(), textContentGlobalOffset.GetY() + contentRect_.Height())) {
311         localOffset.SetY(textContentGlobalOffset.GetY() + contentRect_.Height());
312     }
313 
314     localOffset -= textPaintOffset;
315 
316     CHECK_NULL_VOID(paragraph_);
317     // the handle position is calculated based on the middle of the handle height.
318     if (isFirstHandle) {
319         auto start = GetHandleIndex(Offset(localOffset.GetX(),
320             localOffset.GetY() + (selectOverlayProxy_->IsHandleReverse() ? handleRect.Height() : 0)));
321         HandleSelectionChange(start, textSelector_.destinationOffset);
322     } else {
323         auto end = GetHandleIndex(Offset(localOffset.GetX(),
324             localOffset.GetY() + (selectOverlayProxy_->IsHandleReverse() || NearEqual(localOffset.GetY(), 0)
325                                          ? 0
326                                          : handleRect.Height())));
327         HandleSelectionChange(textSelector_.baseOffset, end);
328     }
329     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
330 
331     CHECK_NULL_VOID(selectOverlayProxy_);
332     auto start = textSelector_.GetTextStart();
333     auto end = textSelector_.GetTextEnd();
334     selectOverlayProxy_->SetSelectInfo(GetSelectedText(start, end));
335 }
336 
OnHandleMoveDone(const RectF & handleRect,bool isFirstHandle)337 void TextPattern::OnHandleMoveDone(const RectF& handleRect, bool isFirstHandle)
338 {
339     textResponseType_ = TextResponseType::LONG_PRESS;
340     UpdateSelectionSpanType(std::min(textSelector_.baseOffset, textSelector_.destinationOffset),
341         std::max(textSelector_.baseOffset, textSelector_.destinationOffset));
342     CalculateHandleOffsetAndShowOverlay();
343     if (selectOverlayProxy_) {
344         SelectHandleInfo handleInfo;
345         if (isFirstHandle) {
346             handleInfo.paintRect = textSelector_.firstHandle;
347             CheckHandles(handleInfo);
348             selectOverlayProxy_->UpdateFirstSelectHandleInfo(handleInfo);
349         } else {
350             handleInfo.paintRect = textSelector_.secondHandle;
351             CheckHandles(handleInfo);
352             selectOverlayProxy_->UpdateSecondSelectHandleInfo(handleInfo);
353         }
354         if (IsSelectAll() && selectMenuInfo_.showCopyAll == true) {
355             selectMenuInfo_.showCopyAll = false;
356             selectOverlayProxy_->UpdateSelectMenuInfo(selectMenuInfo_);
357         } else if (!IsSelectAll() && selectMenuInfo_.showCopyAll == false) {
358             selectMenuInfo_.showCopyAll = true;
359             selectOverlayProxy_->UpdateSelectMenuInfo(selectMenuInfo_);
360         }
361         if (!selectOverlayProxy_->IsClosed() && selectedType_.has_value() &&
362             oldSelectedType_ != selectedType_.value()) {
363             oldSelectedType_ = selectedType_.value();
364             CloseSelectOverlay();
365             ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true);
366         }
367         if (!selectOverlayProxy_->IsMenuShow()) {
368             selectOverlayProxy_->ShowOrHiddenMenu(false);
369         }
370         return;
371     }
372     ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true);
373     auto host = GetHost();
374     CHECK_NULL_VOID(host);
375     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
376 }
377 
IsSelectAll()378 bool TextPattern::IsSelectAll()
379 {
380     return textSelector_.GetTextStart() == 0 &&
381            textSelector_.GetTextEnd() == static_cast<int32_t>(GetWideText().length()) + placeholderCount_;
382 }
GetWideText() const383 std::wstring TextPattern::GetWideText() const
384 {
385     return StringUtils::ToWstring(textForDisplay_);
386 }
387 
GetSelectedText(int32_t start,int32_t end) const388 std::string TextPattern::GetSelectedText(int32_t start, int32_t end) const
389 {
390     if (spans_.empty()) {
391         auto wideText = GetWideText();
392         auto min = std::clamp(std::max(std::min(start, end), 0), 0, static_cast<int32_t>(wideText.length()));
393         auto max = std::clamp(std::min(std::max(start, end), static_cast<int32_t>(wideText.length())), 0,
394             static_cast<int32_t>(wideText.length()));
395         return StringUtils::ToString(wideText.substr(min, max - min));
396     }
397     std::string value;
398     int32_t tag = 0;
399     for (const auto& span : spans_) {
400         if (span->GetSymbolUnicode() != 0) {
401             tag = span->position == -1 ? tag + 1 : span->position;
402             continue;
403         }
404         if (span->position - 1 >= start && span->placeholderIndex == -1 && span->position != -1) {
405             auto wideString = StringUtils::ToWstring(span->GetSpanContent());
406             auto max = std::min(span->position, end);
407             auto min = std::max(start, tag);
408             value += StringUtils::ToString(
409                 wideString.substr(std::clamp((min - tag), 0, static_cast<int32_t>(wideString.length())),
410                     std::clamp((max - min), 0, static_cast<int32_t>(wideString.length()))));
411         }
412         tag = span->position == -1 ? tag + 1 : span->position;
413         if (span->position >= end) {
414             break;
415         }
416     }
417     return value;
418 }
419 
HandleOnCopy()420 void TextPattern::HandleOnCopy()
421 {
422     CHECK_NULL_VOID(clipboard_);
423     if (textSelector_.IsValid() && textSelector_.GetTextStart() == textSelector_.GetTextEnd()) {
424         HandleSelectionChange(-1, -1);
425         return;
426     }
427     auto value = GetSelectedText(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
428     if (value.empty()) {
429         HandleSelectionChange(-1, -1);
430         return;
431     }
432     if (copyOption_ != CopyOptions::None) {
433         clipboard_->SetData(value, copyOption_);
434     }
435     ResetSelection();
436     CloseSelectOverlay(true);
437     auto host = GetHost();
438     CHECK_NULL_VOID(host);
439     auto eventHub = host->GetEventHub<TextEventHub>();
440     CHECK_NULL_VOID(eventHub);
441     eventHub->FireOnCopy(value);
442 }
443 
SetTextSelection(int32_t selectionStart,int32_t selectionEnd)444 void TextPattern::SetTextSelection(int32_t selectionStart, int32_t selectionEnd)
445 {
446     auto host = GetHost();
447     CHECK_NULL_VOID(host);
448     auto eventHub = host->GetEventHub<EventHub>();
449     CHECK_NULL_VOID(eventHub);
450     auto context = PipelineContext::GetCurrentContext();
451     if (context) {
452         context->AddAfterLayoutTask([weak = WeakClaim(this), selectionStart, selectionEnd, eventHub]() {
453             auto textPattern = weak.Upgrade();
454             CHECK_NULL_VOID(textPattern);
455             auto renderContext = textPattern->GetRenderContext();
456             CHECK_NULL_VOID(renderContext);
457             auto obscuredReasons = renderContext->GetObscured().value_or(std::vector<ObscuredReasons>());
458             bool ifHaveObscured = std::any_of(obscuredReasons.begin(), obscuredReasons.end(),
459                 [](const auto& reason) { return reason == ObscuredReasons::PLACEHOLDER; });
460             auto textLayoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
461             CHECK_NULL_VOID(textLayoutProperty);
462             if (textLayoutProperty->GetCalcLayoutConstraint() &&
463                 textLayoutProperty->GetCalcLayoutConstraint()->selfIdealSize.has_value()) {
464                 auto selfIdealSizeWidth = textLayoutProperty->GetCalcLayoutConstraint()->selfIdealSize->Width();
465                 auto selfIdealSizeHeight = textLayoutProperty->GetCalcLayoutConstraint()->selfIdealSize->Height();
466                 auto constraint = textLayoutProperty->GetLayoutConstraint();
467                 if ((selfIdealSizeWidth.has_value() && NearZero(selfIdealSizeWidth->GetDimension().ConvertToPxWithSize(
468                                                            constraint->percentReference.Width()))) ||
469                     (selfIdealSizeHeight.has_value() &&
470                         NearZero(selfIdealSizeHeight->GetDimension().ConvertToPxWithSize(
471                             constraint->percentReference.Height())))) {
472                     return;
473                 }
474             }
475             if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) == CopyOptions::None ||
476                 textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE) {
477                 return;
478             }
479             if ((!textPattern->GetAllChildren().empty() || !ifHaveObscured) && eventHub->IsEnabled()) {
480                 textPattern->ActSetSelection(selectionStart, selectionEnd);
481             }
482         });
483     }
484     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
485 }
486 
GetRenderContext()487 RefPtr<RenderContext> TextPattern::GetRenderContext()
488 {
489     auto frameNode = GetHost();
490     CHECK_NULL_RETURN(frameNode, nullptr);
491     return frameNode->GetRenderContext();
492 }
493 
ShowSelectOverlay(const RectF & firstHandle,const RectF & secondHandle)494 void TextPattern::ShowSelectOverlay(const RectF& firstHandle, const RectF& secondHandle)
495 {
496     ShowSelectOverlay(firstHandle, secondHandle, false);
497 }
ShowSelectOverlay(const RectF & firstHandle,const RectF & secondHandle,bool animation,bool isUsingMouse,bool isShowMenu)498 void TextPattern::ShowSelectOverlay(
499     const RectF& firstHandle, const RectF& secondHandle, bool animation, bool isUsingMouse, bool isShowMenu)
500 {
501     SelectOverlayInfo selectInfo;
502     selectInfo.firstHandle.paintRect = firstHandle;
503     selectInfo.secondHandle.paintRect = secondHandle;
504     selectInfo.onHandleMove = [weak = WeakClaim(this)](const RectF& handleRect, bool isFirst) {
505         auto pattern = weak.Upgrade();
506         CHECK_NULL_VOID(pattern);
507         pattern->OnHandleMove(handleRect, isFirst);
508     };
509     selectInfo.onHandleMoveDone = [weak = WeakClaim(this)](const RectF& handleRect, bool isFirst) {
510         auto pattern = weak.Upgrade();
511         CHECK_NULL_VOID(pattern);
512         pattern->OnHandleMoveDone(handleRect, isFirst);
513     };
514     selectInfo.rightClickOffset = GetRightClickOffset();
515     selectInfo.isUsingMouse = isUsingMouse;
516     selectInfo.menuInfo.menuIsShow = isShowMenu && (selectInfo.firstHandle.isShow || selectInfo.secondHandle.isShow);
517     selectInfo.menuInfo.showCut = false;
518     selectInfo.menuInfo.showCopy = textSelector_.IsValid() && !textSelector_.StartEqualToDest();
519     selectInfo.menuInfo.showPaste = false;
520     selectInfo.menuInfo.showCopyAll = !IsSelectAll();
521     selectInfo.menuCallback.onCopy = [weak = WeakClaim(this)]() {
522         auto pattern = weak.Upgrade();
523         CHECK_NULL_VOID(pattern);
524         pattern->HandleOnCopy();
525         pattern->RemoveAreaChangeInner();
526     };
527     selectInfo.menuCallback.onSelectAll = [weak = WeakClaim(this)]() {
528         auto pattern = weak.Upgrade();
529         CHECK_NULL_VOID(pattern);
530         pattern->HandleOnSelectAll();
531     };
532     selectInfo.onClose = [weak = WeakClaim(this), isUsingMouse](bool closedByGlobalEvent) {
533         auto pattern = weak.Upgrade();
534         CHECK_NULL_VOID(pattern);
535         if (closedByGlobalEvent && !isUsingMouse) {
536             pattern->ResetSelection();
537             pattern->RemoveAreaChangeInner();
538         }
539     };
540 
541     if (!menuOptionItems_.empty()) {
542         selectInfo.menuOptionItems = GetMenuOptionItems();
543     }
544     selectMenuInfo_ = selectInfo.menuInfo;
545     CopySelectionMenuParams(selectInfo, textResponseType_.value_or(TextResponseType::NONE));
546     UpdateSelectOverlayOrCreate(selectInfo, animation);
547 }
548 
HandleOnSelectAll()549 void TextPattern::HandleOnSelectAll()
550 {
551     auto textSize = static_cast<int32_t>(GetWideText().length()) + placeholderCount_;
552     HandleSelectionChange(0, textSize);
553     auto pipeline = PipelineContext::GetCurrentContext();
554     CHECK_NULL_VOID(pipeline);
555     auto selectOverlayInfo = pipeline->GetSelectOverlayManager()->GetSelectOverlayInfo();
556     CalculateHandleOffsetAndShowOverlay();
557     CloseSelectOverlay(true);
558     if (!selectOverlayInfo.isUsingMouse) {
559         ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true);
560     } else {
561         if (IsSelected()) {
562             PushSelectedByMouseInfoToManager();
563         }
564     }
565     selectMenuInfo_.showCopyAll = false;
566     selectOverlayProxy_->UpdateSelectMenuInfo(selectMenuInfo_);
567     auto host = GetHost();
568     CHECK_NULL_VOID(host);
569     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
570 }
571 
CheckHandles(SelectHandleInfo & handleInfo)572 void TextPattern::CheckHandles(SelectHandleInfo& handleInfo)
573 {
574     auto host = GetHost();
575     CHECK_NULL_VOID(host);
576     auto renderContext = host->GetRenderContext();
577     CHECK_NULL_VOID(renderContext);
578     if (!renderContext->GetClipEdge().value_or(true)) {
579         return;
580     }
581     // use global offset.
582     RectF visibleContentRect(contentRect_.GetOffset() + parentGlobalOffset_, contentRect_.GetSize());
583     auto parent = host->GetAncestorNodeOfFrame();
584     visibleContentRect = GetVisibleContentRect(parent, visibleContentRect);
585     auto paintRect = handleInfo.paintRect;
586     PointF bottomPoint = { paintRect.Left(), paintRect.Bottom() - BOX_EPSILON };
587     PointF topPoint = { paintRect.Left(), paintRect.Top() + BOX_EPSILON };
588     handleInfo.isShow = visibleContentRect.IsInRegion(bottomPoint) && visibleContentRect.IsInRegion(topPoint);
589 }
590 
InitLongPressEvent(const RefPtr<GestureEventHub> & gestureHub)591 void TextPattern::InitLongPressEvent(const RefPtr<GestureEventHub>& gestureHub)
592 {
593     constexpr int32_t longPressDelay = 600;
594     if (longPressEvent_) {
595         gestureHub->SetLongPressEvent(longPressEvent_, false, false, longPressDelay);
596         return;
597     }
598     auto longPressCallback = [weak = WeakClaim(this)](GestureEvent& info) {
599         auto pattern = weak.Upgrade();
600         CHECK_NULL_VOID(pattern);
601         pattern->HandleLongPress(info);
602     };
603     longPressEvent_ = MakeRefPtr<LongPressEvent>(std::move(longPressCallback));
604 
605     // Default time is 500, used by drag event. Drag event would trigger if text is selected, but we want
606     // it to only trigger on the second long press, after selection. Therefore, long press delay of Selection needs to
607     // be slightly longer to ensure that order.
608     gestureHub->SetLongPressEvent(longPressEvent_, false, false, longPressDelay);
609 
610     auto onTextSelectorChange = [weak = WeakClaim(this)]() {
611         auto pattern = weak.Upgrade();
612         CHECK_NULL_VOID(pattern);
613         auto frameNode = pattern->GetHost();
614         CHECK_NULL_VOID(frameNode);
615         frameNode->OnAccessibilityEvent(AccessibilityEventType::TEXT_SELECTION_UPDATE);
616     };
617     textSelector_.SetOnAccessibility(std::move(onTextSelectorChange));
618 }
619 
OnHandleTouchUp()620 void TextPattern::OnHandleTouchUp()
621 {
622     CloseSelectOverlay();
623     ResetSelection();
624 }
625 
HandleClickEvent(GestureEvent & info)626 void TextPattern::HandleClickEvent(GestureEvent& info)
627 {
628     if (dataDetectorAdapter_->hasClickedAISpan_) {
629         dataDetectorAdapter_->hasClickedAISpan_ = false;
630     } else if (hasClicked_) {
631         hasClicked_ = false;
632         TimeStamp clickTimeStamp = info.GetTimeStamp();
633         std::chrono::duration<float, std::ratio<1, SECONDS_TO_MILLISECONDS>> timeout =
634             clickTimeStamp - lastClickTimeStamp_;
635         lastClickTimeStamp_ = info.GetTimeStamp();
636         if (timeout.count() < DOUBLECLICK_INTERVAL_MS) {
637             HandleDoubleClickEvent(info);
638             return;
639         }
640     }
641     HandleSingleClickEvent(info);
642 }
643 
HandleSingleClickEvent(GestureEvent & info)644 void TextPattern::HandleSingleClickEvent(GestureEvent& info)
645 {
646     hasClicked_ = true;
647     lastClickTimeStamp_ = info.GetTimeStamp();
648 
649     RectF textContentRect = contentRect_;
650     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
651     textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
652     PointF textOffset = { info.GetLocalLocation().GetX() - textContentRect.GetX(),
653         info.GetLocalLocation().GetY() - textContentRect.GetY() };
654     HandleClickAISpanEvent(textOffset);
655     if (dataDetectorAdapter_->hasClickedAISpan_) {
656         if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
657             selectOverlayProxy_->DisableMenu(true);
658         }
659         return;
660     }
661 
662     if (textSelector_.IsValid() && mouseStatus_ != MouseStatus::MOVE) {
663         CloseSelectOverlay(true);
664         ResetSelection();
665     }
666     bool isClickOnSpan = false;
667     HandleSpanSingleClickEvent(info, textContentRect, textOffset, isClickOnSpan);
668 
669     if (onClick_ && !isClickOnSpan) {
670         auto onClick = onClick_;
671         onClick(info);
672         if (Recorder::EventRecorder::Get().IsComponentRecordEnable()) {
673             auto host = GetHost();
674             CHECK_NULL_VOID(host);
675             auto text = host->GetAccessibilityProperty<NG::AccessibilityProperty>()->GetText();
676             Recorder::EventParamsBuilder builder;
677             builder.SetId(host->GetInspectorIdValue(""))
678                 .SetType(host->GetTag())
679                 .SetText(text)
680                 .SetDescription(host->GetAutoEventParamValue(""));
681             Recorder::EventRecorder::Get().OnClick(std::move(builder));
682         }
683     }
684 }
685 
HandleClickAISpanEvent(const PointF & textOffset)686 void TextPattern::HandleClickAISpanEvent(const PointF& textOffset)
687 {
688     dataDetectorAdapter_->hasClickedAISpan_ = false;
689     if (!NeedShowAIDetect()) {
690         return;
691     }
692 
693     for (const auto& kv : dataDetectorAdapter_->aiSpanMap_) {
694         auto& aiSpan = kv.second;
695         ClickAISpan(textOffset, aiSpan);
696         if (dataDetectorAdapter_->hasClickedAISpan_) {
697             return;
698         }
699     }
700 }
701 
HandleSpanSingleClickEvent(GestureEvent & info,RectF textContentRect,PointF textOffset,bool & isClickOnSpan)702 void TextPattern::HandleSpanSingleClickEvent(
703     GestureEvent& info, RectF textContentRect, PointF textOffset, bool& isClickOnSpan)
704 {
705     auto host = GetHost();
706     CHECK_NULL_VOID(host);
707     auto renderContext = host->GetRenderContext();
708     CHECK_NULL_VOID(renderContext);
709     if (renderContext->GetClipEdge().has_value() && !renderContext->GetClipEdge().value() && overlayMod_) {
710         textContentRect = overlayMod_->GetBoundsRect();
711         textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
712     }
713     if (textContentRect.IsInRegion(PointF(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY())) &&
714         !spans_.empty() && paragraph_) {
715         int32_t start = 0;
716         for (const auto& item : spans_) {
717             if (!item) {
718                 continue;
719             }
720             std::vector<RectF> selectedRects;
721             paragraph_->GetRectsForRange(start, item->position, selectedRects);
722             start = item->position;
723             for (auto&& rect : selectedRects) {
724                 if (rect.IsInRegion(textOffset)) {
725                     if (!item->onClick) {
726                         break;
727                     }
728                     GestureEvent spanClickinfo = info;
729                     EventTarget target = info.GetTarget();
730                     target.area.SetWidth(Dimension(0.0f));
731                     target.area.SetHeight(Dimension(0.0f));
732                     spanClickinfo.SetTarget(target);
733                     item->onClick(spanClickinfo);
734                     if (Recorder::EventRecorder::Get().IsComponentRecordEnable()) {
735                         Recorder::EventParamsBuilder builder;
736                         builder.SetId(item->inspectId).SetText(item->content).SetDescription(item->description);
737                         Recorder::EventRecorder::Get().OnClick(std::move(builder));
738                     }
739                     isClickOnSpan = true;
740                     return;
741                 }
742             }
743         }
744     }
745 }
746 
ClickAISpan(const PointF & textOffset,const AISpan & aiSpan)747 bool TextPattern::ClickAISpan(const PointF& textOffset, const AISpan& aiSpan)
748 {
749     std::vector<RectF> aiRects;
750     paragraph_->GetRectsForRange(aiSpan.start, aiSpan.end, aiRects);
751     for (auto&& rect : aiRects) {
752         if (rect.IsInRegion(textOffset)) {
753             dataDetectorAdapter_->hasClickedAISpan_ = true;
754             if (isMousePressed_) {
755                 dataDetectorAdapter_->ResponseBestMatchItem(aiSpan);
756                 return true;
757             }
758             return ShowUIExtensionMenu(aiSpan);
759         }
760     }
761     return false;
762 }
763 
SetOnClickMenu(const AISpan & aiSpan,const CalculateHandleFunc & calculateHandleFunc,const ShowSelectOverlayFunc & showSelectOverlayFunc)764 void TextPattern::SetOnClickMenu(const AISpan& aiSpan, const CalculateHandleFunc& calculateHandleFunc,
765     const ShowSelectOverlayFunc& showSelectOverlayFunc)
766 
767 {
768     dataDetectorAdapter_->onClickMenu_ = [aiSpan, weak = WeakClaim(this), calculateHandleFunc, showSelectOverlayFunc](
769                                              const std::string& action) {
770         auto pattern = weak.Upgrade();
771         CHECK_NULL_VOID(pattern);
772         if (pattern->copyOption_ == CopyOptions::None) {
773             return;
774         }
775         pattern->CloseSelectOverlay();
776         if (action == std::string(COPY_ACTION)) {
777             pattern->HandleSelectionChange(aiSpan.start, aiSpan.end);
778             pattern->HandleOnCopy();
779         } else if (action == std::string(SELECT_ACTION)) {
780             pattern->HandleSelectionChange(aiSpan.start, aiSpan.end);
781             if (calculateHandleFunc == nullptr) {
782                 pattern->CalculateHandleOffsetAndShowOverlay();
783             } else {
784                 calculateHandleFunc();
785             }
786             if (showSelectOverlayFunc == nullptr) {
787                 pattern->ShowSelectOverlay(
788                     pattern->textSelector_.firstHandle, pattern->textSelector_.secondHandle, true);
789             } else {
790                 showSelectOverlayFunc(pattern->textSelector_.firstHandle, pattern->textSelector_.secondHandle);
791             }
792             auto frameNode = pattern->GetHost();
793             CHECK_NULL_VOID(frameNode);
794             frameNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
795         }
796     };
797 }
798 
ShowUIExtensionMenu(const AISpan & aiSpan,const CalculateHandleFunc & calculateHandleFunc,const ShowSelectOverlayFunc & showSelectOverlayFunc)799 bool TextPattern::ShowUIExtensionMenu(const AISpan& aiSpan, const CalculateHandleFunc& calculateHandleFunc,
800     const ShowSelectOverlayFunc& showSelectOverlayFunc)
801 {
802     auto host = GetHost();
803     CHECK_NULL_RETURN(host, false);
804     SetOnClickMenu(aiSpan, calculateHandleFunc, showSelectOverlayFunc);
805     auto baseOffset = textSelector_.baseOffset;
806     auto destinationOffset = textSelector_.destinationOffset;
807     HandleSelectionChange(aiSpan.start, aiSpan.end);
808     if (calculateHandleFunc == nullptr) {
809         CalculateHandleOffsetAndShowOverlay();
810     } else {
811         calculateHandleFunc();
812     }
813     HandleSelectionChange(baseOffset, destinationOffset);
814     RectF aiRect;
815     if (textSelector_.firstHandle.Top() != textSelector_.secondHandle.Top()) {
816         auto top = std::min(textSelector_.firstHandle.Top(), textSelector_.secondHandle.Top());
817         auto bottom = std::max(textSelector_.firstHandle.Bottom(), textSelector_.secondHandle.Bottom());
818         auto paintRect = host->GetPaintRectWithTransform();
819         auto left = paintRect.Left();
820         auto right = paintRect.Right();
821         aiRect = RectT(left, top, right - left, bottom - top);
822     } else {
823         aiRect = textSelector_.firstHandle.CombineRectT(textSelector_.secondHandle);
824     }
825 
826     return dataDetectorAdapter_->ShowUIExtensionMenu(aiSpan, aiRect, host);
827 }
828 
HandleDoubleClickEvent(GestureEvent & info)829 void TextPattern::HandleDoubleClickEvent(GestureEvent& info)
830 {
831     CheckOnClickEvent(info);
832     if (copyOption_ == CopyOptions::None || textForDisplay_.empty()) {
833         return;
834     }
835     auto host = GetHost();
836     CHECK_NULL_VOID(host);
837     auto hub = host->GetEventHub<EventHub>();
838     CHECK_NULL_VOID(hub);
839     auto gestureHub = hub->GetOrCreateGestureEventHub();
840     CHECK_NULL_VOID(gestureHub);
841     isDoubleClick_ = true;
842     auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
843     Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
844         info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
845     InitSelection(textOffset);
846     textResponseType_ = TextResponseType::NONE;
847     UpdateSelectionSpanType(std::min(textSelector_.baseOffset, textSelector_.destinationOffset),
848         std::max(textSelector_.baseOffset, textSelector_.destinationOffset));
849     CalculateHandleOffsetAndShowOverlay();
850     if (!isMousePressed_) {
851         ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true);
852     }
853     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
854 }
855 
CheckOnClickEvent(GestureEvent & info)856 void TextPattern::CheckOnClickEvent(GestureEvent& info)
857 {
858     bool isClickOnSpan = false;
859     RectF textContentRect = contentRect_;
860     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
861     textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
862     PointF textOffset = { info.GetLocalLocation().GetX() - textContentRect.GetX(),
863         info.GetLocalLocation().GetY() - textContentRect.GetY() };
864     HandleSpanSingleClickEvent(info, textContentRect, textOffset, isClickOnSpan);
865     if (onClick_ && !isClickOnSpan) {
866         auto onClick = onClick_;
867         onClick(info);
868     }
869 }
870 
InitClickEvent(const RefPtr<GestureEventHub> & gestureHub)871 void TextPattern::InitClickEvent(const RefPtr<GestureEventHub>& gestureHub)
872 {
873     CHECK_NULL_VOID(!clickEventInitialized_);
874     auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
875         auto pattern = weak.Upgrade();
876         CHECK_NULL_VOID(pattern);
877         pattern->HandleClickEvent(info);
878     };
879     auto clickListener = MakeRefPtr<ClickEvent>(std::move(clickCallback));
880     gestureHub->AddClickEvent(clickListener);
881     clickEventInitialized_ = true;
882 }
883 
InitMouseEvent()884 void TextPattern::InitMouseEvent()
885 {
886     CHECK_NULL_VOID(!mouseEventInitialized_);
887     auto host = GetHost();
888     CHECK_NULL_VOID(host);
889     auto eventHub = host->GetEventHub<EventHub>();
890     CHECK_NULL_VOID(eventHub);
891     auto inputHub = eventHub->GetOrCreateInputEventHub();
892     CHECK_NULL_VOID(inputHub);
893 
894     auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
895         auto pattern = weak.Upgrade();
896         CHECK_NULL_VOID(pattern);
897         pattern->HandleMouseEvent(info);
898     };
899     auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
900     inputHub->AddOnMouseEvent(mouseEvent);
901     mouseEventInitialized_ = true;
902 }
903 
HandleMouseEvent(const MouseInfo & info)904 void TextPattern::HandleMouseEvent(const MouseInfo& info)
905 {
906     if (copyOption_ == CopyOptions::None) {
907         return;
908     }
909 
910     auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
911     Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
912         info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
913     if (info.GetButton() == MouseButton::LEFT_BUTTON) {
914         HandleMouseLeftButton(info, textOffset);
915         if (IsSelected()) {
916             PushSelectedByMouseInfoToManager();
917         }
918     } else if (info.GetButton() == MouseButton::RIGHT_BUTTON) {
919         HandleMouseRightButton(info, textOffset);
920     }
921 }
922 
PushSelectedByMouseInfoToManager()923 void TextPattern::PushSelectedByMouseInfoToManager()
924 {
925     SelectedByMouseInfo selectedByMouseInfo;
926     selectedByMouseInfo.selectedNode = GetHost();
927     selectedByMouseInfo.onResetSelection = [weak = WeakClaim(this)]() {
928         auto pattern = weak.Upgrade();
929         CHECK_NULL_VOID(pattern);
930         pattern->ResetSelection();
931     };
932     SetSelectionNode(selectedByMouseInfo);
933 }
934 
HandleMouseLeftButton(const MouseInfo & info,const Offset & textOffset)935 void TextPattern::HandleMouseLeftButton(const MouseInfo& info, const Offset& textOffset)
936 {
937     if (info.GetAction() == MouseAction::PRESS) {
938         HandleMouseLeftPressAction(info, textOffset);
939     } else if (info.GetAction() == MouseAction::MOVE) {
940         HandleMouseLeftMoveAction(info, textOffset);
941     } else if (info.GetAction() == MouseAction::RELEASE) {
942         HandleMouseLeftReleaseAction(info, textOffset);
943     }
944 
945     auto host = GetHost();
946     CHECK_NULL_VOID(host);
947     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
948 }
949 
HandleMouseLeftPressAction(const MouseInfo & info,const Offset & textOffset)950 void TextPattern::HandleMouseLeftPressAction(const MouseInfo& info, const Offset& textOffset)
951 {
952     isMousePressed_ = true;
953     if (BetweenSelectedPosition(info.GetGlobalLocation())) {
954         blockPress_ = true;
955         return;
956     }
957     mouseStatus_ = MouseStatus::PRESSED;
958     CHECK_NULL_VOID(paragraph_);
959     auto start = paragraph_->GetGlyphIndexByCoordinate(textOffset);
960     textSelector_.Update(start, start);
961 }
962 
HandleMouseLeftReleaseAction(const MouseInfo & info,const Offset & textOffset)963 void TextPattern::HandleMouseLeftReleaseAction(const MouseInfo& info, const Offset& textOffset)
964 {
965     if (blockPress_) {
966         blockPress_ = false;
967     }
968     auto oldMouseStatus = mouseStatus_;
969     mouseStatus_ = MouseStatus::RELEASED;
970     if (isDoubleClick_) {
971         isDoubleClick_ = false;
972         isMousePressed_ = false;
973         return;
974     }
975 
976     CHECK_NULL_VOID(paragraph_);
977     auto start = textSelector_.baseOffset;
978     auto end = textSelector_.destinationOffset;
979     if (!IsSelected()) {
980         start = -1;
981         end = -1;
982     }
983     if (isMousePressed_ || oldMouseStatus == MouseStatus::MOVE) {
984         HandleSelectionChange(start, end);
985     }
986 
987     if (IsSelected() && oldMouseStatus == MouseStatus::MOVE && IsSelectedBindSelectionMenu()) {
988         mouseReleaseOffset_ = OffsetF(
989             static_cast<float>(info.GetGlobalLocation().GetX()), static_cast<float>(info.GetGlobalLocation().GetY()));
990         textResponseType_ = TextResponseType::SELECTED_BY_MOUSE;
991         ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true, true);
992     }
993     isMousePressed_ = false;
994 }
995 
HandleMouseLeftMoveAction(const MouseInfo & info,const Offset & textOffset)996 void TextPattern::HandleMouseLeftMoveAction(const MouseInfo& info, const Offset& textOffset)
997 {
998     if (blockPress_) {
999         dragBoxes_ = GetTextBoxes();
1000         return;
1001     }
1002     if (isMousePressed_) {
1003         mouseStatus_ = MouseStatus::MOVE;
1004         CHECK_NULL_VOID(paragraph_);
1005         auto end = paragraph_->GetGlyphIndexByCoordinate(textOffset);
1006         HandleSelectionChange(textSelector_.baseOffset, end);
1007     }
1008 }
1009 
HandleMouseRightButton(const MouseInfo & info,const Offset & textOffset)1010 void TextPattern::HandleMouseRightButton(const MouseInfo& info, const Offset& textOffset)
1011 {
1012     if (info.GetAction() == MouseAction::RELEASE) {
1013         HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY()));
1014         if (dataDetectorAdapter_->hasClickedAISpan_) {
1015             return;
1016         }
1017 
1018         mouseReleaseOffset_ = OffsetF(
1019             static_cast<float>(info.GetGlobalLocation().GetX()), static_cast<float>(info.GetGlobalLocation().GetY()));
1020         CalculateHandleOffsetAndShowOverlay(true);
1021         if (SelectOverlayIsOn()) {
1022             CloseSelectOverlay(true);
1023         }
1024         textResponseType_ = TextResponseType::RIGHT_CLICK;
1025         if (!IsSelected()) {
1026             auto spanNode = DynamicCast<FrameNode>(GetChildByIndex(GetSelectionSpanItemIndex(info)));
1027             if (spanNode && spanNode->GetTag() == V2::IMAGE_ETS_TAG) {
1028                 selectedType_ = TextSpanType::IMAGE;
1029             } else {
1030                 selectedType_ = TextSpanType::TEXT;
1031             }
1032         }
1033         ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true, true);
1034         auto host = GetHost();
1035         CHECK_NULL_VOID(host);
1036         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1037     } else if (info.GetAction() == MouseAction::PRESS) {
1038         CloseSelectOverlay(true);
1039     }
1040 }
1041 
InitTouchEvent()1042 void TextPattern::InitTouchEvent()
1043 {
1044     CHECK_NULL_VOID(!touchEventInitialized_);
1045     auto host = GetHost();
1046     CHECK_NULL_VOID(host);
1047     auto gesture = host->GetOrCreateGestureEventHub();
1048     CHECK_NULL_VOID(gesture);
1049 
1050     auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
1051         auto pattern = weak.Upgrade();
1052         CHECK_NULL_VOID(pattern);
1053         pattern->HandleTouchEvent(info);
1054     };
1055     auto touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
1056     gesture->AddTouchEvent(touchListener_);
1057     touchEventInitialized_ = true;
1058 }
1059 
HandleTouchEvent(const TouchEventInfo & info)1060 void TextPattern::HandleTouchEvent(const TouchEventInfo& info)
1061 {
1062     return;
1063 }
1064 
InitPanEvent(const RefPtr<GestureEventHub> & gestureHub)1065 void TextPattern::InitPanEvent(const RefPtr<GestureEventHub>& gestureHub)
1066 {
1067     CHECK_NULL_VOID(!panEventInitialized_);
1068     auto actionStartTask = [weak = WeakClaim(this)](const GestureEvent& info) {
1069         auto pattern = weak.Upgrade();
1070         CHECK_NULL_VOID(pattern);
1071         pattern->HandlePanStart(info);
1072     };
1073     auto actionUpdateTask = [weak = WeakClaim(this)](const GestureEvent& info) {
1074         auto pattern = weak.Upgrade();
1075         CHECK_NULL_VOID(pattern);
1076         pattern->HandlePanUpdate(info);
1077     };
1078     auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
1079         auto pattern = weak.Upgrade();
1080         CHECK_NULL_VOID(pattern);
1081         pattern->HandlePanEnd(info);
1082     };
1083     auto actionCancelTask = [weak = WeakClaim(this)]() {};
1084     auto panEvent = MakeRefPtr<PanEvent>(
1085         std::move(actionStartTask), std::move(actionUpdateTask), std::move(actionEndTask), std::move(actionCancelTask));
1086 
1087     PanDirection panDirection;
1088     panDirection.type = PanDirection::ALL;
1089     gestureHub->AddPanEvent(panEvent, panDirection, DEFAULT_PAN_FINGER, DEFAULT_PAN_DISTANCE);
1090     panEventInitialized_ = true;
1091 }
1092 
HandlePanStart(const GestureEvent & info)1093 void TextPattern::HandlePanStart(const GestureEvent& info)
1094 {
1095     auto host = GetHost();
1096     CHECK_NULL_VOID(host);
1097     auto offset = info.GetLocalLocation();
1098     if (!IsDraggable(offset)) {
1099         return;
1100     }
1101     auto pipelineContext = PipelineContext::GetCurrentContext();
1102     CHECK_NULL_VOID(pipelineContext);
1103 
1104 #if !defined(PREVIEW)
1105     if (!dragWindow_) {
1106         auto rect = pipelineContext->GetCurrentWindowRect();
1107         auto initTextPattern = AceType::Claim(this);
1108 
1109         // create textdrag window
1110         dragWindow_ = DragWindow::CreateTextDragWindow("APP_DRAG_WINDOW",
1111             static_cast<int32_t>(host->GetPaintRectOffset().GetX() + rect.Left()),
1112             static_cast<int32_t>(host->GetPaintRectOffset().GetY() + rect.Top()),
1113             static_cast<int32_t>(contentRect_.Width() + contentRect_.GetX()),
1114             contentRect_.Height() + contentRect_.GetY());
1115         if (dragWindow_) {
1116             dragWindow_->SetOffset(static_cast<int32_t>(host->GetPaintRectOffset().GetX() + rect.Left()),
1117                 static_cast<int32_t>(host->GetPaintRectOffset().GetY() + rect.Top()));
1118             // draw select text on drag window
1119             dragWindow_->DrawTextNG(paragraph_, initTextPattern);
1120             // add select data to clipboard
1121             auto manager = pipelineContext->GetDragDropManager();
1122             CHECK_NULL_VOID(manager);
1123             dragDropProxy_ = manager->CreateTextDragDropProxy();
1124             CHECK_NULL_VOID(dragDropProxy_);
1125             dragDropProxy_->OnTextDragStart(GetSelectedText(textSelector_.GetTextStart(), textSelector_.GetTextEnd()));
1126         }
1127     }
1128 #endif
1129 }
1130 
IsDraggable(const Offset & offset)1131 bool TextPattern::IsDraggable(const Offset& offset)
1132 {
1133     auto host = GetHost();
1134     CHECK_NULL_RETURN(host, false);
1135     auto eventHub = host->GetEventHub<EventHub>();
1136     bool draggable = eventHub->HasOnDragStart();
1137     if (copyOption_ != CopyOptions::None && draggable &&
1138         GreatNotEqual(textSelector_.GetTextEnd(), textSelector_.GetTextStart())) {
1139         // Determine if the pan location is in the selected area
1140         std::vector<RectF> selectedRects;
1141         paragraph_->GetRectsForRange(textSelector_.GetTextStart(), textSelector_.GetTextEnd(), selectedRects);
1142         auto panOffset = OffsetF(offset.GetX(), offset.GetY()) - contentRect_.GetOffset() +
1143                          OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
1144         for (const auto& selectedRect : selectedRects) {
1145             if (selectedRect.IsInRegion(PointF(panOffset.GetX(), panOffset.GetY()))) {
1146                 return true;
1147             }
1148         }
1149     }
1150     return false;
1151 }
1152 
HandlePanUpdate(const GestureEvent & info)1153 void TextPattern::HandlePanUpdate(const GestureEvent& info)
1154 {
1155     if (dragWindow_) {
1156         dragWindow_->TextDragWindowMove(info.GetOffsetX(), info.GetOffsetY());
1157     }
1158     return;
1159 }
1160 
HandlePanEnd(const GestureEvent & info)1161 void TextPattern::HandlePanEnd(const GestureEvent& info)
1162 {
1163     if (dragWindow_) {
1164         dragWindow_->Destroy();
1165         dragWindow_ = nullptr;
1166         if (dragDropProxy_) {
1167             dragDropProxy_->OnDragEnd(info, true);
1168         }
1169         return;
1170     }
1171 }
1172 
OnDragStart(const RefPtr<Ace::DragEvent> & event,const std::string & extraParams)1173 NG::DragDropInfo TextPattern::OnDragStart(const RefPtr<Ace::DragEvent>& event, const std::string& extraParams)
1174 {
1175     DragDropInfo itemInfo;
1176     auto host = GetHost();
1177     CHECK_NULL_RETURN(host, itemInfo);
1178     auto hub = host->GetEventHub<EventHub>();
1179     auto gestureHub = hub->GetOrCreateGestureEventHub();
1180     auto selectStart = textSelector_.GetTextStart();
1181     auto selectEnd = textSelector_.GetTextEnd();
1182     recoverStart_ = selectStart;
1183     recoverEnd_ = selectEnd;
1184     auto textSelectInfo = GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT);
1185     dragResultObjects_ = textSelectInfo.GetSelection().resultObjects;
1186     ResetDragRecordSize(dragResultObjects_.empty() ? -1 : 1);
1187     status_ = Status::DRAGGING;
1188     if (dragResultObjects_.empty() || !gestureHub->GetIsTextDraggable()) {
1189         return itemInfo;
1190     }
1191     RefPtr<UnifiedData> unifiedData = UdmfClient::GetInstance()->CreateUnifiedData();
1192     auto resultProcessor = [unifiedData, weak = WeakClaim(this)](const ResultObject& result) {
1193         auto pattern = weak.Upgrade();
1194         CHECK_NULL_VOID(pattern);
1195         if (result.type == SelectSpanType::TYPESPAN) {
1196             auto data = pattern->GetSelectedSpanText(StringUtils::ToWstring(result.valueString),
1197                 result.offsetInSpan[RichEditorSpanRange::RANGESTART],
1198                 result.offsetInSpan[RichEditorSpanRange::RANGEEND]);
1199             UdmfClient::GetInstance()->AddPlainTextRecord(unifiedData, data);
1200             return;
1201         }
1202         if (result.type == SelectSpanType::TYPEIMAGE) {
1203             if (result.valuePixelMap) {
1204                 const uint8_t* pixels = result.valuePixelMap->GetPixels();
1205                 CHECK_NULL_VOID(pixels);
1206                 int32_t length = result.valuePixelMap->GetByteCount();
1207                 std::vector<uint8_t> data(pixels, pixels + length);
1208                 PixelMapRecordDetails details = { result.valuePixelMap->GetWidth(), result.valuePixelMap->GetHeight(),
1209                     result.valuePixelMap->GetPixelFormat(), result.valuePixelMap->GetAlphaType() };
1210                 UdmfClient::GetInstance()->AddPixelMapRecord(unifiedData, data, details);
1211             } else {
1212                 UdmfClient::GetInstance()->AddImageRecord(unifiedData, result.valueString);
1213             }
1214         }
1215     };
1216     for (const auto& resultObj : dragResultObjects_) {
1217         resultProcessor(resultObj);
1218     }
1219     event->SetData(unifiedData);
1220     CloseOperate();
1221     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1222     return itemInfo;
1223 }
1224 
CloseOperate()1225 void TextPattern::CloseOperate()
1226 {
1227     UpdateSpanItemDragStatus(dragResultObjects_, true);
1228     recoverDragResultObjects_ = dragResultObjects_;
1229     AceEngineExt::GetInstance().DragStartExt();
1230     CloseKeyboard(true);
1231     CloseSelectOverlay();
1232     ResetSelection();
1233 }
1234 
OnDragStartNoChild(const RefPtr<Ace::DragEvent> & event,const std::string & extraParams)1235 DragDropInfo TextPattern::OnDragStartNoChild(const RefPtr<Ace::DragEvent>& event, const std::string& extraParams)
1236 {
1237     auto weakPtr = WeakClaim(this);
1238     DragDropInfo itemInfo;
1239     auto pattern = weakPtr.Upgrade();
1240     auto host = pattern->GetHost();
1241     auto hub = host->GetEventHub<EventHub>();
1242     auto gestureHub = hub->GetOrCreateGestureEventHub();
1243     CHECK_NULL_RETURN(gestureHub, itemInfo);
1244     if (!gestureHub->GetIsTextDraggable()) {
1245         return itemInfo;
1246     }
1247     auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
1248     pattern->status_ = Status::DRAGGING;
1249     pattern->contentMod_->ChangeDragStatus();
1250     pattern->showSelect_ = false;
1251     auto start = textSelector_.GetTextStart();
1252     pattern->recoverStart_ = start;
1253     auto end = textSelector_.GetTextEnd();
1254     pattern->recoverEnd_ = end;
1255     auto beforeStr = GetSelectedText(0, start);
1256     auto selectedStr = GetSelectedText(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
1257     auto afterStr = GetSelectedText(end, GetWideText().length());
1258     pattern->dragContents_ = { beforeStr, selectedStr, afterStr };
1259 
1260     itemInfo.extraInfo = selectedStr;
1261     RefPtr<UnifiedData> unifiedData = UdmfClient::GetInstance()->CreateUnifiedData();
1262     UdmfClient::GetInstance()->AddPlainTextRecord(unifiedData, selectedStr);
1263     event->SetData(unifiedData);
1264     host->MarkDirtyNode(layoutProperty->GetMaxLinesValue(Infinity<float>()) <= 1 ? PROPERTY_UPDATE_MEASURE_SELF
1265                                                                                  : PROPERTY_UPDATE_MEASURE);
1266 
1267     CloseSelectOverlay();
1268     ResetSelection();
1269     return itemInfo;
1270 }
1271 
UpdateSpanItemDragStatus(const std::list<ResultObject> & resultObjects,bool isDragging)1272 void TextPattern::UpdateSpanItemDragStatus(const std::list<ResultObject>& resultObjects, bool isDragging)
1273 {
1274     if (resultObjects.empty()) {
1275         return;
1276     }
1277     auto dragStatusUpdateAction = [weakPtr = WeakClaim(this), isDragging](const ResultObject& resultObj) {
1278         auto pattern = weakPtr.Upgrade();
1279         CHECK_NULL_VOID(pattern);
1280         if (pattern->spans_.empty()) {
1281             return;
1282         }
1283         auto it = pattern->spans_.begin();
1284         if (resultObj.spanPosition.spanIndex >= static_cast<int32_t>(pattern->spans_.size())) {
1285             std::advance(it, pattern->spans_.size() - 1);
1286             TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "resultObj.spanPosition.spanIndex is larger than spans size.");
1287         } else {
1288             std::advance(it, resultObj.spanPosition.spanIndex);
1289         }
1290         auto spanItem = *it;
1291         CHECK_NULL_VOID(spanItem);
1292         if (resultObj.type == SelectSpanType::TYPESPAN) {
1293             if (isDragging) {
1294                 spanItem->StartDrag(resultObj.offsetInSpan[RichEditorSpanRange::RANGESTART],
1295                     resultObj.offsetInSpan[RichEditorSpanRange::RANGEEND]);
1296             } else {
1297                 spanItem->EndDrag();
1298             }
1299             return;
1300         }
1301 
1302         if (resultObj.type == SelectSpanType::TYPEIMAGE) {
1303             auto imageNode = DynamicCast<FrameNode>(pattern->GetChildByIndex(resultObj.spanPosition.spanIndex));
1304             CHECK_NULL_VOID(imageNode);
1305             auto renderContext = imageNode->GetRenderContext();
1306             CHECK_NULL_VOID(renderContext);
1307             renderContext->UpdateOpacity(isDragging ? (double)DRAGGED_TEXT_OPACITY / 255 : 1);
1308             imageNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1309         }
1310     };
1311     for (const auto& resultObj : resultObjects) {
1312         dragStatusUpdateAction(resultObj);
1313     }
1314 }
1315 
OnDragEnd(const RefPtr<Ace::DragEvent> & event)1316 void TextPattern::OnDragEnd(const RefPtr<Ace::DragEvent>& event)
1317 {
1318     ResetDragRecordSize(-1);
1319     auto wk = WeakClaim(this);
1320     auto pattern = wk.Upgrade();
1321     CHECK_NULL_VOID(pattern);
1322     auto host = GetHost();
1323     CHECK_NULL_VOID(host);
1324     if (status_ == Status::DRAGGING) {
1325         status_ = Status::NONE;
1326     }
1327     if (dragResultObjects_.empty()) {
1328         return;
1329     }
1330     UpdateSpanItemDragStatus(dragResultObjects_, false);
1331     dragResultObjects_.clear();
1332     if (event && event->GetResult() != DragRet::DRAG_SUCCESS) {
1333         HandleSelectionChange(recoverStart_, recoverEnd_);
1334         showSelect_ = true;
1335         isShowMenu_ = false;
1336         CalculateHandleOffsetAndShowOverlay();
1337         ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, false, false, false);
1338     }
1339     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1340 }
1341 
OnDragEndNoChild(const RefPtr<Ace::DragEvent> & event)1342 void TextPattern::OnDragEndNoChild(const RefPtr<Ace::DragEvent>& event)
1343 {
1344     auto wk = WeakClaim(this);
1345     auto pattern = wk.Upgrade();
1346     CHECK_NULL_VOID(pattern);
1347     auto host = pattern->GetHost();
1348     CHECK_NULL_VOID(host);
1349     if (pattern->status_ == Status::DRAGGING) {
1350         pattern->status_ = Status::NONE;
1351         pattern->MarkContentChange();
1352         pattern->contentMod_->ChangeDragStatus();
1353         if (event && event->GetResult() != DragRet::DRAG_SUCCESS) {
1354             HandleSelectionChange(recoverStart_, recoverEnd_);
1355             pattern->showSelect_ = true;
1356             isShowMenu_ = false;
1357             CalculateHandleOffsetAndShowOverlay();
1358             ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, false, false, false);
1359         }
1360         auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
1361         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1362     }
1363 }
1364 
OnDragMove(const RefPtr<Ace::DragEvent> & event)1365 void TextPattern::OnDragMove(const RefPtr<Ace::DragEvent>& event)
1366 {
1367     auto weakPtr = WeakClaim(this);
1368     auto pattern = weakPtr.Upgrade();
1369     if (pattern->status_ == Status::DRAGGING) {
1370         CloseSelectOverlay();
1371         pattern->showSelect_ = false;
1372     }
1373 }
1374 
InitDragEvent()1375 void TextPattern::InitDragEvent()
1376 {
1377     auto host = GetHost();
1378     CHECK_NULL_VOID(host);
1379     auto eventHub = host->GetEventHub<EventHub>();
1380     CHECK_NULL_VOID(eventHub);
1381     auto gestureHub = host->GetOrCreateGestureEventHub();
1382     CHECK_NULL_VOID(gestureHub);
1383     gestureHub->InitDragDropEvent();
1384     gestureHub->SetTextDraggable(true);
1385     gestureHub->SetThumbnailCallback(GetThumbnailCallback());
1386     auto onDragStart = [weakPtr = WeakClaim(this)](
1387                            const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& extraParams) -> DragDropInfo {
1388         NG::DragDropInfo itemInfo;
1389         auto pattern = weakPtr.Upgrade();
1390         CHECK_NULL_RETURN(pattern, itemInfo);
1391         auto eventHub = pattern->GetEventHub<EventHub>();
1392         CHECK_NULL_RETURN(eventHub, itemInfo);
1393         if (pattern->spans_.empty()) {
1394             return pattern->OnDragStartNoChild(event, extraParams);
1395         } else {
1396             return pattern->OnDragStart(event, extraParams);
1397         }
1398     };
1399     if (!eventHub->HasOnDragStart()) {
1400         eventHub->SetOnDragStart(std::move(onDragStart));
1401     }
1402     auto onDragMove = [weakPtr = WeakClaim(this)](
1403                           const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& extraParams) {
1404         auto pattern = weakPtr.Upgrade();
1405         CHECK_NULL_VOID(pattern);
1406         pattern->OnDragMove(event);
1407     };
1408     eventHub->SetOnDragMove(std::move(onDragMove));
1409     auto onDragEnd = [weakPtr = WeakClaim(this), scopeId = Container::CurrentId()](
1410                          const RefPtr<OHOS::Ace::DragEvent>& event) {
1411         ContainerScope scope(scopeId);
1412         auto pattern = weakPtr.Upgrade();
1413         CHECK_NULL_VOID(pattern);
1414         if (pattern->spans_.empty()) {
1415             pattern->OnDragEndNoChild(event);
1416         } else {
1417             pattern->OnDragEnd(event);
1418         }
1419     };
1420     eventHub->SetOnDragEnd(std::move(onDragEnd));
1421 }
1422 
GetThumbnailCallback()1423 std::function<void(Offset)> TextPattern::GetThumbnailCallback()
1424 {
1425     return [wk = WeakClaim(this)](const Offset& point) {
1426         auto pattern = wk.Upgrade();
1427         CHECK_NULL_VOID(pattern);
1428         if (pattern->BetweenSelectedPosition(point)) {
1429             auto children = pattern->GetAllChildren();
1430             std::list<RefPtr<FrameNode>> imageChildren;
1431             for (const auto& child : children) {
1432                 auto node = DynamicCast<FrameNode>(child);
1433                 if (!node) {
1434                     continue;
1435                 }
1436                 auto image = node->GetPattern<ImagePattern>();
1437                 if (image) {
1438                     imageChildren.emplace_back(node);
1439                 }
1440             }
1441             pattern->dragNode_ = RichEditorDragPattern::CreateDragNode(pattern->GetHost(), imageChildren);
1442             FrameNode::ProcessOffscreenNode(pattern->dragNode_);
1443         }
1444     };
1445 }
1446 
GetAllChildren() const1447 const std::list<RefPtr<UINode>>& TextPattern::GetAllChildren() const
1448 {
1449     childNodes_.clear();
1450     auto host = GetHost();
1451     CHECK_NULL_RETURN(host, childNodes_);
1452     const auto& children = host->GetChildren();
1453     for (const auto& child : children) {
1454         if (child->GetTag() == V2::CONTAINER_SPAN_ETS_TAG) {
1455             auto spanChildren = child->GetChildren();
1456             childNodes_.insert(childNodes_.end(), spanChildren.begin(), spanChildren.end());
1457         } else {
1458             childNodes_.push_back(child);
1459         }
1460     }
1461     return childNodes_;
1462 }
1463 
GetSelectedSpanText(std::wstring value,int32_t start,int32_t end) const1464 std::string TextPattern::GetSelectedSpanText(std::wstring value, int32_t start, int32_t end) const
1465 {
1466     if (start < 0 || end > static_cast<int32_t>(value.length()) || start >= end) {
1467         return "";
1468     }
1469     auto min = std::min(start, end);
1470     auto max = std::max(start, end);
1471 
1472     return StringUtils::ToString(value.substr(min, max - min));
1473 }
1474 
GetTextStyleObject(const RefPtr<SpanNode> & node)1475 TextStyleResult TextPattern::GetTextStyleObject(const RefPtr<SpanNode>& node)
1476 {
1477     TextStyleResult textStyle;
1478     textStyle.fontColor = node->GetTextColorValue(Color::BLACK).ColorToString();
1479     textStyle.fontSize = node->GetFontSizeValue(Dimension(16.0f, DimensionUnit::VP)).ConvertToVp();
1480     textStyle.fontStyle = static_cast<int32_t>(node->GetItalicFontStyleValue(OHOS::Ace::FontStyle::NORMAL));
1481     textStyle.fontWeight = static_cast<int32_t>(node->GetFontWeightValue(FontWeight::NORMAL));
1482     std::string fontFamilyValue;
1483     const std::vector<std::string> defaultFontFamily = { "HarmonyOS Sans" };
1484     auto fontFamily = node->GetFontFamilyValue(defaultFontFamily);
1485     for (const auto& str : fontFamily) {
1486         fontFamilyValue += str;
1487         fontFamilyValue += ",";
1488     }
1489     fontFamilyValue = fontFamilyValue.substr(0, fontFamilyValue.size() - 1);
1490     textStyle.fontFamily = !fontFamilyValue.empty() ? fontFamilyValue : defaultFontFamily.front();
1491     textStyle.decorationType = static_cast<int32_t>(node->GetTextDecorationValue(TextDecoration::NONE));
1492     textStyle.decorationColor = node->GetTextDecorationColorValue(Color::BLACK).ColorToString();
1493     textStyle.textAlign = static_cast<int32_t>(node->GetTextAlignValue(TextAlign::START));
1494     auto lm = node->GetLeadingMarginValue({});
1495     textStyle.leadingMarginSize[RichEditorLeadingRange::LEADING_START] = Dimension(lm.size.Width()).ConvertToVp();
1496     textStyle.leadingMarginSize[RichEditorLeadingRange::LEADING_END] = Dimension(lm.size.Height()).ConvertToVp();
1497     return textStyle;
1498 }
1499 
GetChildByIndex(int32_t index) const1500 RefPtr<UINode> TextPattern::GetChildByIndex(int32_t index) const
1501 {
1502     const auto& children = GetAllChildren();
1503     int32_t size = static_cast<int32_t>(children.size());
1504     if (index < 0 || index >= size) {
1505         return nullptr;
1506     }
1507     auto pos = children.begin();
1508     std::advance(pos, index);
1509     return *pos;
1510 }
1511 
GetSpanItemByIndex(int32_t index) const1512 RefPtr<SpanItem> TextPattern::GetSpanItemByIndex(int32_t index) const
1513 {
1514     int32_t size = static_cast<int32_t>(spans_.size());
1515     if (index < 0 || index >= size) {
1516         return nullptr;
1517     }
1518     auto pos = spans_.begin();
1519     std::advance(pos, index);
1520     return *pos;
1521 }
1522 
GetTextResultObject(RefPtr<UINode> uinode,int32_t index,int32_t start,int32_t end)1523 ResultObject TextPattern::GetTextResultObject(RefPtr<UINode> uinode, int32_t index, int32_t start, int32_t end)
1524 {
1525     bool selectFlag = false;
1526     ResultObject resultObject;
1527     if (!DynamicCast<SpanNode>(uinode)) {
1528         return resultObject;
1529     }
1530     auto spanItem = DynamicCast<SpanNode>(uinode)->GetSpanItem();
1531     int32_t itemLength = static_cast<int32_t>(StringUtils::ToWstring(spanItem->content).length());
1532     int32_t endPosition = std::min(GetTextContentLength(), spanItem->position);
1533     int32_t startPosition = endPosition - itemLength;
1534 
1535     if (startPosition >= start && endPosition <= end) {
1536         selectFlag = true;
1537         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
1538         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
1539     } else if (startPosition < start && endPosition <= end && endPosition > start) {
1540         selectFlag = true;
1541         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
1542         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
1543     } else if (startPosition >= start && startPosition < end && endPosition >= end) {
1544         selectFlag = true;
1545         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
1546         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
1547     } else if (startPosition <= start && endPosition >= end) {
1548         selectFlag = true;
1549         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
1550         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
1551     }
1552     if (selectFlag) {
1553         resultObject.spanPosition.spanIndex = index;
1554         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
1555         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
1556         resultObject.type = SelectSpanType::TYPESPAN;
1557         resultObject.valueString = spanItem->content;
1558         auto spanNode = DynamicCast<SpanNode>(uinode);
1559         resultObject.textStyle = GetTextStyleObject(spanNode);
1560     }
1561     return resultObject;
1562 }
1563 
GetSymbolSpanResultObject(RefPtr<UINode> uinode,int32_t index,int32_t start,int32_t end)1564 ResultObject TextPattern::GetSymbolSpanResultObject(RefPtr<UINode> uinode, int32_t index, int32_t start, int32_t end)
1565 {
1566     bool selectFlag = false;
1567     ResultObject resultObject;
1568     resultObject.isDraggable = false;
1569     if (!DynamicCast<SpanNode>(uinode)) {
1570         return resultObject;
1571     }
1572     auto spanItem = DynamicCast<SpanNode>(uinode)->GetSpanItem();
1573     int32_t itemLength = static_cast<int32_t>(StringUtils::ToWstring(spanItem->content).length());
1574     int32_t endPosition = std::min(GetTextContentLength(), spanItem->position);
1575     int32_t startPosition = endPosition - itemLength;
1576 
1577     if (startPosition >= start && endPosition <= end) {
1578         selectFlag = true;
1579         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
1580         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
1581     } else if (startPosition < start && endPosition <= end && endPosition > start) {
1582         selectFlag = true;
1583         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
1584         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
1585     } else if (startPosition >= start && startPosition < end && endPosition >= end) {
1586         selectFlag = true;
1587         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
1588         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
1589     } else if (startPosition <= start && endPosition >= end) {
1590         selectFlag = true;
1591         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
1592         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
1593     }
1594     if (selectFlag) {
1595         resultObject.valueResource = spanItem->GetResourceObject();
1596         resultObject.spanPosition.spanIndex = index;
1597         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
1598         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
1599         resultObject.type = SelectSpanType::TYPESYMBOLSPAN;
1600         resultObject.valueString = std::to_string(spanItem->unicode);
1601         auto spanNode = DynamicCast<SpanNode>(uinode);
1602         resultObject.symbolSpanStyle = GetSymbolSpanStyleObject(spanNode);
1603     }
1604     return resultObject;
1605 }
1606 
GetSymbolSpanStyleObject(const RefPtr<SpanNode> & node)1607 SymbolSpanStyle TextPattern::GetSymbolSpanStyleObject(const RefPtr<SpanNode>& node)
1608 {
1609     SymbolSpanStyle symbolSpanStyle;
1610     std::string symbolColorValue;
1611     auto symbolColors = node->GetSymbolColorList();
1612     for (const auto& color : *symbolColors) {
1613         symbolColorValue += color.ColorToString() + ",";
1614     }
1615     symbolColorValue = symbolColorValue.substr(0, symbolColorValue.size() - 1);
1616     symbolSpanStyle.symbolColor = !symbolColorValue.empty() ? symbolColorValue : SYMBOL_COLOR;
1617     symbolSpanStyle.fontSize = node->GetFontSizeValue(Dimension(DIMENSION_VALUE, DimensionUnit::VP)).ConvertToVp();
1618     symbolSpanStyle.fontWeight = static_cast<int32_t>(node->GetFontWeightValue(FontWeight::NORMAL));
1619     symbolSpanStyle.renderingStrategy = node->GetSymbolRenderingStrategyValue(0);
1620     symbolSpanStyle.effectStrategy = node->GetSymbolEffectStrategyValue(0);
1621     return symbolSpanStyle;
1622 }
1623 
GetImageResultObject(RefPtr<UINode> uinode,int32_t index,int32_t start,int32_t end)1624 ResultObject TextPattern::GetImageResultObject(RefPtr<UINode> uinode, int32_t index, int32_t start, int32_t end)
1625 {
1626     int32_t itemLength = 1;
1627     ResultObject resultObject;
1628     if (!DynamicCast<FrameNode>(uinode) || !GetSpanItemByIndex(index)) {
1629         return resultObject;
1630     }
1631     int32_t endPosition = std::min(GetTextContentLength(), GetSpanItemByIndex(index)->position);
1632     int32_t startPosition = endPosition - itemLength;
1633     if ((start <= startPosition) && (end >= endPosition)) {
1634         auto imageNode = DynamicCast<FrameNode>(uinode);
1635         auto imageLayoutProperty = DynamicCast<ImageLayoutProperty>(imageNode->GetLayoutProperty());
1636         resultObject.spanPosition.spanIndex = index;
1637         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
1638         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
1639         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
1640         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
1641         resultObject.type = SelectSpanType::TYPEIMAGE;
1642         if (!imageLayoutProperty->GetImageSourceInfo()->GetPixmap()) {
1643             resultObject.valueString = imageLayoutProperty->GetImageSourceInfo()->GetSrc();
1644         } else {
1645             resultObject.valuePixelMap = imageLayoutProperty->GetImageSourceInfo()->GetPixmap();
1646         }
1647         auto geometryNode = imageNode->GetGeometryNode();
1648         resultObject.imageStyle.size[RichEditorImageSize::SIZEWIDTH] = geometryNode->GetMarginFrameSize().Width();
1649         resultObject.imageStyle.size[RichEditorImageSize::SIZEHEIGHT] = geometryNode->GetMarginFrameSize().Height();
1650         if (imageLayoutProperty->HasImageFit()) {
1651             resultObject.imageStyle.verticalAlign = static_cast<int32_t>(imageLayoutProperty->GetImageFitValue());
1652         }
1653         if (imageLayoutProperty->HasVerticalAlign()) {
1654             resultObject.imageStyle.objectFit = static_cast<int32_t>(imageLayoutProperty->GetVerticalAlignValue());
1655         }
1656         if (geometryNode->GetMargin()) {
1657             resultObject.imageStyle.margin = geometryNode->GetMargin()->ToJsonString();
1658         }
1659         auto imageRenderCtx = imageNode->GetRenderContext();
1660         if (imageRenderCtx->GetBorderRadius()) {
1661             BorderRadiusProperty brp;
1662             auto jsonObject = JsonUtil::Create(true);
1663             auto jsonBorder = JsonUtil::Create(true);
1664             imageRenderCtx->GetBorderRadiusValue(brp).ToJsonValue(jsonObject, jsonBorder);
1665             resultObject.imageStyle.borderRadius = jsonObject->ToString();
1666         }
1667     }
1668     return resultObject;
1669 }
1670 
1671 // ===========================================================
1672 // TextDragBase implementations
GetLineHeight() const1673 float TextPattern::GetLineHeight() const
1674 {
1675     std::vector<RectF> selectedRects;
1676     paragraph_->GetRectsForRange(textSelector_.GetTextStart(), textSelector_.GetTextEnd(), selectedRects);
1677     CHECK_NULL_RETURN(selectedRects.size(), {});
1678     return selectedRects.front().Height();
1679 }
1680 
GetTextBoxes()1681 std::vector<RectF> TextPattern::GetTextBoxes()
1682 {
1683     std::vector<RectF> selectedRects;
1684     paragraph_->GetRectsForRange(textSelector_.GetTextStart(), textSelector_.GetTextEnd(), selectedRects);
1685     return selectedRects;
1686 }
1687 
GetParentGlobalOffset() const1688 OffsetF TextPattern::GetParentGlobalOffset() const
1689 {
1690     auto host = GetHost();
1691     CHECK_NULL_RETURN(host, {});
1692     auto pipeline = PipelineContext::GetCurrentContext();
1693     CHECK_NULL_RETURN(pipeline, {});
1694     auto rootOffset = pipeline->GetRootRect().GetOffset();
1695     return host->GetPaintRectOffset() - rootOffset;
1696 }
1697 
CreateHandles()1698 void TextPattern::CreateHandles()
1699 {
1700     if (IsDragging()) {
1701         TAG_LOGI(AceLogTag::ACE_TEXT, "do not show handles when dragging");
1702         return;
1703     }
1704     ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle);
1705 }
1706 
BetweenSelectedPosition(const Offset & globalOffset)1707 bool TextPattern::BetweenSelectedPosition(const Offset& globalOffset)
1708 {
1709     auto host = GetHost();
1710     CHECK_NULL_RETURN(host, false);
1711     auto offset = host->GetPaintRectOffset();
1712     auto localOffset = globalOffset - Offset(offset.GetX(), offset.GetY());
1713     return IsDraggable(localOffset);
1714 }
1715 
1716 // end of TextDragBase implementations
1717 // ===========================================================
1718 
OnModifyDone()1719 void TextPattern::OnModifyDone()
1720 {
1721 #if (defined(__aarch64__) || defined(__x86_64__))
1722     FrameNode::PostTask(
1723         [weak = WeakClaim(this)]() {
1724             auto pattern = weak.Upgrade();
1725             CHECK_NULL_VOID(pattern);
1726             pattern->OnAfterModifyDone();
1727         },
1728         TaskExecutor::TaskType::UI);
1729 #endif
1730     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1731     CHECK_NULL_VOID(textLayoutProperty);
1732     auto host = GetHost();
1733     CHECK_NULL_VOID(host);
1734     auto renderContext = host->GetRenderContext();
1735     CHECK_NULL_VOID(renderContext);
1736 
1737     if (CheckNeedMeasure(textLayoutProperty->GetPropertyChangeFlag())) {
1738         // measure flag changed, reset paragraph.
1739         paragraph_.Reset();
1740     }
1741 
1742     if (!(PipelineContext::GetCurrentContext() &&
1743             PipelineContext::GetCurrentContext()->GetMinPlatformVersion() > API_PROTEXTION_GREATER_NINE)) {
1744         bool shouldClipToContent =
1745             textLayoutProperty->GetTextOverflow().value_or(TextOverflow::CLIP) == TextOverflow::CLIP;
1746         host->GetRenderContext()->SetClipToFrame(shouldClipToContent);
1747     }
1748 
1749     if (textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE) {
1750         CloseSelectOverlay();
1751         ResetSelection();
1752         copyOption_ = CopyOptions::None;
1753     } else {
1754         copyOption_ = textLayoutProperty->GetCopyOption().value_or(CopyOptions::None);
1755     }
1756     if (GetAllChildren().empty()) {
1757         auto obscuredReasons = renderContext->GetObscured().value_or(std::vector<ObscuredReasons>());
1758         bool ifHaveObscured = std::any_of(obscuredReasons.begin(), obscuredReasons.end(),
1759             [](const auto& reason) { return reason == ObscuredReasons::PLACEHOLDER; });
1760         if (ifHaveObscured) {
1761             CloseSelectOverlay();
1762             ResetSelection();
1763             copyOption_ = CopyOptions::None;
1764         }
1765 
1766         std::string textCache = textForDisplay_;
1767         textForDisplay_ = textLayoutProperty->GetContent().value_or("");
1768         if (textCache != textForDisplay_) {
1769             host->OnAccessibilityEvent(AccessibilityEventType::TEXT_CHANGE, textCache, textForDisplay_);
1770             dataDetectorAdapter_->aiDetectInitialized_ = false;
1771             CloseSelectOverlay();
1772             ResetSelection();
1773         }
1774         if (CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
1775             dataDetectorAdapter_->textForAI_ = textForDisplay_;
1776             dataDetectorAdapter_->StartAITask();
1777         }
1778     }
1779 
1780     auto gestureEventHub = host->GetOrCreateGestureEventHub();
1781     CHECK_NULL_VOID(gestureEventHub);
1782     if (copyOption_ != CopyOptions::None) {
1783         auto context = PipelineContext::GetCurrentContext();
1784         CHECK_NULL_VOID(context);
1785         if (!clipboard_ && context) {
1786             clipboard_ = ClipboardProxy::GetInstance()->GetClipboard(context->GetTaskExecutor());
1787         }
1788         InitLongPressEvent(gestureEventHub);
1789         if (host->IsDraggable()) {
1790             InitDragEvent();
1791         }
1792         InitMouseEvent();
1793         InitTouchEvent();
1794         SetAccessibilityAction();
1795     } else {
1796         if (host->IsDraggable() || gestureEventHub->GetTextDraggable()) {
1797             gestureEventHub->SetTextDraggable(false);
1798         }
1799     }
1800     if (onClick_ || copyOption_ != CopyOptions::None) {
1801         InitClickEvent(gestureEventHub);
1802     }
1803     auto eventHub = host->GetEventHub<EventHub>();
1804     CHECK_NULL_VOID(eventHub);
1805     bool enabledCache = eventHub->IsEnabled();
1806     if (textDetectEnable_ && enabledCache != enabled_) {
1807         enabled_ = enabledCache;
1808         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1809     }
1810 }
1811 
ToJsonValue(std::unique_ptr<JsonValue> & json) const1812 void TextPattern::ToJsonValue(std::unique_ptr<JsonValue>& json) const
1813 {
1814     json->Put("enableDataDetector", textDetectEnable_ ? "true" : "false");
1815     auto jsonValue = JsonUtil::Create(true);
1816     jsonValue->Put("types", "");
1817     json->Put("dataDetectorConfig", jsonValue->ToString().c_str());
1818 }
1819 
OnAfterModifyDone()1820 void TextPattern::OnAfterModifyDone()
1821 {
1822     auto host = GetHost();
1823     CHECK_NULL_VOID(host);
1824     auto inspectorId = host->GetInspectorId().value_or("");
1825     if (!inspectorId.empty()) {
1826         auto prop = host->GetAccessibilityProperty<NG::AccessibilityProperty>();
1827         Recorder::NodeDataCache::Get().PutString(inspectorId, prop->GetText());
1828     }
1829 }
1830 
ActSetSelection(int32_t start,int32_t end)1831 void TextPattern::ActSetSelection(int32_t start, int32_t end)
1832 {
1833     if (start == -1 && end == -1) {
1834         ResetSelection();
1835         CloseSelectOverlay();
1836         return;
1837     }
1838     int32_t min = 0;
1839     int32_t textSize = static_cast<int32_t>(GetWideText().length()) + placeholderCount_;
1840     start = start < min ? min : start;
1841     end = end < min ? min : end;
1842     start = start > textSize ? textSize : start;
1843     end = end > textSize ? textSize : end;
1844     if (start >= end) {
1845         FireOnSelectionChange(-1, -1);
1846         return;
1847     }
1848     HandleSelectionChange(start, end);
1849     CalculateHandleOffsetAndShowOverlay();
1850     ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle);
1851     auto host = GetHost();
1852     CHECK_NULL_VOID(host);
1853     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1854 }
1855 
UpdateSelectOverlayOrCreate(SelectOverlayInfo & selectInfo,bool animation)1856 void TextPattern::UpdateSelectOverlayOrCreate(SelectOverlayInfo& selectInfo, bool animation)
1857 {
1858     if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
1859         SelectHandleInfo firstHandleInfo;
1860         firstHandleInfo.paintRect = textSelector_.firstHandle;
1861         CheckHandles(firstHandleInfo);
1862 
1863         SelectHandleInfo secondHandleInfo;
1864         secondHandleInfo.paintRect = textSelector_.secondHandle;
1865         CheckHandles(secondHandleInfo);
1866 
1867         auto start = textSelector_.GetTextStart();
1868         auto end = textSelector_.GetTextEnd();
1869         selectOverlayProxy_->SetSelectInfo(GetSelectedText(start, end));
1870         if (selectInfo.isNewAvoid) {
1871             selectOverlayProxy_->UpdateSelectArea(selectInfo.selectArea);
1872         }
1873         selectOverlayProxy_->UpdateFirstAndSecondHandleInfo(firstHandleInfo, secondHandleInfo);
1874         selectOverlayProxy_->ShowOrHiddenMenu(!firstHandleInfo.isShow && !secondHandleInfo.isShow);
1875     } else {
1876         auto pipeline = PipelineContext::GetCurrentContext();
1877         CHECK_NULL_VOID(pipeline);
1878         auto host = GetHost();
1879         CHECK_NULL_VOID(host);
1880         pipeline->AddOnAreaChangeNode(host->GetId());
1881         selectInfo.callerFrameNode = GetHost();
1882         selectInfo.hitTestMode = HitTestMode::HTMDEFAULT;
1883         if (!selectInfo.isUsingMouse) {
1884             CheckHandles(selectInfo.firstHandle);
1885             CheckHandles(selectInfo.secondHandle);
1886         }
1887         selectOverlayProxy_ =
1888             pipeline->GetSelectOverlayManager()->CreateAndShowSelectOverlay(selectInfo, WeakClaim(this), animation);
1889         CHECK_NULL_VOID(selectOverlayProxy_);
1890         auto start = textSelector_.GetTextStart();
1891         auto end = textSelector_.GetTextEnd();
1892         selectOverlayProxy_->SetSelectInfo(GetSelectedText(start, end));
1893     }
1894 }
1895 
RedisplaySelectOverlay()1896 void TextPattern::RedisplaySelectOverlay()
1897 {
1898     if (!isShowMenu_) {
1899         TAG_LOGD(AceLogTag::ACE_TEXT, "Do not redisplaySelectOverlay when drag failed");
1900         isShowMenu_ = true;
1901         return;
1902     }
1903     if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
1904         CalculateHandleOffsetAndShowOverlay();
1905         if (selectOverlayProxy_->IsMenuShow()) {
1906             ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle);
1907         } else {
1908             ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle);
1909             selectOverlayProxy_->ShowOrHiddenMenu(true);
1910         }
1911     }
1912 }
1913 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)1914 bool TextPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
1915 {
1916     if (config.skipMeasure || dirty->SkipMeasureContent()) {
1917         RedisplaySelectOverlay();
1918         return false;
1919     }
1920 
1921     contentRect_ = dirty->GetGeometryNode()->GetContentRect();
1922 
1923     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
1924     CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
1925     auto textLayoutAlgorithm = DynamicCast<TextLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1926     CHECK_NULL_RETURN(textLayoutAlgorithm, false);
1927     auto&& paragraph = textLayoutAlgorithm->GetParagraph();
1928     if (!paragraph) {
1929         return false;
1930     }
1931     paragraph_ = paragraph;
1932     // The handle calculation needs to be after the paragraph is assigned.
1933     RedisplaySelectOverlay();
1934     baselineOffset_ = textLayoutAlgorithm->GetBaselineOffset();
1935     contentOffset_ = dirty->GetGeometryNode()->GetContentOffset();
1936     textStyle_ = textLayoutAlgorithm->GetTextStyle();
1937     return true;
1938 }
1939 
PreCreateLayoutWrapper()1940 void TextPattern::PreCreateLayoutWrapper()
1941 {
1942     auto host = GetHost();
1943     CHECK_NULL_VOID(host);
1944 
1945     // mark content dirty
1946     if (contentMod_) {
1947         contentMod_->ContentChange();
1948     }
1949 
1950     auto paintProperty = GetPaintProperty<PaintProperty>();
1951     CHECK_NULL_VOID(paintProperty);
1952     auto flag = paintProperty->GetPropertyChangeFlag();
1953     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1954     CHECK_NULL_VOID(textLayoutProperty);
1955     auto layoutFlag = textLayoutProperty->GetPropertyChangeFlag();
1956     if (!CheckNeedMeasure(flag) && !CheckNeedMeasure(layoutFlag)) {
1957         return;
1958     }
1959 
1960     spans_.clear();
1961 
1962     // When dirty areas are marked because of child node changes, the text rendering node tree is reset.
1963     const auto& children = host->GetChildren();
1964     if (children.empty()) {
1965         placeholderCount_ = 0;
1966         return;
1967     }
1968 
1969     // Depth-first iterates through all host's child nodes to collect the SpanNode object, building a text rendering
1970     // tree.
1971     std::stack<SpanNodeInfo> nodes;
1972     for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
1973         nodes.push({ .node = *iter });
1974     }
1975 
1976     InitSpanItem(nodes);
1977 }
1978 
InitSpanItem(std::stack<SpanNodeInfo> nodes)1979 void TextPattern::InitSpanItem(std::stack<SpanNodeInfo> nodes)
1980 {
1981     auto host = GetHost();
1982     CHECK_NULL_VOID(host);
1983     std::string textCache;
1984     std::string textForAICache;
1985     int32_t oldPlaceholderCount = placeholderCount_;
1986     placeholderCount_ = 0;
1987     if (!nodes.empty()) {
1988         textCache = textForDisplay_;
1989         textForAICache = dataDetectorAdapter_->textForAI_;
1990         textForDisplay_.clear();
1991         dataDetectorAdapter_->textForAI_.clear();
1992     }
1993 
1994     bool isSpanHasClick = false;
1995     CollectSpanNodes(nodes, isSpanHasClick);
1996     if (oldPlaceholderCount != placeholderCount_) {
1997         CloseSelectOverlay();
1998         ResetSelection();
1999     }
2000 
2001     if (textCache != textForDisplay_) {
2002         host->OnAccessibilityEvent(AccessibilityEventType::TEXT_CHANGE, textCache, textForDisplay_);
2003         dataDetectorAdapter_->aiDetectInitialized_ = false;
2004         OnAfterModifyDone();
2005         for (const auto& item : spans_) {
2006             if (item->inspectId.empty()) {
2007                 continue;
2008             }
2009             Recorder::NodeDataCache::Get().PutString(item->inspectId, item->content);
2010         }
2011         CloseSelectOverlay();
2012         ResetSelection();
2013     }
2014     if (isSpanHasClick) {
2015         auto gestureEventHub = host->GetOrCreateGestureEventHub();
2016         InitClickEvent(gestureEventHub);
2017     }
2018     if (CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
2019         dataDetectorAdapter_->StartAITask();
2020     }
2021 }
2022 
BeforeCreateLayoutWrapper()2023 void TextPattern::BeforeCreateLayoutWrapper()
2024 {
2025     PreCreateLayoutWrapper();
2026 }
2027 
CollectSpanNodes(std::stack<SpanNodeInfo> nodes,bool & isSpanHasClick)2028 void TextPattern::CollectSpanNodes(std::stack<SpanNodeInfo> nodes, bool& isSpanHasClick)
2029 {
2030     while (!nodes.empty()) {
2031         auto current = nodes.top();
2032         nodes.pop();
2033         // TODO: Add the judgment of display.
2034         if (!current.node) {
2035             continue;
2036         }
2037         UpdateContainerChildren(current.containerSpanNode, current.node);
2038         auto spanNode = DynamicCast<SpanNode>(current.node);
2039         auto tag = current.node->GetTag();
2040         if (spanNode && tag == V2::SYMBOL_SPAN_ETS_TAG) {
2041             spanNode->CleanSpanItemChildren();
2042             UpdateChildProperty(spanNode);
2043             spanNode->MountToParagraph();
2044             textForDisplay_.append(StringUtils::Str16ToStr8(SYMBOL_TRANS));
2045         } else if (spanNode && tag != V2::PLACEHOLDER_SPAN_ETS_TAG) {
2046             spanNode->CleanSpanItemChildren();
2047             UpdateChildProperty(spanNode);
2048             spanNode->MountToParagraph();
2049             textForDisplay_.append(spanNode->GetSpanItem()->content);
2050             dataDetectorAdapter_->textForAI_.append(spanNode->GetSpanItem()->content);
2051             if (spanNode->GetSpanItem()->onClick) {
2052                 isSpanHasClick = true;
2053             }
2054         } else if (tag == V2::IMAGE_ETS_TAG || tag == V2::PLACEHOLDER_SPAN_ETS_TAG) {
2055             placeholderCount_++;
2056             AddChildSpanItem(current.node);
2057             dataDetectorAdapter_->textForAI_.append("\n");
2058             auto imageNode = DynamicCast<FrameNode>(current.node);
2059             if (!imageNode) {
2060                 continue;
2061             }
2062             auto focus_hub = imageNode->GetOrCreateFocusHub();
2063             if (focus_hub && focus_hub->GetOnClickCallback()) {
2064                 isSpanHasClick = true;
2065             }
2066         }
2067         if (tag == V2::PLACEHOLDER_SPAN_ETS_TAG) {
2068             continue;
2069         }
2070         auto containerSpanNode = tag == V2::CONTAINER_SPAN_ETS_TAG ? current.node : current.containerSpanNode;
2071         const auto& nextChildren = current.node->GetChildren();
2072         for (auto iter = nextChildren.rbegin(); iter != nextChildren.rend(); ++iter) {
2073             nodes.push({ .node = *iter, .containerSpanNode = containerSpanNode });
2074         }
2075     }
2076 }
2077 
UpdateContainerChildren(const RefPtr<UINode> & parentNode,const RefPtr<UINode> & child)2078 void TextPattern::UpdateContainerChildren(const RefPtr<UINode>& parentNode, const RefPtr<UINode>& child)
2079 {
2080     CHECK_NULL_VOID(child);
2081     auto parent = DynamicCast<ContainerSpanNode>(parentNode);
2082     CHECK_NULL_VOID(parent);
2083     auto baseSpan = DynamicCast<BaseSpan>(child);
2084     if (baseSpan) {
2085         if (baseSpan->HasTextBackgroundStyle()) {
2086             return;
2087         }
2088         baseSpan->UpdateTextBackgroundFromParent(parent->GetTextBackgroundStyle());
2089         return;
2090     }
2091     if (child->GetTag() == V2::IMAGE_ETS_TAG) {
2092         auto imageNode = DynamicCast<FrameNode>(child);
2093         CHECK_NULL_VOID(imageNode);
2094         auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
2095         CHECK_NULL_VOID(imageLayoutProperty);
2096         if (imageLayoutProperty->GetHasPlaceHolderStyleValue(false)) {
2097             return;
2098         }
2099         if (parent->GetTextBackgroundStyle().has_value()) {
2100             imageLayoutProperty->UpdatePlaceHolderStyle(parent->GetTextBackgroundStyle().value());
2101         }
2102     }
2103 }
2104 
GetGlobalOffset(Offset & offset)2105 void TextPattern::GetGlobalOffset(Offset& offset)
2106 {
2107     auto host = GetHost();
2108     CHECK_NULL_VOID(host);
2109     auto pipeline = PipelineContext::GetCurrentContext();
2110     CHECK_NULL_VOID(pipeline);
2111     auto rootOffset = pipeline->GetRootRect().GetOffset();
2112     auto globalOffset = host->GetPaintRectOffset() - rootOffset;
2113     offset = Offset(globalOffset.GetX(), globalOffset.GetY());
2114 }
2115 
OnVisibleChange(bool isVisible)2116 void TextPattern::OnVisibleChange(bool isVisible)
2117 {
2118     if (!isVisible) {
2119         if (textSelector_.IsValid()) {
2120             CloseSelectOverlay();
2121             ResetSelection();
2122         }
2123         if (textDetectEnable_) {
2124             dataDetectorAdapter_->aiDetectDelayTask_.Cancel();
2125         }
2126     } else {
2127         if (CanStartAITask()) {
2128             dataDetectorAdapter_->StartAITask();
2129         }
2130     }
2131 }
2132 
InitSurfaceChangedCallback()2133 void TextPattern::InitSurfaceChangedCallback()
2134 {
2135     auto pipeline = PipelineContext::GetCurrentContext();
2136     CHECK_NULL_VOID(pipeline);
2137     if (!HasSurfaceChangedCallback()) {
2138         auto callbackId = pipeline->RegisterSurfaceChangedCallback(
2139             [weak = WeakClaim(this)](int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight,
2140                 WindowSizeChangeReason type) {
2141                 auto pattern = weak.Upgrade();
2142                 if (pattern) {
2143                     pattern->HandleSurfaceChanged(newWidth, newHeight, prevWidth, prevHeight);
2144                 }
2145             });
2146         UpdateSurfaceChangedCallbackId(callbackId);
2147     }
2148 }
2149 
HandleSurfaceChanged(int32_t newWidth,int32_t newHeight,int32_t prevWidth,int32_t prevHeight)2150 void TextPattern::HandleSurfaceChanged(int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight)
2151 {
2152     if (newWidth == prevWidth && newHeight == prevHeight) {
2153         return;
2154     }
2155     CHECK_NULL_VOID(selectOverlayProxy_);
2156     selectOverlayProxy_->ShowOrHiddenMenu(true);
2157 }
2158 
InitSurfacePositionChangedCallback()2159 void TextPattern::InitSurfacePositionChangedCallback()
2160 {
2161     auto pipeline = PipelineContext::GetCurrentContext();
2162     CHECK_NULL_VOID(pipeline);
2163     if (!HasSurfacePositionChangedCallback()) {
2164         auto callbackId =
2165             pipeline->RegisterSurfacePositionChangedCallback([weak = WeakClaim(this)](int32_t posX, int32_t posY) {
2166                 auto pattern = weak.Upgrade();
2167                 if (pattern) {
2168                     pattern->HandleSurfacePositionChanged(posX, posY);
2169                 }
2170             });
2171         UpdateSurfacePositionChangedCallbackId(callbackId);
2172     }
2173 }
2174 
AddChildSpanItem(const RefPtr<UINode> & child)2175 void TextPattern::AddChildSpanItem(const RefPtr<UINode>& child)
2176 {
2177     CHECK_NULL_VOID(child);
2178     auto chidNode = DynamicCast<FrameNode>(child);
2179     if (chidNode && chidNode->GetLayoutProperty() && chidNode->GetLayoutProperty()->IsOverlayNode()) {
2180         return;
2181     }
2182 
2183     if (child->GetTag() == V2::SPAN_ETS_TAG || child->GetTag() == V2::SYMBOL_SPAN_ETS_TAG) {
2184         auto spanNode = DynamicCast<SpanNode>(child);
2185         if (spanNode) {
2186             spans_.emplace_back(spanNode->GetSpanItem());
2187         }
2188     } else if (child->GetTag() == V2::IMAGE_ETS_TAG) {
2189         auto imageSpanNode = DynamicCast<ImageSpanNode>(child);
2190         if (imageSpanNode) {
2191             spans_.emplace_back(imageSpanNode->GetSpanItem());
2192             spans_.back()->imageNodeId = imageSpanNode->GetId();
2193             return;
2194         }
2195         auto imageNode = DynamicCast<FrameNode>(child);
2196         if (imageNode) {
2197             auto imageSpanItem = MakeRefPtr<ImageSpanItem>();
2198             imageSpanItem->imageNodeId = imageNode->GetId();
2199             imageSpanItem->UpdatePlaceholderBackgroundStyle(imageNode);
2200             auto focus_hub = imageNode->GetOrCreateFocusHub();
2201             CHECK_NULL_VOID(focus_hub);
2202             auto clickCall = focus_hub->GetOnClickCallback();
2203             if (clickCall) {
2204                 imageSpanItem->SetOnClickEvent(std::move(clickCall));
2205             }
2206             spans_.emplace_back(imageSpanItem);
2207             auto gesture = imageNode->GetOrCreateGestureEventHub();
2208             CHECK_NULL_VOID(gesture);
2209             gesture->SetHitTestMode(HitTestMode::HTMNONE);
2210 
2211             return;
2212         }
2213     } else if (child->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG) {
2214         auto placeholderSpanNode = DynamicCast<PlaceholderSpanNode>(child);
2215         if (placeholderSpanNode) {
2216             auto placeholderSpan = placeholderSpanNode->GetSpanItem();
2217             placeholderSpan->placeholderSpanNodeId = placeholderSpanNode->GetId();
2218             spans_.emplace_back(placeholderSpan);
2219         }
2220     }
2221 }
2222 
DumpAdvanceInfo()2223 void TextPattern::DumpAdvanceInfo()
2224 {
2225     auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
2226     CHECK_NULL_VOID(textLayoutProp);
2227     DumpLog::GetInstance().AddDesc(std::string("Content: ").append(textLayoutProp->GetContent().value_or(" ")));
2228     DumpLog::GetInstance().AddDesc(
2229         std::string("FontColor: ").append(textLayoutProp->GetTextColor().value_or(Color::BLACK).ColorToString()));
2230     DumpLog::GetInstance().AddDesc(
2231         std::string("FontSize: ")
2232             .append(
2233                 (textStyle_.has_value() ? textStyle_->GetFontSize() : Dimension(16.0, DimensionUnit::FP)).ToString()));
2234     DumpLog::GetInstance().AddDesc(std::string("contentRect-->x:")
2235                                        .append(std::to_string(contentRect_.GetX()))
2236                                        .append(" y:")
2237                                        .append(std::to_string(contentRect_.GetY())));
2238     DumpLog::GetInstance().AddDesc(std::string("Selection: ").append("(").append(textSelector_.ToString()).append(")"));
2239     if (SystemProperties::GetDebugEnabled() && paragraph_) {
2240         DumpLog::GetInstance().AddDesc(std::string("from TextEngine paragraph_ info :"));
2241         DumpLog::GetInstance().AddDesc(
2242             std::string("DidExceedMaxLinesx:").append(std::to_string(paragraph_->DidExceedMaxLines())));
2243 
2244         DumpLog::GetInstance().AddDesc(std::string("GetTextWidth:")
2245                                            .append(std::to_string(paragraph_->GetTextWidth()))
2246                                            .append(" GetHeight:")
2247                                            .append(std::to_string(paragraph_->GetHeight()))
2248                                            .append(" GetMaxWidth:")
2249                                            .append(std::to_string(paragraph_->GetMaxWidth()))
2250                                            .append(" GetMaxIntrinsicWidth:")
2251                                            .append(std::to_string(paragraph_->GetMaxIntrinsicWidth())));
2252         DumpLog::GetInstance().AddDesc(std::string("GetLineCount:")
2253                                            .append(std::to_string(paragraph_->GetLineCount()))
2254                                            .append(" GetLongestLine:")
2255                                            .append(std::to_string(paragraph_->GetLongestLine()))
2256                                            .append(" GetAlphabeticBaseline:")
2257                                            .append(std::to_string(paragraph_->GetAlphabeticBaseline())));
2258     }
2259 
2260     DumpLog::GetInstance().AddDesc(
2261         std::string("BindSelectionMenu: ").append(std::to_string(selectionMenuMap_.empty())));
2262 }
2263 
DumpInfo()2264 void TextPattern::DumpInfo()
2265 {
2266     auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
2267     CHECK_NULL_VOID(textLayoutProp);
2268     DumpLog::GetInstance().AddDesc(std::string("Content: ").append(textLayoutProp->GetContent().value_or(" ")));
2269     DumpLog::GetInstance().AddDesc(
2270         std::string("FontColor: ").append(textLayoutProp->GetTextColor().value_or(Color::BLACK).ColorToString()));
2271     DumpLog::GetInstance().AddDesc(
2272         std::string("FontSize: ")
2273             .append(
2274                 (textStyle_.has_value() ? textStyle_->GetFontSize() : Dimension(16.0, DimensionUnit::FP)).ToString()));
2275     DumpLog::GetInstance().AddDesc(std::string("Selection: ").append("(").append(textSelector_.ToString()).append(")"));
2276 }
2277 
UpdateChildProperty(const RefPtr<SpanNode> & child) const2278 void TextPattern::UpdateChildProperty(const RefPtr<SpanNode>& child) const
2279 {
2280     CHECK_NULL_VOID(child);
2281     auto host = GetHost();
2282     CHECK_NULL_VOID(host);
2283     auto textLayoutProp = host->GetLayoutProperty<TextLayoutProperty>();
2284     CHECK_NULL_VOID(textLayoutProp);
2285 
2286     auto inheritPropertyInfo = child->CalculateInheritPropertyInfo();
2287     for (const PropertyInfo& info : inheritPropertyInfo) {
2288         switch (info) {
2289             case PropertyInfo::FONTSIZE:
2290                 if (textLayoutProp->HasFontSize()) {
2291                     child->UpdateFontSizeWithoutFlushDirty(textLayoutProp->GetFontSize().value());
2292                 }
2293                 break;
2294             case PropertyInfo::FONTCOLOR:
2295                 if (textLayoutProp->HasTextColor()) {
2296                     child->UpdateTextColorWithoutFlushDirty(textLayoutProp->GetTextColor().value());
2297                 }
2298                 break;
2299             case PropertyInfo::FONTSTYLE:
2300                 if (textLayoutProp->HasItalicFontStyle()) {
2301                     child->UpdateItalicFontStyleWithoutFlushDirty(textLayoutProp->GetItalicFontStyle().value());
2302                 }
2303                 break;
2304             case PropertyInfo::FONTWEIGHT:
2305                 if (textLayoutProp->HasFontWeight()) {
2306                     child->UpdateFontWeightWithoutFlushDirty(textLayoutProp->GetFontWeight().value());
2307                 }
2308                 break;
2309             case PropertyInfo::FONTFAMILY:
2310                 if (textLayoutProp->HasFontFamily()) {
2311                     child->UpdateFontFamilyWithoutFlushDirty(textLayoutProp->GetFontFamily().value());
2312                 }
2313                 break;
2314             case PropertyInfo::TEXTDECORATION:
2315                 if (textLayoutProp->HasTextDecoration()) {
2316                     child->UpdateTextDecorationWithoutFlushDirty(textLayoutProp->GetTextDecoration().value());
2317                     if (textLayoutProp->HasTextDecorationColor()) {
2318                         child->UpdateTextDecorationColorWithoutFlushDirty(
2319                             textLayoutProp->GetTextDecorationColor().value());
2320                     }
2321                     if (textLayoutProp->HasTextDecorationStyle()) {
2322                         child->UpdateTextDecorationStyleWithoutFlushDirty(
2323                             textLayoutProp->GetTextDecorationStyle().value());
2324                     }
2325                 }
2326                 break;
2327             case PropertyInfo::TEXTCASE:
2328                 if (textLayoutProp->HasTextCase()) {
2329                     child->UpdateTextCaseWithoutFlushDirty(textLayoutProp->GetTextCase().value());
2330                 }
2331                 break;
2332             case PropertyInfo::LETTERSPACE:
2333                 if (textLayoutProp->HasLetterSpacing()) {
2334                     child->UpdateLetterSpacingWithoutFlushDirty(textLayoutProp->GetLetterSpacing().value());
2335                 }
2336                 break;
2337             case PropertyInfo::LINEHEIGHT:
2338                 if (textLayoutProp->HasLineHeight()) {
2339                     child->UpdateLineHeightWithoutFlushDirty(textLayoutProp->GetLineHeight().value());
2340                 }
2341                 break;
2342             case PropertyInfo::TEXTSHADOW:
2343                 if (textLayoutProp->HasTextShadow()) {
2344                     child->UpdateTextShadowWithoutFlushDirty(textLayoutProp->GetTextShadow().value());
2345                 }
2346                 break;
2347             default:
2348                 break;
2349         }
2350     }
2351 }
2352 
SetAccessibilityAction()2353 void TextPattern::SetAccessibilityAction()
2354 {
2355     auto host = GetHost();
2356     CHECK_NULL_VOID(host);
2357     auto textAccessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
2358     CHECK_NULL_VOID(textAccessibilityProperty);
2359     textAccessibilityProperty->SetActionSetSelection([weakPtr = WeakClaim(this)](int32_t start, int32_t end) {
2360         const auto& pattern = weakPtr.Upgrade();
2361         CHECK_NULL_VOID(pattern);
2362         auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
2363         CHECK_NULL_VOID(textLayoutProperty);
2364         if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None) {
2365             pattern->ActSetSelection(start, end);
2366         }
2367     });
2368 
2369     textAccessibilityProperty->SetActionClearSelection([weakPtr = WeakClaim(this)]() {
2370         const auto& pattern = weakPtr.Upgrade();
2371         CHECK_NULL_VOID(pattern);
2372         auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
2373         CHECK_NULL_VOID(textLayoutProperty);
2374         if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None) {
2375             pattern->CloseSelectOverlay(true);
2376             pattern->ResetSelection();
2377         }
2378     });
2379 
2380     textAccessibilityProperty->SetActionCopy([weakPtr = WeakClaim(this)]() {
2381         const auto& pattern = weakPtr.Upgrade();
2382         CHECK_NULL_VOID(pattern);
2383         auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
2384         CHECK_NULL_VOID(textLayoutProperty);
2385         if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None) {
2386             pattern->HandleOnCopy();
2387             pattern->CloseSelectOverlay(true);
2388             pattern->ResetSelection();
2389         }
2390     });
2391 }
2392 
OnColorConfigurationUpdate()2393 void TextPattern::OnColorConfigurationUpdate()
2394 {
2395     auto context = PipelineContext::GetCurrentContext();
2396     CHECK_NULL_VOID(context);
2397     auto theme = context->GetTheme<TextTheme>();
2398     CHECK_NULL_VOID(theme);
2399     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
2400     CHECK_NULL_VOID(textLayoutProperty);
2401     textLayoutProperty->UpdateTextColor(theme->GetTextStyle().GetTextColor());
2402 }
2403 
GetDragUpperLeftCoordinates()2404 OffsetF TextPattern::GetDragUpperLeftCoordinates()
2405 {
2406     if (dragBoxes_.empty()) {
2407         return { 0.0f, 0.0f };
2408     }
2409     auto startY = dragBoxes_.front().Top();
2410     auto startX = dragBoxes_.front().Left();
2411 
2412     auto endY = dragBoxes_.back().Top();
2413     OffsetF offset;
2414     if (NearEqual(startY, endY)) {
2415         offset = { contentRect_.GetX() + startX, startY + contentRect_.GetY() };
2416     } else {
2417         offset = { contentRect_.GetX(), startY + contentRect_.GetY() };
2418     }
2419 
2420     return GetParentGlobalOffset() + offset;
2421 }
2422 
ProcessBoundRectByTextShadow(RectF & rect)2423 void TextPattern::ProcessBoundRectByTextShadow(RectF& rect)
2424 {
2425     auto property = GetHost()->GetLayoutProperty<TextLayoutProperty>();
2426     auto shadows = property->GetTextShadow();
2427     if (!shadows.has_value()) {
2428         return;
2429     }
2430     float leftOffsetX = 0.0f;
2431     float rightOffsetX = 0.0f;
2432     float upOffsetY = 0.0f;
2433     float downOffsetY = 0.0f;
2434     for (const auto& shadow : shadows.value()) {
2435         auto shadowBlurRadius = shadow.GetBlurRadius() * 2.0f;
2436         if (LessNotEqual(shadow.GetOffset().GetX() - shadowBlurRadius, leftOffsetX)) {
2437             leftOffsetX = shadow.GetOffset().GetX() - shadowBlurRadius;
2438         }
2439 
2440         if (GreatNotEqual(shadow.GetOffset().GetX() + shadowBlurRadius, rightOffsetX)) {
2441             rightOffsetX = shadow.GetOffset().GetX() + shadowBlurRadius;
2442         }
2443 
2444         if (LessNotEqual(shadow.GetOffset().GetY() - shadowBlurRadius, upOffsetY)) {
2445             upOffsetY = shadow.GetOffset().GetY() - shadowBlurRadius;
2446         }
2447 
2448         if (GreatNotEqual(shadow.GetOffset().GetY() + shadowBlurRadius, downOffsetY)) {
2449             downOffsetY = shadow.GetOffset().GetY() + shadowBlurRadius;
2450         }
2451     }
2452     rect.SetRect(
2453         leftOffsetX, upOffsetY, rect.Width() + rightOffsetX - leftOffsetX, rect.Height() + downOffsetY - upOffsetY);
2454 }
2455 
ProcessBoundRectByTextMarquee(RectF & rect)2456 void TextPattern::ProcessBoundRectByTextMarquee(RectF& rect)
2457 {
2458     auto host = GetHost();
2459     CHECK_NULL_VOID(host);
2460     auto textLayoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
2461     CHECK_NULL_VOID(textLayoutProperty);
2462     if (!(textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE)) {
2463         return;
2464     }
2465     auto geometryNode = host->GetGeometryNode();
2466     CHECK_NULL_VOID(geometryNode);
2467     auto frameSize = geometryNode->GetFrameSize();
2468     CHECK_NULL_VOID(paragraph_);
2469     if (paragraph_->GetTextWidth() < frameSize.Width()) {
2470         return;
2471     }
2472     auto relativeSelfLeftOffsetX =
2473         std::max(-1 * host->GetOffsetRelativeToWindow().GetX(), rect.GetOffset().GetX() - paragraph_->GetTextWidth());
2474     rect.SetLeft(relativeSelfLeftOffsetX);
2475     rect.SetWidth(frameSize.Width() + paragraph_->GetTextWidth() - relativeSelfLeftOffsetX);
2476 }
2477 
CreateNodePaintMethod()2478 RefPtr<NodePaintMethod> TextPattern::CreateNodePaintMethod()
2479 {
2480     if (!contentMod_) {
2481         contentMod_ = MakeRefPtr<TextContentModifier>(textStyle_);
2482     }
2483     if (!overlayMod_) {
2484         overlayMod_ = MakeRefPtr<TextOverlayModifier>();
2485     }
2486     if (isCustomFont_) {
2487         contentMod_->SetIsCustomFont(true);
2488     }
2489     auto paintMethod = MakeRefPtr<TextPaintMethod>(WeakClaim(this), baselineOffset_, contentMod_, overlayMod_);
2490     auto host = GetHost();
2491     CHECK_NULL_RETURN(host, paintMethod);
2492     auto context = host->GetRenderContext();
2493     CHECK_NULL_RETURN(context, paintMethod);
2494     if (context->GetClipEdge().has_value()) {
2495         auto geometryNode = host->GetGeometryNode();
2496         auto frameSize = geometryNode->GetFrameSize();
2497         CHECK_NULL_RETURN(paragraph_, paintMethod);
2498         RectF boundsRect = overlayMod_->GetBoundsRect();
2499         auto boundsWidth = contentRect_.GetX() + static_cast<float>(paragraph_->GetLongestLine());
2500         auto boundsHeight =
2501             contentRect_.GetY() + static_cast<float>(paragraph_->GetHeight() + std::fabs(baselineOffset_));
2502         boundsRect.SetWidth(boundsWidth);
2503         boundsRect.SetHeight(boundsHeight);
2504         ProcessBoundRectByTextShadow(boundsRect);
2505         ProcessBoundRectByTextMarquee(boundsRect);
2506         if (!context->GetClipEdge().value() && (LessNotEqual(frameSize.Width(), boundsRect.Width()) ||
2507                                                    LessNotEqual(frameSize.Height(), boundsRect.Height()))) {
2508             boundsWidth = std::max(frameSize.Width(), boundsRect.Width());
2509             boundsHeight = std::max(frameSize.Height(), boundsRect.Height());
2510             boundsRect.SetWidth(boundsWidth);
2511             boundsRect.SetHeight(boundsHeight);
2512             overlayMod_->SetBoundsRect(boundsRect);
2513 
2514             auto gestureHub = host->GetOrCreateGestureEventHub();
2515             CHECK_NULL_RETURN(gestureHub, paintMethod);
2516             std::vector<DimensionRect> hotZoneRegions;
2517             DimensionRect hotZoneRegion;
2518             hotZoneRegion.SetSize(DimensionSize(Dimension(boundsWidth), Dimension(boundsHeight)));
2519             hotZoneRegion.SetOffset(
2520                 DimensionOffset(Dimension(contentOffset_.GetX()), Dimension(contentOffset_.GetY())));
2521             hotZoneRegions.emplace_back(hotZoneRegion);
2522             gestureHub->SetResponseRegion(hotZoneRegions);
2523         }
2524     }
2525     return paintMethod;
2526 }
2527 
GetHandleIndex(const Offset & offset) const2528 int32_t TextPattern::GetHandleIndex(const Offset& offset) const
2529 {
2530     return paragraph_->GetGlyphIndexByCoordinate(offset);
2531 }
2532 
OnAreaChangedInner()2533 void TextPattern::OnAreaChangedInner()
2534 {
2535     if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
2536         auto parentGlobalOffset = GetParentGlobalOffset();
2537         if (parentGlobalOffset != parentGlobalOffset_) {
2538             parentGlobalOffset_ = parentGlobalOffset;
2539             CalculateHandleOffsetAndShowOverlay();
2540             ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true);
2541         }
2542     }
2543 }
2544 
RemoveAreaChangeInner()2545 void TextPattern::RemoveAreaChangeInner()
2546 {
2547     auto pipeline = PipelineContext::GetCurrentContext();
2548     CHECK_NULL_VOID(pipeline);
2549     auto host = GetHost();
2550     CHECK_NULL_VOID(host);
2551     auto eventHub = host->GetEventHub<TextEventHub>();
2552     CHECK_NULL_VOID(eventHub);
2553     if (eventHub->HasOnAreaChanged()) {
2554         return;
2555     }
2556     pipeline->RemoveOnAreaChangeNode(host->GetId());
2557 }
2558 
NeedShowAIDetect()2559 bool TextPattern::NeedShowAIDetect()
2560 {
2561     return textDetectEnable_ && copyOption_ != CopyOptions::None && enabled_ &&
2562            !dataDetectorAdapter_->aiSpanMap_.empty();
2563 }
2564 
BindSelectionMenu(TextSpanType spanType,TextResponseType responseType,std::function<void ()> & menuBuilder,std::function<void (int32_t,int32_t)> & onAppear,std::function<void ()> & onDisappear)2565 void TextPattern::BindSelectionMenu(TextSpanType spanType, TextResponseType responseType,
2566     std::function<void()>& menuBuilder, std::function<void(int32_t, int32_t)>& onAppear,
2567     std::function<void()>& onDisappear)
2568 {
2569     auto key = std::make_pair(spanType, responseType);
2570     auto it = selectionMenuMap_.find(key);
2571     if (it != selectionMenuMap_.end()) {
2572         if (menuBuilder == nullptr) {
2573             selectionMenuMap_.erase(it);
2574             return;
2575         }
2576         it->second->buildFunc = menuBuilder;
2577         it->second->onAppear = onAppear;
2578         it->second->onDisappear = onDisappear;
2579         return;
2580     }
2581 
2582     auto selectionMenuParams =
2583         std::make_shared<SelectionMenuParams>(spanType, menuBuilder, onAppear, onDisappear, responseType);
2584     selectionMenuMap_[key] = selectionMenuParams;
2585     auto host = GetHost();
2586     CHECK_NULL_VOID(host);
2587     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
2588 }
2589 
CloseSelectionMenu()2590 void TextPattern::CloseSelectionMenu()
2591 {
2592     textResponseType_ = TextResponseType::NONE;
2593     CloseSelectOverlay(true);
2594 }
2595 
GetMenuParams(TextSpanType spanType,TextResponseType responseType)2596 std::shared_ptr<SelectionMenuParams> TextPattern::GetMenuParams(TextSpanType spanType, TextResponseType responseType)
2597 {
2598     auto key = std::make_pair(spanType, responseType);
2599     auto it = selectionMenuMap_.find(key);
2600     if (it != selectionMenuMap_.end()) {
2601         return it->second;
2602     }
2603 
2604     TAG_LOGD(AceLogTag::ACE_TEXT, "The key not in selectionMenuMap_");
2605     return nullptr;
2606 }
2607 
CopySelectionMenuParams(SelectOverlayInfo & selectInfo,TextResponseType responseType)2608 void TextPattern::CopySelectionMenuParams(SelectOverlayInfo& selectInfo, TextResponseType responseType)
2609 {
2610     auto currentSpanType = selectedType_.value_or(TextSpanType::NONE);
2611     std::shared_ptr<SelectionMenuParams> menuParams = nullptr;
2612     menuParams = GetMenuParams(currentSpanType, responseType);
2613     if (menuParams == nullptr) {
2614         return;
2615     }
2616 
2617     selectInfo.menuInfo.menuIsShow = true;
2618     CopyBindSelectionMenuParams(selectInfo, menuParams);
2619 }
2620 
CopyBindSelectionMenuParams(SelectOverlayInfo & selectInfo,std::shared_ptr<SelectionMenuParams> menuParams)2621 void TextPattern::CopyBindSelectionMenuParams(
2622     SelectOverlayInfo& selectInfo, std::shared_ptr<SelectionMenuParams> menuParams)
2623 {
2624     selectInfo.menuInfo.menuBuilder = menuParams->buildFunc;
2625     if (menuParams->onAppear) {
2626         auto weak = AceType::WeakClaim(this);
2627         auto callback = [weak, menuParams]() {
2628             auto pattern = weak.Upgrade();
2629             CHECK_NULL_VOID(pattern);
2630             CHECK_NULL_VOID(menuParams->onAppear);
2631 
2632             auto& textSelector = pattern->textSelector_;
2633             auto selectStart = std::min(textSelector.baseOffset, textSelector.destinationOffset);
2634             auto selectEnd = std::max(textSelector.baseOffset, textSelector.destinationOffset);
2635             menuParams->onAppear(selectStart, selectEnd);
2636         };
2637         selectInfo.menuCallback.onAppear = std::move(callback);
2638     }
2639     selectInfo.menuCallback.onDisappear = menuParams->onDisappear;
2640 }
2641 
FireOnSelectionChange(int32_t start,int32_t end)2642 void TextPattern::FireOnSelectionChange(int32_t start, int32_t end)
2643 {
2644     auto host = GetHost();
2645     CHECK_NULL_VOID(host);
2646     auto eventHub = host->GetEventHub<TextEventHub>();
2647     CHECK_NULL_VOID(eventHub);
2648     eventHub->FireOnSelectionChange(start, end);
2649 }
2650 
HandleSelectionChange(int32_t start,int32_t end)2651 void TextPattern::HandleSelectionChange(int32_t start, int32_t end)
2652 {
2653     if (textSelector_.GetStart() == start && textSelector_.GetEnd() == end) {
2654         return;
2655     }
2656 
2657     UpdateSelectionSpanType(std::min(start, end), std::max(start, end));
2658     textSelector_.Update(start, end);
2659     FireOnSelectionChange(std::min(start, end), std::max(start, end));
2660 }
2661 
IsSelectedBindSelectionMenu()2662 bool TextPattern::IsSelectedBindSelectionMenu()
2663 {
2664     auto currentSpanType = selectedType_.value_or(TextSpanType::TEXT);
2665     return GetMenuParams(currentSpanType, TextResponseType::SELECTED_BY_MOUSE) != nullptr;
2666 }
2667 
UpdateSelectionSpanType(int32_t selectStart,int32_t selectEnd)2668 void TextPattern::UpdateSelectionSpanType(int32_t selectStart, int32_t selectEnd)
2669 {
2670     UpdateSelectionType(GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT));
2671     if ((selectedType_ == TextSpanType::NONE && !textSelector_.StartEqualToDest()) ||
2672         textSelector_.StartEqualToDest()) {
2673         selectedType_ = TextSpanType::TEXT;
2674     }
2675 }
2676 
UpdateSelectionType(const SelectionInfo & selection)2677 void TextPattern::UpdateSelectionType(const SelectionInfo& selection)
2678 {
2679     selectedType_ = TextSpanType::NONE;
2680     auto list = selection.GetSelection().resultObjects;
2681     bool imageSelected = false;
2682     bool textSelected = false;
2683     for (const auto& obj : list) {
2684         if (obj.type == SelectSpanType::TYPEIMAGE) {
2685             imageSelected = true;
2686         } else if (obj.type == SelectSpanType::TYPESPAN) {
2687             textSelected = true;
2688         }
2689         if (imageSelected && textSelected) {
2690             selectedType_ = TextSpanType::MIXED;
2691             return;
2692         }
2693     }
2694     if (imageSelected) {
2695         selectedType_ = TextSpanType::IMAGE;
2696     } else if (textSelected) {
2697         selectedType_ = TextSpanType::TEXT;
2698     }
2699 
2700     TAG_LOGD(AceLogTag::ACE_TEXT, "UpdateSelectionSpanType, selectedType_: %{public}d", selectedType_.value());
2701 }
2702 
GetSelectionSpanItemIndex(const MouseInfo & info)2703 int32_t TextPattern::GetSelectionSpanItemIndex(const MouseInfo& info)
2704 {
2705     RectF textContentRect = contentRect_;
2706     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
2707     textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
2708     PointF textOffset = { info.GetLocalLocation().GetX() - textContentRect.GetX(),
2709         info.GetLocalLocation().GetY() - textContentRect.GetY() };
2710     if (!textContentRect.IsInRegion(PointF(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY())) ||
2711         spans_.empty() || !paragraph_) {
2712         return -1;
2713     }
2714     int32_t start = 0;
2715     bool isFind = false;
2716     int32_t index = -1;
2717     for (const auto& item : spans_) {
2718         index++;
2719         if (!item) {
2720             continue;
2721         }
2722         std::vector<RectF> selectedRects;
2723         paragraph_->GetRectsForRange(start, item->position, selectedRects);
2724         start = item->position;
2725         for (auto&& rect : selectedRects) {
2726             if (rect.IsInRegion(textOffset)) {
2727                 isFind = true;
2728                 break;
2729             }
2730         }
2731         if (isFind) {
2732             TAG_LOGD(AceLogTag::ACE_TEXT, "GetSelectionSpanItemIndex index: %{public}d", index);
2733             return index;
2734         }
2735     }
2736     return -1;
2737 }
2738 
GetBuilderResultObject(RefPtr<UINode> uiNode,int32_t index,int32_t start,int32_t end)2739 ResultObject TextPattern::GetBuilderResultObject(RefPtr<UINode> uiNode, int32_t index, int32_t start, int32_t end)
2740 {
2741     int32_t itemLength = 1;
2742     ResultObject resultObject;
2743     resultObject.isDraggable = false;
2744     if (!DynamicCast<FrameNode>(uiNode) || !GetSpanItemByIndex(index)) {
2745         return resultObject;
2746     }
2747     int32_t endPosition = std::min(GetTextContentLength(), GetSpanItemByIndex(index)->position);
2748     int32_t startPosition = endPosition - itemLength;
2749     if ((start <= startPosition) && (end >= endPosition)) {
2750         auto builderNode = DynamicCast<FrameNode>(uiNode);
2751         CHECK_NULL_RETURN(builderNode, resultObject);
2752         resultObject.spanPosition.spanIndex = index;
2753         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
2754         resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
2755         resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
2756         resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
2757         resultObject.type = SelectSpanType::TYPEIMAGE;
2758         auto geometryNode = builderNode->GetGeometryNode();
2759         CHECK_NULL_RETURN(geometryNode, resultObject);
2760         resultObject.imageStyle.size[RichEditorImageSize::SIZEWIDTH] = geometryNode->GetMarginFrameSize().Width();
2761         resultObject.imageStyle.size[RichEditorImageSize::SIZEHEIGHT] = geometryNode->GetMarginFrameSize().Height();
2762         resultObject.valueString = " ";
2763     }
2764     return resultObject;
2765 }
2766 } // namespace OHOS::Ace::NG
2767