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