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