• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/text/text_select_overlay.h"
17 
18 #include "core/components_ng/pattern/select_overlay/select_overlay_property.h"
19 #include "core/components_ng/pattern/text/text_pattern.h"
20 
21 namespace OHOS::Ace::NG {
22 namespace {
23 constexpr float BOX_EPSILON = 0.5f;
24 }
25 
PreProcessOverlay(const OverlayRequest & request)26 bool TextSelectOverlay::PreProcessOverlay(const OverlayRequest& request)
27 {
28     auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
29     CHECK_NULL_RETURN(pipeline, false);
30     auto textPattern = GetPattern<TextPattern>();
31     CHECK_NULL_RETURN(textPattern, false);
32     SetUsingMouse(textPattern->IsUsingMouse());
33     SetEnableHandleLevel(true);
34     SetEnableSubWindowMenu(true);
35     textPattern->CalculateHandleOffsetAndShowOverlay();
36     selectTextUseTopHandle = true;
37     CheckEnableContainerModal();
38     return true;
39 }
40 
GetFirstHandleInfo()41 std::optional<SelectHandleInfo> TextSelectOverlay::GetFirstHandleInfo()
42 {
43     auto textPattern = GetPattern<TextPattern>();
44     CHECK_NULL_RETURN(textPattern, std::nullopt);
45     SelectHandleInfo handleInfo;
46     handleInfo.paintRect = textPattern->GetTextSelector().firstHandle;
47     handleInfo.isShow = CheckAndAdjustHandle(handleInfo.paintRect);
48 
49     auto localPaintRect = textPattern->GetTextSelector().firstHandle;
50     localPaintRect.SetOffset(localPaintRect.GetOffset() - GetPaintOffsetWithoutTransform());
51     handleInfo.localPaintRect = localPaintRect;
52     SetTransformPaintInfo(handleInfo, localPaintRect);
53     handleInfo.forceDraw = !CheckSwitchToMode(HandleLevelMode::OVERLAY);
54     return handleInfo;
55 }
56 
GetSecondHandleInfo()57 std::optional<SelectHandleInfo> TextSelectOverlay::GetSecondHandleInfo()
58 {
59     auto textPattern = GetPattern<TextPattern>();
60     CHECK_NULL_RETURN(textPattern, std::nullopt);
61     SelectHandleInfo handleInfo;
62     handleInfo.paintRect = textPattern->GetTextSelector().secondHandle;
63     handleInfo.isShow = CheckAndAdjustHandle(handleInfo.paintRect);
64 
65     auto localPaintRect = textPattern->GetTextSelector().secondHandle;
66     localPaintRect.SetOffset(localPaintRect.GetOffset() - GetPaintOffsetWithoutTransform());
67     handleInfo.localPaintRect = localPaintRect;
68     SetTransformPaintInfo(handleInfo, localPaintRect);
69     handleInfo.forceDraw = !CheckSwitchToMode(HandleLevelMode::OVERLAY);
70     return handleInfo;
71 }
72 
GetHandleLocalPaintRect(DragHandleIndex dragHandleIndex)73 RectF TextSelectOverlay::GetHandleLocalPaintRect(DragHandleIndex dragHandleIndex)
74 {
75     auto textPattern = GetPattern<TextPattern>();
76     CHECK_NULL_RETURN(textPattern, RectF());
77     RectF localPaintRect;
78     if (dragHandleIndex == DragHandleIndex::FIRST) {
79         localPaintRect = textPattern->GetTextSelector().firstHandle;
80     } else if (dragHandleIndex == DragHandleIndex::SECOND) {
81         localPaintRect = textPattern->GetTextSelector().secondHandle;
82     } else { // dragHandleIndex::NONE
83         return RectF();
84     }
85     localPaintRect.SetOffset(localPaintRect.GetOffset() - GetPaintOffsetWithoutTransform());
86     return localPaintRect;
87 }
88 
CheckAndAdjustHandle(RectF & paintRect)89 bool TextSelectOverlay::CheckAndAdjustHandle(RectF& paintRect)
90 {
91     auto textPattern = GetPattern<TextPattern>();
92     CHECK_NULL_RETURN(textPattern, false);
93     auto host = textPattern->GetHost();
94     CHECK_NULL_RETURN(host, false);
95     if (!GetRenderClipValue()) {
96         if (handleLevelMode_ == HandleLevelMode::EMBED) {
97             return true;
98         }
99         auto contentRect = textPattern->GetTextContentRect();
100         auto localPaintRect = paintRect;
101         localPaintRect.SetOffset(localPaintRect.GetOffset() - GetPaintOffsetWithoutTransform());
102         localPaintRect.SetOffset(OffsetF(localPaintRect.GetX() + localPaintRect.Width() / 2.0f, localPaintRect.GetY()));
103         auto visibleContentRect = contentRect.CombineRectT(localPaintRect);
104         visibleContentRect.SetOffset(visibleContentRect.GetOffset() + textPattern->GetTextPaintOffset());
105         visibleContentRect = GetVisibleRect(host, visibleContentRect);
106         return CheckAndAdjustHandleWithContent(visibleContentRect, paintRect);
107     }
108     auto contentRect = textPattern->GetTextContentRect();
109     RectF visibleContentRect(contentRect.GetOffset() + textPattern->GetTextPaintOffset(), contentRect.GetSize());
110     if (handleLevelMode_ == HandleLevelMode::OVERLAY) {
111         visibleContentRect = GetVisibleRect(host, visibleContentRect);
112     }
113     return CheckAndAdjustHandleWithContent(visibleContentRect, paintRect);
114 }
115 
CheckAndAdjustHandleWithContent(const RectF & visibleContentRect,RectF & paintRect)116 bool TextSelectOverlay::CheckAndAdjustHandleWithContent(const RectF& visibleContentRect, RectF& paintRect)
117 {
118     auto paintLeft = paintRect.GetX() + paintRect.Width() / 2.0f;
119     PointF bottomPoint = { paintLeft, paintRect.Bottom() - BOX_EPSILON };
120     PointF topPoint = { paintLeft, paintRect.Top() + BOX_EPSILON };
121     bool bottomInRegion = visibleContentRect.IsInRegion(bottomPoint);
122     bool topInRegion = visibleContentRect.IsInRegion(topPoint);
123     if (IsClipHandleWithViewPort()) {
124         return bottomInRegion || topInRegion;
125     }
126     if (!bottomInRegion && topInRegion) {
127         paintRect.SetHeight(visibleContentRect.Bottom() - paintRect.Top());
128     } else if (bottomInRegion && !topInRegion) {
129         paintRect.SetHeight(paintRect.Bottom() - visibleContentRect.Top());
130         paintRect.SetTop(visibleContentRect.Top());
131     }
132     return bottomInRegion || topInRegion;
133 }
134 
CheckHandleVisible(const RectF & paintRect)135 bool TextSelectOverlay::CheckHandleVisible(const RectF& paintRect)
136 {
137     auto textPattern = GetPattern<TextPattern>();
138     CHECK_NULL_RETURN(textPattern, false);
139     auto host = textPattern->GetHost();
140     CHECK_NULL_RETURN(host, false);
141     if (!GetRenderClipValue()) {
142         return true;
143     }
144     auto contentRect = textPattern->GetTextContentRect();
145     RectF visibleContentRect(contentRect.GetOffset() + textPattern->GetTextPaintOffset(), contentRect.GetSize());
146     visibleContentRect = GetVisibleRect(host, visibleContentRect);
147     PointF bottomPoint = { paintRect.Left(), paintRect.Bottom() - BOX_EPSILON };
148     PointF topPoint = { paintRect.Left(), paintRect.Top() + BOX_EPSILON };
149     return visibleContentRect.IsInRegion(bottomPoint) && visibleContentRect.IsInRegion(topPoint);
150 }
151 
OnResetTextSelection()152 void TextSelectOverlay::OnResetTextSelection()
153 {
154     auto textPattern = GetPattern<TextPattern>();
155     CHECK_NULL_VOID(textPattern);
156     textPattern->ResetSelection();
157 }
158 
OnHandleMove(const RectF & handleRect,bool isFirst)159 void TextSelectOverlay::OnHandleMove(const RectF& handleRect, bool isFirst)
160 {
161     if (!SelectOverlayIsOn()) {
162         return;
163     }
164     auto textPattern = GetPattern<TextPattern>();
165     CHECK_NULL_VOID(textPattern);
166     auto host = textPattern->GetHost();
167     CHECK_NULL_VOID(host);
168     auto localHandleOffset = handleRect.GetOffset();
169     if (IsOverlayMode()) {
170         localHandleOffset -= GetPaintOffsetWithoutTransform();
171     }
172     localHandleOffset.SetY(localHandleOffset.GetY() + handleRect.Height() / 2.0f);
173     if (textPattern->HasContent() && textPattern->GetOrCreateMagnifier()) {
174         textPattern->GetMagnifierController()->SetLocalOffset(localHandleOffset);
175     }
176     // the handle position is calculated based on the middle of the handle height.
177     auto handleOffset = GetHandleReferenceOffset(handleRect);
178     UpdateSelectorOnHandleMove(handleOffset, isFirst);
179     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
180     auto overlayManager = GetManager<SelectContentOverlayManager>();
181     CHECK_NULL_VOID(overlayManager);
182     overlayManager->MarkInfoChange(DIRTY_SELECT_TEXT);
183 }
184 
GetHandleReferenceOffset(const RectF & handleRect)185 OffsetF TextSelectOverlay::GetHandleReferenceOffset(const RectF& handleRect)
186 {
187     auto handleOffset = handleRect.GetOffset();
188     handleOffset.SetY(handleOffset.GetY() + handleRect.Height() / 2.0f);
189     auto textPattern = GetPattern<TextPattern>();
190     CHECK_NULL_RETURN(textPattern, handleOffset);
191     auto contentRect = textPattern->GetTextContentRect();
192     auto contentOffset = contentRect.GetOffset();
193     if (IsOverlayMode()) {
194         contentOffset = contentOffset + GetPaintOffsetWithoutTransform();
195     }
196     if (GetRenderClipValue()) {
197         handleOffset.SetX(
198             std::clamp(handleOffset.GetX(), contentOffset.GetX(), contentOffset.GetX() + contentRect.Width()));
199         handleOffset.SetY(
200             std::clamp(handleOffset.GetY(), contentOffset.GetY(), contentOffset.GetY() + contentRect.Height()));
201     }
202     auto textPaintOffset = contentOffset - OffsetF(0.0f, std::min(textPattern->GetBaselineOffset(), 0.0f));
203     handleOffset -= textPaintOffset;
204     return handleOffset;
205 }
206 
UpdateSelectorOnHandleMove(const OffsetF & handleOffset,bool isFirstHandle)207 void TextSelectOverlay::UpdateSelectorOnHandleMove(const OffsetF& handleOffset, bool isFirstHandle)
208 {
209     auto textPattern = GetPattern<TextPattern>();
210     CHECK_NULL_VOID(textPattern);
211     auto currentHandleIndex = textPattern->GetHandleIndex(Offset(handleOffset.GetX(), handleOffset.GetY()));
212     if (isFirstHandle) {
213         textPattern->StartVibratorByIndexChange(currentHandleIndex, textPattern->GetTextSelector().baseOffset);
214         textPattern->HandleSelectionChange(currentHandleIndex, textPattern->GetTextSelector().destinationOffset);
215     } else {
216         textPattern->StartVibratorByIndexChange(currentHandleIndex, textPattern->GetTextSelector().destinationOffset);
217         textPattern->HandleSelectionChange(textPattern->GetTextSelector().baseOffset, currentHandleIndex);
218     }
219 }
220 
OnHandleMoveDone(const RectF & rect,bool isFirst)221 void TextSelectOverlay::OnHandleMoveDone(const RectF& rect, bool isFirst)
222 {
223     BaseTextSelectOverlay::OnHandleMoveDone(rect, isFirst);
224     auto textPattern = GetPattern<TextPattern>();
225     CHECK_NULL_VOID(textPattern);
226     if (textPattern->GetMagnifierController()) {
227         textPattern->GetMagnifierController()->RemoveMagnifierFrameNode();
228     }
229     auto textSelector = textPattern->GetTextSelector();
230     textPattern->UpdateSelectionSpanType(textSelector.GetTextStart(), textSelector.GetTextEnd());
231     textPattern->CalculateHandleOffsetAndShowOverlay();
232     auto overlayManager = GetManager<SelectContentOverlayManager>();
233     CHECK_NULL_VOID(overlayManager);
234     if (!textPattern->IsSelectedTypeChange()) {
235         overlayManager->ShowOptionMenu();
236     }
237     textPattern->UpdateAIMenuOptions();
238     overlayManager->MarkInfoChange((isFirst ? DIRTY_FIRST_HANDLE : DIRTY_SECOND_HANDLE) | DIRTY_SELECT_AREA |
239                                    DIRTY_SELECT_TEXT | DIRTY_COPY_ALL_ITEM | DIRTY_AI_MENU_ITEM | DIRTY_ASK_CELIA);
240     if (textPattern->CheckSelectedTypeChange()) {
241         CloseOverlay(false, CloseReason::CLOSE_REASON_NORMAL);
242         ProcessOverlay({ .animation = true });
243     }
244     overlayManager->SetHandleCircleIsShow(isFirst, true);
245     if (textPattern->GetTextSelector().SelectNothing()) {
246         TAG_LOGI(AceLogTag::ACE_TEXT, "Close the selectoverlay when nothing is selected.");
247         CloseOverlay(false, CloseReason::CLOSE_REASON_NORMAL);
248     }
249     auto host = textPattern->GetHost();
250     CHECK_NULL_VOID(host);
251     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
252 }
253 
GetSelectedText()254 std::string TextSelectOverlay::GetSelectedText()
255 {
256     auto textPattern = GetPattern<TextPattern>();
257     CHECK_NULL_RETURN(textPattern, "");
258     auto start = textPattern->GetTextSelector().GetTextStart();
259     auto end = textPattern->GetTextSelector().GetTextEnd();
260     return UtfUtils::Str16DebugToStr8(textPattern->GetSelectedText(start, end));
261 }
262 
GetSelectAreaFromRects(SelectRectsType pos)263 RectF TextSelectOverlay::GetSelectAreaFromRects(SelectRectsType pos)
264 {
265     auto pattern = GetPattern<TextPattern>();
266     RectF res;
267     CHECK_NULL_RETURN(pattern, res);
268     auto selectRects = pattern->GetTextBoxes();
269     auto textPaintOffset = GetPaintOffsetWithoutTransform();
270     if (selectRects.empty()) {
271         res.SetOffset(res.GetOffset() + textPaintOffset);
272         GetSelectAreaFromHandle(res);
273         ApplySelectAreaWithKeyboard(res);
274         return res;
275     }
276     if (pos == SelectRectsType::LEFT_TOP_POINT) {
277         selectRects.erase(std::next(selectRects.begin()), selectRects.end());
278         selectRects.front().SetSize({0, 0});
279     } else if (pos == SelectRectsType::RIGHT_BOTTOM_POINT) {
280         selectRects.erase(selectRects.begin(), std::prev(selectRects.end()));
281         selectRects.front().SetRect({selectRects.front().Right(), selectRects.front().Bottom()}, {0, 0});
282     }
283     auto contentRect = pattern->GetTextContentRect();
284     auto textRect = pattern->GetTextRect();
285     auto textContentRect = pattern->GetTextContentRect(true);
286     res = MergeSelectedBoxes(selectRects, contentRect, textRect, textPaintOffset);
287     RectF visibleContentRect;
288     if (GetRenderClipValue() || LessOrEqual(textContentRect.Height(), contentRect.Height()) ||
289         !GetClipHandleViewPort(visibleContentRect)) {
290         visibleContentRect = RectF(contentRect.GetOffset() + textPaintOffset, contentRect.GetSize());
291         visibleContentRect = GetVisibleRect(pattern->GetHost(), visibleContentRect);
292     }
293     auto intersectRect = res.IntersectRectT(visibleContentRect);
294     intersectRect.SetWidth(std::max(intersectRect.Width(), 0.0f));
295     intersectRect.SetHeight(std::max(intersectRect.Height(), 0.0f));
296     if (hasTransform_) {
297         intersectRect.SetOffset(intersectRect.GetOffset() - textPaintOffset);
298         GetGlobalRectWithTransform(intersectRect);
299     }
300     ApplySelectAreaWithKeyboard(intersectRect);
301     return intersectRect;
302 }
303 
GetSelectAreaFromHandle(RectF & rect)304 void TextSelectOverlay::GetSelectAreaFromHandle(RectF& rect)
305 {
306     auto firstHandle = GetFirstHandleInfo();
307     if (firstHandle) {
308         auto firstRect = firstHandle->paintRect;
309         if (hasTransform_) {
310             firstRect.SetOffset(firstRect.GetOffset() - GetPaintOffsetWithoutTransform());
311             GetGlobalRectWithTransform(firstRect);
312         }
313         rect = firstRect;
314         return;
315     }
316     auto secondHandle = GetSecondHandleInfo();
317     if (secondHandle) {
318         auto secondRect = secondHandle->paintRect;
319         if (hasTransform_) {
320             secondRect.SetOffset(secondRect.GetOffset() - GetPaintOffsetWithoutTransform());
321             GetGlobalRectWithTransform(secondRect);
322         }
323         rect = secondRect;
324     }
325 }
326 
OnUpdateMenuInfo(SelectMenuInfo & menuInfo,SelectOverlayDirtyFlag dirtyFlag)327 void TextSelectOverlay::OnUpdateMenuInfo(SelectMenuInfo& menuInfo, SelectOverlayDirtyFlag dirtyFlag)
328 {
329     auto textPattern = GetPattern<TextPattern>();
330     CHECK_NULL_VOID(textPattern);
331     menuInfo.showCopyAll = !textPattern->IsSelectAll();
332     menuInfo.showCopy = !textPattern->GetTextSelector().SelectNothing();
333     menuInfo.showTranslate = menuInfo.showCopy && textPattern->IsShowTranslate() && IsNeedMenuTranslate();
334     menuInfo.showSearch = menuInfo.showCopy && textPattern->IsShowSearch() && IsNeedMenuSearch();
335     menuInfo.showShare = menuInfo.showCopy && IsSupportMenuShare() && IsNeedMenuShare();
336     if (textPattern->IsShowAIMenuOption()) {
337         // do not support two selected ai entity, hence it's enough to pick first item to determine type
338         auto firstSpanItem = textPattern->GetAIItemOption().begin()->second;
339         menuInfo.aiMenuOptionType = firstSpanItem.type;
340     } else {
341         menuInfo.aiMenuOptionType = TextDataDetectType::INVALID;
342     }
343     menuInfo.isAskCeliaEnabled = textPattern->IsAskCeliaEnabled();
344     menuInfo.menuIsShow = IsShowMenu();
345     menuInfo.showCut = false;
346     menuInfo.showPaste = false;
347     menuInfo.hasOnPrepareMenuCallback = onPrepareMenuCallback_ ? true : false;
348 }
349 
OnUpdateSelectOverlayInfo(SelectOverlayInfo & overlayInfo,int32_t requestCode)350 void TextSelectOverlay::OnUpdateSelectOverlayInfo(SelectOverlayInfo& overlayInfo, int32_t requestCode)
351 {
352     overlayInfo.clipHandleDrawRect = IsClipHandleWithViewPort();
353     BaseTextSelectOverlay::OnUpdateSelectOverlayInfo(overlayInfo, requestCode);
354     auto textPattern = GetPattern<TextPattern>();
355     CHECK_NULL_VOID(textPattern);
356     textPattern->CopySelectionMenuParams(overlayInfo);
357     auto layoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
358     CHECK_NULL_VOID(layoutProperty);
359     overlayInfo.handlerColor = layoutProperty->GetCursorColor();
360     OnUpdateOnCreateMenuCallback(overlayInfo);
361     auto scrollableParent = FindScrollableParent();
362     if (scrollableParent) {
363         auto weakParent = WeakClaim(AceType::RawPtr(scrollableParent));
364         overlayInfo.onHandlePanMove = [weak = WeakClaim(this), weakParent](const GestureEvent& event, bool isFirst) {
365             auto overlay = weak.Upgrade();
366             CHECK_NULL_VOID(overlay);
367             overlay->TriggerScrollableParentToScroll(weakParent.Upgrade(), event.GetGlobalLocation(), false);
368         };
369         overlayInfo.onHandlePanEnd = [weak = WeakClaim(this), weakParent](const GestureEvent& event, bool isFirst) {
370             auto overlay = weak.Upgrade();
371             CHECK_NULL_VOID(overlay);
372             overlay->TriggerScrollableParentToScroll(weakParent.Upgrade(), event.GetGlobalLocation(), true);
373         };
374         overlayInfo.getDeltaHandleOffset = [weak = WeakClaim(this)]() {
375             auto overlay = weak.Upgrade();
376             CHECK_NULL_RETURN(overlay, OffsetF());
377             auto hostPaintOffset = overlay->GetHotPaintOffset();
378             auto deltaOffset = overlay->hostPaintOffset_ - hostPaintOffset;
379             overlay->hostPaintOffset_ = hostPaintOffset;
380             return deltaOffset;
381         };
382     }
383     overlayInfo.menuCallback.showMenuOnMoveDone = [weak = WeakClaim(this)]() {
384         auto overlay = weak.Upgrade();
385         CHECK_NULL_RETURN(overlay, true);
386         auto textPattern = overlay->GetPattern<TextPattern>();
387         CHECK_NULL_RETURN(textPattern, true);
388         return !textPattern->IsSelectedTypeChange();
389     };
390 }
391 
OnMenuItemAction(OptionMenuActionId id,OptionMenuType type)392 void TextSelectOverlay::OnMenuItemAction(OptionMenuActionId id, OptionMenuType type)
393 {
394     auto textPattern = GetPattern<TextPattern>();
395     CHECK_NULL_VOID(textPattern);
396     switch (id) {
397         case OptionMenuActionId::COPY:
398             textPattern->HandleOnCopy();
399             break;
400         case OptionMenuActionId::SELECT_ALL:
401             textPattern->HandleOnSelectAll();
402             break;
403         case OptionMenuActionId::TRANSLATE:
404             HandleOnTranslate();
405             break;
406         case OptionMenuActionId::SEARCH:
407             HandleOnSearch();
408             break;
409         case OptionMenuActionId::SHARE:
410             HandleOnShare();
411             break;
412         case OptionMenuActionId::ASK_CELIA:
413             textPattern->HandleOnAskCelia();
414             break;
415         default:
416             TAG_LOGI(AceLogTag::ACE_TEXT, "Unsupported menu option id %{public}d", id);
417             break;
418     }
419 }
420 
OnMenuItemAction(OptionMenuActionId id,OptionMenuType type,const std::string & labelInfo)421 void TextSelectOverlay::OnMenuItemAction(OptionMenuActionId id, OptionMenuType type, const std::string& labelInfo)
422 {
423     auto textPattern = GetPattern<TextPattern>();
424     CHECK_NULL_VOID(textPattern);
425     if (labelInfo == "") {
426         OnMenuItemAction(id, type);
427         return;
428     }
429     switch (id) {
430         case OptionMenuActionId::AI_MENU_OPTION:
431             textPattern->HandleAIMenuOption(labelInfo);
432             break;
433         default:
434             TAG_LOGI(AceLogTag::ACE_TEXT, "Unsupported menu option id %{public}d", id);
435             break;
436     }
437 }
438 
IsAIMenuOptionChanged(SelectMenuInfo & menuInfo)439 void TextSelectOverlay::IsAIMenuOptionChanged(SelectMenuInfo& menuInfo)
440 {
441     auto textPattern = GetPattern<TextPattern>();
442     CHECK_NULL_VOID(textPattern);
443 
444     auto oldIsShowAIMenuOption = textPattern->IsShowAIMenuOption();
445     auto oldIsShowAskCelia = textPattern->IsAskCeliaEnabled();
446     textPattern->UpdateAIMenuOptions();
447     menuInfo.isShowAIMenuOptionChanged =
448         oldIsShowAIMenuOption != textPattern->IsShowAIMenuOption() ||
449         oldIsShowAskCelia != textPattern->IsAskCeliaEnabled();
450 
451     if (textPattern->IsShowAIMenuOption()) {
452         // do not support two selected ai entity, hence it's enough to pick first item to determine type
453         auto firstSpanItem = textPattern->GetAIItemOption().begin()->second; // null check
454         menuInfo.aiMenuOptionType = firstSpanItem.type;
455     } else {
456         menuInfo.aiMenuOptionType = TextDataDetectType::INVALID;
457     }
458     menuInfo.isAskCeliaEnabled = textPattern->IsAskCeliaEnabled();
459 }
460 
OnCloseOverlay(OptionMenuType menuType,CloseReason reason,RefPtr<OverlayInfo> info)461 void TextSelectOverlay::OnCloseOverlay(OptionMenuType menuType, CloseReason reason, RefPtr<OverlayInfo> info)
462 {
463     auto isDragging = GetIsHandleDragging();
464     if (isDragging) {
465         TriggerScrollableParentToScroll(FindScrollableParent(), Offset(), true);
466     }
467     BaseTextSelectOverlay::OnCloseOverlay(menuType, reason, info);
468     auto textPattern = GetPattern<TextPattern>();
469     CHECK_NULL_VOID(textPattern);
470     if (reason == CloseReason::CLOSE_REASON_HOLD_BY_OTHER || reason == CloseReason::CLOSE_REASON_TOOL_BAR ||
471         reason == CloseReason::CLOSE_REASON_BACK_PRESSED) {
472         textPattern->ResetSelection();
473     }
474     if (textPattern->GetMagnifierController()) {
475         textPattern->GetMagnifierController()->RemoveMagnifierFrameNode();
476     }
477 }
478 
OnHandleGlobalTouchEvent(SourceType sourceType,TouchType touchType,bool touchInside)479 void TextSelectOverlay::OnHandleGlobalTouchEvent(SourceType sourceType, TouchType touchType, bool touchInside)
480 {
481     auto textPattern = GetPattern<TextPattern>();
482     CHECK_NULL_VOID(textPattern);
483     if (IsMouseClickDown(sourceType, touchType) && !touchInside) {
484         textPattern->ResetSelection();
485     }
486     BaseTextSelectOverlay::OnHandleGlobalTouchEvent(sourceType, touchType);
487 }
488 
OnAncestorNodeChanged(FrameNodeChangeInfoFlag flag)489 void TextSelectOverlay::OnAncestorNodeChanged(FrameNodeChangeInfoFlag flag)
490 {
491     auto isDragging = GetIsHandleDragging();
492     if (IsAncestorNodeGeometryChange(flag)) {
493         auto textPattern = GetPattern<TextPattern>();
494         CHECK_NULL_VOID(textPattern);
495         textPattern->UpdateParentGlobalOffset();
496         textPattern->CalculateHandleOffsetAndShowOverlay();
497         UpdateViewPort();
498         if (isDragging && isDraggingFirstHandle_) {
499             UpdateSecondHandleOffset();
500             return;
501         }
502         if (isDragging && !isDraggingFirstHandle_) {
503             UpdateFirstHandleOffset();
504             return;
505         }
506         UpdateAllHandlesOffset();
507     }
508     if (!isDragging) {
509         BaseTextSelectOverlay::OnAncestorNodeChanged(flag);
510     }
511 }
512 
OnHandleLevelModeChanged(HandleLevelMode mode)513 void TextSelectOverlay::OnHandleLevelModeChanged(HandleLevelMode mode)
514 {
515     if (handleLevelMode_ != mode && mode == HandleLevelMode::OVERLAY) {
516         auto textPattern = GetPattern<TextPattern>();
517         CHECK_NULL_VOID(textPattern);
518         textPattern->UpdateParentGlobalOffset();
519         textPattern->CalculateHandleOffsetAndShowOverlay();
520         UpdateAllHandlesOffset();
521     }
522     if (mode == HandleLevelMode::OVERLAY) {
523         BaseTextSelectOverlay::OnHandleLevelModeChanged(mode);
524     } else {
525         BaseTextSelectOverlay::SetHandleLevelMode(mode);
526         BaseTextSelectOverlay::UpdateViewPort();
527     }
528 }
529 
OnHandleMoveStart(const GestureEvent & event,bool isFirst)530 void TextSelectOverlay::OnHandleMoveStart(const GestureEvent& event, bool isFirst)
531 {
532     BaseTextSelectOverlay::OnHandleMoveStart(event, isFirst);
533     auto textPattern = GetPattern<TextPattern>();
534     CHECK_NULL_VOID(textPattern);
535     textPattern->ChangeHandleHeight(event, isFirst, IsOverlayMode());
536     auto manager = GetManager<SelectContentOverlayManager>();
537     CHECK_NULL_VOID(manager);
538     manager->MarkInfoChange(isFirst ? DIRTY_FIRST_HANDLE : DIRTY_SECOND_HANDLE);
539     manager->SetHandleCircleIsShow(isFirst, false);
540     isDraggingFirstHandle_ = isFirst;
541     hostPaintOffset_ = GetHotPaintOffset();
542     textPattern->SetupMagnifier();
543 }
544 
OnOverlayClick(const GestureEvent & event,bool isFirst)545 void TextSelectOverlay::OnOverlayClick(const GestureEvent& event, bool isFirst)
546 {
547     if (!IsSingleHandle()) {
548         ToggleMenu();
549     }
550 }
551 
UpdateClipHandleViewPort(RectF & rect)552 void TextSelectOverlay::UpdateClipHandleViewPort(RectF& rect)
553 {
554     auto host = GetOwner();
555     CHECK_NULL_VOID(host);
556     auto renderContext = host->GetRenderContext();
557     CHECK_NULL_VOID(renderContext);
558     if (GetRenderClipValue()) {
559         return;
560     }
561     auto clipNode = host->GetAncestorNodeOfFrame(true);
562     RefPtr<FrameNode> prevNode;
563     while (clipNode) {
564         renderContext = clipNode->GetRenderContext();
565         CHECK_NULL_VOID(renderContext);
566         if (renderContext->GetClipEdge().value_or(false)) {
567             break;
568         }
569         prevNode = clipNode;
570         clipNode = clipNode->GetAncestorNodeOfFrame(true);
571     }
572     if (clipNode) {
573         RectF visibleRect;
574         RectF frameRect;
575         clipNode->GetVisibleRect(visibleRect, frameRect);
576         if (GreatNotEqual(rect.Top(), visibleRect.Bottom()) || GreatNotEqual(rect.Left(), visibleRect.Right())) {
577             return;
578         }
579         rect.SetHeight(visibleRect.Bottom() - rect.Top());
580         rect.SetWidth(visibleRect.Right() - rect.Left());
581         return;
582     }
583     // root node.
584     if (prevNode) {
585         auto geoNode = prevNode->GetGeometryNode();
586         CHECK_NULL_VOID(geoNode);
587         RectF visibleRect = geoNode->GetFrameRect();
588         if (GreatNotEqual(rect.Top(), visibleRect.Height()) || GreatNotEqual(rect.Left(), visibleRect.Width())) {
589             return;
590         }
591         rect.SetHeight(visibleRect.Height() - rect.Top());
592         rect.SetWidth(visibleRect.Width() - rect.Left());
593     }
594 }
595 
TriggerScrollableParentToScroll(const RefPtr<ScrollablePattern> scrollablePattern,const Offset & globalOffset,bool isStopAutoScroll)596 void TextSelectOverlay::TriggerScrollableParentToScroll(
597     const RefPtr<ScrollablePattern> scrollablePattern, const Offset& globalOffset, bool isStopAutoScroll)
598 {
599     CHECK_NULL_VOID(scrollablePattern);
600     auto scrollAxis = scrollablePattern->GetAxis();
601     if (!scrollablePattern->IsScrollable() || (scrollAxis != Axis::VERTICAL && scrollAxis != Axis::HORIZONTAL)) {
602         return;
603     }
604     auto scrollableHost = scrollablePattern->GetHost();
605     CHECK_NULL_VOID(scrollableHost);
606     auto scrollableFrameRect = scrollableHost->GetPaintRectWithTransform();
607     auto host = GetOwner();
608     CHECK_NULL_VOID(host);
609     auto hostRect = host->GetPaintRectWithTransform();
610     auto hostSize = hostRect.Height();
611     auto scrollableParentSize = scrollableFrameRect.Height();
612     if (scrollAxis == Axis::HORIZONTAL) {
613         hostSize = hostRect.Width();
614         scrollableParentSize = scrollableFrameRect.Width();
615     }
616     if (LessOrEqual(hostSize, scrollableParentSize)) {
617         return;
618     }
619     RefPtr<NotifyDragEvent> notifyDragEvent = AceType::MakeRefPtr<NotifyDragEvent>();
620     notifyDragEvent->SetX(globalOffset.GetX());
621     notifyDragEvent->SetY(globalOffset.GetY());
622     scrollablePattern->HandleOnDragStatusCallback(
623         isStopAutoScroll ? DragEventType::DROP : DragEventType::MOVE, notifyDragEvent);
624 }
625 
FindScrollableParent()626 const RefPtr<ScrollablePattern> TextSelectOverlay::FindScrollableParent()
627 {
628     auto host = GetOwner();
629     CHECK_NULL_RETURN(host, nullptr);
630     auto parent = host->GetAncestorNodeOfFrame(true);
631     while (parent) {
632         auto scrollablePattern = parent->GetPattern<ScrollablePattern>();
633         if (scrollablePattern && scrollablePattern->IsScrollable()) {
634             return scrollablePattern;
635         }
636         parent = parent->GetAncestorNodeOfFrame(true);
637     }
638     return nullptr;
639 }
640 
GetHotPaintOffset()641 OffsetF TextSelectOverlay::GetHotPaintOffset()
642 {
643     auto host = GetOwner();
644     CHECK_NULL_RETURN(host, hostPaintOffset_);
645     auto renderContext = host->GetRenderContext();
646     CHECK_NULL_RETURN(renderContext, hostPaintOffset_);
647     return renderContext->GetPaintRectWithTransform().GetOffset();
648 }
649 
GetHandleColor()650 std::optional<Color> TextSelectOverlay::GetHandleColor()
651 {
652     auto textPattern = GetPattern<TextPattern>();
653     CHECK_NULL_RETURN(textPattern, std::nullopt);
654     auto layoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
655     CHECK_NULL_RETURN(layoutProperty, std::nullopt);
656     return layoutProperty->GetCursorColor();
657 }
658 
AllowTranslate()659 bool TextSelectOverlay::AllowTranslate()
660 {
661     auto textPattern = GetPattern<TextPattern>();
662     CHECK_NULL_RETURN(textPattern, false);
663     return !textPattern->GetTextSelector().SelectNothing();
664 }
665 
AllowSearch()666 bool TextSelectOverlay::AllowSearch()
667 {
668     auto textPattern = GetPattern<TextPattern>();
669     CHECK_NULL_RETURN(textPattern, false);
670     return !textPattern->GetTextSelector().SelectNothing();
671 }
672 
AllowShare()673 bool TextSelectOverlay::AllowShare()
674 {
675     auto textPattern = GetPattern<TextPattern>();
676     CHECK_NULL_RETURN(textPattern, false);
677     return !textPattern->GetTextSelector().SelectNothing();
678 }
679 
GetRenderClipValue() const680 bool TextSelectOverlay::GetRenderClipValue() const
681 {
682     auto defaultClipValue = Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE);
683     auto pattern = GetPattern<Pattern>();
684     CHECK_NULL_RETURN(pattern, defaultClipValue);
685     auto host = pattern->GetHost();
686     CHECK_NULL_RETURN(host, defaultClipValue);
687     auto renderContext = host->GetRenderContext();
688     CHECK_NULL_RETURN(renderContext, defaultClipValue);
689     return renderContext->GetClipEdge().value_or(defaultClipValue);
690 }
691 
GetSelectOverlayInfo()692 std::optional<SelectOverlayInfo> TextSelectOverlay::GetSelectOverlayInfo()
693 {
694     auto manager = GetManager<SelectContentOverlayManager>();
695     CHECK_NULL_RETURN(manager, std::optional<SelectOverlayInfo>());
696     return manager->GetSelectOverlayInfo();
697 }
698 
ChangeSecondHandleHeight(const GestureEvent & event,bool isOverlayMode)699 bool TextSelectOverlay::ChangeSecondHandleHeight(const GestureEvent& event, bool isOverlayMode)
700 {
701     if (isOverlayMode || CheckSwitchToMode(HandleLevelMode::OVERLAY)) {
702         return false;
703     }
704     auto secondHandleInfo = GetSecondHandleInfo();
705     CHECK_NULL_RETURN(secondHandleInfo, false);
706     auto handleRect = secondHandleInfo->localPaintRect;
707     auto height = handleRect.Height();
708     auto textPattern = GetPattern<TextPattern>();
709     CHECK_NULL_RETURN(textPattern, false);
710     textPattern->CalculateDefaultHandleHeight(height);
711     auto touchOffset = event.GetLocalLocation();
712     bool isTouchHandleCircle = GreatNotEqual(touchOffset.GetY(), handleRect.Bottom());
713     auto handleOffsetY =
714         isTouchHandleCircle ? handleRect.Bottom() - height : static_cast<float>(touchOffset.GetY()) - height / 2.0f;
715     auto secondHandle = textPattern->GetTextSelector().secondHandle;
716     secondHandle.SetTop(handleOffsetY + handleGlobalOffset_.GetY());
717     secondHandle.SetHeight(height);
718     textPattern->UpdateTextSelectorSecondHandle(secondHandle);
719     return true;
720 }
721 
GetVisibleDragViewHandles(RectF & first,RectF & second)722 void TextSelectOverlay::GetVisibleDragViewHandles(RectF& first, RectF& second)
723 {
724     auto selectOverlayInfo = GetSelectOverlayInfos();
725     CHECK_NULL_VOID(selectOverlayInfo);
726     RectF firstHandle;
727     RectF secondHandle;
728     if (!GetDragViewHandleRects(firstHandle, secondHandle)) {
729         return;
730     }
731     if (selectOverlayInfo->firstHandle.isShow) {
732         first = firstHandle;
733     }
734     if (selectOverlayInfo->secondHandle.isShow) {
735         second = secondHandle;
736     }
737 }
738 } // namespace OHOS::Ace::NG
739