• 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 <stack>
19 
20 #include "base/geometry/ng/offset_t.h"
21 #include "base/geometry/ng/rect_t.h"
22 #include "base/geometry/offset.h"
23 #include "base/log/dump_log.h"
24 #include "base/utils/utils.h"
25 #include "base/window/drag_window.h"
26 #include "core/common/font_manager.h"
27 #include "core/components_ng/base/ui_node.h"
28 #include "core/components_ng/event/gesture_event_hub.h"
29 #include "core/components_ng/event/long_press_event.h"
30 #include "core/components_ng/manager/select_overlay/select_overlay_manager.h"
31 #include "core/components_ng/pattern/select_overlay/select_overlay_property.h"
32 #include "core/components_ng/pattern/text/text_layout_algorithm.h"
33 #include "core/components_ng/pattern/text/text_layout_property.h"
34 #include "core/components_ng/pattern/text_drag/text_drag_pattern.h"
35 #include "core/components_ng/property/property.h"
36 #include "core/gestures/gesture_info.h"
37 
38 #ifdef ENABLE_DRAG_FRAMEWORK
39 #include "core/common/ace_engine_ext.h"
40 #include "core/common/udmf/udmf_client.h"
41 #endif
42 
43 namespace OHOS::Ace::NG {
44 namespace {
45 constexpr int32_t API_PROTEXTION_GREATER_NINE = 9;
46 // uncertainty range when comparing selectedTextBox to contentRect
47 constexpr float BOX_EPSILON = 0.5f;
48 }; // namespace
49 
OnAttachToFrameNode()50 void TextPattern::OnAttachToFrameNode()
51 {
52     auto pipeline = PipelineContext::GetCurrentContext();
53     CHECK_NULL_VOID_NOLOG(pipeline);
54     auto host = GetHost();
55     CHECK_NULL_VOID_NOLOG(host);
56     if (pipeline->GetMinPlatformVersion() > API_PROTEXTION_GREATER_NINE) {
57         host->GetRenderContext()->UpdateClipEdge(true);
58     }
59     InitSurfaceChangedCallback();
60     InitSurfacePositionChangedCallback();
61     auto theme = pipeline->GetTheme<TextTheme>();
62     CHECK_NULL_VOID_NOLOG(theme);
63     host->SetDraggable(theme->GetDraggable());
64     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
65     textLayoutProperty->UpdateTextAlign(TextAlign::START);
66     textLayoutProperty->UpdateAlignment(Alignment::CENTER_LEFT);
67 }
68 
OnDetachFromFrameNode(FrameNode * node)69 void TextPattern::OnDetachFromFrameNode(FrameNode* node)
70 {
71     CloseSelectOverlay();
72     auto pipeline = PipelineContext::GetCurrentContext();
73     CHECK_NULL_VOID(pipeline);
74     if (HasSurfaceChangedCallback()) {
75         LOGD("Unregister surface change callback with id %{public}d", surfaceChangedCallbackId_.value_or(-1));
76         pipeline->UnregisterSurfaceChangedCallback(surfaceChangedCallbackId_.value_or(-1));
77     }
78     if (HasSurfacePositionChangedCallback()) {
79         LOGD("Unregister surface position change callback with id %{public}d",
80             surfacePositionChangedCallbackId_.value_or(-1));
81         pipeline->UnregisterSurfacePositionChangedCallback(surfacePositionChangedCallbackId_.value_or(-1));
82     }
83     auto frameNode = WeakClaim(node);
84     pipeline->RemoveFontNodeNG(frameNode);
85     auto fontManager = pipeline->GetFontManager();
86     if (fontManager) {
87         fontManager->UnRegisterCallbackNG(frameNode);
88         fontManager->RemoveVariationNodeNG(frameNode);
89     }
90 }
91 
CloseSelectOverlay()92 void TextPattern::CloseSelectOverlay()
93 {
94     CloseSelectOverlay(false);
95 }
96 
CloseSelectOverlay(bool animation)97 void TextPattern::CloseSelectOverlay(bool animation)
98 {
99     if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
100         selectOverlayProxy_->Close(animation);
101     }
102 }
103 
ResetSelection()104 void TextPattern::ResetSelection()
105 {
106     if (textSelector_.IsValid()) {
107         textSelector_.Update(-1, -1);
108         auto host = GetHost();
109         CHECK_NULL_VOID(host);
110         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
111     }
112 }
113 
GetGraphemeClusterLength(int32_t extend) const114 int32_t TextPattern::GetGraphemeClusterLength(int32_t extend) const
115 {
116     auto text = textForDisplay_;
117     char16_t aroundChar = 0;
118 
119     if (static_cast<size_t>(extend) < (text.length())) {
120         aroundChar = text[std::min(static_cast<int32_t>(text.length() - 1), extend)];
121     }
122     return StringUtils::NotInUtf16Bmp(aroundChar) ? 2 : 1;
123 }
124 
InitSelection(const Offset & pos)125 void TextPattern::InitSelection(const Offset& pos)
126 {
127     CHECK_NULL_VOID(paragraph_);
128     int32_t extend = paragraph_->GetHandlePositionForClick(pos);
129     int32_t start = 0;
130     int32_t end = 0;
131     if (!paragraph_->GetWordBoundary(extend, start, end)) {
132         start = extend;
133         end = std::min(
134             static_cast<int32_t>(GetWideText().length()) + imageCount_, extend + GetGraphemeClusterLength(extend));
135     }
136     textSelector_.Update(start, end);
137 }
138 
CalcCursorOffsetByPosition(int32_t position,float & selectLineHeight)139 OffsetF TextPattern::CalcCursorOffsetByPosition(int32_t position, float& selectLineHeight)
140 {
141     auto host = GetHost();
142     CHECK_NULL_RETURN(host, OffsetF(0.0f, 0.0f));
143     auto rect = host->GetGeometryNode()->GetFrameRect();
144     CHECK_NULL_RETURN(paragraph_, OffsetF(0.0f, 0.0f));
145     CaretMetrics metrics;
146     auto computeSuccess = paragraph_->ComputeOffsetForCaretUpstream(position, metrics) ||
147                           paragraph_->ComputeOffsetForCaretDownstream(position, metrics);
148     if (!computeSuccess) {
149         LOGW("Get caret offset failed, set it to text tail");
150         return { rect.Width(), 0.0f };
151     }
152     selectLineHeight = metrics.height;
153     return { static_cast<float>(metrics.offset.GetX()), static_cast<float>(metrics.offset.GetY()) };
154 }
155 
CalculateHandleOffsetAndShowOverlay(bool isUsingMouse)156 void TextPattern::CalculateHandleOffsetAndShowOverlay(bool isUsingMouse)
157 {
158     auto host = GetHost();
159     CHECK_NULL_VOID(host);
160     auto pipeline = host->GetContext();
161     CHECK_NULL_VOID(pipeline);
162     auto rootOffset = pipeline->GetRootRect().GetOffset();
163     auto offset = host->GetPaintRectOffset() + contentRect_.GetOffset();
164     auto textPaintOffset = offset - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
165 
166     // calculate firstHandleOffset, secondHandleOffset and handlePaintSize
167     float startSelectHeight = 0.0f;
168     float endSelectHeight = 0.0f;
169     auto startOffset = CalcCursorOffsetByPosition(textSelector_.baseOffset, startSelectHeight);
170     auto endOffset = CalcCursorOffsetByPosition(textSelector_.destinationOffset, endSelectHeight);
171     OffsetF firstHandleOffset = startOffset + textPaintOffset - rootOffset;
172     OffsetF secondHandleOffset = endOffset + textPaintOffset - rootOffset;
173 
174     textSelector_.selectionBaseOffset = firstHandleOffset;
175     textSelector_.selectionDestinationOffset = secondHandleOffset;
176 
177     RectF firstHandle;
178     firstHandle.SetOffset(firstHandleOffset);
179     firstHandle.SetSize({ SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), startSelectHeight });
180     textSelector_.firstHandle = firstHandle;
181 
182     RectF secondHandle;
183     secondHandle.SetOffset(secondHandleOffset);
184     secondHandle.SetSize({ SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), endSelectHeight });
185     secondHandle.SetHeight(endSelectHeight);
186     textSelector_.secondHandle = secondHandle;
187 }
188 
HandleLongPress(GestureEvent & info)189 void TextPattern::HandleLongPress(GestureEvent& info)
190 {
191     if (copyOption_ == CopyOptions::None || isMousePressed_) {
192         return;
193     }
194     auto host = GetHost();
195     CHECK_NULL_VOID(host);
196     auto hub = host->GetEventHub<EventHub>();
197     CHECK_NULL_VOID(hub);
198     auto gestureHub = hub->GetOrCreateGestureEventHub();
199     CHECK_NULL_VOID(gestureHub);
200     if (IsDraggable(info.GetLocalLocation())) {
201         dragBoxes_ = GetTextBoxes();
202         // prevent long press event from being triggered when dragging
203 #ifdef ENABLE_DRAG_FRAMEWORK
204         gestureHub->SetIsTextDraggable(true);
205 #endif
206         return;
207     }
208 #ifdef ENABLE_DRAG_FRAMEWORK
209     gestureHub->SetIsTextDraggable(false);
210 #endif
211     auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
212     Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
213         info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
214     InitSelection(textOffset);
215     CalculateHandleOffsetAndShowOverlay();
216     CloseSelectOverlay(true);
217     ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true);
218     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
219 }
220 
OnHandleMove(const RectF & handleRect,bool isFirstHandle)221 void TextPattern::OnHandleMove(const RectF& handleRect, bool isFirstHandle)
222 {
223     auto host = GetHost();
224     CHECK_NULL_VOID(host);
225     auto pipeline = host->GetContext();
226     CHECK_NULL_VOID(pipeline);
227     auto rootOffset = pipeline->GetRootRect().GetOffset();
228     auto offset = host->GetPaintRectOffset() + contentRect_.GetOffset() - rootOffset;
229     auto textPaintOffset = offset - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
230 
231     auto localOffsetX = handleRect.GetX();
232     auto localOffsetY = handleRect.GetY();
233 
234     if (localOffsetX < offset.GetX()) {
235         localOffsetX = offset.GetX();
236     } else if (GreatOrEqual(localOffsetX, offset.GetX() + contentRect_.Width())) {
237         localOffsetX = offset.GetX() + contentRect_.Width();
238     }
239 
240     if (localOffsetY < offset.GetY()) {
241         localOffsetY = offset.GetY();
242     } else if (GreatNotEqual(localOffsetY, offset.GetY() + contentRect_.Height())) {
243         localOffsetY = offset.GetY() + contentRect_.Height();
244     }
245 
246     localOffsetX = localOffsetX - textPaintOffset.GetX();
247     localOffsetY = localOffsetY - textPaintOffset.GetY();
248 
249     CHECK_NULL_VOID(paragraph_);
250     // the handle position is calculated based on the middle of the handle height.
251     if (isFirstHandle) {
252         auto start =
253             paragraph_->GetHandlePositionForClick(Offset(localOffsetX, localOffsetY + handleRect.Height() / 2));
254         textSelector_.Update(start, textSelector_.destinationOffset);
255     } else {
256         auto end = paragraph_->GetHandlePositionForClick(Offset(localOffsetX, localOffsetY + handleRect.Height() / 2));
257         textSelector_.Update(textSelector_.baseOffset, end);
258     }
259     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
260 
261     CHECK_NULL_VOID_NOLOG(selectOverlayProxy_);
262     auto start = textSelector_.GetTextStart();
263     auto end = textSelector_.GetTextEnd();
264     selectOverlayProxy_->SetSelectInfo(GetSelectedText(start, end));
265 }
266 
OnHandleMoveDone(const RectF & handleRect,bool isFirstHandle)267 void TextPattern::OnHandleMoveDone(const RectF& handleRect, bool isFirstHandle)
268 {
269     CalculateHandleOffsetAndShowOverlay();
270     if (selectOverlayProxy_) {
271         SelectHandleInfo handleInfo;
272         if (isFirstHandle) {
273             handleInfo.paintRect = textSelector_.firstHandle;
274             selectOverlayProxy_->UpdateFirstSelectHandleInfo(handleInfo);
275         } else {
276             handleInfo.paintRect = textSelector_.secondHandle;
277             selectOverlayProxy_->UpdateSecondSelectHandleInfo(handleInfo);
278         }
279         if (IsSelectAll() && selectMenuInfo_.showCopyAll == true) {
280             selectMenuInfo_.showCopyAll = false;
281             selectOverlayProxy_->UpdateSelectMenuInfo(selectMenuInfo_);
282         } else if (!IsSelectAll() && selectMenuInfo_.showCopyAll == false) {
283             selectMenuInfo_.showCopyAll = true;
284             selectOverlayProxy_->UpdateSelectMenuInfo(selectMenuInfo_);
285         }
286         return;
287     }
288     ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true);
289     auto host = GetHost();
290     CHECK_NULL_VOID(host);
291     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
292 }
293 
IsSelectAll()294 bool TextPattern::IsSelectAll()
295 {
296     return textSelector_.GetTextStart() == 0 &&
297            textSelector_.GetTextEnd() == static_cast<int32_t>(GetWideText().length()) + imageCount_;
298 }
GetWideText() const299 std::wstring TextPattern::GetWideText() const
300 {
301     return StringUtils::ToWstring(textForDisplay_);
302 }
303 
GetSelectedText(int32_t start,int32_t end) const304 std::string TextPattern::GetSelectedText(int32_t start, int32_t end) const
305 {
306     auto wideText = GetWideText();
307     auto min = std::clamp(std::max(std::min(start, end), 0), 0, static_cast<int32_t>(wideText.length()));
308     auto max = std::clamp(std::min(std::max(start, end), static_cast<int32_t>(wideText.length())), 0,
309         static_cast<int32_t>(wideText.length()));
310     return StringUtils::ToString(wideText.substr(min, max - min));
311 }
312 
HandleOnCopy()313 void TextPattern::HandleOnCopy()
314 {
315     CHECK_NULL_VOID(clipboard_);
316     if (textSelector_.IsValid() && textSelector_.GetTextStart() == textSelector_.GetTextEnd()) {
317         textSelector_.Update(-1, -1);
318         LOGW("Nothing to select");
319         return;
320     }
321     auto value = GetSelectedText(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
322     if (value.empty()) {
323         textSelector_.Update(-1, -1);
324         LOGW("Copy value is empty");
325         return;
326     }
327     if (copyOption_ != CopyOptions::None) {
328         clipboard_->SetData(value, copyOption_);
329     }
330     ResetSelection();
331 }
332 
ShowSelectOverlay(const RectF & firstHandle,const RectF & secondHandle)333 void TextPattern::ShowSelectOverlay(const RectF& firstHandle, const RectF& secondHandle)
334 {
335     ShowSelectOverlay(firstHandle, secondHandle, false);
336 }
ShowSelectOverlay(const RectF & firstHandle,const RectF & secondHandle,bool animation)337 void TextPattern::ShowSelectOverlay(const RectF& firstHandle, const RectF& secondHandle, bool animation)
338 {
339     SelectOverlayInfo selectInfo;
340     selectInfo.firstHandle.paintRect = firstHandle;
341     selectInfo.secondHandle.paintRect = secondHandle;
342     CheckHandles(selectInfo.secondHandle);
343     selectInfo.onHandleMove = [weak = WeakClaim(this)](const RectF& handleRect, bool isFirst) {
344         auto pattern = weak.Upgrade();
345         CHECK_NULL_VOID(pattern);
346         pattern->OnHandleMove(handleRect, isFirst);
347     };
348     selectInfo.onHandleMoveDone = [weak = WeakClaim(this)](const RectF& handleRect, bool isFirst) {
349         auto pattern = weak.Upgrade();
350         CHECK_NULL_VOID(pattern);
351         pattern->OnHandleMoveDone(handleRect, isFirst);
352     };
353     selectInfo.menuInfo.menuIsShow = true;
354     selectInfo.menuInfo.showCut = false;
355     selectInfo.menuInfo.showPaste = false;
356     selectInfo.menuCallback.onCopy = [weak = WeakClaim(this)]() {
357         auto pattern = weak.Upgrade();
358         CHECK_NULL_VOID(pattern);
359         pattern->HandleOnCopy();
360     };
361     selectInfo.menuCallback.onSelectAll = [weak = WeakClaim(this)]() {
362         auto pattern = weak.Upgrade();
363         CHECK_NULL_VOID(pattern);
364         pattern->HandleOnSelectAll();
365     };
366     selectInfo.onClose = [weak = WeakClaim(this)](bool closedByGlobalEvent) {
367         auto pattern = weak.Upgrade();
368         CHECK_NULL_VOID(pattern);
369         if (closedByGlobalEvent) {
370             pattern->ResetSelection();
371         }
372     };
373 
374     if (!menuOptionItems_.empty()) {
375         selectInfo.menuOptionItems = GetMenuOptionItems();
376     }
377     selectMenuInfo_ = selectInfo.menuInfo;
378     UpdateSelectOverlayOrCreate(selectInfo, animation);
379 }
380 
HandleOnSelectAll()381 void TextPattern::HandleOnSelectAll()
382 {
383     auto textSize = GetWideText().length() + imageCount_;
384     textSelector_.Update(0, textSize);
385     CalculateHandleOffsetAndShowOverlay();
386     CloseSelectOverlay(true);
387     ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true);
388     selectMenuInfo_.showCopyAll = false;
389     selectOverlayProxy_->UpdateSelectMenuInfo(selectMenuInfo_);
390     auto host = GetHost();
391     CHECK_NULL_VOID(host);
392     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
393 }
394 
CheckHandles(SelectHandleInfo & handleInfo)395 void TextPattern::CheckHandles(SelectHandleInfo& handleInfo)
396 {
397     auto frameNode = GetHost();
398     CHECK_NULL_VOID(frameNode);
399     auto renderContext = frameNode->GetRenderContext();
400     CHECK_NULL_VOID(renderContext);
401     if (renderContext->GetClipEdge().value_or(true) == false) {
402         return;
403     }
404 
405     auto host = GetHost();
406     CHECK_NULL_VOID(host);
407     auto pipeline = host->GetContext();
408     CHECK_NULL_VOID(pipeline);
409     auto offset = host->GetPaintRectOffset() + contentRect_.GetOffset();
410     RectF contentGlobalRect(offset, contentRect_.GetSize());
411     auto handleOffset = handleInfo.paintRect.GetOffset();
412     if (!contentGlobalRect.IsInRegion(PointF(handleOffset.GetX(), handleOffset.GetY() + BOX_EPSILON))) {
413         handleInfo.isShow = false;
414     }
415 }
416 
InitLongPressEvent(const RefPtr<GestureEventHub> & gestureHub)417 void TextPattern::InitLongPressEvent(const RefPtr<GestureEventHub>& gestureHub)
418 {
419     CHECK_NULL_VOID_NOLOG(!longPressEvent_);
420     auto longPressCallback = [weak = WeakClaim(this)](GestureEvent& info) {
421         auto pattern = weak.Upgrade();
422         CHECK_NULL_VOID(pattern);
423         pattern->HandleLongPress(info);
424     };
425     longPressEvent_ = MakeRefPtr<LongPressEvent>(std::move(longPressCallback));
426 
427     constexpr int32_t longPressDelay = 600;
428     // Default time is 500, used by drag event. Drag event would trigger if text is selected, but we want
429     // it to only trigger on the second long press, after selection. Therefore, long press delay of Selection needs to
430     // be slightly longer to ensure that order.
431     gestureHub->SetLongPressEvent(longPressEvent_, false, false, longPressDelay);
432 
433     auto onTextSelectorChange = [weak = WeakClaim(this)]() {
434         auto pattern = weak.Upgrade();
435         CHECK_NULL_VOID(pattern);
436         auto frameNode = pattern->GetHost();
437         CHECK_NULL_VOID(frameNode);
438         frameNode->OnAccessibilityEvent(AccessibilityEventType::TEXT_SELECTION_UPDATE);
439     };
440     textSelector_.SetOnAccessibility(std::move(onTextSelectorChange));
441 }
442 
OnHandleTouchUp()443 void TextPattern::OnHandleTouchUp()
444 {
445     CloseSelectOverlay();
446     ResetSelection();
447 }
448 
HandleClickEvent(GestureEvent & info)449 void TextPattern::HandleClickEvent(GestureEvent& info)
450 {
451     if (textSelector_.IsValid()) {
452         CloseSelectOverlay(true);
453         ResetSelection();
454     }
455 
456     RectF textContentRect = contentRect_;
457     textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
458     textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
459     bool isClickOnSpan = false;
460     if (textContentRect.IsInRegion(PointF(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY())) &&
461         !spanItemChildren_.empty() && paragraph_) {
462         Offset textOffset = { info.GetLocalLocation().GetX() - textContentRect.GetX(),
463             info.GetLocalLocation().GetY() - textContentRect.GetY() };
464         auto position = paragraph_->GetHandlePositionForClick(textOffset);
465         for (const auto& item : spanItemChildren_) {
466             if (item && position < item->position) {
467                 if (!item->onClick) {
468                     break;
469                 }
470                 GestureEvent spanClickinfo = info;
471                 EventTarget target = info.GetTarget();
472                 target.area.SetWidth(Dimension(0.0f));
473                 target.area.SetHeight(Dimension(0.0f));
474                 spanClickinfo.SetTarget(target);
475                 item->onClick(spanClickinfo);
476                 isClickOnSpan = true;
477                 break;
478             }
479         }
480     }
481     if (onClick_ && !isClickOnSpan) {
482         onClick_(info);
483     }
484 }
485 
InitClickEvent(const RefPtr<GestureEventHub> & gestureHub)486 void TextPattern::InitClickEvent(const RefPtr<GestureEventHub>& gestureHub)
487 {
488     CHECK_NULL_VOID_NOLOG(!clickEventInitialized_);
489     auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
490         auto pattern = weak.Upgrade();
491         CHECK_NULL_VOID(pattern);
492         pattern->HandleClickEvent(info);
493     };
494 
495     auto clickListener = MakeRefPtr<ClickEvent>(std::move(clickCallback));
496     gestureHub->AddClickEvent(clickListener);
497     clickEventInitialized_ = true;
498 }
499 
InitMouseEvent()500 void TextPattern::InitMouseEvent()
501 {
502     CHECK_NULL_VOID_NOLOG(!mouseEventInitialized_);
503     auto host = GetHost();
504     CHECK_NULL_VOID(host);
505     auto eventHub = host->GetEventHub<EventHub>();
506     CHECK_NULL_VOID(eventHub);
507     auto inputHub = eventHub->GetOrCreateInputEventHub();
508     CHECK_NULL_VOID(inputHub);
509 
510     auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
511         auto pattern = weak.Upgrade();
512         CHECK_NULL_VOID_NOLOG(pattern);
513         pattern->HandleMouseEvent(info);
514     };
515     auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
516     inputHub->AddOnMouseEvent(mouseEvent);
517     mouseEventInitialized_ = true;
518 }
519 
HandleMouseEvent(const MouseInfo & info)520 void TextPattern::HandleMouseEvent(const MouseInfo& info)
521 {
522     if (copyOption_ == CopyOptions::None) {
523         return;
524     }
525     if (info.GetButton() == MouseButton::RIGHT_BUTTON && info.GetAction() == MouseAction::PRESS) {
526         auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
527         Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
528             info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
529         InitSelection(textOffset);
530         CalculateHandleOffsetAndShowOverlay(true);
531         ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true);
532         auto host = GetHost();
533         CHECK_NULL_VOID(host);
534         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
535         return;
536     }
537     if (info.GetButton() == MouseButton::LEFT_BUTTON) {
538         if (info.GetAction() == MouseAction::PRESS) {
539             isMousePressed_ = true;
540             if (BetweenSelectedPosition(info.GetGlobalLocation())) {
541                 blockPress_ = true;
542                 return;
543             }
544             mouseStatus_ = MouseStatus::PRESSED;
545             auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
546             Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
547                 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
548             CHECK_NULL_VOID(paragraph_);
549             auto start = paragraph_->GetHandlePositionForClick(textOffset);
550             textSelector_.Update(start, start);
551         }
552 
553         if (info.GetAction() == MouseAction::MOVE) {
554             if (blockPress_) {
555                 return;
556             }
557             mouseStatus_ = MouseStatus::MOVE;
558             auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
559             Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
560                 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
561             CHECK_NULL_VOID(paragraph_);
562             auto end = paragraph_->GetHandlePositionForClick(textOffset);
563             textSelector_.Update(textSelector_.baseOffset, end);
564         }
565 
566         if (info.GetAction() == MouseAction::RELEASE) {
567             if (blockPress_) {
568                 blockPress_ = false;
569             }
570             mouseStatus_ = MouseStatus::RELEASED;
571             auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
572             Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
573                 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
574             CHECK_NULL_VOID(paragraph_);
575             auto end = paragraph_->GetHandlePositionForClick(textOffset);
576             textSelector_.Update(textSelector_.baseOffset, end);
577             isMousePressed_ = false;
578         }
579     }
580     auto host = GetHost();
581     CHECK_NULL_VOID(host);
582     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
583 }
584 
InitTouchEvent()585 void TextPattern::InitTouchEvent()
586 {
587     CHECK_NULL_VOID_NOLOG(!touchEventInitialized_);
588     auto host = GetHost();
589     CHECK_NULL_VOID(host);
590     auto gesture = host->GetOrCreateGestureEventHub();
591     CHECK_NULL_VOID(gesture);
592 
593     auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
594         auto pattern = weak.Upgrade();
595         CHECK_NULL_VOID_NOLOG(pattern);
596         pattern->HandleTouchEvent(info);
597     };
598     auto touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
599     gesture->AddTouchEvent(touchListener_);
600     touchEventInitialized_ = true;
601 }
602 
HandleTouchEvent(const TouchEventInfo & info)603 void TextPattern::HandleTouchEvent(const TouchEventInfo& info)
604 {
605     return;
606 }
607 
InitPanEvent(const RefPtr<GestureEventHub> & gestureHub)608 void TextPattern::InitPanEvent(const RefPtr<GestureEventHub>& gestureHub)
609 {
610     CHECK_NULL_VOID_NOLOG(!panEventInitialized_);
611     auto actionStartTask = [weak = WeakClaim(this)](const GestureEvent& info) {
612         auto pattern = weak.Upgrade();
613         CHECK_NULL_VOID_NOLOG(pattern);
614         pattern->HandlePanStart(info);
615     };
616     auto actionUpdateTask = [weak = WeakClaim(this)](const GestureEvent& info) {
617         auto pattern = weak.Upgrade();
618         CHECK_NULL_VOID_NOLOG(pattern);
619         pattern->HandlePanUpdate(info);
620     };
621     auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
622         auto pattern = weak.Upgrade();
623         CHECK_NULL_VOID_NOLOG(pattern);
624         pattern->HandlePanEnd(info);
625     };
626     auto actionCancelTask = [weak = WeakClaim(this)]() {};
627     auto panEvent = MakeRefPtr<PanEvent>(
628         std::move(actionStartTask), std::move(actionUpdateTask), std::move(actionEndTask), std::move(actionCancelTask));
629 
630     PanDirection panDirection;
631     panDirection.type = PanDirection::ALL;
632     gestureHub->AddPanEvent(panEvent, panDirection, DEFAULT_PAN_FINGER, DEFAULT_PAN_DISTANCE);
633     panEventInitialized_ = true;
634 }
635 
HandlePanStart(const GestureEvent & info)636 void TextPattern::HandlePanStart(const GestureEvent& info)
637 {
638     auto host = GetHost();
639     CHECK_NULL_VOID(host);
640     auto offset = info.GetLocalLocation();
641     if (!IsDraggable(offset)) {
642         return;
643     }
644     auto pipelineContext = host->GetContext();
645     CHECK_NULL_VOID(pipelineContext);
646 
647 #if !defined(PREVIEW)
648     if (!dragWindow_) {
649         auto rect = pipelineContext->GetCurrentWindowRect();
650         auto initTextPattern = AceType::Claim(this);
651 
652         // create textdrag window
653         dragWindow_ = DragWindow::CreateTextDragWindow("APP_DRAG_WINDOW",
654             static_cast<int32_t>(host->GetPaintRectOffset().GetX() + rect.Left()),
655             static_cast<int32_t>(host->GetPaintRectOffset().GetY() + rect.Top()),
656             static_cast<int32_t>(contentRect_.Width() + contentRect_.GetX()),
657             contentRect_.Height() + contentRect_.GetY());
658         if (dragWindow_) {
659             dragWindow_->SetOffset(static_cast<int32_t>(host->GetPaintRectOffset().GetX() + rect.Left()),
660                 static_cast<int32_t>(host->GetPaintRectOffset().GetY() + rect.Top()));
661             // draw select text on drag window
662             dragWindow_->DrawTextNG(paragraph_, initTextPattern);
663             // add select data to clipboard
664             auto manager = pipelineContext->GetDragDropManager();
665             CHECK_NULL_VOID(manager);
666             dragDropProxy_ = manager->CreateTextDragDropProxy();
667             CHECK_NULL_VOID(dragDropProxy_);
668             dragDropProxy_->OnTextDragStart(GetSelectedText(textSelector_.GetTextStart(), textSelector_.GetTextEnd()));
669         }
670     }
671 #endif
672 }
673 
IsDraggable(const Offset & offset)674 bool TextPattern::IsDraggable(const Offset& offset)
675 {
676     auto host = GetHost();
677     CHECK_NULL_RETURN(host, false);
678     auto eventHub = host->GetEventHub<EventHub>();
679     bool draggable = eventHub->HasOnDragStart();
680     if (copyOption_ != CopyOptions::None && draggable &&
681         GreatNotEqual(textSelector_.GetTextEnd(), textSelector_.GetTextStart())) {
682         // Determine if the pan location is in the selected area
683         std::vector<Rect> selectedRects;
684         paragraph_->GetRectsForRange(textSelector_.GetTextStart(), textSelector_.GetTextEnd(), selectedRects);
685         auto panOffset = OffsetF(offset.GetX(), offset.GetY()) - contentRect_.GetOffset() +
686                          OffsetF(0.0, std::min(baselineOffset_, 0.0f));
687         for (const auto& selectedRect : selectedRects) {
688             if (selectedRect.IsInRegion(Point(panOffset.GetX(), panOffset.GetY()))) {
689                 return true;
690             }
691         }
692     }
693     return false;
694 }
695 
HandlePanUpdate(const GestureEvent & info)696 void TextPattern::HandlePanUpdate(const GestureEvent& info)
697 {
698     if (dragWindow_) {
699         if (dragWindow_) {
700             dragWindow_->TextDragWindowMove(info.GetOffsetX(), info.GetOffsetY());
701         }
702         return;
703     }
704 }
705 
HandlePanEnd(const GestureEvent & info)706 void TextPattern::HandlePanEnd(const GestureEvent& info)
707 {
708     if (dragWindow_) {
709         dragWindow_->Destroy();
710         dragWindow_ = nullptr;
711         if (dragDropProxy_) {
712             dragDropProxy_->OnDragEnd(info, true);
713         }
714         return;
715     }
716 }
717 
718 #ifdef ENABLE_DRAG_FRAMEWORK
OnDragStart(const RefPtr<Ace::DragEvent> & event,const std::string & extraParams)719 DragDropInfo TextPattern::OnDragStart(const RefPtr<Ace::DragEvent>& event, const std::string& extraParams)
720 {
721     auto host = GetHost();
722     CHECK_NULL_RETURN(host, {});
723     CHECK_NULL_RETURN(dragNodeWk_.Upgrade(), {});
724 
725     DragDropInfo itemInfo;
726     auto selectedStr = GetSelectedText(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
727     itemInfo.extraInfo = selectedStr;
728     RefPtr<UnifiedData> unifiedData = UdmfClient::GetInstance()->CreateUnifiedData();
729     UdmfClient::GetInstance()->AddPlainTextRecord(unifiedData, selectedStr);
730     event->SetData(unifiedData);
731 
732     AceEngineExt::GetInstance().DragStartExt();
733 
734     CloseSelectOverlay();
735     ResetSelection();
736     return itemInfo;
737 }
738 
InitDragEvent()739 void TextPattern::InitDragEvent()
740 {
741     auto host = GetHost();
742     CHECK_NULL_VOID(host);
743     auto eventHub = host->GetEventHub<EventHub>();
744     CHECK_NULL_VOID(eventHub);
745     if (eventHub->HasOnDragStart()) {
746         LOGD("drag event has already been initialized");
747         return;
748     }
749 
750     auto gestureHub = host->GetOrCreateGestureEventHub();
751     gestureHub->InitDragDropEvent();
752     gestureHub->SetTextDraggable(true);
753     gestureHub->SetThumbnailCallback(GetThumbnailCallback());
754     auto onDragStart = [weakPtr = WeakClaim(this)](
755                            const RefPtr<Ace::DragEvent>& event, const std::string& extraParams) -> DragDropInfo {
756         auto pattern = weakPtr.Upgrade();
757         CHECK_NULL_RETURN(pattern, {});
758         return pattern->OnDragStart(event, extraParams);
759     };
760     if (!eventHub->HasOnDragStart()) {
761         eventHub->SetOnDragStart(std::move(onDragStart));
762     }
763 }
764 
GetThumbnailCallback()765 std::function<void(Offset)> TextPattern::GetThumbnailCallback()
766 {
767     return [wk = WeakClaim(this)](const Offset& point) {
768         auto pattern = wk.Upgrade();
769         CHECK_NULL_VOID(pattern);
770         if (pattern->BetweenSelectedPosition(point)) {
771             pattern->dragNode_ = TextDragPattern::CreateDragNode(pattern->GetHost());
772             pattern->dragNodeWk_ = pattern->dragNode_;
773             FrameNode::ProcessOffscreenNode(pattern->dragNode_);
774         }
775     };
776 }
777 #endif // ENABLE_DRAG_FRAMEWORK
778 
779 // ===========================================================
780 // TextDragBase implementations
GetLineHeight() const781 float TextPattern::GetLineHeight() const
782 {
783     std::vector<Rect> selectedRects;
784     paragraph_->GetRectsForRange(textSelector_.GetTextStart(), textSelector_.GetTextEnd(), selectedRects);
785     CHECK_NULL_RETURN(selectedRects.size(), {});
786     return selectedRects.front().Height();
787 }
788 
ConvertRect(const Rect & rect)789 RSTypographyProperties::TextBox TextPattern::ConvertRect(const Rect& rect)
790 {
791     return { RSRect(rect.Left(), rect.Top(), rect.Right(), rect.Bottom()), RSTextDirection::LTR };
792 }
793 
GetTextBoxes()794 std::vector<RSTypographyProperties::TextBox> TextPattern::GetTextBoxes()
795 {
796     std::vector<Rect> selectedRects;
797     paragraph_->GetRectsForRange(textSelector_.GetTextStart(), textSelector_.GetTextEnd(), selectedRects);
798     std::vector<RSTypographyProperties::TextBox> res;
799     res.reserve(selectedRects.size());
800     for (auto&& rect : selectedRects) {
801         res.emplace_back(ConvertRect(rect));
802     }
803     return res;
804 }
805 
GetParentGlobalOffset() const806 OffsetF TextPattern::GetParentGlobalOffset() const
807 {
808     auto host = GetHost();
809     CHECK_NULL_RETURN(host, {});
810     return host->GetPaintRectOffset();
811 }
812 
CreateHandles()813 void TextPattern::CreateHandles()
814 {
815     ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle);
816 }
817 
BetweenSelectedPosition(const Offset & globalOffset)818 bool TextPattern::BetweenSelectedPosition(const Offset& globalOffset)
819 {
820     auto host = GetHost();
821     CHECK_NULL_RETURN(host, false);
822     auto offset = host->GetPaintRectOffset();
823     auto localOffset = globalOffset - Offset(offset.GetX(), offset.GetY());
824     return IsDraggable(localOffset);
825 }
826 
827 // end of TextDragBase implementations
828 // ===========================================================
829 
OnModifyDone()830 void TextPattern::OnModifyDone()
831 {
832     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
833     CHECK_NULL_VOID(textLayoutProperty);
834     auto host = GetHost();
835     CHECK_NULL_VOID(host);
836     auto renderContext = host->GetRenderContext();
837     CHECK_NULL_VOID(renderContext);
838 
839     if (CheckNeedMeasure(textLayoutProperty->GetPropertyChangeFlag())) {
840         // measure flag changed, reset paragraph.
841         LOGD("reset on modify done!");
842         paragraph_.Reset();
843     }
844 
845     if (!(PipelineContext::GetCurrentContext() &&
846             PipelineContext::GetCurrentContext()->GetMinPlatformVersion() > API_PROTEXTION_GREATER_NINE)) {
847         bool shouldClipToContent =
848             textLayoutProperty->GetTextOverflow().value_or(TextOverflow::CLIP) == TextOverflow::CLIP;
849         host->GetRenderContext()->SetClipToFrame(shouldClipToContent);
850     }
851     std::string textCache = textForDisplay_;
852     textForDisplay_ = textLayoutProperty->GetContent().value_or("");
853     if (textCache != textForDisplay_) {
854         host->OnAccessibilityEvent(AccessibilityEventType::TEXT_CHANGE, textCache, textForDisplay_);
855     }
856 
857     auto obscuredReasons = renderContext->GetObscured().value_or(std::vector<ObscuredReasons>());
858     bool ifHaveObscured = std::any_of(obscuredReasons.begin(), obscuredReasons.end(),
859         [](const auto& reason) { return reason == ObscuredReasons::PLACEHOLDER; });
860     if (textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE || ifHaveObscured) {
861         CloseSelectOverlay();
862         ResetSelection();
863         copyOption_ = CopyOptions::None;
864         return;
865     }
866     copyOption_ = textLayoutProperty->GetCopyOption().value_or(CopyOptions::None);
867     if (ignoreEvent_) {
868         return;
869     }
870     auto gestureEventHub = host->GetOrCreateGestureEventHub();
871     CHECK_NULL_VOID(gestureEventHub);
872     if (copyOption_ != CopyOptions::None) {
873         auto context = host->GetContext();
874         CHECK_NULL_VOID(context);
875         if (!clipboard_ && context) {
876             clipboard_ = ClipboardProxy::GetInstance()->GetClipboard(context->GetTaskExecutor());
877         }
878         InitLongPressEvent(gestureEventHub);
879         if (host->IsDraggable()) {
880 #ifdef ENABLE_DRAG_FRAMEWORK
881             InitDragEvent();
882 #else
883             InitPanEvent(gestureEventHub);
884 #endif
885         }
886         InitMouseEvent();
887         InitTouchEvent();
888         SetAccessibilityAction();
889     }
890     if (onClick_ || copyOption_ != CopyOptions::None) {
891         InitClickEvent(gestureEventHub);
892     }
893 }
894 
ActSetSelection(int32_t start,int32_t end)895 void TextPattern::ActSetSelection(int32_t start, int32_t end)
896 {
897     if (start < 0) {
898         start = GetWideText().length();
899     }
900     if (end < 0) {
901         end = GetWideText().length();
902     }
903     textSelector_.Update(start, end);
904     CalculateHandleOffsetAndShowOverlay();
905     ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle);
906     auto host = GetHost();
907     CHECK_NULL_VOID(host);
908     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
909 }
910 
UpdateSelectOverlayOrCreate(SelectOverlayInfo selectInfo,bool animation)911 void TextPattern::UpdateSelectOverlayOrCreate(SelectOverlayInfo selectInfo, bool animation)
912 {
913     if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
914         SelectHandleInfo firstHandleInfo;
915         SelectHandleInfo secondHandleInfo;
916         firstHandleInfo.paintRect = textSelector_.firstHandle;
917         secondHandleInfo.paintRect = textSelector_.secondHandle;
918         auto start = textSelector_.GetTextStart();
919         auto end = textSelector_.GetTextEnd();
920         selectOverlayProxy_->SetSelectInfo(GetSelectedText(start, end));
921         selectOverlayProxy_->UpdateFirstAndSecondHandleInfo(firstHandleInfo, secondHandleInfo);
922     } else {
923         auto pipeline = PipelineContext::GetCurrentContext();
924         CHECK_NULL_VOID(pipeline);
925         selectInfo.callerFrameNode = GetHost();
926         selectOverlayProxy_ =
927             pipeline->GetSelectOverlayManager()->CreateAndShowSelectOverlay(selectInfo, WeakClaim(this), animation);
928         CHECK_NULL_VOID_NOLOG(selectOverlayProxy_);
929         auto start = textSelector_.GetTextStart();
930         auto end = textSelector_.GetTextEnd();
931         selectOverlayProxy_->SetSelectInfo(GetSelectedText(start, end));
932     }
933 }
934 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)935 bool TextPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
936 {
937     if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
938         CalculateHandleOffsetAndShowOverlay();
939         ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle);
940     }
941     if (config.skipMeasure || dirty->SkipMeasureContent()) {
942         return false;
943     }
944     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
945     CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
946     auto textLayoutAlgorithm = DynamicCast<TextLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
947     CHECK_NULL_RETURN(textLayoutAlgorithm, false);
948     auto paragraph = textLayoutAlgorithm->GetParagraph();
949     if (!paragraph) {
950         LOGD("on layout process, just return");
951         return false;
952     }
953     LOGD("on layout process, continue");
954     paragraph_ = textLayoutAlgorithm->GetParagraph();
955     baselineOffset_ = textLayoutAlgorithm->GetBaselineOffset();
956     contentRect_ = dirty->GetGeometryNode()->GetContentRect();
957     contentOffset_ = dirty->GetGeometryNode()->GetContentOffset();
958     textStyle_ = textLayoutAlgorithm->GetTextStyle();
959     return true;
960 }
961 
BeforeCreateLayoutWrapper()962 void TextPattern::BeforeCreateLayoutWrapper()
963 {
964     auto host = GetHost();
965     CHECK_NULL_VOID(host);
966     const auto& layoutProperty = host->GetLayoutProperty();
967     auto flag = layoutProperty ? layoutProperty->GetPropertyChangeFlag() : PROPERTY_UPDATE_NORMAL;
968     // When updating the scenario, needs to determine whether the SpanNode node is refreshed.
969     if (paragraph_ && (flag & PROPERTY_UPDATE_BY_CHILD_REQUEST) != PROPERTY_UPDATE_BY_CHILD_REQUEST) {
970         LOGD("no need to refresh span node");
971         return;
972     }
973     imageCount_ = 0;
974     // When dirty areas are marked because of child node changes, the text rendering node tree is reset.
975     const auto& children = host->GetChildren();
976     if (children.empty()) {
977         return;
978     }
979 
980     if (paragraph_) {
981         LOGD("reset before create layoutwrapper");
982         paragraph_.Reset();
983     }
984     spanItemChildren_.clear();
985 
986     // Depth-first iterates through all host's child nodes to collect the SpanNode object, building a text rendering
987     // tree.
988     std::stack<RefPtr<UINode>> nodes;
989     for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
990         nodes.push(*iter);
991     }
992 
993     std::string textCache;
994     if (!nodes.empty()) {
995         textCache = textForDisplay_;
996         textForDisplay_.clear();
997     }
998 
999     bool isSpanHasClick = false;
1000     CollectSpanNodes(nodes, isSpanHasClick);
1001 
1002     if (textCache != textForDisplay_) {
1003         host->OnAccessibilityEvent(AccessibilityEventType::TEXT_CHANGE, textCache, textForDisplay_);
1004     }
1005     if (isSpanHasClick) {
1006         auto gestureEventHub = host->GetOrCreateGestureEventHub();
1007         InitClickEvent(gestureEventHub);
1008     }
1009 }
1010 
CollectSpanNodes(std::stack<RefPtr<UINode>> nodes,bool & isSpanHasClick)1011 void TextPattern::CollectSpanNodes(std::stack<RefPtr<UINode>> nodes, bool& isSpanHasClick)
1012 {
1013     while (!nodes.empty()) {
1014         auto current = nodes.top();
1015         nodes.pop();
1016         // TODO: Add the judgment of display.
1017         if (!current) {
1018             continue;
1019         }
1020         auto spanNode = DynamicCast<SpanNode>(current);
1021         if (spanNode) {
1022             spanNode->CleanSpanItemChildren();
1023             UpdateChildProperty(spanNode);
1024             spanNode->MountToParagraph();
1025             textForDisplay_.append(spanNode->GetSpanItem()->content);
1026             if (spanNode->GetSpanItem()->onClick) {
1027                 isSpanHasClick = true;
1028             }
1029         } else if (current->GetTag() == V2::IMAGE_ETS_TAG) {
1030             imageCount_++;
1031             AddChildSpanItem(current);
1032         }
1033         const auto& nextChildren = current->GetChildren();
1034         for (auto iter = nextChildren.rbegin(); iter != nextChildren.rend(); ++iter) {
1035             nodes.push(*iter);
1036         }
1037     }
1038 }
1039 
1040 
GetGlobalOffset(Offset & offset)1041 void TextPattern::GetGlobalOffset(Offset& offset)
1042 {
1043     auto host = GetHost();
1044     CHECK_NULL_VOID(host);
1045     auto pipeline = host->GetContext();
1046     CHECK_NULL_VOID(pipeline);
1047     auto rootOffset = pipeline->GetRootRect().GetOffset();
1048     auto globalOffset = host->GetPaintRectOffset() - rootOffset;
1049     offset = Offset(globalOffset.GetX(), globalOffset.GetY());
1050 }
1051 
OnVisibleChange(bool isVisible)1052 void TextPattern::OnVisibleChange(bool isVisible)
1053 {
1054     if (!isVisible) {
1055         if (textSelector_.IsValid()) {
1056             CloseSelectOverlay();
1057             ResetSelection();
1058         }
1059     }
1060 }
1061 
InitSurfaceChangedCallback()1062 void TextPattern::InitSurfaceChangedCallback()
1063 {
1064     auto host = GetHost();
1065     CHECK_NULL_VOID(host);
1066     auto pipeline = host->GetContext();
1067     CHECK_NULL_VOID(pipeline);
1068     if (!HasSurfaceChangedCallback()) {
1069         auto callbackId = pipeline->RegisterSurfaceChangedCallback(
1070             [weak = WeakClaim(this)](int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight,
1071                 WindowSizeChangeReason type) {
1072                 auto pattern = weak.Upgrade();
1073                 if (pattern) {
1074                     pattern->HandleSurfaceChanged(newWidth, newHeight, prevWidth, prevHeight);
1075                 }
1076             });
1077         LOGD("Add surface changed callback id %{public}d", callbackId);
1078         UpdateSurfaceChangedCallbackId(callbackId);
1079     }
1080 }
1081 
HandleSurfaceChanged(int32_t newWidth,int32_t newHeight,int32_t prevWidth,int32_t prevHeight)1082 void TextPattern::HandleSurfaceChanged(int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight)
1083 {
1084     LOGD("TextPattern handle surface change, new width %{public}d, new height %{public}d, prev width %{public}d, prev "
1085          "height %{public}d",
1086         newWidth, newHeight, prevWidth, prevHeight);
1087     CloseSelectOverlay();
1088     ResetSelection();
1089 }
1090 
InitSurfacePositionChangedCallback()1091 void TextPattern::InitSurfacePositionChangedCallback()
1092 {
1093     auto host = GetHost();
1094     CHECK_NULL_VOID(host);
1095     auto pipeline = host->GetContext();
1096     CHECK_NULL_VOID(pipeline);
1097     if (!HasSurfacePositionChangedCallback()) {
1098         auto callbackId =
1099             pipeline->RegisterSurfacePositionChangedCallback([weak = WeakClaim(this)](int32_t posX, int32_t posY) {
1100                 auto pattern = weak.Upgrade();
1101                 if (pattern) {
1102                     pattern->HandleSurfacePositionChanged(posX, posY);
1103                 }
1104             });
1105         LOGI("Add position changed callback id %{public}d", callbackId);
1106         UpdateSurfacePositionChangedCallbackId(callbackId);
1107     }
1108 }
1109 
AddChildSpanItem(const RefPtr<UINode> & child)1110 void TextPattern::AddChildSpanItem(const RefPtr<UINode>& child)
1111 {
1112     CHECK_NULL_VOID(child);
1113     auto chidNode = DynamicCast<FrameNode>(child);
1114     if (chidNode && chidNode->GetLayoutProperty() && chidNode->GetLayoutProperty()->IsOverlayNode()) {
1115         return;
1116     }
1117 
1118     if (child->GetTag() == V2::SPAN_ETS_TAG) {
1119         auto spanNode = DynamicCast<SpanNode>(child);
1120         if (spanNode) {
1121             spanItemChildren_.emplace_back(spanNode->GetSpanItem());
1122         }
1123     } else if (child->GetTag() == V2::IMAGE_ETS_TAG) {
1124         auto imageNode = DynamicCast<FrameNode>(child);
1125         if (imageNode) {
1126             spanItemChildren_.emplace_back(MakeRefPtr<ImageSpanItem>());
1127         }
1128     }
1129 }
1130 
DumpInfo()1131 void TextPattern::DumpInfo()
1132 {
1133     auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
1134     CHECK_NULL_VOID(textLayoutProp);
1135     DumpLog::GetInstance().AddDesc(std::string("Content: ").append(textLayoutProp->GetContent().value_or(" ")));
1136     DumpLog::GetInstance().AddDesc(
1137         std::string("FontColor: ").append(textLayoutProp->GetTextColor().value_or(Color::BLACK).ColorToString()));
1138     DumpLog::GetInstance().AddDesc(
1139         std::string("FontSize: ")
1140             .append(
1141                 (textStyle_.has_value() ? textStyle_->GetFontSize() : Dimension(16.0, DimensionUnit::FP)).ToString()));
1142 }
1143 
UpdateChildProperty(const RefPtr<SpanNode> & child) const1144 void TextPattern::UpdateChildProperty(const RefPtr<SpanNode>& child) const
1145 {
1146     CHECK_NULL_VOID(child);
1147     auto host = GetHost();
1148     CHECK_NULL_VOID(host);
1149     auto textLayoutProp = host->GetLayoutProperty<TextLayoutProperty>();
1150     CHECK_NULL_VOID(textLayoutProp);
1151 
1152     auto inheritPropertyInfo = child->CaculateInheritPropertyInfo();
1153     for (const PropertyInfo& info : inheritPropertyInfo) {
1154         switch (info) {
1155             case PropertyInfo::FONTSIZE:
1156                 if (textLayoutProp->HasFontSize()) {
1157                     child->UpdateFontSizeWithoutFlushDirty(textLayoutProp->GetFontSize().value());
1158                 }
1159                 break;
1160             case PropertyInfo::FONTCOLOR:
1161                 if (textLayoutProp->HasTextColor()) {
1162                     child->UpdateTextColorWithoutFlushDirty(textLayoutProp->GetTextColor().value());
1163                 }
1164                 break;
1165             case PropertyInfo::FONTSTYLE:
1166                 if (textLayoutProp->HasItalicFontStyle()) {
1167                     child->UpdateItalicFontStyleWithoutFlushDirty(textLayoutProp->GetItalicFontStyle().value());
1168                 }
1169                 break;
1170             case PropertyInfo::FONTWEIGHT:
1171                 if (textLayoutProp->HasFontWeight()) {
1172                     child->UpdateFontWeightWithoutFlushDirty(textLayoutProp->GetFontWeight().value());
1173                 }
1174                 break;
1175             case PropertyInfo::FONTFAMILY:
1176                 if (textLayoutProp->HasFontFamily()) {
1177                     child->UpdateFontFamilyWithoutFlushDirty(textLayoutProp->GetFontFamily().value());
1178                 }
1179                 break;
1180             case PropertyInfo::TEXTDECORATION:
1181                 if (textLayoutProp->HasTextDecoration()) {
1182                     child->UpdateTextDecorationWithoutFlushDirty(textLayoutProp->GetTextDecoration().value());
1183                     if (textLayoutProp->HasTextDecorationColor()) {
1184                         child->UpdateTextDecorationColorWithoutFlushDirty(
1185                             textLayoutProp->GetTextDecorationColor().value());
1186                     }
1187                 }
1188                 break;
1189             case PropertyInfo::TEXTCASE:
1190                 if (textLayoutProp->HasTextCase()) {
1191                     child->UpdateTextCaseWithoutFlushDirty(textLayoutProp->GetTextCase().value());
1192                 }
1193                 break;
1194             case PropertyInfo::LETTERSPACE:
1195                 if (textLayoutProp->HasLetterSpacing()) {
1196                     child->UpdateLetterSpacingWithoutFlushDirty(textLayoutProp->GetLetterSpacing().value());
1197                 }
1198                 break;
1199             case PropertyInfo::LINEHEIGHT:
1200                 if (textLayoutProp->HasLineHeight()) {
1201                     child->UpdateLineHeightWithoutFlushDirty(textLayoutProp->GetLineHeight().value());
1202                 }
1203                 break;
1204             default:
1205                 LOGW("Inherited properties are not supported.");
1206                 break;
1207         }
1208     }
1209 }
1210 
SetAccessibilityAction()1211 void TextPattern::SetAccessibilityAction()
1212 {
1213     auto host = GetHost();
1214     CHECK_NULL_VOID(host);
1215     auto textAccessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
1216     CHECK_NULL_VOID(textAccessibilityProperty);
1217     textAccessibilityProperty->SetActionSetSelection([weakPtr = WeakClaim(this)](int32_t start, int32_t end) {
1218         const auto& pattern = weakPtr.Upgrade();
1219         CHECK_NULL_VOID(pattern);
1220         auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
1221         CHECK_NULL_VOID(textLayoutProperty);
1222         if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None) {
1223             pattern->ActSetSelection(start, end);
1224         }
1225     });
1226 
1227     textAccessibilityProperty->SetActionClearSelection([weakPtr = WeakClaim(this)]() {
1228         const auto& pattern = weakPtr.Upgrade();
1229         CHECK_NULL_VOID(pattern);
1230         auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
1231         CHECK_NULL_VOID(textLayoutProperty);
1232         if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None) {
1233             pattern->CloseSelectOverlay(true);
1234             pattern->ResetSelection();
1235         }
1236     });
1237 
1238     textAccessibilityProperty->SetActionCopy([weakPtr = WeakClaim(this)]() {
1239         const auto& pattern = weakPtr.Upgrade();
1240         CHECK_NULL_VOID(pattern);
1241         auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
1242         CHECK_NULL_VOID(textLayoutProperty);
1243         if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None) {
1244             pattern->HandleOnCopy();
1245             pattern->CloseSelectOverlay(true);
1246             pattern->ResetSelection();
1247         }
1248     });
1249 }
1250 
OnColorConfigurationUpdate()1251 void TextPattern::OnColorConfigurationUpdate()
1252 {
1253     auto host = GetHost();
1254     CHECK_NULL_VOID(host);
1255     auto context = host->GetContext();
1256     CHECK_NULL_VOID(context);
1257     auto theme = context->GetTheme<TextTheme>();
1258     CHECK_NULL_VOID(theme);
1259     auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1260     CHECK_NULL_VOID(textLayoutProperty);
1261     textLayoutProperty->UpdateTextColor(theme->GetTextStyle().GetTextColor());
1262 }
1263 
GetDragUpperLeftCoordinates()1264 OffsetF TextPattern::GetDragUpperLeftCoordinates()
1265 {
1266     if (dragBoxes_.empty()) {
1267         return { 0.0f, 0.0f };
1268     }
1269     auto startY = dragBoxes_.front().rect_.GetTop();
1270     auto startX = dragBoxes_.front().rect_.GetLeft();
1271     auto endY = dragBoxes_.back().rect_.GetTop();
1272 
1273     OffsetF offset;
1274     if (NearEqual(startY, endY)) {
1275         offset = { contentRect_.GetX() + startX, startY + contentRect_.GetY() };
1276     } else {
1277         offset = { contentRect_.GetX(), startY + contentRect_.GetY() };
1278     }
1279 
1280     return GetParentGlobalOffset() + offset;
1281 }
1282 } // namespace OHOS::Ace::NG
1283