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