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