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/text/text_select_overlay.h"
17
18 #include "core/components_ng/pattern/select_overlay/select_overlay_property.h"
19 #include "core/components_ng/pattern/text/text_pattern.h"
20
21 namespace OHOS::Ace::NG {
22 namespace {
23 constexpr float BOX_EPSILON = 0.5f;
24 }
25
PreProcessOverlay(const OverlayRequest & request)26 bool TextSelectOverlay::PreProcessOverlay(const OverlayRequest& request)
27 {
28 auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
29 CHECK_NULL_RETURN(pipeline, false);
30 auto textPattern = GetPattern<TextPattern>();
31 CHECK_NULL_RETURN(textPattern, false);
32 SetUsingMouse(textPattern->IsUsingMouse());
33 SetEnableHandleLevel(true);
34 SetEnableSubWindowMenu(true);
35 textPattern->CalculateHandleOffsetAndShowOverlay();
36 selectTextUseTopHandle = true;
37 CheckEnableContainerModal();
38 return true;
39 }
40
GetFirstHandleInfo()41 std::optional<SelectHandleInfo> TextSelectOverlay::GetFirstHandleInfo()
42 {
43 auto textPattern = GetPattern<TextPattern>();
44 CHECK_NULL_RETURN(textPattern, std::nullopt);
45 SelectHandleInfo handleInfo;
46 handleInfo.paintRect = textPattern->GetTextSelector().firstHandle;
47 handleInfo.isShow = CheckAndAdjustHandle(handleInfo.paintRect);
48
49 auto localPaintRect = textPattern->GetTextSelector().firstHandle;
50 localPaintRect.SetOffset(localPaintRect.GetOffset() - GetPaintOffsetWithoutTransform());
51 handleInfo.localPaintRect = localPaintRect;
52 SetTransformPaintInfo(handleInfo, localPaintRect);
53 handleInfo.forceDraw = !CheckSwitchToMode(HandleLevelMode::OVERLAY);
54 return handleInfo;
55 }
56
GetSecondHandleInfo()57 std::optional<SelectHandleInfo> TextSelectOverlay::GetSecondHandleInfo()
58 {
59 auto textPattern = GetPattern<TextPattern>();
60 CHECK_NULL_RETURN(textPattern, std::nullopt);
61 SelectHandleInfo handleInfo;
62 handleInfo.paintRect = textPattern->GetTextSelector().secondHandle;
63 handleInfo.isShow = CheckAndAdjustHandle(handleInfo.paintRect);
64
65 auto localPaintRect = textPattern->GetTextSelector().secondHandle;
66 localPaintRect.SetOffset(localPaintRect.GetOffset() - GetPaintOffsetWithoutTransform());
67 handleInfo.localPaintRect = localPaintRect;
68 SetTransformPaintInfo(handleInfo, localPaintRect);
69 handleInfo.forceDraw = !CheckSwitchToMode(HandleLevelMode::OVERLAY);
70 return handleInfo;
71 }
72
GetHandleLocalPaintRect(DragHandleIndex dragHandleIndex)73 RectF TextSelectOverlay::GetHandleLocalPaintRect(DragHandleIndex dragHandleIndex)
74 {
75 auto textPattern = GetPattern<TextPattern>();
76 CHECK_NULL_RETURN(textPattern, RectF());
77 RectF localPaintRect;
78 if (dragHandleIndex == DragHandleIndex::FIRST) {
79 localPaintRect = textPattern->GetTextSelector().firstHandle;
80 } else if (dragHandleIndex == DragHandleIndex::SECOND) {
81 localPaintRect = textPattern->GetTextSelector().secondHandle;
82 } else { // dragHandleIndex::NONE
83 return RectF();
84 }
85 localPaintRect.SetOffset(localPaintRect.GetOffset() - GetPaintOffsetWithoutTransform());
86 return localPaintRect;
87 }
88
CheckAndAdjustHandle(RectF & paintRect)89 bool TextSelectOverlay::CheckAndAdjustHandle(RectF& paintRect)
90 {
91 auto textPattern = GetPattern<TextPattern>();
92 CHECK_NULL_RETURN(textPattern, false);
93 auto host = textPattern->GetHost();
94 CHECK_NULL_RETURN(host, false);
95 if (!GetRenderClipValue()) {
96 if (handleLevelMode_ == HandleLevelMode::EMBED) {
97 return true;
98 }
99 auto contentRect = textPattern->GetTextContentRect();
100 auto localPaintRect = paintRect;
101 localPaintRect.SetOffset(localPaintRect.GetOffset() - GetPaintOffsetWithoutTransform());
102 localPaintRect.SetOffset(OffsetF(localPaintRect.GetX() + localPaintRect.Width() / 2.0f, localPaintRect.GetY()));
103 auto visibleContentRect = contentRect.CombineRectT(localPaintRect);
104 visibleContentRect.SetOffset(visibleContentRect.GetOffset() + textPattern->GetTextPaintOffset());
105 visibleContentRect = GetVisibleRect(host, visibleContentRect);
106 return CheckAndAdjustHandleWithContent(visibleContentRect, paintRect);
107 }
108 auto contentRect = textPattern->GetTextContentRect();
109 RectF visibleContentRect(contentRect.GetOffset() + textPattern->GetTextPaintOffset(), contentRect.GetSize());
110 if (handleLevelMode_ == HandleLevelMode::OVERLAY) {
111 visibleContentRect = GetVisibleRect(host, visibleContentRect);
112 }
113 return CheckAndAdjustHandleWithContent(visibleContentRect, paintRect);
114 }
115
CheckAndAdjustHandleWithContent(const RectF & visibleContentRect,RectF & paintRect)116 bool TextSelectOverlay::CheckAndAdjustHandleWithContent(const RectF& visibleContentRect, RectF& paintRect)
117 {
118 auto paintLeft = paintRect.GetX() + paintRect.Width() / 2.0f;
119 PointF bottomPoint = { paintLeft, paintRect.Bottom() - BOX_EPSILON };
120 PointF topPoint = { paintLeft, paintRect.Top() + BOX_EPSILON };
121 bool bottomInRegion = visibleContentRect.IsInRegion(bottomPoint);
122 bool topInRegion = visibleContentRect.IsInRegion(topPoint);
123 if (IsClipHandleWithViewPort()) {
124 return bottomInRegion || topInRegion;
125 }
126 if (!bottomInRegion && topInRegion) {
127 paintRect.SetHeight(visibleContentRect.Bottom() - paintRect.Top());
128 } else if (bottomInRegion && !topInRegion) {
129 paintRect.SetHeight(paintRect.Bottom() - visibleContentRect.Top());
130 paintRect.SetTop(visibleContentRect.Top());
131 }
132 return bottomInRegion || topInRegion;
133 }
134
CheckHandleVisible(const RectF & paintRect)135 bool TextSelectOverlay::CheckHandleVisible(const RectF& paintRect)
136 {
137 auto textPattern = GetPattern<TextPattern>();
138 CHECK_NULL_RETURN(textPattern, false);
139 auto host = textPattern->GetHost();
140 CHECK_NULL_RETURN(host, false);
141 if (!GetRenderClipValue()) {
142 return true;
143 }
144 auto contentRect = textPattern->GetTextContentRect();
145 RectF visibleContentRect(contentRect.GetOffset() + textPattern->GetTextPaintOffset(), contentRect.GetSize());
146 visibleContentRect = GetVisibleRect(host, visibleContentRect);
147 PointF bottomPoint = { paintRect.Left(), paintRect.Bottom() - BOX_EPSILON };
148 PointF topPoint = { paintRect.Left(), paintRect.Top() + BOX_EPSILON };
149 return visibleContentRect.IsInRegion(bottomPoint) && visibleContentRect.IsInRegion(topPoint);
150 }
151
OnResetTextSelection()152 void TextSelectOverlay::OnResetTextSelection()
153 {
154 auto textPattern = GetPattern<TextPattern>();
155 CHECK_NULL_VOID(textPattern);
156 textPattern->ResetSelection();
157 }
158
OnHandleMove(const RectF & handleRect,bool isFirst)159 void TextSelectOverlay::OnHandleMove(const RectF& handleRect, bool isFirst)
160 {
161 if (!SelectOverlayIsOn()) {
162 return;
163 }
164 auto textPattern = GetPattern<TextPattern>();
165 CHECK_NULL_VOID(textPattern);
166 auto host = textPattern->GetHost();
167 CHECK_NULL_VOID(host);
168 auto localHandleOffset = handleRect.GetOffset();
169 if (IsOverlayMode()) {
170 localHandleOffset -= GetPaintOffsetWithoutTransform();
171 }
172 localHandleOffset.SetY(localHandleOffset.GetY() + handleRect.Height() / 2.0f);
173 if (textPattern->HasContent() && textPattern->GetOrCreateMagnifier()) {
174 textPattern->GetMagnifierController()->SetLocalOffset(localHandleOffset);
175 }
176 // the handle position is calculated based on the middle of the handle height.
177 auto handleOffset = GetHandleReferenceOffset(handleRect);
178 UpdateSelectorOnHandleMove(handleOffset, isFirst);
179 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
180 auto overlayManager = GetManager<SelectContentOverlayManager>();
181 CHECK_NULL_VOID(overlayManager);
182 overlayManager->MarkInfoChange(DIRTY_SELECT_TEXT);
183 }
184
GetHandleReferenceOffset(const RectF & handleRect)185 OffsetF TextSelectOverlay::GetHandleReferenceOffset(const RectF& handleRect)
186 {
187 auto handleOffset = handleRect.GetOffset();
188 handleOffset.SetY(handleOffset.GetY() + handleRect.Height() / 2.0f);
189 auto textPattern = GetPattern<TextPattern>();
190 CHECK_NULL_RETURN(textPattern, handleOffset);
191 auto contentRect = textPattern->GetTextContentRect();
192 auto contentOffset = contentRect.GetOffset();
193 if (IsOverlayMode()) {
194 contentOffset = contentOffset + GetPaintOffsetWithoutTransform();
195 }
196 if (GetRenderClipValue()) {
197 handleOffset.SetX(
198 std::clamp(handleOffset.GetX(), contentOffset.GetX(), contentOffset.GetX() + contentRect.Width()));
199 handleOffset.SetY(
200 std::clamp(handleOffset.GetY(), contentOffset.GetY(), contentOffset.GetY() + contentRect.Height()));
201 }
202 auto textPaintOffset = contentOffset - OffsetF(0.0f, std::min(textPattern->GetBaselineOffset(), 0.0f));
203 handleOffset -= textPaintOffset;
204 return handleOffset;
205 }
206
UpdateSelectorOnHandleMove(const OffsetF & handleOffset,bool isFirstHandle)207 void TextSelectOverlay::UpdateSelectorOnHandleMove(const OffsetF& handleOffset, bool isFirstHandle)
208 {
209 auto textPattern = GetPattern<TextPattern>();
210 CHECK_NULL_VOID(textPattern);
211 auto currentHandleIndex = textPattern->GetHandleIndex(Offset(handleOffset.GetX(), handleOffset.GetY()));
212 if (isFirstHandle) {
213 textPattern->StartVibratorByIndexChange(currentHandleIndex, textPattern->GetTextSelector().baseOffset);
214 textPattern->HandleSelectionChange(currentHandleIndex, textPattern->GetTextSelector().destinationOffset);
215 } else {
216 textPattern->StartVibratorByIndexChange(currentHandleIndex, textPattern->GetTextSelector().destinationOffset);
217 textPattern->HandleSelectionChange(textPattern->GetTextSelector().baseOffset, currentHandleIndex);
218 }
219 }
220
OnHandleMoveDone(const RectF & rect,bool isFirst)221 void TextSelectOverlay::OnHandleMoveDone(const RectF& rect, bool isFirst)
222 {
223 BaseTextSelectOverlay::OnHandleMoveDone(rect, isFirst);
224 auto textPattern = GetPattern<TextPattern>();
225 CHECK_NULL_VOID(textPattern);
226 if (textPattern->GetMagnifierController()) {
227 textPattern->GetMagnifierController()->RemoveMagnifierFrameNode();
228 }
229 auto textSelector = textPattern->GetTextSelector();
230 textPattern->UpdateSelectionSpanType(textSelector.GetTextStart(), textSelector.GetTextEnd());
231 textPattern->CalculateHandleOffsetAndShowOverlay();
232 auto overlayManager = GetManager<SelectContentOverlayManager>();
233 CHECK_NULL_VOID(overlayManager);
234 if (!textPattern->IsSelectedTypeChange()) {
235 overlayManager->ShowOptionMenu();
236 }
237 textPattern->UpdateAIMenuOptions();
238 overlayManager->MarkInfoChange((isFirst ? DIRTY_FIRST_HANDLE : DIRTY_SECOND_HANDLE) | DIRTY_SELECT_AREA |
239 DIRTY_SELECT_TEXT | DIRTY_COPY_ALL_ITEM | DIRTY_AI_MENU_ITEM | DIRTY_ASK_CELIA);
240 if (textPattern->CheckSelectedTypeChange()) {
241 CloseOverlay(false, CloseReason::CLOSE_REASON_NORMAL);
242 ProcessOverlay({ .animation = true });
243 }
244 overlayManager->SetHandleCircleIsShow(isFirst, true);
245 if (textPattern->GetTextSelector().SelectNothing()) {
246 TAG_LOGI(AceLogTag::ACE_TEXT, "Close the selectoverlay when nothing is selected.");
247 CloseOverlay(false, CloseReason::CLOSE_REASON_NORMAL);
248 }
249 auto host = textPattern->GetHost();
250 CHECK_NULL_VOID(host);
251 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
252 }
253
GetSelectedText()254 std::string TextSelectOverlay::GetSelectedText()
255 {
256 auto textPattern = GetPattern<TextPattern>();
257 CHECK_NULL_RETURN(textPattern, "");
258 auto start = textPattern->GetTextSelector().GetTextStart();
259 auto end = textPattern->GetTextSelector().GetTextEnd();
260 return UtfUtils::Str16DebugToStr8(textPattern->GetSelectedText(start, end));
261 }
262
GetSelectAreaFromRects(SelectRectsType pos)263 RectF TextSelectOverlay::GetSelectAreaFromRects(SelectRectsType pos)
264 {
265 auto pattern = GetPattern<TextPattern>();
266 RectF res;
267 CHECK_NULL_RETURN(pattern, res);
268 auto selectRects = pattern->GetTextBoxes();
269 auto textPaintOffset = GetPaintOffsetWithoutTransform();
270 if (selectRects.empty()) {
271 res.SetOffset(res.GetOffset() + textPaintOffset);
272 GetSelectAreaFromHandle(res);
273 ApplySelectAreaWithKeyboard(res);
274 return res;
275 }
276 if (pos == SelectRectsType::LEFT_TOP_POINT) {
277 selectRects.erase(std::next(selectRects.begin()), selectRects.end());
278 selectRects.front().SetSize({0, 0});
279 } else if (pos == SelectRectsType::RIGHT_BOTTOM_POINT) {
280 selectRects.erase(selectRects.begin(), std::prev(selectRects.end()));
281 selectRects.front().SetRect({selectRects.front().Right(), selectRects.front().Bottom()}, {0, 0});
282 }
283 auto contentRect = pattern->GetTextContentRect();
284 auto textRect = pattern->GetTextRect();
285 auto textContentRect = pattern->GetTextContentRect(true);
286 res = MergeSelectedBoxes(selectRects, contentRect, textRect, textPaintOffset);
287 RectF visibleContentRect;
288 if (GetRenderClipValue() || LessOrEqual(textContentRect.Height(), contentRect.Height()) ||
289 !GetClipHandleViewPort(visibleContentRect)) {
290 visibleContentRect = RectF(contentRect.GetOffset() + textPaintOffset, contentRect.GetSize());
291 visibleContentRect = GetVisibleRect(pattern->GetHost(), visibleContentRect);
292 }
293 auto intersectRect = res.IntersectRectT(visibleContentRect);
294 intersectRect.SetWidth(std::max(intersectRect.Width(), 0.0f));
295 intersectRect.SetHeight(std::max(intersectRect.Height(), 0.0f));
296 if (hasTransform_) {
297 intersectRect.SetOffset(intersectRect.GetOffset() - textPaintOffset);
298 GetGlobalRectWithTransform(intersectRect);
299 }
300 ApplySelectAreaWithKeyboard(intersectRect);
301 return intersectRect;
302 }
303
GetSelectAreaFromHandle(RectF & rect)304 void TextSelectOverlay::GetSelectAreaFromHandle(RectF& rect)
305 {
306 auto firstHandle = GetFirstHandleInfo();
307 if (firstHandle) {
308 auto firstRect = firstHandle->paintRect;
309 if (hasTransform_) {
310 firstRect.SetOffset(firstRect.GetOffset() - GetPaintOffsetWithoutTransform());
311 GetGlobalRectWithTransform(firstRect);
312 }
313 rect = firstRect;
314 return;
315 }
316 auto secondHandle = GetSecondHandleInfo();
317 if (secondHandle) {
318 auto secondRect = secondHandle->paintRect;
319 if (hasTransform_) {
320 secondRect.SetOffset(secondRect.GetOffset() - GetPaintOffsetWithoutTransform());
321 GetGlobalRectWithTransform(secondRect);
322 }
323 rect = secondRect;
324 }
325 }
326
OnUpdateMenuInfo(SelectMenuInfo & menuInfo,SelectOverlayDirtyFlag dirtyFlag)327 void TextSelectOverlay::OnUpdateMenuInfo(SelectMenuInfo& menuInfo, SelectOverlayDirtyFlag dirtyFlag)
328 {
329 auto textPattern = GetPattern<TextPattern>();
330 CHECK_NULL_VOID(textPattern);
331 menuInfo.showCopyAll = !textPattern->IsSelectAll();
332 menuInfo.showCopy = !textPattern->GetTextSelector().SelectNothing();
333 menuInfo.showTranslate = menuInfo.showCopy && textPattern->IsShowTranslate() && IsNeedMenuTranslate();
334 menuInfo.showSearch = menuInfo.showCopy && textPattern->IsShowSearch() && IsNeedMenuSearch();
335 menuInfo.showShare = menuInfo.showCopy && IsSupportMenuShare() && IsNeedMenuShare();
336 if (textPattern->IsShowAIMenuOption()) {
337 // do not support two selected ai entity, hence it's enough to pick first item to determine type
338 auto firstSpanItem = textPattern->GetAIItemOption().begin()->second;
339 menuInfo.aiMenuOptionType = firstSpanItem.type;
340 } else {
341 menuInfo.aiMenuOptionType = TextDataDetectType::INVALID;
342 }
343 menuInfo.isAskCeliaEnabled = textPattern->IsAskCeliaEnabled();
344 menuInfo.menuIsShow = IsShowMenu();
345 menuInfo.showCut = false;
346 menuInfo.showPaste = false;
347 menuInfo.hasOnPrepareMenuCallback = onPrepareMenuCallback_ ? true : false;
348 }
349
OnUpdateSelectOverlayInfo(SelectOverlayInfo & overlayInfo,int32_t requestCode)350 void TextSelectOverlay::OnUpdateSelectOverlayInfo(SelectOverlayInfo& overlayInfo, int32_t requestCode)
351 {
352 overlayInfo.clipHandleDrawRect = IsClipHandleWithViewPort();
353 BaseTextSelectOverlay::OnUpdateSelectOverlayInfo(overlayInfo, requestCode);
354 auto textPattern = GetPattern<TextPattern>();
355 CHECK_NULL_VOID(textPattern);
356 textPattern->CopySelectionMenuParams(overlayInfo);
357 auto layoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
358 CHECK_NULL_VOID(layoutProperty);
359 overlayInfo.handlerColor = layoutProperty->GetCursorColor();
360 OnUpdateOnCreateMenuCallback(overlayInfo);
361 auto scrollableParent = FindScrollableParent();
362 if (scrollableParent) {
363 auto weakParent = WeakClaim(AceType::RawPtr(scrollableParent));
364 overlayInfo.onHandlePanMove = [weak = WeakClaim(this), weakParent](const GestureEvent& event, bool isFirst) {
365 auto overlay = weak.Upgrade();
366 CHECK_NULL_VOID(overlay);
367 overlay->TriggerScrollableParentToScroll(weakParent.Upgrade(), event.GetGlobalLocation(), false);
368 };
369 overlayInfo.onHandlePanEnd = [weak = WeakClaim(this), weakParent](const GestureEvent& event, bool isFirst) {
370 auto overlay = weak.Upgrade();
371 CHECK_NULL_VOID(overlay);
372 overlay->TriggerScrollableParentToScroll(weakParent.Upgrade(), event.GetGlobalLocation(), true);
373 };
374 overlayInfo.getDeltaHandleOffset = [weak = WeakClaim(this)]() {
375 auto overlay = weak.Upgrade();
376 CHECK_NULL_RETURN(overlay, OffsetF());
377 auto hostPaintOffset = overlay->GetHotPaintOffset();
378 auto deltaOffset = overlay->hostPaintOffset_ - hostPaintOffset;
379 overlay->hostPaintOffset_ = hostPaintOffset;
380 return deltaOffset;
381 };
382 }
383 overlayInfo.menuCallback.showMenuOnMoveDone = [weak = WeakClaim(this)]() {
384 auto overlay = weak.Upgrade();
385 CHECK_NULL_RETURN(overlay, true);
386 auto textPattern = overlay->GetPattern<TextPattern>();
387 CHECK_NULL_RETURN(textPattern, true);
388 return !textPattern->IsSelectedTypeChange();
389 };
390 }
391
OnMenuItemAction(OptionMenuActionId id,OptionMenuType type)392 void TextSelectOverlay::OnMenuItemAction(OptionMenuActionId id, OptionMenuType type)
393 {
394 auto textPattern = GetPattern<TextPattern>();
395 CHECK_NULL_VOID(textPattern);
396 switch (id) {
397 case OptionMenuActionId::COPY:
398 textPattern->HandleOnCopy();
399 break;
400 case OptionMenuActionId::SELECT_ALL:
401 textPattern->HandleOnSelectAll();
402 break;
403 case OptionMenuActionId::TRANSLATE:
404 HandleOnTranslate();
405 break;
406 case OptionMenuActionId::SEARCH:
407 HandleOnSearch();
408 break;
409 case OptionMenuActionId::SHARE:
410 HandleOnShare();
411 break;
412 case OptionMenuActionId::ASK_CELIA:
413 textPattern->HandleOnAskCelia();
414 break;
415 default:
416 TAG_LOGI(AceLogTag::ACE_TEXT, "Unsupported menu option id %{public}d", id);
417 break;
418 }
419 }
420
OnMenuItemAction(OptionMenuActionId id,OptionMenuType type,const std::string & labelInfo)421 void TextSelectOverlay::OnMenuItemAction(OptionMenuActionId id, OptionMenuType type, const std::string& labelInfo)
422 {
423 auto textPattern = GetPattern<TextPattern>();
424 CHECK_NULL_VOID(textPattern);
425 if (labelInfo == "") {
426 OnMenuItemAction(id, type);
427 return;
428 }
429 switch (id) {
430 case OptionMenuActionId::AI_MENU_OPTION:
431 textPattern->HandleAIMenuOption(labelInfo);
432 break;
433 default:
434 TAG_LOGI(AceLogTag::ACE_TEXT, "Unsupported menu option id %{public}d", id);
435 break;
436 }
437 }
438
IsAIMenuOptionChanged(SelectMenuInfo & menuInfo)439 void TextSelectOverlay::IsAIMenuOptionChanged(SelectMenuInfo& menuInfo)
440 {
441 auto textPattern = GetPattern<TextPattern>();
442 CHECK_NULL_VOID(textPattern);
443
444 auto oldIsShowAIMenuOption = textPattern->IsShowAIMenuOption();
445 auto oldIsShowAskCelia = textPattern->IsAskCeliaEnabled();
446 textPattern->UpdateAIMenuOptions();
447 menuInfo.isShowAIMenuOptionChanged =
448 oldIsShowAIMenuOption != textPattern->IsShowAIMenuOption() ||
449 oldIsShowAskCelia != textPattern->IsAskCeliaEnabled();
450
451 if (textPattern->IsShowAIMenuOption()) {
452 // do not support two selected ai entity, hence it's enough to pick first item to determine type
453 auto firstSpanItem = textPattern->GetAIItemOption().begin()->second; // null check
454 menuInfo.aiMenuOptionType = firstSpanItem.type;
455 } else {
456 menuInfo.aiMenuOptionType = TextDataDetectType::INVALID;
457 }
458 menuInfo.isAskCeliaEnabled = textPattern->IsAskCeliaEnabled();
459 }
460
OnCloseOverlay(OptionMenuType menuType,CloseReason reason,RefPtr<OverlayInfo> info)461 void TextSelectOverlay::OnCloseOverlay(OptionMenuType menuType, CloseReason reason, RefPtr<OverlayInfo> info)
462 {
463 auto isDragging = GetIsHandleDragging();
464 if (isDragging) {
465 TriggerScrollableParentToScroll(FindScrollableParent(), Offset(), true);
466 }
467 BaseTextSelectOverlay::OnCloseOverlay(menuType, reason, info);
468 auto textPattern = GetPattern<TextPattern>();
469 CHECK_NULL_VOID(textPattern);
470 if (reason == CloseReason::CLOSE_REASON_HOLD_BY_OTHER || reason == CloseReason::CLOSE_REASON_TOOL_BAR ||
471 reason == CloseReason::CLOSE_REASON_BACK_PRESSED) {
472 textPattern->ResetSelection();
473 }
474 if (textPattern->GetMagnifierController()) {
475 textPattern->GetMagnifierController()->RemoveMagnifierFrameNode();
476 }
477 }
478
OnHandleGlobalTouchEvent(SourceType sourceType,TouchType touchType,bool touchInside)479 void TextSelectOverlay::OnHandleGlobalTouchEvent(SourceType sourceType, TouchType touchType, bool touchInside)
480 {
481 auto textPattern = GetPattern<TextPattern>();
482 CHECK_NULL_VOID(textPattern);
483 if (IsMouseClickDown(sourceType, touchType) && !touchInside) {
484 textPattern->ResetSelection();
485 }
486 BaseTextSelectOverlay::OnHandleGlobalTouchEvent(sourceType, touchType);
487 }
488
OnAncestorNodeChanged(FrameNodeChangeInfoFlag flag)489 void TextSelectOverlay::OnAncestorNodeChanged(FrameNodeChangeInfoFlag flag)
490 {
491 auto isDragging = GetIsHandleDragging();
492 if (IsAncestorNodeGeometryChange(flag)) {
493 auto textPattern = GetPattern<TextPattern>();
494 CHECK_NULL_VOID(textPattern);
495 textPattern->UpdateParentGlobalOffset();
496 textPattern->CalculateHandleOffsetAndShowOverlay();
497 UpdateViewPort();
498 if (isDragging && isDraggingFirstHandle_) {
499 UpdateSecondHandleOffset();
500 return;
501 }
502 if (isDragging && !isDraggingFirstHandle_) {
503 UpdateFirstHandleOffset();
504 return;
505 }
506 UpdateAllHandlesOffset();
507 }
508 if (!isDragging) {
509 BaseTextSelectOverlay::OnAncestorNodeChanged(flag);
510 }
511 }
512
OnHandleLevelModeChanged(HandleLevelMode mode)513 void TextSelectOverlay::OnHandleLevelModeChanged(HandleLevelMode mode)
514 {
515 if (handleLevelMode_ != mode && mode == HandleLevelMode::OVERLAY) {
516 auto textPattern = GetPattern<TextPattern>();
517 CHECK_NULL_VOID(textPattern);
518 textPattern->UpdateParentGlobalOffset();
519 textPattern->CalculateHandleOffsetAndShowOverlay();
520 UpdateAllHandlesOffset();
521 }
522 if (mode == HandleLevelMode::OVERLAY) {
523 BaseTextSelectOverlay::OnHandleLevelModeChanged(mode);
524 } else {
525 BaseTextSelectOverlay::SetHandleLevelMode(mode);
526 BaseTextSelectOverlay::UpdateViewPort();
527 }
528 }
529
OnHandleMoveStart(const GestureEvent & event,bool isFirst)530 void TextSelectOverlay::OnHandleMoveStart(const GestureEvent& event, bool isFirst)
531 {
532 BaseTextSelectOverlay::OnHandleMoveStart(event, isFirst);
533 auto textPattern = GetPattern<TextPattern>();
534 CHECK_NULL_VOID(textPattern);
535 textPattern->ChangeHandleHeight(event, isFirst, IsOverlayMode());
536 auto manager = GetManager<SelectContentOverlayManager>();
537 CHECK_NULL_VOID(manager);
538 manager->MarkInfoChange(isFirst ? DIRTY_FIRST_HANDLE : DIRTY_SECOND_HANDLE);
539 manager->SetHandleCircleIsShow(isFirst, false);
540 isDraggingFirstHandle_ = isFirst;
541 hostPaintOffset_ = GetHotPaintOffset();
542 textPattern->SetupMagnifier();
543 }
544
OnOverlayClick(const GestureEvent & event,bool isFirst)545 void TextSelectOverlay::OnOverlayClick(const GestureEvent& event, bool isFirst)
546 {
547 if (!IsSingleHandle()) {
548 ToggleMenu();
549 }
550 }
551
UpdateClipHandleViewPort(RectF & rect)552 void TextSelectOverlay::UpdateClipHandleViewPort(RectF& rect)
553 {
554 auto host = GetOwner();
555 CHECK_NULL_VOID(host);
556 auto renderContext = host->GetRenderContext();
557 CHECK_NULL_VOID(renderContext);
558 if (GetRenderClipValue()) {
559 return;
560 }
561 auto clipNode = host->GetAncestorNodeOfFrame(true);
562 RefPtr<FrameNode> prevNode;
563 while (clipNode) {
564 renderContext = clipNode->GetRenderContext();
565 CHECK_NULL_VOID(renderContext);
566 if (renderContext->GetClipEdge().value_or(false)) {
567 break;
568 }
569 prevNode = clipNode;
570 clipNode = clipNode->GetAncestorNodeOfFrame(true);
571 }
572 if (clipNode) {
573 RectF visibleRect;
574 RectF frameRect;
575 clipNode->GetVisibleRect(visibleRect, frameRect);
576 if (GreatNotEqual(rect.Top(), visibleRect.Bottom()) || GreatNotEqual(rect.Left(), visibleRect.Right())) {
577 return;
578 }
579 rect.SetHeight(visibleRect.Bottom() - rect.Top());
580 rect.SetWidth(visibleRect.Right() - rect.Left());
581 return;
582 }
583 // root node.
584 if (prevNode) {
585 auto geoNode = prevNode->GetGeometryNode();
586 CHECK_NULL_VOID(geoNode);
587 RectF visibleRect = geoNode->GetFrameRect();
588 if (GreatNotEqual(rect.Top(), visibleRect.Height()) || GreatNotEqual(rect.Left(), visibleRect.Width())) {
589 return;
590 }
591 rect.SetHeight(visibleRect.Height() - rect.Top());
592 rect.SetWidth(visibleRect.Width() - rect.Left());
593 }
594 }
595
TriggerScrollableParentToScroll(const RefPtr<ScrollablePattern> scrollablePattern,const Offset & globalOffset,bool isStopAutoScroll)596 void TextSelectOverlay::TriggerScrollableParentToScroll(
597 const RefPtr<ScrollablePattern> scrollablePattern, const Offset& globalOffset, bool isStopAutoScroll)
598 {
599 CHECK_NULL_VOID(scrollablePattern);
600 auto scrollAxis = scrollablePattern->GetAxis();
601 if (!scrollablePattern->IsScrollable() || (scrollAxis != Axis::VERTICAL && scrollAxis != Axis::HORIZONTAL)) {
602 return;
603 }
604 auto scrollableHost = scrollablePattern->GetHost();
605 CHECK_NULL_VOID(scrollableHost);
606 auto scrollableFrameRect = scrollableHost->GetPaintRectWithTransform();
607 auto host = GetOwner();
608 CHECK_NULL_VOID(host);
609 auto hostRect = host->GetPaintRectWithTransform();
610 auto hostSize = hostRect.Height();
611 auto scrollableParentSize = scrollableFrameRect.Height();
612 if (scrollAxis == Axis::HORIZONTAL) {
613 hostSize = hostRect.Width();
614 scrollableParentSize = scrollableFrameRect.Width();
615 }
616 if (LessOrEqual(hostSize, scrollableParentSize)) {
617 return;
618 }
619 RefPtr<NotifyDragEvent> notifyDragEvent = AceType::MakeRefPtr<NotifyDragEvent>();
620 notifyDragEvent->SetX(globalOffset.GetX());
621 notifyDragEvent->SetY(globalOffset.GetY());
622 scrollablePattern->HandleOnDragStatusCallback(
623 isStopAutoScroll ? DragEventType::DROP : DragEventType::MOVE, notifyDragEvent);
624 }
625
FindScrollableParent()626 const RefPtr<ScrollablePattern> TextSelectOverlay::FindScrollableParent()
627 {
628 auto host = GetOwner();
629 CHECK_NULL_RETURN(host, nullptr);
630 auto parent = host->GetAncestorNodeOfFrame(true);
631 while (parent) {
632 auto scrollablePattern = parent->GetPattern<ScrollablePattern>();
633 if (scrollablePattern && scrollablePattern->IsScrollable()) {
634 return scrollablePattern;
635 }
636 parent = parent->GetAncestorNodeOfFrame(true);
637 }
638 return nullptr;
639 }
640
GetHotPaintOffset()641 OffsetF TextSelectOverlay::GetHotPaintOffset()
642 {
643 auto host = GetOwner();
644 CHECK_NULL_RETURN(host, hostPaintOffset_);
645 auto renderContext = host->GetRenderContext();
646 CHECK_NULL_RETURN(renderContext, hostPaintOffset_);
647 return renderContext->GetPaintRectWithTransform().GetOffset();
648 }
649
GetHandleColor()650 std::optional<Color> TextSelectOverlay::GetHandleColor()
651 {
652 auto textPattern = GetPattern<TextPattern>();
653 CHECK_NULL_RETURN(textPattern, std::nullopt);
654 auto layoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
655 CHECK_NULL_RETURN(layoutProperty, std::nullopt);
656 return layoutProperty->GetCursorColor();
657 }
658
AllowTranslate()659 bool TextSelectOverlay::AllowTranslate()
660 {
661 auto textPattern = GetPattern<TextPattern>();
662 CHECK_NULL_RETURN(textPattern, false);
663 return !textPattern->GetTextSelector().SelectNothing();
664 }
665
AllowSearch()666 bool TextSelectOverlay::AllowSearch()
667 {
668 auto textPattern = GetPattern<TextPattern>();
669 CHECK_NULL_RETURN(textPattern, false);
670 return !textPattern->GetTextSelector().SelectNothing();
671 }
672
AllowShare()673 bool TextSelectOverlay::AllowShare()
674 {
675 auto textPattern = GetPattern<TextPattern>();
676 CHECK_NULL_RETURN(textPattern, false);
677 return !textPattern->GetTextSelector().SelectNothing();
678 }
679
GetRenderClipValue() const680 bool TextSelectOverlay::GetRenderClipValue() const
681 {
682 auto defaultClipValue = Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE);
683 auto pattern = GetPattern<Pattern>();
684 CHECK_NULL_RETURN(pattern, defaultClipValue);
685 auto host = pattern->GetHost();
686 CHECK_NULL_RETURN(host, defaultClipValue);
687 auto renderContext = host->GetRenderContext();
688 CHECK_NULL_RETURN(renderContext, defaultClipValue);
689 return renderContext->GetClipEdge().value_or(defaultClipValue);
690 }
691
GetSelectOverlayInfo()692 std::optional<SelectOverlayInfo> TextSelectOverlay::GetSelectOverlayInfo()
693 {
694 auto manager = GetManager<SelectContentOverlayManager>();
695 CHECK_NULL_RETURN(manager, std::optional<SelectOverlayInfo>());
696 return manager->GetSelectOverlayInfo();
697 }
698
ChangeSecondHandleHeight(const GestureEvent & event,bool isOverlayMode)699 bool TextSelectOverlay::ChangeSecondHandleHeight(const GestureEvent& event, bool isOverlayMode)
700 {
701 if (isOverlayMode || CheckSwitchToMode(HandleLevelMode::OVERLAY)) {
702 return false;
703 }
704 auto secondHandleInfo = GetSecondHandleInfo();
705 CHECK_NULL_RETURN(secondHandleInfo, false);
706 auto handleRect = secondHandleInfo->localPaintRect;
707 auto height = handleRect.Height();
708 auto textPattern = GetPattern<TextPattern>();
709 CHECK_NULL_RETURN(textPattern, false);
710 textPattern->CalculateDefaultHandleHeight(height);
711 auto touchOffset = event.GetLocalLocation();
712 bool isTouchHandleCircle = GreatNotEqual(touchOffset.GetY(), handleRect.Bottom());
713 auto handleOffsetY =
714 isTouchHandleCircle ? handleRect.Bottom() - height : static_cast<float>(touchOffset.GetY()) - height / 2.0f;
715 auto secondHandle = textPattern->GetTextSelector().secondHandle;
716 secondHandle.SetTop(handleOffsetY + handleGlobalOffset_.GetY());
717 secondHandle.SetHeight(height);
718 textPattern->UpdateTextSelectorSecondHandle(secondHandle);
719 return true;
720 }
721
GetVisibleDragViewHandles(RectF & first,RectF & second)722 void TextSelectOverlay::GetVisibleDragViewHandles(RectF& first, RectF& second)
723 {
724 auto selectOverlayInfo = GetSelectOverlayInfos();
725 CHECK_NULL_VOID(selectOverlayInfo);
726 RectF firstHandle;
727 RectF secondHandle;
728 if (!GetDragViewHandleRects(firstHandle, secondHandle)) {
729 return;
730 }
731 if (selectOverlayInfo->firstHandle.isShow) {
732 first = firstHandle;
733 }
734 if (selectOverlayInfo->secondHandle.isShow) {
735 second = secondHandle;
736 }
737 }
738 } // namespace OHOS::Ace::NG
739