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