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