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_field/text_field_select_overlay.h"
17
18 #include <algorithm>
19 #include <cstdint>
20
21 #include "base/geometry/ng/rect_t.h"
22 #include "base/memory/ace_type.h"
23 #include "base/utils/utf_helper.h"
24 #include "base/utils/utils.h"
25 #include "core/components_ng/pattern/text_field/text_field_pattern.h"
26
27 #ifndef ACE_UNITTEST
28 #ifdef ENABLE_STANDARD_INPUT
29 #include "input_method_controller.h"
30 #endif
31 #endif
32
33 namespace OHOS::Ace::NG {
34 namespace {
35 // uncertainty range when comparing selectedTextBox to contentRect
36 constexpr float BOX_EPSILON = 0.5f;
37 constexpr uint32_t REQUEST_SELECT_ALL = 1 << 1;
38 } // namespace
39
PreProcessOverlay(const OverlayRequest & request)40 bool TextFieldSelectOverlay::PreProcessOverlay(const OverlayRequest& request)
41 {
42 auto pattern = GetPattern<TextFieldPattern>();
43 CHECK_NULL_RETURN(pattern, false);
44 auto host = pattern->GetHost();
45 CHECK_NULL_RETURN(host, false);
46 auto layoutProperty = host->GetLayoutProperty<TextFieldLayoutProperty>();
47 CHECK_NULL_RETURN(layoutProperty, false);
48 bool isHideRightClickMenu = layoutProperty->GetSelectionMenuHiddenValue(false) && IsUsingMouse();
49 bool isFontSizeZero = layoutProperty->HasFontSize() && NearZero(layoutProperty->GetFontSize()->Value());
50 if (isHideRightClickMenu || (isFontSizeZero && !SelectOverlayIsOn())) {
51 TAG_LOGI(AceLogTag::ACE_TEXT_FIELD,
52 "The selection menu is not displayed cause Font size is zero or selectionMenuHidden is true");
53 return false;
54 }
55 UpdatePattern(request);
56 CHECK_NULL_RETURN(!pattern->IsTransparent(), false);
57 pattern->ShowSelect();
58 SetEnableHandleLevel(true);
59 SetEnableSubWindowMenu(true);
60 CheckEnableContainerModal();
61 return true;
62 }
63
UpdatePattern(const OverlayRequest & request)64 void TextFieldSelectOverlay::UpdatePattern(const OverlayRequest& request)
65 {
66 if (IsSingleHandle()) {
67 return;
68 }
69 auto pattern = GetPattern<TextFieldPattern>();
70 CHECK_NULL_VOID(pattern);
71 bool isRequestSelectAll = (static_cast<uint32_t>(request.requestCode) & REQUEST_SELECT_ALL) == REQUEST_SELECT_ALL;
72 auto selectController = pattern->GetTextSelectController();
73 if (pattern->IsSelected() && selectController->IsHandleSamePosition()) {
74 SetIsSingleHandle(true);
75 selectController->UpdateCaretIndex(selectController->GetFirstHandleIndex());
76 selectController->UpdateCaretOffset();
77 selectController->MoveCaretToContentRect(selectController->GetCaretIndex());
78 } else if (!IsSingleHandle() && !isRequestSelectAll) {
79 auto rects = pattern->GetTextBoxes();
80 if (!rects.empty() && NearEqual(rects.size(), 1) && NearZero(rects[0].Width())) {
81 SetIsSingleHandle(true);
82 selectController->UpdateCaretIndex(selectController->GetFirstHandleIndex());
83 selectController->UpdateCaretOffset();
84 }
85 }
86 }
87
OnAfterSelectOverlayShow(bool isCreate)88 void TextFieldSelectOverlay::OnAfterSelectOverlayShow(bool isCreate)
89 {
90 CHECK_NULL_VOID(latestReqeust_);
91 auto manager = GetManager<SelectContentOverlayManager>();
92 CHECK_NULL_VOID(manager);
93 auto pattern = GetPattern<TextFieldPattern>();
94 CHECK_NULL_VOID(pattern);
95 if (latestReqeust_->hideHandle) {
96 manager->HideHandle();
97 }
98 auto selectOverlayInfo = manager->GetSelectOverlayInfo();
99 CHECK_NULL_VOID(selectOverlayInfo);
100 if (!selectOverlayInfo->isUsingMouse) {
101 if (manager->IsHiddenHandle()) {
102 pattern->StartTwinkling();
103 } else {
104 pattern->StopTwinkling();
105 }
106 }
107 manager->MarkInfoChange(DIRTY_SELECT_TEXT | DIRTY_SELECT_AREA);
108 latestReqeust_.reset();
109 }
110
OnCloseOverlay(OptionMenuType menuType,CloseReason reason,RefPtr<OverlayInfo> info)111 void TextFieldSelectOverlay::OnCloseOverlay(OptionMenuType menuType, CloseReason reason, RefPtr<OverlayInfo> info)
112 {
113 BaseTextSelectOverlay::OnCloseOverlay(menuType, reason, info);
114 auto pattern = GetPattern<TextFieldPattern>();
115 CHECK_NULL_VOID(pattern);
116 CloseMagnifier();
117 if (CloseReason::CLOSE_REASON_BACK_PRESSED == reason) {
118 OnResetTextSelection();
119 if (info && info->isSingleHandle) {
120 TAG_LOGI(AceLogTag::ACE_TEXT_FIELD, "SingleHandle OnCloseOverlay");
121 pattern->OnBackPressed();
122 }
123 } else if (CloseReason::CLOSE_REASON_HOLD_BY_OTHER == reason) {
124 OnResetTextSelection();
125 }
126 pattern->StopContentScroll();
127 RemoveAvoidKeyboardCallback();
128 if (pattern->GetFloatingCursorVisible()) {
129 pattern->ResetFloatingCursorState();
130 auto host = pattern->GetHost();
131 CHECK_NULL_VOID(host);
132 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
133 }
134 }
135
OnHandleGlobalTouchEvent(SourceType sourceType,TouchType touchType,bool touchInside)136 void TextFieldSelectOverlay::OnHandleGlobalTouchEvent(SourceType sourceType, TouchType touchType, bool touchInside)
137 {
138 BaseTextSelectOverlay::OnHandleGlobalTouchEvent(sourceType, touchType);
139 SetLastSourceType(sourceType);
140 }
141
HandleOnShowMenu()142 void TextFieldSelectOverlay::HandleOnShowMenu()
143 {
144 auto selectArea = GetSelectArea();
145 if (Negative(selectArea.Width()) || Negative(selectArea.Height())) {
146 TAG_LOGI(AceLogTag::ACE_TEXT_FIELD, "The selected area is not visible.");
147 return;
148 }
149 if (SelectOverlayIsOn()) {
150 auto manager = GetManager<SelectContentOverlayManager>();
151 manager->ShowOptionMenu();
152 return;
153 }
154 auto pattern = GetPattern<TextFieldPattern>();
155 CHECK_NULL_VOID(pattern);
156 SetUsingMouse(lastSourceType_ == SourceType::MOUSE);
157 if (IsUsingMouse()) {
158 auto controller = pattern->GetTextSelectController();
159 SetMouseMenuOffset(controller->GetCaretRect().GetOffset() + pattern->GetParentGlobalOffset());
160 } else {
161 auto isSingleHandle = pattern->GetTextContentController()->IsEmpty() || !pattern->IsSelected();
162 SetIsSingleHandle(isSingleHandle);
163 }
164 ProcessOverlay({ .animation = true });
165 }
166
GetFirstHandleInfo()167 std::optional<SelectHandleInfo> TextFieldSelectOverlay::GetFirstHandleInfo()
168 {
169 if (IsSingleHandle()) {
170 return std::nullopt;
171 }
172 auto handleRect = GetHandleLocalPaintRect(DragHandleIndex::FIRST);
173 return GetHandleInfo(handleRect);
174 }
175
GetSecondHandleInfo()176 std::optional<SelectHandleInfo> TextFieldSelectOverlay::GetSecondHandleInfo()
177 {
178 auto handleRect = GetHandleLocalPaintRect(DragHandleIndex::SECOND);
179 return GetHandleInfo(handleRect);
180 }
181
GetHandleLocalPaintRect(DragHandleIndex dragHandleIndex)182 RectF TextFieldSelectOverlay::GetHandleLocalPaintRect(DragHandleIndex dragHandleIndex)
183 {
184 auto pattern = GetPattern<TextFieldPattern>();
185 CHECK_NULL_RETURN(pattern, RectF());
186 auto controller = pattern->GetTextSelectController();
187 CHECK_NULL_RETURN(controller, RectF());
188 controller->AdjustAllHandlesWithBoundary();
189 if (dragHandleIndex == DragHandleIndex::FIRST) {
190 return controller->GetFirstHandleRect();
191 } else if (dragHandleIndex == DragHandleIndex::SECOND) {
192 if (IsSingleHandle()) {
193 return controller->GetCaretInfo().originalRect;
194 }
195 return controller->GetSecondHandleRect();
196 } else { // DragHandleIndex::NONE
197 return RectF();
198 }
199 }
200
GetHandleInfo(const RectF & handlePaintRect)201 std::optional<SelectHandleInfo> TextFieldSelectOverlay::GetHandleInfo(const RectF& handlePaintRect)
202 {
203 auto handleRect = handlePaintRect;
204 SelectHandleInfo handleInfo;
205 handleInfo.localPaintRect = handlePaintRect;
206 handleRect.SetOffset(handleRect.GetOffset() + GetPaintOffsetWithoutTransform());
207 handleInfo.paintRect = handleRect;
208 handleInfo.isShow = CheckHandleVisible(handleRect);
209
210 SetTransformPaintInfo(handleInfo, handlePaintRect);
211 return handleInfo;
212 }
213
CheckHandleVisible(const RectF & handlePaintRect)214 bool TextFieldSelectOverlay::CheckHandleVisible(const RectF& handlePaintRect)
215 {
216 auto pattern = GetPattern<TextFieldPattern>();
217 CHECK_NULL_RETURN(pattern, false);
218 // use global offset.
219 auto contentRect = pattern->GetContentRect();
220 auto visibleRect = GetVisibleContentRect();
221 // revert to original offset.
222 auto paintRect = handlePaintRect;
223 paintRect.SetOffset(OffsetF(paintRect.GetX() + paintRect.Width() / 2.0f, paintRect.GetY()));
224 if (!pattern->IsTextArea()) {
225 auto verticalEpsilon = std::max(0.0f, paintRect.Height() - contentRect.Height());
226 return GreatOrEqual(paintRect.Top() + verticalEpsilon, visibleRect.Top()) &&
227 LessOrEqual(paintRect.Bottom() - verticalEpsilon, visibleRect.Bottom()) &&
228 LessOrEqual(paintRect.Left() - paintRect.Width() - BOX_EPSILON, visibleRect.Right()) &&
229 GreatOrEqual(paintRect.Right(), visibleRect.Left());
230 }
231 PointF bottomPoint = { paintRect.Left(), paintRect.Bottom() - BOX_EPSILON };
232 PointF topPoint = { paintRect.Left(), paintRect.Top() + BOX_EPSILON };
233 visibleRect.SetLeft(visibleRect.GetX() - BOX_EPSILON);
234 visibleRect.SetWidth(visibleRect.Width() + 2.0f * BOX_EPSILON);
235 visibleRect.SetTop(visibleRect.GetY() - BOX_EPSILON);
236 visibleRect.SetHeight(visibleRect.Height() + 2.0f * BOX_EPSILON);
237 return visibleRect.IsInRegion(bottomPoint) && visibleRect.IsInRegion(topPoint);
238 }
239
OnResetTextSelection()240 void TextFieldSelectOverlay::OnResetTextSelection()
241 {
242 auto pattern = GetPattern<TextFieldPattern>();
243 CHECK_NULL_VOID(pattern);
244 pattern->SetCaretPosition(pattern->GetTextSelectController()->GetEndIndex());
245 }
246
AfterCloseOverlay()247 void TextFieldSelectOverlay::AfterCloseOverlay()
248 {
249 CloseMagnifier();
250 }
251
CloseMagnifier()252 void TextFieldSelectOverlay::CloseMagnifier()
253 {
254 auto pattern = GetPattern<TextFieldPattern>();
255 CHECK_NULL_VOID(pattern);
256 if (pattern->GetMagnifierController()->GetShowMagnifier()) {
257 pattern->GetMagnifierController()->RemoveMagnifierFrameNode();
258 }
259 }
260
OnUpdateMenuInfo(SelectMenuInfo & menuInfo,SelectOverlayDirtyFlag dirtyFlag)261 void TextFieldSelectOverlay::OnUpdateMenuInfo(SelectMenuInfo& menuInfo, SelectOverlayDirtyFlag dirtyFlag)
262 {
263 auto pattern = GetPattern<TextFieldPattern>();
264 CHECK_NULL_VOID(pattern);
265 auto host = pattern->GetHost();
266 CHECK_NULL_VOID(host);
267 auto layoutProperty = host->GetLayoutProperty<TextFieldLayoutProperty>();
268 CHECK_NULL_VOID(layoutProperty);
269 auto hasText = pattern->HasText();
270 if ((dirtyFlag & DIRTY_COPY_ALL_ITEM) == DIRTY_COPY_ALL_ITEM) {
271 menuInfo.showCopyAll = hasText && !pattern->IsSelectAll();
272 return;
273 }
274 bool isHideSelectionMenu = layoutProperty->GetSelectionMenuHiddenValue(false);
275 #if defined(ENABLE_STANDARD_INPUT)
276 auto inputMethod = MiscServices::InputMethodController::GetInstance();
277 auto isSupportCameraInput = inputMethod &&
278 inputMethod->IsInputTypeSupported(MiscServices::InputType::CAMERA_INPUT) &&
279 !pattern->IsInPasswordMode();
280 #else
281 auto isSupportCameraInput = false;
282 #endif
283 menuInfo.showCameraInput = !pattern->IsSelected() && isSupportCameraInput && !pattern->HasCustomKeyboard();
284 if (IsUsingMouse()) {
285 auto manager = SelectContentOverlayManager::GetOverlayManager();
286 CHECK_NULL_VOID(manager);
287 menuInfo.menuIsShow = !isHideSelectionMenu || manager->IsOpen();
288 } else {
289 menuInfo.menuIsShow = (hasText || IsShowPaste() || menuInfo.showCameraInput) &&
290 !isHideSelectionMenu && IsShowMenu();
291 }
292 menuInfo.menuDisable = isHideSelectionMenu;
293 menuInfo.showPaste = IsShowPaste();
294 menuInfo.menuType = IsUsingMouse() ? OptionMenuType::MOUSE_MENU : OptionMenuType::TOUCH_MENU;
295 menuInfo.showCopy = hasText && pattern->AllowCopy() && pattern->IsSelected();
296 menuInfo.showCut = menuInfo.showCopy;
297 menuInfo.showCopyAll = hasText && !pattern->IsSelectAll();
298 menuInfo.showTranslate = menuInfo.showCopy && pattern->IsShowTranslate() && IsNeedMenuTranslate();
299 menuInfo.showSearch = menuInfo.showCopy && pattern->IsShowSearch() && IsNeedMenuSearch();
300 menuInfo.showShare = menuInfo.showCopy && IsSupportMenuShare() && IsNeedMenuShare();
301 menuInfo.showAIWrite = pattern->IsShowAIWrite() && pattern->IsSelected();
302 }
303
OnUpdateSelectOverlayInfo(SelectOverlayInfo & overlayInfo,int32_t requestCode)304 void TextFieldSelectOverlay::OnUpdateSelectOverlayInfo(SelectOverlayInfo& overlayInfo, int32_t requestCode)
305 {
306 overlayInfo.clipHandleDrawRect = IsClipHandleWithViewPort();
307 BaseTextSelectOverlay::OnUpdateSelectOverlayInfo(overlayInfo, requestCode);
308 auto textFieldPattern = GetPattern<TextFieldPattern>();
309 CHECK_NULL_VOID(textFieldPattern);
310 auto paintProperty = textFieldPattern->GetPaintProperty<TextFieldPaintProperty>();
311 CHECK_NULL_VOID(paintProperty);
312 overlayInfo.handlerColor = paintProperty->GetCursorColor();
313 OnUpdateOnCreateMenuCallback(overlayInfo);
314 auto layoutProperty =
315 DynamicCast<TextFieldLayoutProperty>(textFieldPattern->GetLayoutProperty<TextFieldLayoutProperty>());
316 CHECK_NULL_VOID(layoutProperty);
317 if (layoutProperty->HasMaxLines()) {
318 uint32_t maxLine = layoutProperty->GetMaxLinesValue(Infinity<uint32_t>());
319 overlayInfo.isSingleLine = (maxLine == 1);
320 }
321 if (!textFieldPattern->IsTextArea()) {
322 overlayInfo.checkHandleReverse = [](const RectF& first, const RectF& second) {
323 return GreatNotEqual(first.Left(), second.Left());
324 };
325 }
326 }
327
GetSelectAreaFromRects(SelectRectsType pos)328 RectF TextFieldSelectOverlay::GetSelectAreaFromRects(SelectRectsType pos)
329 {
330 auto pattern = GetPattern<TextFieldPattern>();
331 CHECK_NULL_RETURN(pattern, {});
332 auto host = pattern->GetHost();
333 CHECK_NULL_RETURN(host, {});
334 auto selectRects = pattern->GetTextBoxes();
335 RectF res(pattern->GetCaretRect());
336 auto textPaintOffset = host->GetTransformRelativeOffset();
337 if (selectRects.empty()) {
338 res.SetOffset(res.GetOffset() + textPaintOffset);
339 } else {
340 auto contentRect = pattern->GetContentRect();
341 auto textRect = pattern->GetTextRect();
342 if (pos == SelectRectsType::LEFT_TOP_POINT) {
343 selectRects.erase(std::next(selectRects.begin()), selectRects.end());
344 selectRects.front().SetSize({0, 0});
345 } else if (pos == SelectRectsType::RIGHT_BOTTOM_POINT) {
346 selectRects.erase(selectRects.begin(), std::prev(selectRects.end()));
347 selectRects.front().SetRect({selectRects.front().Right(), selectRects.front().Bottom()}, {0, 0});
348 }
349 res = MergeSelectedBoxes(selectRects, contentRect, textRect, textPaintOffset);
350 if (NearZero(res.Width())) {
351 pattern->AdjustSelectedBlankLineWidth(res);
352 }
353 }
354 auto globalContentRect = GetVisibleContentRect(true);
355 auto intersectRect = res.IntersectRectT(globalContentRect);
356 if (hasTransform_) {
357 intersectRect.SetOffset(intersectRect.GetOffset() - textPaintOffset);
358 GetGlobalRectWithTransform(intersectRect);
359 }
360 ApplySelectAreaWithKeyboard(intersectRect);
361 return intersectRect;
362 }
363
GetSelectedText()364 std::string TextFieldSelectOverlay::GetSelectedText()
365 {
366 auto pattern = GetPattern<TextFieldPattern>();
367 CHECK_NULL_RETURN(pattern, "");
368 auto textSelectController = pattern->GetTextSelectController();
369 CHECK_NULL_RETURN(textSelectController, "");
370 auto start = textSelectController->GetStartIndex();
371 auto end = textSelectController->GetEndIndex();
372 return UtfUtils::Str16DebugToStr8(pattern->GetTextContentController()->GetSelectedValue(start, end));
373 }
374
OnMenuItemAction(OptionMenuActionId id,OptionMenuType type)375 void TextFieldSelectOverlay::OnMenuItemAction(OptionMenuActionId id, OptionMenuType type)
376 {
377 auto pattern = GetPattern<TextFieldPattern>();
378 CHECK_NULL_VOID(pattern);
379 TAG_LOGI(AceLogTag::ACE_TEXT_FIELD, "Handle overlay menu, men id %{public}d, menu type %{public}d", id, type);
380 switch (id) {
381 case OptionMenuActionId::COPY:
382 pattern->HandleOnCopy();
383 return;
384 case OptionMenuActionId::CUT:
385 pattern->HandleOnCut();
386 return;
387 case OptionMenuActionId::SELECT_ALL:
388 pattern->HandleOnSelectAll(type == OptionMenuType::MOUSE_MENU, false, true);
389 return;
390 case OptionMenuActionId::PASTE:
391 pattern->HandleOnPaste();
392 return;
393 case OptionMenuActionId::TRANSLATE:
394 HandleOnTranslate();
395 return;
396 case OptionMenuActionId::SEARCH:
397 HandleOnSearch();
398 return;
399 case OptionMenuActionId::SHARE:
400 HandleOnShare();
401 return;
402 case OptionMenuActionId::CAMERA_INPUT:
403 pattern->HandleOnCameraInput();
404 return;
405 case OptionMenuActionId::AI_WRITE:
406 pattern->HandleOnAIWrite();
407 return;
408 default:
409 return;
410 }
411 }
412
GetTextAreaCaretPosition(const OffsetF & localOffset)413 int32_t TextFieldSelectOverlay::GetTextAreaCaretPosition(const OffsetF& localOffset)
414 {
415 auto pattern = GetPattern<TextFieldPattern>();
416 auto contentRect = pattern->GetContentRect();
417 auto paddingLeft = pattern->GetPaddingLeft();
418 auto textRect = pattern->GetTextRect();
419 Offset offset;
420 if (LessNotEqual(localOffset.GetY(), contentRect.GetY())) {
421 offset = Offset(localOffset.GetX() - paddingLeft, contentRect.GetY() - textRect.GetY());
422 } else if (GreatOrEqual(localOffset.GetY(), contentRect.GetY() + contentRect.Height())) {
423 offset = Offset(localOffset.GetX() - paddingLeft, contentRect.GetY() + contentRect.Height() - textRect.GetY());
424 } else {
425 offset = Offset(localOffset.GetX() - paddingLeft, localOffset.GetY() - textRect.GetY());
426 }
427 return pattern->ConvertTouchOffsetToCaretPosition(offset);
428 }
429
GetTextInputCaretPosition(const OffsetF & localOffset,bool isFirst)430 int32_t TextFieldSelectOverlay::GetTextInputCaretPosition(const OffsetF& localOffset, bool isFirst)
431 {
432 auto pattern = GetPattern<TextFieldPattern>();
433 auto contentRect = pattern->GetContentRect();
434 auto selectController = pattern->GetTextSelectController();
435 auto wideText = pattern->GetTextUtf16Value();
436 if (LessNotEqual(localOffset.GetX(), contentRect.GetX())) {
437 auto index = selectController->GetStartIndex();
438 if (!SelectOverlayIsOn()) {
439 return index;
440 }
441 if ((!isFirst && !IsHandleReverse()) || (isFirst && IsHandleReverse())) {
442 index = selectController->GetEndIndex();
443 }
444 return index;
445 }
446 if (GreatOrEqual(localOffset.GetX(), contentRect.GetX() + contentRect.Width())) {
447 auto index = selectController->GetEndIndex();
448 if (!SelectOverlayIsOn()) {
449 return index;
450 }
451 if ((isFirst && !IsHandleReverse()) || (!isFirst && IsHandleReverse())) {
452 index = selectController->GetStartIndex();
453 }
454 return index;
455 }
456 Offset offset(localOffset.GetX() - pattern->GetTextRect().GetX(), 0.0f);
457 return pattern->ConvertTouchOffsetToCaretPosition(offset);
458 }
459
GetCaretPositionOnHandleMove(const OffsetF & localOffset,bool isFirst)460 int32_t TextFieldSelectOverlay::GetCaretPositionOnHandleMove(const OffsetF& localOffset, bool isFirst)
461 {
462 auto pattern = GetPattern<TextFieldPattern>();
463 CHECK_NULL_RETURN(pattern, 0);
464 if (pattern->IsTextArea()) {
465 return GetTextAreaCaretPosition(localOffset);
466 }
467 return GetTextInputCaretPosition(localOffset, isFirst);
468 }
469
OnHandleMove(const RectF & handleRect,bool isFirst)470 void TextFieldSelectOverlay::OnHandleMove(const RectF& handleRect, bool isFirst)
471 {
472 auto pattern = GetPattern<TextFieldPattern>();
473 CHECK_NULL_VOID(pattern);
474 CHECK_NULL_VOID(pattern->HasText());
475 auto localOffset = handleRect.GetOffset();
476 localOffset.SetY(localOffset.GetY() + handleRect.Height() / 2.0f);
477 if (IsOverlayMode()) {
478 localOffset = localOffset - GetPaintOffsetWithoutTransform();
479 }
480 auto selectController = pattern->GetTextSelectController();
481 CHECK_NULL_VOID(selectController);
482 int32_t startIndex = selectController->GetFirstHandleIndex();
483 int32_t endIndex = selectController->GetSecondHandleIndex();
484 TriggerContentToScroll(localOffset, false);
485 if (IsSingleHandle()) {
486 auto isScrolling = pattern->GetContentScrollerIsScrolling();
487 auto contentRect = pattern->GetContentRect();
488 auto touchCaretX = std::clamp(localOffset.GetX(), contentRect.Left(), contentRect.Right());
489 // 1/4 line height.
490 auto yOffset = pattern->PreferredLineHeight() * 0.25f;
491 auto touchCaretY = std::clamp(localOffset.GetY(), contentRect.Top() + yOffset, contentRect.Bottom() - yOffset);
492 selectController->UpdateCaretInfoByOffset(!isScrolling ? Offset(touchCaretX, touchCaretY) :
493 Offset(localOffset.GetX(), localOffset.GetY()), !isScrolling, true);
494 pattern->ShowCaretAndStopTwinkling();
495 } else {
496 auto position = GetCaretPositionOnHandleMove(localOffset, isFirst);
497 pattern->StartVibratorByIndexChange(position, isFirst ? startIndex : endIndex);
498 if (isFirst) {
499 selectController->MoveFirstHandleToContentRect(position, false, false);
500 UpdateSecondHandleOffset();
501 } else {
502 selectController->MoveSecondHandleToContentRect(position, false, false);
503 UpdateFirstHandleOffset();
504 }
505 }
506 if (pattern->GetMagnifierController() && SelectOverlayIsOn()) {
507 if (IsSingleHandle()) {
508 pattern->SetMagnifierLocalOffsetToFloatingCaretPos();
509 } else {
510 auto magnifierLocalOffset = OffsetF(localOffset.GetX(), localOffset.GetY());
511 if (IsOverlayMode()) {
512 GetLocalPointWithTransform(magnifierLocalOffset);
513 }
514 pattern->GetMagnifierController()->SetLocalOffset(magnifierLocalOffset);
515 }
516 }
517 pattern->PlayScrollBarAppearAnimation();
518 auto tmpHost = pattern->GetHost();
519 CHECK_NULL_VOID(tmpHost);
520 tmpHost->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
521 }
522
OnHandleMoveDone(const RectF & rect,bool isFirst)523 void TextFieldSelectOverlay::OnHandleMoveDone(const RectF& rect, bool isFirst)
524 {
525 BaseTextSelectOverlay::OnHandleMoveDone(rect, isFirst);
526 auto pattern = GetPattern<TextFieldPattern>();
527 CHECK_NULL_VOID(pattern);
528 auto host = pattern->GetHost();
529 CHECK_NULL_VOID(host);
530 auto layoutProperty = host->GetLayoutProperty<TextFieldLayoutProperty>();
531 CHECK_NULL_VOID(layoutProperty);
532 auto overlayManager = GetManager<SelectContentOverlayManager>();
533 CHECK_NULL_VOID(overlayManager);
534 if (!layoutProperty->GetSelectionMenuHiddenValue(false)) {
535 overlayManager->MarkInfoChange(DIRTY_COPY_ALL_ITEM);
536 }
537 if (pattern->GetMagnifierController()) {
538 pattern->GetMagnifierController()->RemoveMagnifierFrameNode();
539 }
540 auto selectController = pattern->GetTextSelectController();
541 TriggerContentToScroll(OffsetF(), true);
542 overlayManager->ShowOptionMenu();
543 if (!IsSingleHandle()) {
544 if (selectController->GetFirstHandleIndex() == selectController->GetSecondHandleIndex()) {
545 CloseOverlay(true, CloseReason::CLOSE_REASON_NORMAL);
546 pattern->StartTwinkling();
547 selectController->MoveCaretToContentRect(pattern->GetCaretIndex());
548 } else {
549 if (isFirst) {
550 selectController->MoveFirstHandleToContentRect(selectController->GetFirstHandleIndex(), false);
551 } else {
552 selectController->MoveSecondHandleToContentRect(selectController->GetSecondHandleIndex(), false);
553 }
554 overlayManager->MarkInfoChange(DIRTY_DOUBLE_HANDLE | DIRTY_SELECT_AREA | DIRTY_SELECT_TEXT);
555 }
556 } else {
557 pattern->StopTwinkling();
558 // single handle use caret offset.
559 auto caretRect = selectController->GetCaretRect();
560 selectController->UpdateCaretInfoByOffset(
561 Offset(caretRect.Left(), caretRect.Top() + caretRect.Height() / 2.0f));
562 overlayManager->MarkInfoChange(DIRTY_SECOND_HANDLE);
563 }
564 overlayManager->SetHandleCircleIsShow(isFirst, true);
565 if (IsSingleHandle()) {
566 overlayManager->SetIsHandleLineShow(true);
567 }
568 pattern->ScheduleDisappearDelayTask();
569 pattern->UpdateCaretInfoToController();
570 pattern->FloatingCaretLand();
571 auto tmpHost = pattern->GetHost();
572 CHECK_NULL_VOID(tmpHost);
573 tmpHost->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
574 }
575
ProcessSelectAllOverlay(const OverlayRequest & request)576 void TextFieldSelectOverlay::ProcessSelectAllOverlay(const OverlayRequest& request)
577 {
578 OverlayRequest newRequest = request;
579 newRequest.requestCode = static_cast<uint32_t>(newRequest.requestCode) | REQUEST_SELECT_ALL;
580 ProcessOverlay(newRequest);
581 }
582
OnAncestorNodeChanged(FrameNodeChangeInfoFlag flag)583 void TextFieldSelectOverlay::OnAncestorNodeChanged(FrameNodeChangeInfoFlag flag)
584 {
585 if (IsAncestorNodeGeometryChange(flag) || IsAncestorNodeTransformChange(flag)) {
586 UpdateAllHandlesOffset();
587 }
588 BaseTextSelectOverlay::OnAncestorNodeChanged(flag);
589 }
590
OnHandleLevelModeChanged(HandleLevelMode mode)591 void TextFieldSelectOverlay::OnHandleLevelModeChanged(HandleLevelMode mode)
592 {
593 if (handleLevelMode_ != mode && mode == HandleLevelMode::OVERLAY) {
594 auto pattern = GetPattern<TextFieldPattern>();
595 if (pattern) {
596 pattern->UpdateParentGlobalOffset();
597 pattern->GetTextSelectController()->CalculateHandleOffset();
598 }
599 }
600 BaseTextSelectOverlay::OnHandleLevelModeChanged(mode);
601 }
602
OnOverlayClick(const GestureEvent & event,bool isFirst)603 void TextFieldSelectOverlay::OnOverlayClick(const GestureEvent& event, bool isFirst)
604 {
605 auto pattern = GetPattern<TextFieldPattern>();
606 CHECK_NULL_VOID(pattern);
607 auto recognizer = pattern->GetMultipleClickRecognizer();
608 CHECK_NULL_VOID(recognizer);
609 if (recognizer->IsValidClick(event)) {
610 TAG_LOGI(AceLogTag::ACE_TEXT_FIELD, "textfield overlayClick multiple click recognizer is running.");
611 auto overlayEvent = event;
612 overlayEvent.SetLocalLocation(recognizer->GetBeginLocalLocation());
613 overlayEvent.SetGlobalLocation(recognizer->GetBeginGlobalLocation());
614 pattern->HandleClickEvent(overlayEvent);
615 } else if (!IsSingleHandle()) {
616 if (pattern->HandleBetweenSelectedPosition(event)) {
617 TAG_LOGI(AceLogTag::ACE_TEXT_FIELD, "textfield HandleBetweenSelectedPosition");
618 return;
619 }
620 auto selectController = pattern->GetTextSelectController();
621 auto index = isFirst ? selectController->GetFirstHandleIndex() : selectController->GetSecondHandleIndex();
622 pattern->HandleSetSelection(index, index, false);
623 pattern->StartTwinkling();
624 } else {
625 TAG_LOGI(AceLogTag::ACE_TEXT_FIELD, "textfield overlayClick");
626 }
627 }
628
OnHandleIsHidden()629 void TextFieldSelectOverlay::OnHandleIsHidden()
630 {
631 auto pattern = GetPattern<TextFieldPattern>();
632 CHECK_NULL_VOID(pattern);
633 pattern->StartTwinkling();
634 }
635
OnHandleMoveStart(const GestureEvent & event,bool isFirst)636 void TextFieldSelectOverlay::OnHandleMoveStart(const GestureEvent& event, bool isFirst)
637 {
638 BaseTextSelectOverlay::OnHandleMoveStart(event, isFirst);
639 auto manager = GetManager<SelectContentOverlayManager>();
640 CHECK_NULL_VOID(manager);
641 auto pattern = GetPattern<TextFieldPattern>();
642 CHECK_NULL_VOID(pattern);
643 manager->SetHandleCircleIsShow(isFirst, false);
644 if (IsSingleHandle()) {
645 manager->SetIsHandleLineShow(false);
646 if (!pattern->HasText()) {
647 pattern->StartTwinkling();
648 }
649 }
650 pattern->StopContentScroll();
651 }
652
TriggerContentToScroll(const OffsetF & localOffset,bool isEnd)653 void TextFieldSelectOverlay::TriggerContentToScroll(const OffsetF& localOffset, bool isEnd)
654 {
655 auto pattern = GetPattern<TextFieldPattern>();
656 CHECK_NULL_VOID(pattern);
657 if (pattern->GetScrollEnabled()) {
658 if (isEnd) {
659 pattern->StopContentScroll();
660 } else {
661 pattern->UpdateContentScroller(Offset(localOffset.GetX(), localOffset.GetY()));
662 }
663 }
664 }
665
GetHandleColor()666 std::optional<Color> TextFieldSelectOverlay::GetHandleColor()
667 {
668 auto textFieldPattern = GetPattern<TextFieldPattern>();
669 CHECK_NULL_RETURN(textFieldPattern, std::nullopt);
670 auto paintProperty = textFieldPattern->GetPaintProperty<TextFieldPaintProperty>();
671 CHECK_NULL_RETURN(paintProperty, std::nullopt);
672 return paintProperty->GetCursorColor();
673 }
674
UpdateAllHandlesOffset()675 void TextFieldSelectOverlay::UpdateAllHandlesOffset()
676 {
677 if (dragHandleIndex_ == DragHandleIndex::NONE) {
678 BaseTextSelectOverlay::UpdateAllHandlesOffset();
679 } else if (dragHandleIndex_ == DragHandleIndex::FIRST) {
680 BaseTextSelectOverlay::UpdateSecondHandleOffset();
681 } else {
682 BaseTextSelectOverlay::UpdateFirstHandleOffset();
683 }
684 }
685
UpdateFirstHandleOffset()686 void TextFieldSelectOverlay::UpdateFirstHandleOffset()
687 {
688 if (dragHandleIndex_ == DragHandleIndex::FIRST) {
689 return;
690 }
691 BaseTextSelectOverlay::UpdateFirstHandleOffset();
692 }
693
UpdateSecondHandleOffset()694 void TextFieldSelectOverlay::UpdateSecondHandleOffset()
695 {
696 if (dragHandleIndex_ == DragHandleIndex::SECOND) {
697 return;
698 }
699 BaseTextSelectOverlay::UpdateSecondHandleOffset();
700 }
701
AllowTranslate()702 bool TextFieldSelectOverlay::AllowTranslate()
703 {
704 auto pattern = GetPattern<TextFieldPattern>();
705 CHECK_NULL_RETURN(pattern, false);
706 return pattern->AllowCopy();
707 }
708
AllowSearch()709 bool TextFieldSelectOverlay::AllowSearch()
710 {
711 auto pattern = GetPattern<TextFieldPattern>();
712 CHECK_NULL_RETURN(pattern, false);
713 return pattern->AllowCopy();
714 }
715
AllowShare()716 bool TextFieldSelectOverlay::AllowShare()
717 {
718 auto pattern = GetPattern<TextFieldPattern>();
719 CHECK_NULL_RETURN(pattern, false);
720 return pattern->AllowCopy();
721 }
722
IsStopBackPress() const723 bool TextFieldSelectOverlay::IsStopBackPress() const
724 {
725 auto pattern = GetPattern<TextFieldPattern>();
726 CHECK_NULL_RETURN(pattern, true);
727 return pattern->IsStopBackPress();
728 }
729 } // namespace OHOS::Ace::NG
730