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