• 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/rich_editor/rich_editor_select_overlay.h"
17 
18 #include "core/components_ng/pattern/rich_editor/rich_editor_pattern.h"
19 #include "core/components/text_overlay/text_overlay_theme.h"
20 
21 namespace OHOS::Ace::NG {
22 namespace {
23 constexpr float BOX_EPSILON = 0.5f;
24 constexpr float DOUBLE = 2.0f;
25 }
26 
PreProcessOverlay(const OverlayRequest & request)27 bool RichEditorSelectOverlay::PreProcessOverlay(const OverlayRequest& request)
28 {
29     auto pipeline = PipelineContext::GetCurrentContextSafely();
30     CHECK_NULL_RETURN(pipeline, false);
31     auto pattern = GetPattern<RichEditorPattern>();
32     CHECK_NULL_RETURN(pattern, false);
33     SetUsingMouse(pattern->IsUsingMouse());
34     auto host = pattern->GetHost();
35     CHECK_NULL_RETURN(host, false);
36     pipeline->AddOnAreaChangeNode(host->GetId());
37     SetEnableHandleLevel(true);
38     SetEnableSubWindowMenu(true);
39     CheckEnableContainerModal();
40     IF_TRUE(request.requestCode == REQUEST_RECREATE, needRefreshMenu_ = false);
41     return true;
42 }
43 
GetFirstHandleInfo()44 std::optional<SelectHandleInfo> RichEditorSelectOverlay::GetFirstHandleInfo()
45 {
46     auto pattern = GetPattern<RichEditorPattern>();
47     CHECK_NULL_RETURN(pattern, std::nullopt);
48     SelectHandleInfo handleInfo;
49     handleInfo.paintRect = pattern->textSelector_.firstHandle;
50     handleInfo.isShow = dragHandleIndex_ == DragHandleIndex::FIRST || CheckHandleVisible(handleInfo.paintRect);
51     handleInfo.isTouchable = handleInfo.isShow;
52     auto localPaintRect = handleInfo.paintRect;
53     localPaintRect.SetOffset(localPaintRect.GetOffset() - GetPaintOffsetWithoutTransform());
54     handleInfo.localPaintRect = localPaintRect;
55     SetTransformPaintInfo(handleInfo, localPaintRect);
56     return handleInfo;
57 }
58 
GetSecondHandleInfo()59 std::optional<SelectHandleInfo> RichEditorSelectOverlay::GetSecondHandleInfo()
60 {
61     auto pattern = GetPattern<RichEditorPattern>();
62     CHECK_NULL_RETURN(pattern, std::nullopt);
63     SelectHandleInfo handleInfo;
64     handleInfo.paintRect = pattern->textSelector_.secondHandle;
65     handleInfo.isShow = (dragHandleIndex_ == DragHandleIndex::SECOND && !IsSingleHandle()) ||
66         CheckHandleVisible(handleInfo.paintRect);
67     handleInfo.isTouchable = handleInfo.isShow;
68     auto localPaintRect = handleInfo.paintRect;
69     localPaintRect.SetOffset(localPaintRect.GetOffset() - GetPaintOffsetWithoutTransform());
70     handleInfo.localPaintRect = localPaintRect;
71     SetTransformPaintInfo(handleInfo, localPaintRect);
72     return handleInfo;
73 }
74 
CheckHandleVisible(const RectF & paintRect)75 bool RichEditorSelectOverlay::CheckHandleVisible(const RectF& paintRect)
76 {
77     auto pattern = GetPattern<RichEditorPattern>();
78     CHECK_NULL_RETURN(pattern, false);
79     auto host = pattern->GetHost();
80     CHECK_NULL_RETURN(host, false);
81     if (IsUsingMouse()) {
82         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "No need to show handle when using mouse");
83         return false;
84     }
85 
86     auto visibleRect = GetVisibleRect();
87     if (visibleRect.IsEmpty()) {
88         return false;
89     }
90     auto paintLeft = paintRect.Left() + paintRect.Width() / 2.0f;
91     PointF bottomPoint = { paintLeft, paintRect.Bottom() - BOX_EPSILON };
92     PointF topPoint = { paintLeft, paintRect.Top() + BOX_EPSILON };
93     visibleRect.SetLeft(visibleRect.GetX() - BOX_EPSILON);
94     visibleRect.SetWidth(visibleRect.Width() + DOUBLE * BOX_EPSILON);
95     visibleRect.SetTop(visibleRect.GetY() - BOX_EPSILON);
96     visibleRect.SetHeight(visibleRect.Height() + DOUBLE * BOX_EPSILON);
97     return visibleRect.IsInRegion(bottomPoint) && visibleRect.IsInRegion(topPoint);
98 }
99 
GetVisibleRect()100 RectF RichEditorSelectOverlay::GetVisibleRect()
101 {
102     RectF visibleRect;
103     auto pattern = GetPattern<Pattern>();
104     CHECK_NULL_RETURN(pattern, visibleRect);
105     auto host = pattern->GetHost();
106     CHECK_NULL_RETURN(host, visibleRect);
107     auto geometryNode = host->GetGeometryNode();
108     CHECK_NULL_RETURN(geometryNode, visibleRect);
109     OffsetF paddingOffset = geometryNode->GetPaddingOffset() - geometryNode->GetFrameOffset();
110     auto paintOffset = host->GetPaintRectWithTransform().GetOffset();
111     visibleRect = RectF(paddingOffset + paintOffset, geometryNode->GetPaddingSize());
112     CalculateClippedRect(visibleRect);
113     return visibleRect;
114 }
115 
OnResetTextSelection()116 void RichEditorSelectOverlay::OnResetTextSelection()
117 {
118     auto textPattern = GetPattern<TextPattern>();
119     CHECK_NULL_VOID(textPattern);
120     textPattern->ResetSelection();
121 }
122 
AfterCloseOverlay()123 void RichEditorSelectOverlay::AfterCloseOverlay()
124 {
125     RemoveAreaChangeInner();
126     CloseMagnifier();
127 }
128 
RemoveAreaChangeInner()129 void RichEditorSelectOverlay::RemoveAreaChangeInner()
130 {
131     auto textPattern = GetPattern<TextPattern>();
132     CHECK_NULL_VOID(textPattern);
133     textPattern->RemoveAreaChangeInner();
134 }
135 
CloseMagnifier()136 void RichEditorSelectOverlay::CloseMagnifier()
137 {
138     auto pattern = GetPattern<RichEditorPattern>();
139     CHECK_NULL_VOID(pattern);
140     pattern->magnifierController_->RemoveMagnifierFrameNode();
141 }
142 
OnHandleMove(const RectF & handleRect,bool isFirst)143 void RichEditorSelectOverlay::OnHandleMove(const RectF& handleRect, bool isFirst)
144 {
145     auto pattern = GetPattern<RichEditorPattern>();
146     CHECK_NULL_VOID(pattern);
147     CHECK_NULL_VOID(pattern->HasFocus());
148     CHECK_NULL_VOID(SelectOverlayIsOn());
149     CHECK_NULL_VOID(!pattern->spans_.empty());
150     auto host = pattern->GetHost();
151     CHECK_NULL_VOID(host);
152     // the handle position is calculated based on the middle of the handle height.
153     auto handleOffset = GetHandleReferenceOffset(handleRect);
154     UpdateSelectorOnHandleMove(handleOffset, isFirst);
155     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
156     auto overlayManager = GetManager<SelectContentOverlayManager>();
157     CHECK_NULL_VOID(overlayManager);
158     overlayManager->MarkInfoChange(DIRTY_SELECT_TEXT);
159     auto localOffset = handleRect.GetOffset();
160     if (IsOverlayMode()) {
161         auto parentGlobalOffset = hasTransform_ ? GetPaintOffsetWithoutTransform() : pattern->GetParentGlobalOffset();
162         localOffset = localOffset - parentGlobalOffset; // original offset
163     }
164     SetMagnifierOffset(localOffset, handleRect);
165 
166     bool isChangeSecondHandle = isFirst ? pattern->textSelector_.StartGreaterDest() :
167         (!pattern->textSelector_.StartGreaterDest());
168     IF_TRUE(isChangeSecondHandle, pattern->TriggerAvoidOnCaretChange());
169     AutoScrollParam param = { .autoScrollEvent = AutoScrollEvent::HANDLE,
170         .handleRect = handleRect,
171         .isFirstHandle = isFirst,
172         .showScrollbar = true };
173     pattern->AutoScrollByEdgeDetection(param, localOffset, EdgeDetectionStrategy::OUT_BOUNDARY);
174 }
175 
SetMagnifierOffset(const OffsetF & localOffset,const RectF & handleRect)176 void RichEditorSelectOverlay::SetMagnifierOffset(const OffsetF& localOffset, const RectF& handleRect)
177 {
178     auto pattern = GetPattern<RichEditorPattern>();
179     CHECK_NULL_VOID(pattern);
180     if (IsSingleHandle()) {
181         auto [caretOffset, caretHeight] = pattern->CalculateCaretOffsetAndHeight();
182         auto floatingCaretCenter = Offset(localOffset.GetX(), caretOffset.GetY() + caretHeight / 2);
183         pattern->SetMagnifierOffsetWithAnimation(floatingCaretCenter);
184     } else {
185         auto handleCenter = Offset(localOffset.GetX(), localOffset.GetY() + handleRect.Height() / 2);
186         pattern->SetMagnifierLocalOffset(handleCenter);
187     }
188 }
189 
UpdateSelectorOnHandleMove(const OffsetF & handleOffset,bool isFirst)190 void RichEditorSelectOverlay::UpdateSelectorOnHandleMove(const OffsetF& handleOffset, bool isFirst)
191 {
192     auto pattern = GetPattern<RichEditorPattern>();
193     auto& textSelector = pattern->textSelector_;
194     auto currentHandleIndex = pattern->GetHandleIndex(Offset(handleOffset.GetX(), handleOffset.GetY()));
195     auto preHandleIndex = isFirst ? textSelector.baseOffset : textSelector.destinationOffset;
196     pattern->SetCaretPosition(currentHandleIndex);
197     if (isFirst) {
198         pattern->HandleSelectionChange(currentHandleIndex, initSelector_.second);
199     } else {
200         if (IsSingleHandle()) {
201             auto localOffset = handleOffset + pattern->contentRect_.GetOffset();
202             auto textOffset = localOffset - pattern->richTextRect_.GetOffset();
203             pattern->CalcAndRecordLastClickCaretInfo(Offset(textOffset.GetX(), textOffset.GetY()));
204             textSelector.Update(currentHandleIndex);
205             IF_TRUE(isHandleMoving_, pattern->SetCaretTouchMoveOffset(Offset(localOffset.GetX(), localOffset.GetY())));
206         } else {
207             pattern->HandleSelectionChange(initSelector_.first, currentHandleIndex);
208         }
209     }
210     auto finalHandleIndex = isFirst ? textSelector.baseOffset : textSelector.destinationOffset;
211     IF_TRUE(!IsSingleHandle(), pattern->StartVibratorByIndexChange(finalHandleIndex, preHandleIndex));
212 }
213 
OnHandleMoveDone(const RectF & handleRect,bool isFirstHandle)214 void RichEditorSelectOverlay::OnHandleMoveDone(const RectF& handleRect, bool isFirstHandle)
215 {
216     BaseTextSelectOverlay::OnHandleMoveDone(handleRect, isFirstHandle);
217     isHandleMoving_ = false;
218     auto pattern = GetPattern<RichEditorPattern>();
219     CHECK_NULL_VOID(pattern);
220     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "handleRect=%{public}s, isFirstHandle=%{public}d, isSingle=%{public}d",
221         handleRect.ToString().c_str(), isFirstHandle, IsSingleHandle());
222     auto host = pattern->GetHost();
223     CHECK_NULL_VOID(host);
224     auto& textSelector = pattern->textSelector_;
225     auto selectStart = std::min(textSelector.baseOffset, textSelector.destinationOffset);
226     auto selectEnd = std::max(textSelector.baseOffset, textSelector.destinationOffset);
227     pattern->FireOnSelect(selectStart, selectEnd);
228     if (!IsSingleHandle()) {
229         pattern->SetCaretPositionWithAffinity({ selectEnd, TextAffinity::UPSTREAM });
230     } else {
231         pattern->StartFloatingCaretLand();
232     }
233     pattern->StopAutoScroll();
234     pattern->magnifierController_->RemoveMagnifierFrameNode();
235     if (!IsSingleHandle() && textSelector.StartEqualToDest()) {
236         HideMenu();
237         CloseOverlay(true, CloseReason::CLOSE_REASON_NORMAL);
238         IF_TRUE(pattern->IsEditing(), pattern->StartTwinkling());
239         return;
240     }
241     auto overlayManager = GetManager<SelectContentOverlayManager>();
242     CHECK_NULL_VOID(overlayManager);
243     overlayManager->SetHandleCircleIsShow(isFirstHandle, true);
244     if (!isFirstHandle && IsSingleHandle()) {
245         overlayManager->SetIsHandleLineShow(true);
246     }
247     pattern->CalculateHandleOffsetAndShowOverlay();
248     overlayManager->MarkInfoChange((isFirstHandle ? DIRTY_FIRST_HANDLE : DIRTY_SECOND_HANDLE) | DIRTY_SELECT_AREA |
249                             DIRTY_SELECT_TEXT | DIRTY_COPY_ALL_ITEM);
250     ProcessOverlay({ .animation = true, .requestCode = recreateAfterMoveDone_ ? REQUEST_RECREATE : 0 });
251     recreateAfterMoveDone_ = false;
252     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
253 }
254 
GetSelectedText()255 std::string RichEditorSelectOverlay::GetSelectedText()
256 {
257     return TextSelectOverlay::GetSelectedText();
258 }
259 
GetSelectAreaFromRects(SelectRectsType pos)260 RectF RichEditorSelectOverlay::GetSelectAreaFromRects(SelectRectsType pos)
261 {
262     auto pattern = GetPattern<RichEditorPattern>();
263     CHECK_NULL_RETURN(pattern, {});
264     auto intersectRect = pattern->GetSelectArea(pos);
265 
266     if (hasTransform_) {
267         auto textPaintOffset = GetPaintOffsetWithoutTransform();
268         intersectRect.SetOffset(intersectRect.GetOffset() - textPaintOffset);
269         GetGlobalRectWithTransform(intersectRect);
270     }
271     ApplySelectAreaWithKeyboard(intersectRect);
272     return intersectRect;
273 }
274 
IsStopBackPress() const275 bool RichEditorSelectOverlay::IsStopBackPress() const
276 {
277     auto pattern = GetPattern<RichEditorPattern>();
278     CHECK_NULL_RETURN(pattern, true);
279     return pattern->IsStopBackPress();
280 }
281 
OnUpdateMenuInfo(SelectMenuInfo & menuInfo,SelectOverlayDirtyFlag dirtyFlag)282 void RichEditorSelectOverlay::OnUpdateMenuInfo(SelectMenuInfo& menuInfo, SelectOverlayDirtyFlag dirtyFlag)
283 {
284     auto pattern = GetPattern<RichEditorPattern>();
285     CHECK_NULL_VOID(pattern);
286     auto hasValue = pattern->GetTextContentLength() > 0;
287     menuInfo.showCopyAll = !pattern->IsSelectAll() && hasValue;
288     if (dirtyFlag == DIRTY_COPY_ALL_ITEM) {
289         return;
290     }
291     bool isShowItem = pattern->copyOption_ != CopyOptions::None;
292     menuInfo.showCopy = isShowItem && hasValue && !pattern->textSelector_.SelectNothing();
293     menuInfo.showCut = isShowItem && hasValue && !pattern->textSelector_.SelectNothing();
294     menuInfo.showPaste = IsShowPaste();
295     menuInfo.menuIsShow = IsShowMenu();
296     menuInfo.showTranslate = menuInfo.showCopy && pattern->IsShowTranslate() && IsNeedMenuTranslate();
297     menuInfo.showShare = menuInfo.showCopy && IsSupportMenuShare() && IsNeedMenuShare();
298     menuInfo.showSearch = menuInfo.showCopy && pattern->IsShowSearch() && IsNeedMenuSearch();
299     menuInfo.showAIWrite = pattern->IsShowAIWrite() && hasValue;
300     pattern->UpdateSelectMenuInfo(menuInfo);
301 }
302 
303 // param filling except callback
OnUpdateSelectOverlayInfo(SelectOverlayInfo & selectInfo,int32_t requestCode)304 void RichEditorSelectOverlay::OnUpdateSelectOverlayInfo(SelectOverlayInfo& selectInfo, int32_t requestCode)
305 {
306     auto pattern = GetPattern<RichEditorPattern>();
307     CHECK_NULL_VOID(pattern);
308     BaseTextSelectOverlay::OnUpdateSelectOverlayInfo(selectInfo, requestCode);
309     selectInfo.pattern = AceType::WeakClaim(AceType::RawPtr(pattern));
310     selectInfo.handlerColor = pattern->GetCaretColor();
311     selectInfo.handleReverse = IsHandleReverse();
312     OnUpdateOnCreateMenuCallback(selectInfo);
313     bool usingMouse = pattern->IsUsingMouse();
314     auto responseType = pattern->textResponseType_.value_or(TextResponseType::NONE);
315     auto& firstHandle = pattern->textSelector_.firstHandle;
316     auto& secondHandle = pattern->textSelector_.secondHandle;
317     if (usingMouse && pattern->sourceType_ == SourceType::MOUSE) {
318         if (responseType == TextResponseType::LONG_PRESS) {
319             pattern->SetTextResponseType(TextResponseType::RIGHT_CLICK);
320             responseType = TextResponseType::RIGHT_CLICK;
321         }
322         selectInfo.isUsingMouse = true;
323         selectInfo.rightClickOffset = pattern->GetSelectionMenuOffset();
324         pattern->ResetIsMousePressed();
325     } else {
326         selectInfo.firstHandle.paintRect = firstHandle;
327         selectInfo.secondHandle.paintRect = secondHandle;
328     }
329     selectInfo.menuInfo.responseType = static_cast<int32_t>(responseType);
330     selectInfo.menuInfo.editorType = static_cast<int32_t>(pattern->GetEditorType());
331     selectInfo.callerFrameNode = pattern->GetHost();
332     selectInfo.isNewAvoid = true;
333     selectInfo.selectArea = GetSelectArea();
334     selectInfo.checkIsTouchInHostArea =
335     [weak = AceType::WeakClaim(AceType::RawPtr(pattern))](const PointF& touchPoint) -> bool {
336         auto pattern = weak.Upgrade();
337         CHECK_NULL_RETURN(pattern, false);
338         return pattern->IsTouchInFrameArea(touchPoint);
339     };
340     selectInfo.isSingleHandle = IsSingleHandle();
341     selectInfo.recreateOverlay = requestCode == REQUEST_RECREATE;
342     CheckMenuParamChange(selectInfo, pattern->GetEditorType(), responseType);
343     pattern->CopySelectionMenuParams(selectInfo, responseType);
344     if (hasTransform_) {
345         selectInfo.callerNodeInfo = {
346             .paintFrameRect = GetPaintRectWithTransform(),
347             .paintOffset = GetPaintRectOffsetWithTransform()
348         };
349     }
350 }
351 
OnUpdateOnCreateMenuCallback(SelectOverlayInfo & selectInfo)352 void RichEditorSelectOverlay::OnUpdateOnCreateMenuCallback(SelectOverlayInfo& selectInfo)
353 {
354     BaseTextSelectOverlay::OnUpdateOnCreateMenuCallback(selectInfo);
355     selectInfo.menuCallback.showMenuOnMoveDone = [weak = WeakClaim(this)]() {
356         auto overlay = weak.Upgrade();
357         CHECK_NULL_RETURN(overlay, true);
358         auto pattern = overlay->GetPattern<RichEditorPattern>();
359         CHECK_NULL_RETURN(pattern, true);
360         return !pattern->IsSelectedTypeChange();
361     };
362 }
363 
CheckMenuParamChange(SelectOverlayInfo & selectInfo,TextSpanType selectType,TextResponseType responseType)364 void RichEditorSelectOverlay::CheckMenuParamChange(SelectOverlayInfo& selectInfo,
365     TextSpanType selectType, TextResponseType responseType)
366 {
367     auto pattern = GetPattern<RichEditorPattern>();
368     auto menuParams = pattern ? pattern->GetMenuParams(selectType, responseType) : nullptr;
369     std::pair<TextSpanType, TextResponseType> selectResponseComb = { selectType, responseType };
370     selectInfo.recreateOverlay |= (lastMenuParams_ || menuParams) && selectResponseComb != lastSelectResponseComb_;
371     lastMenuParams_ = menuParams;
372     lastSelectResponseComb_ = selectResponseComb;
373 }
374 
375 // set menu callback
OnMenuItemAction(OptionMenuActionId id,OptionMenuType type)376 void RichEditorSelectOverlay::OnMenuItemAction(OptionMenuActionId id, OptionMenuType type)
377 {
378     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "MenuActionId=%{public}d, MenuType=%{public}d", id, type);
379     auto pattern = GetPattern<RichEditorPattern>();
380     CHECK_NULL_VOID(pattern);
381     switch (id) {
382         case OptionMenuActionId::COPY:
383             needRefreshMenu_ = !IsShowPaste() && pattern->copyOption_ != CopyOptions::None;
384             pattern->HandleOnCopy();
385             break;
386         case OptionMenuActionId::CUT:
387             pattern->HandleOnCut();
388             break;
389         case OptionMenuActionId::PASTE:
390             pattern->HandleOnPaste();
391             CloseOverlay(true, CloseReason::CLOSE_REASON_NORMAL);
392             break;
393         case OptionMenuActionId::SELECT_ALL:
394             pattern->HandleMenuCallbackOnSelectAll();
395             break;
396         case OptionMenuActionId::TRANSLATE:
397             HandleOnTranslate();
398             return;
399         case OptionMenuActionId::SHARE:
400             pattern->HandleOnShare();
401             break;
402         case OptionMenuActionId::SEARCH:
403             HandleOnSearch();
404             break;
405         case OptionMenuActionId::CAMERA_INPUT:
406             pattern->HandleOnCameraInput();
407             break;
408         case OptionMenuActionId::AI_WRITE:
409             pattern->HandleOnAIWrite();
410             break;
411         case OptionMenuActionId::DISAPPEAR:
412             if (pattern->GetTextDetectEnable() && !pattern->HasFocus()) {
413                 pattern->ResetSelection();
414             }
415             break;
416         default:
417             TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Unsupported menu option id %{public}d", id);
418             break;
419     }
420 }
421 
IsMenuShow()422 bool RichEditorSelectOverlay::IsMenuShow()
423 {
424     auto manager = GetManager<SelectContentOverlayManager>();
425     return manager && manager->IsMenuShow();
426 }
427 
ToggleMenu()428 void RichEditorSelectOverlay::ToggleMenu()
429 {
430     if (IsMenuShow()) {
431         HideMenu();
432         return;
433     }
434     if (needRefreshMenu_) {
435         needRefreshMenu_ = false;
436         ProcessOverlay({ .menuIsShow = true, .animation = true, .requestCode = REQUEST_RECREATE });
437     } else {
438         UpdateMenuOffset();
439         ShowMenu();
440     }
441 }
442 
OnCloseOverlay(OptionMenuType menuType,CloseReason reason,RefPtr<OverlayInfo> info)443 void RichEditorSelectOverlay::OnCloseOverlay(OptionMenuType menuType, CloseReason reason, RefPtr<OverlayInfo> info)
444 {
445     bool isSingleHandle = info && info->isSingleHandle;
446     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "menuType=%{public}d, closeReason=%{public}d, isSingleHandle=%{public}d",
447         menuType, reason, isSingleHandle);
448     auto pattern = GetPattern<RichEditorPattern>();
449     CHECK_NULL_VOID(pattern);
450     BaseTextSelectOverlay::OnCloseOverlay(menuType, reason, info);
451     isHandleMoving_ = false;
452     if (isSingleHandle) {
453         pattern->floatingCaretState_.Reset();
454         pattern->isCursorAlwaysDisplayed_ = false;
455     }
456     auto needResetSelection = pattern->GetTextDetectEnable() && !pattern->HasFocus() &&
457         reason != CloseReason::CLOSE_REASON_DRAG_FLOATING;
458     auto isBackPressed = reason == CloseReason::CLOSE_REASON_BACK_PRESSED;
459     auto isHoldByOther = reason == CloseReason::CLOSE_REASON_HOLD_BY_OTHER;
460     needResetSelection = needResetSelection || isBackPressed || isHoldByOther;
461     IF_TRUE(needResetSelection, pattern->ResetSelection());
462     IF_TRUE(isHoldByOther, pattern->CloseSelectOverlay());
463     if (isBackPressed) {
464         IF_TRUE(isSingleHandle, pattern->OnBackPressed());
465         if (!pattern->IsEditing() && pattern->HasFocus()) {
466             FocusHub::LostFocusToViewRoot();
467         }
468         ResumeTwinkling();
469     }
470 }
471 
OnHandleGlobalTouchEvent(SourceType sourceType,TouchType touchType,bool touchInside)472 void RichEditorSelectOverlay::OnHandleGlobalTouchEvent(SourceType sourceType, TouchType touchType, bool touchInside)
473 {
474     BaseTextSelectOverlay::OnHandleGlobalTouchEvent(sourceType, touchType);
475     CHECK_NULL_VOID(IsMouseClickDown(sourceType, touchType) || IsTouchUp(sourceType, touchType));
476     if (IsSingleHandle()) {
477         CloseOverlay(false, CloseReason::CLOSE_REASON_CLICK_OUTSIDE);
478         ResumeTwinkling();
479     } else {
480         HideMenu();
481     }
482 }
483 
OnHandleLevelModeChanged(HandleLevelMode mode)484 void RichEditorSelectOverlay::OnHandleLevelModeChanged(HandleLevelMode mode)
485 {
486     if (handleLevelMode_ != mode && mode == HandleLevelMode::OVERLAY) {
487         auto pattern = GetPattern<RichEditorPattern>();
488         CHECK_NULL_VOID(pattern);
489         pattern->CalculateHandleOffsetAndShowOverlay();
490     }
491     BaseTextSelectOverlay::OnHandleLevelModeChanged(mode);
492 }
493 
GetSelectOverlayInfo()494 std::optional<SelectOverlayInfo> RichEditorSelectOverlay::GetSelectOverlayInfo()
495 {
496     auto manager = GetManager<SelectContentOverlayManager>();
497     CHECK_NULL_RETURN(manager, std::optional<SelectOverlayInfo>());
498     return manager->GetSelectOverlayInfo();
499 }
500 
IsSingleHandleShow()501 bool RichEditorSelectOverlay::IsSingleHandleShow()
502 {
503     auto manager = GetManager<SelectContentOverlayManager>();
504     CHECK_NULL_RETURN(manager && manager->IsSingleHandle(), false);
505     auto overlayInfo = manager->GetSelectOverlayInfo();
506     CHECK_NULL_RETURN(overlayInfo, false);
507     return overlayInfo->secondHandle.isShow;
508 }
509 
UpdateMenuOffset()510 void RichEditorSelectOverlay::UpdateMenuOffset()
511 {
512     auto manager = GetManager<SelectContentOverlayManager>();
513     CHECK_NULL_VOID(manager);
514     manager->MarkInfoChange(DIRTY_SELECT_AREA | DIRTY_ALL_MENU_ITEM);
515 }
516 
IsBothHandlesShow()517 bool RichEditorSelectOverlay::IsBothHandlesShow()
518 {
519     auto manager = GetManager<SelectContentOverlayManager>();
520     CHECK_NULL_RETURN(manager && manager->IsHandlesShow(), false);
521     auto overlayInfo = manager->GetSelectOverlayInfo();
522     CHECK_NULL_RETURN(overlayInfo, false);
523     return overlayInfo->firstHandle.isShow && overlayInfo->secondHandle.isShow;
524 }
525 
IsHandleShow()526 bool RichEditorSelectOverlay::IsHandleShow()
527 {
528     return IsBothHandlesShow() || IsSingleHandleShow();
529 }
530 
OnAncestorNodeChanged(FrameNodeChangeInfoFlag flag)531 void RichEditorSelectOverlay::OnAncestorNodeChanged(FrameNodeChangeInfoFlag flag)
532 {
533     if (IsAncestorNodeGeometryChange(flag)) {
534         UpdateAllHandlesOffset();
535     }
536     auto pattern = GetPattern<RichEditorPattern>();
537     CHECK_NULL_VOID(pattern);
538     auto host = pattern->GetHost();
539     CHECK_NULL_VOID(host);
540     FrameNodeChangeInfoFlag changeFlag = flag;
541     if (IsAncestorNodeStartScroll(host->GetChangeInfoFlag()) || IsAncestorNodeEndScroll(host->GetChangeInfoFlag())) {
542         auto parent = host->GetAncestorNodeOfFrame(true);
543         changeFlag = FRAME_NODE_CHANGE_INFO_NONE;
544         while (parent) {
545             if (parent->GetChangeInfoFlag() != FRAME_NODE_CHANGE_INFO_NONE) {
546                 changeFlag = changeFlag | parent->GetChangeInfoFlag();
547             }
548             parent = parent->GetAncestorNodeOfFrame(true);
549         }
550     }
551     BaseTextSelectOverlay::OnAncestorNodeChanged(changeFlag);
552 }
553 
OnHandleMoveStart(const GestureEvent & event,bool isFirst)554 void RichEditorSelectOverlay::OnHandleMoveStart(const GestureEvent& event, bool isFirst)
555 {
556     BaseTextSelectOverlay::OnHandleMoveStart(event, isFirst);
557     isHandleMoving_ = true;
558     auto pattern = GetPattern<RichEditorPattern>();
559     CHECK_NULL_VOID(pattern);
560     initSelector_ = { pattern->textSelector_.GetTextStart(), pattern->textSelector_.GetTextEnd() };
561     IF_TRUE(!IsSingleHandle(), pattern->ChangeHandleHeight(event, isFirst, IsOverlayMode()));
562     auto manager = GetManager<SelectContentOverlayManager>();
563     CHECK_NULL_VOID(manager);
564     manager->MarkInfoChange(isFirst ? DIRTY_FIRST_HANDLE : DIRTY_SECOND_HANDLE);
565     manager->SetHandleCircleIsShow(isFirst, false);
566     if (IsSingleHandle()) {
567         pattern->ShowCaretWithoutTwinkling();
568         manager->SetIsHandleLineShow(false);
569     }
570 }
571 
OnOverlayTouchDown(const TouchEventInfo & event)572 void RichEditorSelectOverlay::OnOverlayTouchDown(const TouchEventInfo& event)
573 {
574     auto pattern = GetPattern<RichEditorPattern>();
575     CHECK_NULL_VOID(pattern);
576     if (event.GetSourceTool() == SourceTool::MOUSE && IsHandleShow()) {
577         pattern->CloseSelectOverlay();
578     }
579     pattern->RequestFocusWhenSelected();
580 }
581 
UpdateHandleOffset()582 void RichEditorSelectOverlay::UpdateHandleOffset()
583 {
584     auto manager = GetManager<SelectContentOverlayManager>();
585     CHECK_NULL_VOID(manager);
586     manager->MarkInfoChange(DIRTY_FIRST_HANDLE | DIRTY_SECOND_HANDLE);
587 }
588 
UpdateFirstHandleOffset()589 void RichEditorSelectOverlay::UpdateFirstHandleOffset()
590 {
591     CHECK_NULL_VOID(dragHandleIndex_ != DragHandleIndex::FIRST);
592     BaseTextSelectOverlay::UpdateFirstHandleOffset();
593 }
594 
UpdateSecondHandleOffset()595 void RichEditorSelectOverlay::UpdateSecondHandleOffset()
596 {
597     CHECK_NULL_VOID(dragHandleIndex_ != DragHandleIndex::SECOND);
598     BaseTextSelectOverlay::UpdateSecondHandleOffset();
599 }
600 
UpdateSelectOverlayOnAreaChanged()601 void RichEditorSelectOverlay::UpdateSelectOverlayOnAreaChanged()
602 {
603     CHECK_NULL_VOID(SelectOverlayIsOn() || SelectOverlayIsCreating());
604     auto pattern = GetPattern<RichEditorPattern>();
605     CHECK_NULL_VOID(pattern);
606     pattern->CalculateHandleOffsetAndShowOverlay();
607     auto menuParams = pattern->GetMenuParams(pattern->GetEditorType(),
608         pattern->textResponseType_.value_or(TextResponseType::NONE));
609     bool needRecreate = (lastMenuParams_ == nullptr) ^ (menuParams == nullptr);
610     if (!needRecreate) {
611         UpdateHandleOffset();
612         IF_TRUE(IsMenuShow(), UpdateMenuOffset());
613         return;
614     }
615     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "menu type changed, recreate menu isHandleMoving:%{public}d", isHandleMoving_);
616     if (isHandleMoving_) {
617         recreateAfterMoveDone_ = true;
618     } else {
619         ProcessOverlay({ .menuIsShow = IsCurrentMenuVisibile(), .requestCode = REQUEST_RECREATE });
620     }
621 }
622 
SwitchCaretState(std::shared_ptr<SelectOverlayInfo> info)623 void RichEditorSelectOverlay::SwitchCaretState(std::shared_ptr<SelectOverlayInfo> info)
624 {
625     CHECK_NULL_VOID(info && IsSingleHandle() && !isHandleMoving_);
626     auto pattern = GetPattern<RichEditorPattern>();
627     CHECK_NULL_VOID(pattern);
628     bool isSingleHandleShow = !handleIsHidden_ && info->secondHandle.isShow;
629     bool isCaretTwinkling = pattern->caretTwinkling_ && !pattern->isCursorAlwaysDisplayed_;
630     CHECK_NULL_VOID(isSingleHandleShow == isCaretTwinkling);
631     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Switch caret state singleHandleShow=%{public}d", isSingleHandleShow);
632     if (isSingleHandleShow) {
633         pattern->StopTwinkling();
634     } else {
635         ResumeTwinkling();
636     }
637 }
638 
ResumeTwinkling()639 void RichEditorSelectOverlay::ResumeTwinkling()
640 {
641     auto pattern = GetPattern<RichEditorPattern>();
642     CHECK_NULL_VOID(pattern && pattern->IsEditing());
643     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "only show caret for edit state");
644     pattern->isCursorAlwaysDisplayed_ = false;
645     pattern->StartTwinkling();
646 }
647 
OnHandleIsHidden()648 void RichEditorSelectOverlay::OnHandleIsHidden()
649 {
650     TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Start twinking when singleHandle hide");
651     auto pattern = GetPattern<RichEditorPattern>();
652     CHECK_NULL_VOID(pattern);
653     pattern->isCursorAlwaysDisplayed_ = false;
654     pattern->StartTwinkling();
655     handleIsHidden_ = true;
656 }
657 
OnOverlayClick(const GestureEvent & event,bool isFirst)658 void RichEditorSelectOverlay::OnOverlayClick(const GestureEvent& event, bool isFirst)
659 {
660     auto pattern = GetPattern<RichEditorPattern>();
661     CHECK_NULL_VOID(pattern);
662     if (!pattern->IsEditing() && !IsSingleHandle()) {
663         ToggleMenu();
664     }
665     auto globalOffset = pattern->GetGlobalOffset();
666     auto overlayEvent = event;
667     auto localLocation = Offset(overlayEvent.GetGlobalLocation().GetX() - globalOffset.GetX(),
668         overlayEvent.GetGlobalLocation().GetY() - globalOffset.GetY());
669     overlayEvent.SetLocalLocation(localLocation);
670     pattern->HandleClickEvent(overlayEvent);
671 }
672 
OnHandleMouseEvent(const MouseInfo & event)673 void RichEditorSelectOverlay::OnHandleMouseEvent(const MouseInfo& event)
674 {
675     auto pattern = GetPattern<RichEditorPattern>();
676     CHECK_NULL_VOID(pattern);
677     if (event.GetAction() == MouseAction::PRESS && IsHandleShow()) {
678         pattern->CloseSelectOverlay();
679     }
680 }
681 
OnAfterSelectOverlayShow(bool isCreate)682 void RichEditorSelectOverlay::OnAfterSelectOverlayShow(bool isCreate)
683 {
684     handleIsHidden_ = false;
685     auto manager = GetManager<SelectContentOverlayManager>();
686     CHECK_NULL_VOID(manager);
687     manager->MarkInfoChange(DIRTY_SELECT_AREA);
688     auto pattern = GetPattern<RichEditorPattern>();
689     CHECK_NULL_VOID(pattern);
690     IF_TRUE(IsSingleHandleShow(), pattern->StopTwinkling());
691     if (IsRightButtonCustomMenuShow()) {
692         TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Change mouse style to default after right-button custom menu show");
693         pattern->ChangeMouseStyle(MouseFormat::DEFAULT);
694     }
695 }
696 
IsRightButtonCustomMenuShow()697 bool RichEditorSelectOverlay::IsRightButtonCustomMenuShow()
698 {
699     auto manager = GetManager<SelectContentOverlayManager>();
700     CHECK_NULL_RETURN(manager && manager->IsMenuShow(), false);
701     auto overlayInfo = manager->GetSelectOverlayInfo();
702     CHECK_NULL_RETURN(overlayInfo, false);
703     auto menuInfo = overlayInfo->menuInfo;
704     auto responseType = menuInfo.responseType.value_or(static_cast<int>(TextResponseType::NONE));
705     return responseType == static_cast<int>(TextResponseType::RIGHT_CLICK) && menuInfo.menuBuilder != nullptr;
706 }
707 
GetHandleHotZoneRadius()708 float RichEditorSelectOverlay::GetHandleHotZoneRadius()
709 {
710     auto hotZoneRadius = 0.0f;
711     auto pattern = GetPattern<RichEditorPattern>();
712     CHECK_NULL_RETURN(pattern, hotZoneRadius);
713     auto host = pattern->GetHost();
714     CHECK_NULL_RETURN(host, hotZoneRadius);
715     auto pipeline = host->GetContext();
716     CHECK_NULL_RETURN(pipeline, hotZoneRadius);
717     auto theme = pipeline->GetTheme<TextOverlayTheme>();
718     CHECK_NULL_RETURN(theme, hotZoneRadius);
719     hotZoneRadius = theme->GetHandleHotZoneRadius().ConvertToPx();
720     return hotZoneRadius;
721 }
722 
OnHandleMarkInfoChange(std::shared_ptr<SelectOverlayInfo> info,SelectOverlayDirtyFlag flag)723 void RichEditorSelectOverlay::OnHandleMarkInfoChange(
724     std::shared_ptr<SelectOverlayInfo> info, SelectOverlayDirtyFlag flag)
725 {
726     IF_TRUE((flag & DIRTY_SECOND_HANDLE) == DIRTY_SECOND_HANDLE, SwitchCaretState(info));
727     CHECK_NULL_VOID((flag & DIRTY_HANDLE_COLOR_FLAG) == DIRTY_HANDLE_COLOR_FLAG);
728     CHECK_NULL_VOID(info);
729 
730     auto manager = GetManager<SelectContentOverlayManager>();
731     CHECK_NULL_VOID(manager);
732     auto pattern = GetPattern<RichEditorPattern>();
733     CHECK_NULL_VOID(pattern);
734     info->handlerColor = pattern->caretColor_;
735     manager->MarkHandleDirtyNode(PROPERTY_UPDATE_RENDER);
736 }
737 
UpdateHandleColor()738 void RichEditorSelectOverlay::UpdateHandleColor()
739 {
740     auto manager = GetManager<SelectContentOverlayManager>();
741     CHECK_NULL_VOID(manager);
742     manager->MarkInfoChange(DIRTY_HANDLE_COLOR_FLAG);
743 }
744 
745 } // namespace OHOS::Ace::NG
746