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 menuInfo.hasOnPrepareMenuCallback = onPrepareMenuCallback_ ? true : false;
270 auto hasText = pattern->HasText();
271 if ((dirtyFlag & DIRTY_COPY_ALL_ITEM) == DIRTY_COPY_ALL_ITEM) {
272 menuInfo.showCopyAll = hasText && !pattern->IsSelectAll();
273 return;
274 }
275 bool isHideSelectionMenu = layoutProperty->GetSelectionMenuHiddenValue(false);
276 #if defined(ENABLE_STANDARD_INPUT)
277 auto inputMethod = MiscServices::InputMethodController::GetInstance();
278 auto isSupportCameraInput = inputMethod &&
279 inputMethod->IsInputTypeSupported(MiscServices::InputType::CAMERA_INPUT) &&
280 !pattern->IsInPasswordMode();
281 #else
282 auto isSupportCameraInput = false;
283 #endif
284 menuInfo.showCameraInput = !pattern->IsSelected() && isSupportCameraInput && !pattern->HasCustomKeyboard();
285 if (IsUsingMouse()) {
286 auto manager = SelectContentOverlayManager::GetOverlayManager();
287 CHECK_NULL_VOID(manager);
288 menuInfo.menuIsShow = !isHideSelectionMenu || manager->IsOpen();
289 } else {
290 menuInfo.menuIsShow = (hasText || IsShowPaste() || menuInfo.showCameraInput) &&
291 !isHideSelectionMenu && IsShowMenu();
292 }
293 menuInfo.menuDisable = isHideSelectionMenu;
294 menuInfo.showPaste = IsShowPaste();
295 menuInfo.menuType = IsUsingMouse() ? OptionMenuType::MOUSE_MENU : OptionMenuType::TOUCH_MENU;
296 menuInfo.showCopy = hasText && pattern->AllowCopy() && pattern->IsSelected();
297 menuInfo.showCut = menuInfo.showCopy;
298 menuInfo.showCopyAll = hasText && !pattern->IsSelectAll();
299 menuInfo.showTranslate = menuInfo.showCopy && pattern->IsShowTranslate() && IsNeedMenuTranslate();
300 menuInfo.showSearch = menuInfo.showCopy && pattern->IsShowSearch() && IsNeedMenuSearch();
301 menuInfo.showShare = menuInfo.showCopy && IsSupportMenuShare() && IsNeedMenuShare();
302 menuInfo.showAIWrite = pattern->IsShowAIWrite() && pattern->IsSelected();
303 }
304
OnUpdateSelectOverlayInfo(SelectOverlayInfo & overlayInfo,int32_t requestCode)305 void TextFieldSelectOverlay::OnUpdateSelectOverlayInfo(SelectOverlayInfo& overlayInfo, int32_t requestCode)
306 {
307 overlayInfo.clipHandleDrawRect = IsClipHandleWithViewPort();
308 BaseTextSelectOverlay::OnUpdateSelectOverlayInfo(overlayInfo, requestCode);
309 auto textFieldPattern = GetPattern<TextFieldPattern>();
310 CHECK_NULL_VOID(textFieldPattern);
311 auto paintProperty = textFieldPattern->GetPaintProperty<TextFieldPaintProperty>();
312 CHECK_NULL_VOID(paintProperty);
313 overlayInfo.handlerColor = paintProperty->GetCursorColor();
314 OnUpdateOnCreateMenuCallback(overlayInfo);
315 auto layoutProperty =
316 DynamicCast<TextFieldLayoutProperty>(textFieldPattern->GetLayoutProperty<TextFieldLayoutProperty>());
317 CHECK_NULL_VOID(layoutProperty);
318 if (layoutProperty->HasMaxLines()) {
319 uint32_t maxLine = layoutProperty->GetMaxLinesValue(Infinity<uint32_t>());
320 overlayInfo.isSingleLine = (maxLine == 1);
321 }
322 if (!textFieldPattern->IsTextArea()) {
323 overlayInfo.checkHandleReverse = [](const RectF& first, const RectF& second) {
324 return GreatNotEqual(first.Left(), second.Left());
325 };
326 }
327 }
328
GetSelectAreaFromRects(SelectRectsType pos)329 RectF TextFieldSelectOverlay::GetSelectAreaFromRects(SelectRectsType pos)
330 {
331 auto pattern = GetPattern<TextFieldPattern>();
332 CHECK_NULL_RETURN(pattern, {});
333 auto host = pattern->GetHost();
334 CHECK_NULL_RETURN(host, {});
335 auto selectRects = pattern->GetTextBoxes();
336 RectF res(pattern->GetCaretRect());
337 auto textPaintOffset = host->GetTransformRelativeOffset();
338 if (selectRects.empty()) {
339 res.SetOffset(res.GetOffset() + textPaintOffset);
340 } else {
341 auto contentRect = pattern->GetContentRect();
342 auto textRect = pattern->GetTextRect();
343 if (pos == SelectRectsType::LEFT_TOP_POINT) {
344 selectRects.erase(std::next(selectRects.begin()), selectRects.end());
345 selectRects.front().SetSize({0, 0});
346 } else if (pos == SelectRectsType::RIGHT_BOTTOM_POINT) {
347 selectRects.erase(selectRects.begin(), std::prev(selectRects.end()));
348 selectRects.front().SetRect({selectRects.front().Right(), selectRects.front().Bottom()}, {0, 0});
349 }
350 res = MergeSelectedBoxes(selectRects, contentRect, textRect, textPaintOffset);
351 if (NearZero(res.Width())) {
352 auto tempRes = res;
353 // need to convert to local coordinates before AdjustSelectedBlankLineWidth
354 tempRes -= textPaintOffset;
355 pattern->AdjustSelectedBlankLineWidth(tempRes);
356 res.SetLeft(tempRes.Left() + textPaintOffset.GetX());
357 res.SetWidth(tempRes.Width());
358 }
359 }
360 auto globalContentRect = GetVisibleContentRect(true);
361 auto intersectRect = res.IntersectRectT(globalContentRect);
362 if (hasTransform_) {
363 intersectRect.SetOffset(intersectRect.GetOffset() - textPaintOffset);
364 GetGlobalRectWithTransform(intersectRect);
365 }
366 ApplySelectAreaWithKeyboard(intersectRect);
367 return intersectRect;
368 }
369
GetSelectedText()370 std::string TextFieldSelectOverlay::GetSelectedText()
371 {
372 auto pattern = GetPattern<TextFieldPattern>();
373 CHECK_NULL_RETURN(pattern, "");
374 auto textSelectController = pattern->GetTextSelectController();
375 CHECK_NULL_RETURN(textSelectController, "");
376 auto start = textSelectController->GetStartIndex();
377 auto end = textSelectController->GetEndIndex();
378 return UtfUtils::Str16DebugToStr8(pattern->GetTextContentController()->GetSelectedValue(start, end));
379 }
380
OnMenuItemAction(OptionMenuActionId id,OptionMenuType type)381 void TextFieldSelectOverlay::OnMenuItemAction(OptionMenuActionId id, OptionMenuType type)
382 {
383 auto pattern = GetPattern<TextFieldPattern>();
384 CHECK_NULL_VOID(pattern);
385 TAG_LOGI(AceLogTag::ACE_TEXT_FIELD, "Handle overlay menu, men id %{public}d, menu type %{public}d", id, type);
386 switch (id) {
387 case OptionMenuActionId::COPY:
388 pattern->HandleOnCopy();
389 return;
390 case OptionMenuActionId::CUT:
391 pattern->HandleOnCut();
392 return;
393 case OptionMenuActionId::SELECT_ALL:
394 pattern->HandleOnSelectAll(type == OptionMenuType::MOUSE_MENU, false, true);
395 return;
396 case OptionMenuActionId::PASTE:
397 pattern->HandleOnPaste();
398 return;
399 case OptionMenuActionId::TRANSLATE:
400 HandleOnTranslate();
401 return;
402 case OptionMenuActionId::SEARCH:
403 HandleOnSearch();
404 return;
405 case OptionMenuActionId::SHARE:
406 HandleOnShare();
407 return;
408 case OptionMenuActionId::CAMERA_INPUT:
409 pattern->HandleOnCameraInput();
410 return;
411 case OptionMenuActionId::AI_WRITE:
412 pattern->HandleOnAIWrite();
413 return;
414 default:
415 return;
416 }
417 }
418
GetTextAreaCaretPosition(const OffsetF & localOffset)419 int32_t TextFieldSelectOverlay::GetTextAreaCaretPosition(const OffsetF& localOffset)
420 {
421 auto pattern = GetPattern<TextFieldPattern>();
422 auto contentRect = pattern->GetContentRect();
423 auto paddingLeft = pattern->GetPaddingLeft();
424 auto textRect = pattern->GetTextRect();
425 Offset offset;
426 if (LessNotEqual(localOffset.GetY(), contentRect.GetY())) {
427 offset = Offset(localOffset.GetX() - paddingLeft, contentRect.GetY() - textRect.GetY());
428 } else if (GreatOrEqual(localOffset.GetY(), contentRect.GetY() + contentRect.Height())) {
429 offset = Offset(localOffset.GetX() - paddingLeft, contentRect.GetY() + contentRect.Height() - textRect.GetY());
430 } else {
431 offset = Offset(localOffset.GetX() - paddingLeft, localOffset.GetY() - textRect.GetY());
432 }
433 return pattern->ConvertTouchOffsetToCaretPosition(offset);
434 }
435
GetTextInputCaretPosition(const OffsetF & localOffset,bool isFirst)436 int32_t TextFieldSelectOverlay::GetTextInputCaretPosition(const OffsetF& localOffset, bool isFirst)
437 {
438 auto pattern = GetPattern<TextFieldPattern>();
439 CHECK_NULL_RETURN(pattern, 0);
440 auto contentRect = pattern->GetContentRect();
441 auto selectController = pattern->GetTextSelectController();
442 auto wideText = pattern->GetTextUtf16Value();
443 if (LessNotEqual(localOffset.GetX(), contentRect.GetX())) {
444 auto index = selectController->GetStartIndex();
445 if (!SelectOverlayIsOn()) {
446 return index;
447 }
448 if ((!isFirst && !IsHandleReverse()) || (isFirst && IsHandleReverse())) {
449 index = selectController->GetEndIndex();
450 }
451 return index;
452 }
453 if (GreatOrEqual(localOffset.GetX(), contentRect.GetX() + contentRect.Width())) {
454 auto index = selectController->GetEndIndex();
455 if (!SelectOverlayIsOn()) {
456 return index;
457 }
458 if ((isFirst && !IsHandleReverse()) || (!isFirst && IsHandleReverse())) {
459 index = selectController->GetStartIndex();
460 }
461 return index;
462 }
463 Offset offset(localOffset.GetX() - pattern->GetTextRect().GetX(), 0.0f);
464 return pattern->ConvertTouchOffsetToCaretPosition(offset);
465 }
466
GetCaretPositionOnHandleMove(const OffsetF & offset,bool isFirst)467 int32_t TextFieldSelectOverlay::GetCaretPositionOnHandleMove(const OffsetF& offset, bool isFirst)
468 {
469 auto pattern = GetPattern<TextFieldPattern>();
470 CHECK_NULL_RETURN(pattern, 0);
471 auto localOffset = offset;
472 if (pattern->GetContentScrollerIsScrolling()) {
473 auto adjustOffset = pattern->AdjustAutoScrollOffset(Offset(offset.GetX(), offset.GetY()));
474 localOffset = OffsetF(adjustOffset.GetX(), adjustOffset.GetY());
475 }
476 if (pattern->IsTextArea()) {
477 return GetTextAreaCaretPosition(localOffset);
478 }
479 return GetTextInputCaretPosition(localOffset, isFirst);
480 }
481
OnHandleMove(const RectF & handleRect,bool isFirst)482 void TextFieldSelectOverlay::OnHandleMove(const RectF& handleRect, bool isFirst)
483 {
484 auto pattern = GetPattern<TextFieldPattern>();
485 CHECK_NULL_VOID(pattern);
486 CHECK_NULL_VOID(pattern->HasText());
487 auto localOffset = handleRect.GetOffset();
488 localOffset.SetY(localOffset.GetY() + handleRect.Height() / 2.0f);
489 if (IsOverlayMode()) {
490 localOffset = localOffset - GetPaintOffsetWithoutTransform();
491 }
492 auto selectController = pattern->GetTextSelectController();
493 CHECK_NULL_VOID(selectController);
494 int32_t startIndex = selectController->GetFirstHandleIndex();
495 int32_t endIndex = selectController->GetSecondHandleIndex();
496 TriggerContentToScroll(localOffset, false);
497 if (IsSingleHandle()) {
498 auto isScrolling = pattern->GetContentScrollerIsScrolling();
499 auto contentRect = pattern->GetContentRect();
500 auto touchCaretX = std::clamp(localOffset.GetX(), contentRect.Left(), contentRect.Right());
501 // 1/4 line height.
502 auto yOffset = pattern->PreferredLineHeight() * 0.25f;
503 auto touchCaretY = std::clamp(localOffset.GetY(), contentRect.Top() + yOffset, contentRect.Bottom() - yOffset);
504 selectController->UpdateCaretInfoByOffset(!isScrolling ? Offset(touchCaretX, touchCaretY) :
505 Offset(localOffset.GetX(), localOffset.GetY()), !isScrolling, true);
506 pattern->ShowCaretAndStopTwinkling();
507 } else {
508 auto position = GetCaretPositionOnHandleMove(localOffset, isFirst);
509 pattern->StartVibratorByIndexChange(position, isFirst ? startIndex : endIndex);
510 if (isFirst) {
511 selectController->MoveFirstHandleToContentRect(position, false, false);
512 UpdateSecondHandleOffset();
513 } else {
514 selectController->MoveSecondHandleToContentRect(position, false, false);
515 UpdateFirstHandleOffset();
516 }
517 }
518 UpdateMagnifier(OffsetF(localOffset.GetX(), localOffset.GetY()), false);
519 pattern->PlayScrollBarAppearAnimation();
520 auto tmpHost = pattern->GetHost();
521 CHECK_NULL_VOID(tmpHost);
522 tmpHost->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
523 }
524
OnHandleMoveDone(const RectF & rect,bool isFirst)525 void TextFieldSelectOverlay::OnHandleMoveDone(const RectF& rect, bool isFirst)
526 {
527 BaseTextSelectOverlay::OnHandleMoveDone(rect, isFirst);
528 auto pattern = GetPattern<TextFieldPattern>();
529 CHECK_NULL_VOID(pattern);
530 auto host = pattern->GetHost();
531 CHECK_NULL_VOID(host);
532 auto layoutProperty = host->GetLayoutProperty<TextFieldLayoutProperty>();
533 CHECK_NULL_VOID(layoutProperty);
534 auto overlayManager = GetManager<SelectContentOverlayManager>();
535 CHECK_NULL_VOID(overlayManager);
536 if (!layoutProperty->GetSelectionMenuHiddenValue(false)) {
537 overlayManager->MarkInfoChange(DIRTY_COPY_ALL_ITEM);
538 }
539 if (pattern->GetMagnifierController()) {
540 pattern->GetMagnifierController()->RemoveMagnifierFrameNode();
541 }
542 auto selectController = pattern->GetTextSelectController();
543 TriggerContentToScroll(OffsetF(), true);
544 overlayManager->ShowOptionMenu();
545 if (!IsSingleHandle()) {
546 if (selectController->GetFirstHandleIndex() == selectController->GetSecondHandleIndex()) {
547 CloseOverlay(true, CloseReason::CLOSE_REASON_NORMAL);
548 pattern->StartTwinkling();
549 selectController->MoveCaretToContentRect(pattern->GetCaretIndex());
550 } else {
551 if (isFirst) {
552 selectController->MoveFirstHandleToContentRect(selectController->GetFirstHandleIndex(), false);
553 } else {
554 selectController->MoveSecondHandleToContentRect(selectController->GetSecondHandleIndex(), false);
555 }
556 overlayManager->MarkInfoChange(DIRTY_DOUBLE_HANDLE | DIRTY_SELECT_AREA | DIRTY_SELECT_TEXT);
557 }
558 } else {
559 pattern->StopTwinkling();
560 // single handle use caret offset.
561 auto caretRect = selectController->GetCaretRect();
562 selectController->UpdateCaretInfoByOffset(
563 Offset(caretRect.Left(), caretRect.Top() + caretRect.Height() / 2.0f));
564 overlayManager->MarkInfoChange(DIRTY_SECOND_HANDLE);
565 }
566 overlayManager->SetHandleCircleIsShow(isFirst, true);
567 if (IsSingleHandle()) {
568 overlayManager->SetIsHandleLineShow(true);
569 }
570 pattern->ScheduleDisappearDelayTask();
571 pattern->UpdateCaretInfoToController();
572 pattern->FloatingCaretLand();
573 auto tmpHost = pattern->GetHost();
574 CHECK_NULL_VOID(tmpHost);
575 tmpHost->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
576 }
577
ProcessSelectAllOverlay(const OverlayRequest & request)578 void TextFieldSelectOverlay::ProcessSelectAllOverlay(const OverlayRequest& request)
579 {
580 OverlayRequest newRequest = request;
581 newRequest.requestCode = static_cast<uint32_t>(newRequest.requestCode) | REQUEST_SELECT_ALL;
582 ProcessOverlay(newRequest);
583 }
584
OnAncestorNodeChanged(FrameNodeChangeInfoFlag flag)585 void TextFieldSelectOverlay::OnAncestorNodeChanged(FrameNodeChangeInfoFlag flag)
586 {
587 if (IsAncestorNodeGeometryChange(flag) || IsAncestorNodeTransformChange(flag)) {
588 UpdateAllHandlesOffset();
589 }
590 BaseTextSelectOverlay::OnAncestorNodeChanged(flag);
591 }
592
OnHandleLevelModeChanged(HandleLevelMode mode)593 void TextFieldSelectOverlay::OnHandleLevelModeChanged(HandleLevelMode mode)
594 {
595 if (handleLevelMode_ != mode && mode == HandleLevelMode::OVERLAY) {
596 auto pattern = GetPattern<TextFieldPattern>();
597 if (pattern) {
598 pattern->UpdateParentGlobalOffset();
599 pattern->GetTextSelectController()->CalculateHandleOffset();
600 }
601 }
602 BaseTextSelectOverlay::OnHandleLevelModeChanged(mode);
603 }
604
OnOverlayClick(const GestureEvent & event,bool isFirst)605 void TextFieldSelectOverlay::OnOverlayClick(const GestureEvent& event, bool isFirst)
606 {
607 auto pattern = GetPattern<TextFieldPattern>();
608 CHECK_NULL_VOID(pattern);
609 auto recognizer = pattern->GetOrCreateMultipleClickRecognizer();
610 CHECK_NULL_VOID(recognizer);
611 if (recognizer->IsValidClick(event)) {
612 TAG_LOGI(AceLogTag::ACE_TEXT_FIELD, "textfield overlayClick multiple click recognizer is running.");
613 auto overlayEvent = event;
614 overlayEvent.SetLocalLocation(recognizer->GetBeginLocalLocation());
615 overlayEvent.SetGlobalLocation(recognizer->GetBeginGlobalLocation());
616 pattern->HandleClickEvent(overlayEvent);
617 } else if (!IsSingleHandle()) {
618 if (pattern->HandleBetweenSelectedPosition(event)) {
619 TAG_LOGI(AceLogTag::ACE_TEXT_FIELD, "textfield HandleBetweenSelectedPosition");
620 return;
621 }
622 auto selectController = pattern->GetTextSelectController();
623 auto index = isFirst ? selectController->GetFirstHandleIndex() : selectController->GetSecondHandleIndex();
624 pattern->HandleSetSelection(index, index, false);
625 pattern->StartTwinkling();
626 } else {
627 TAG_LOGI(AceLogTag::ACE_TEXT_FIELD, "textfield overlayClick");
628 }
629 }
630
OnHandleIsHidden()631 void TextFieldSelectOverlay::OnHandleIsHidden()
632 {
633 auto pattern = GetPattern<TextFieldPattern>();
634 CHECK_NULL_VOID(pattern);
635 pattern->StartTwinkling();
636 }
637
OnHandleMoveStart(const GestureEvent & event,bool isFirst)638 void TextFieldSelectOverlay::OnHandleMoveStart(const GestureEvent& event, bool isFirst)
639 {
640 BaseTextSelectOverlay::OnHandleMoveStart(event, isFirst);
641 auto manager = GetManager<SelectContentOverlayManager>();
642 CHECK_NULL_VOID(manager);
643 auto pattern = GetPattern<TextFieldPattern>();
644 CHECK_NULL_VOID(pattern);
645 manager->SetHandleCircleIsShow(isFirst, false);
646 if (IsSingleHandle()) {
647 manager->SetIsHandleLineShow(false);
648 if (!pattern->HasText()) {
649 pattern->StartTwinkling();
650 }
651 }
652 pattern->StopContentScroll();
653 if (IsSingleHandle()) {
654 pattern->AddContentScrollingCallback([weak = WeakClaim(this)](const Offset& offset) {
655 auto overlay = weak.Upgrade();
656 CHECK_NULL_VOID(overlay);
657 overlay->UpdateMagnifier(OffsetF(), true);
658 });
659 }
660 }
661
TriggerContentToScroll(const OffsetF & localOffset,bool isEnd)662 void TextFieldSelectOverlay::TriggerContentToScroll(const OffsetF& localOffset, bool isEnd)
663 {
664 auto pattern = GetPattern<TextFieldPattern>();
665 CHECK_NULL_VOID(pattern);
666 if (pattern->GetScrollEnabled()) {
667 if (isEnd) {
668 pattern->StopContentScroll();
669 } else {
670 pattern->UpdateContentScroller(Offset(localOffset.GetX(), localOffset.GetY()));
671 }
672 }
673 }
674
GetHandleColor()675 std::optional<Color> TextFieldSelectOverlay::GetHandleColor()
676 {
677 auto textFieldPattern = GetPattern<TextFieldPattern>();
678 CHECK_NULL_RETURN(textFieldPattern, std::nullopt);
679 auto paintProperty = textFieldPattern->GetPaintProperty<TextFieldPaintProperty>();
680 CHECK_NULL_RETURN(paintProperty, std::nullopt);
681 return paintProperty->GetCursorColor();
682 }
683
UpdateAllHandlesOffset()684 void TextFieldSelectOverlay::UpdateAllHandlesOffset()
685 {
686 if (dragHandleIndex_ == DragHandleIndex::NONE) {
687 BaseTextSelectOverlay::UpdateAllHandlesOffset();
688 } else if (dragHandleIndex_ == DragHandleIndex::FIRST) {
689 BaseTextSelectOverlay::UpdateSecondHandleOffset();
690 } else {
691 BaseTextSelectOverlay::UpdateFirstHandleOffset();
692 }
693 }
694
UpdateFirstHandleOffset()695 void TextFieldSelectOverlay::UpdateFirstHandleOffset()
696 {
697 if (dragHandleIndex_ == DragHandleIndex::FIRST) {
698 return;
699 }
700 BaseTextSelectOverlay::UpdateFirstHandleOffset();
701 }
702
UpdateSecondHandleOffset()703 void TextFieldSelectOverlay::UpdateSecondHandleOffset()
704 {
705 if (dragHandleIndex_ == DragHandleIndex::SECOND) {
706 return;
707 }
708 BaseTextSelectOverlay::UpdateSecondHandleOffset();
709 }
710
AllowTranslate()711 bool TextFieldSelectOverlay::AllowTranslate()
712 {
713 auto pattern = GetPattern<TextFieldPattern>();
714 CHECK_NULL_RETURN(pattern, false);
715 return pattern->AllowCopy();
716 }
717
AllowSearch()718 bool TextFieldSelectOverlay::AllowSearch()
719 {
720 auto pattern = GetPattern<TextFieldPattern>();
721 CHECK_NULL_RETURN(pattern, false);
722 return pattern->AllowCopy();
723 }
724
AllowShare()725 bool TextFieldSelectOverlay::AllowShare()
726 {
727 auto pattern = GetPattern<TextFieldPattern>();
728 CHECK_NULL_RETURN(pattern, false);
729 return pattern->AllowCopy();
730 }
731
IsStopBackPress() const732 bool TextFieldSelectOverlay::IsStopBackPress() const
733 {
734 auto pattern = GetPattern<TextFieldPattern>();
735 CHECK_NULL_RETURN(pattern, true);
736 return pattern->IsStopBackPress();
737 }
738
BeforeOnPrepareMenu()739 void TextFieldSelectOverlay::BeforeOnPrepareMenu()
740 {
741 auto pattern = GetPattern<TextFieldPattern>();
742 CHECK_NULL_VOID(pattern);
743 auto selectController = pattern->GetTextSelectController();
744 CHECK_NULL_VOID(selectController);
745 // If the onPrepareMenu property exists, the onTextSelectionChange event needs to be triggered first to ensure the
746 // application side can obtain the latest selected area.
747 selectController->FireSelectEvent();
748 }
749
UpdateMagnifier(const OffsetF & offset,bool updateOnScroll)750 void TextFieldSelectOverlay::UpdateMagnifier(const OffsetF& offset, bool updateOnScroll)
751 {
752 auto pattern = GetPattern<TextFieldPattern>();
753 CHECK_NULL_VOID(pattern);
754 if (!pattern->GetMagnifierController() || !SelectOverlayIsOn()) {
755 return;
756 }
757 if (IsSingleHandle()) {
758 if (updateOnScroll || !pattern->GetContentScrollerIsScrolling()) {
759 pattern->SetMagnifierLocalOffsetToFloatingCaretPos();
760 }
761 return;
762 }
763 auto magnifierLocalOffset = offset;
764 if (IsOverlayMode()) {
765 GetLocalPointWithTransform(magnifierLocalOffset);
766 }
767 pattern->GetMagnifierController()->SetLocalOffset(magnifierLocalOffset);
768 }
769 } // namespace OHOS::Ace::NG
770