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