1 /*
2  * Copyright (c) 2021-2022 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/text_overlay/render_text_overlay.h"
17 
18 #include "base/geometry/rect.h"
19 #include "core/animation/scheduler.h"
20 #include "core/components/container_modal/container_modal_constants.h"
21 #include "core/components/focus_collaboration/render_focus_collaboration.h"
22 #include "core/components/stack/stack_element.h"
23 #ifdef WEB_SUPPORTED
24 #include "core/components/web/render_web.h"
25 #endif
26 
27 namespace OHOS::Ace {
28 namespace {
29 
30 const Offset DOT1_OFFSET = Offset(-0.790737726, -1.290737726);
31 const Offset DOT2_OFFSET = Offset(-0.623475836, -1.290737726);
32 const Offset DOT3_OFFSET = Offset(-0.790737726, -0.123475836);
33 const Offset DOT4_OFFSET = Offset(0.790737726, 1.290737726);
34 const Offset END_POINT = DOT1_POSITION + DOT1_OFFSET;
35 constexpr Dimension TOOL_BAR_HEIGHT = 40.0_vp;
36 constexpr Dimension HANDLE_HOTZONE_DIAMETER = 20.0_vp;
37 constexpr Dimension MORE_BUTTON_SIZE = 40.0_vp;
38 constexpr Dimension ANIMATION_OFFSET_X = 16.0_vp;
39 constexpr double DEFAULT_SPACING = 10.0;
40 constexpr double FIFTY_PERCENT = 0.5;
41 constexpr double ROTATE_DEGREE = -45.0;
42 constexpr double CLIP_WIDTH = 1.0;
43 constexpr double OPACITY_KEYFRAME = 250.0 / 350.0; // Clip and translate animation is 350ms, opacity animation is 250ms.
44 constexpr float KEYFRAME_PERCENT_THIRTY = 0.33f;
45 constexpr float KEYFRAME_BEGINNING = 0.0f;
46 constexpr float KEYFRAME_ENDING = 1.0f;
47 constexpr int32_t MORE_ANIMATION_DURATION = 300; // Duration of more icon animation.
48 constexpr int32_t ANIMATION_DURATION = 350;      // Duration of clip and translate animation
49 
50 } // namespace
51 
RenderTextOverlay()52 RenderTextOverlay::RenderTextOverlay()
53 {
54     clickDetector_ = AceType::MakeRefPtr<ClickRecognizer>();
55     clickDetector_->SetOnClick([weak = AceType::WeakClaim(this)](const ClickInfo& clickInfo) {
56         auto overlay = weak.Upgrade();
57         if (overlay) {
58             overlay->HandleClick(clickInfo.GetLocalLocation());
59         }
60     });
61 
62     dragDetector_ = AceType::MakeRefPtr<DragRecognizer>(Axis::FREE);
63     dragDetector_->SetOnDragStart([weak = AceType::WeakClaim(this)](const DragStartInfo& startInfo) {
64         auto overlay = weak.Upgrade();
65         if (overlay) {
66             overlay->HandleDragStart(startInfo.GetLocalLocation());
67         }
68     });
69 
70     dragDetector_->SetOnDragUpdate([weak = AceType::WeakClaim(this)](const DragUpdateInfo& updateInfo) {
71         auto overlay = weak.Upgrade();
72         if (overlay) {
73             overlay->HandleDragUpdateAndEnd(updateInfo.GetLocalLocation());
74         }
75     });
76 
77     dragDetector_->SetOnDragEnd([weak = AceType::WeakClaim(this)](const DragEndInfo& endInfo) {
78         auto overlay = weak.Upgrade();
79         if (overlay) {
80             overlay->HandleDragUpdateAndEnd(endInfo.GetLocalLocation());
81             overlay->isDragging_ = false;
82         }
83     });
84 
85     touchDetector_ = AceType::MakeRefPtr<RawRecognizer>();
86     touchDetector_->SetOnTouchDown([weak = AceType::WeakClaim(this)](const TouchEventInfo& info) {
87         auto overlay = weak.Upgrade();
88         if (overlay) {
89             overlay->showMagnifier_ = true;
90             auto startOffset = info.GetTouches().front().GetLocalLocation();
91             if (overlay->startHandleRegion_.ContainsInRegion(startOffset.GetX(), startOffset.GetY())) {
92                 overlay->isTouchStartDrag_ = true;
93                 overlay->isTouchEndDrag_ = false;
94             } else {
95                 overlay->isTouchStartDrag_ = false;
96                 overlay->isTouchEndDrag_ =
97                     overlay->endHandleRegion_.ContainsInRegion(startOffset.GetX(), startOffset.GetY());
98             }
99         }
100     });
101 
102     touchDetector_->SetOnTouchUp([weak = AceType::WeakClaim(this)](const TouchEventInfo&) {
103         auto overlay = weak.Upgrade();
104         if (overlay) {
105             overlay->showMagnifier_ = false;
106         }
107     });
108 }
109 
~RenderTextOverlay()110 RenderTextOverlay::~RenderTextOverlay()
111 {
112     auto renderTextField = weakTextField_.Upgrade();
113     if (renderTextField) {
114         renderTextField->SetIsOverlayShowed(false, needStartTwinkling_);
115     }
116     auto spOverlayComponent = overlayComponent_.Upgrade();
117     if (spOverlayComponent) {
118         RemoveBackendEvent(spOverlayComponent);
119     }
120     auto context = GetContext().Upgrade();
121     if (context) {
122         auto textOverlayManager = context->GetTextOverlayManager();
123         if (textOverlayManager) {
124             textOverlayManager->ClearTextOverlayRect();
125         }
126     }
127 }
128 
Update(const RefPtr<Component> & component)129 void RenderTextOverlay::Update(const RefPtr<Component>& component)
130 {
131     auto overlay = AceType::DynamicCast<TextOverlayComponent>(component);
132     if (!overlay) {
133         return;
134     }
135     overlayComponent_ = overlay;
136     overlay->SetPopOverlay([weak = WeakClaim(this)]() {
137         auto overlay = weak.Upgrade();
138         if (overlay) {
139             overlay->needCloseKeyboard_ = false;
140             overlay->PopOverlay();
141         }
142     });
143     onFocusChange_ = overlay->GetOnFocusChange();
144     onCut_ = overlay->GetOnCut();
145     onCopy_ = overlay->GetOnCopy();
146     onPaste_ = overlay->GetOnPaste();
147     onCopyAll_ = overlay->GetOnCopyAll();
148     startHandleOffset_ = overlay->GetStartHandleOffset();
149     endHandleOffset_ = overlay->GetEndHandleOffset();
150     mouseOffset_ = overlay->GetMouseOffset();
151     onStartHandleMove_ = overlay->GetOnStartHandleMove();
152     onEndHandleMove_ = overlay->GetOnEndHandleMove();
153     isSingleHandle_ = overlay->GetIsSingleHandle() || (startHandleOffset_ == endHandleOffset_);
154     lineHeight_ = overlay->GetLineHeight();
155     startHandleHeight_ = overlay->GetStartHandleHeight();
156     endHandleHeight_ = overlay->GetEndHandleHeight();
157     handleDiameter_ = overlay->GetHandleDiameter();
158     menuSpacingWithHandle_ = handleDiameter_;
159     handleDiameterInner_ = overlay->GetHandleDiameterInner();
160     handleRadius_ = handleDiameter_ / 2.0;
161     handleRadiusInner_ = handleDiameterInner_ / 2.0;
162     menuSpacingWithText_ = overlay->GetMenuSpacingWithText();
163     handleColor_ = overlay->GetHandleColor();
164     handleColorInner_ = overlay->GetHandleColorInner();
165     clipRect_ = overlay->GetClipRect();
166     textDirection_ = overlay->GetTextDirection();
167     realTextDirection_ = overlay->GetRealTextDirection();
168     isUsingMouse_ = overlay->GetIsUsingMouse();
169     needClipRect_ = overlay->GetNeedCilpRect();
170     BindBackendEvent(overlay);
171     UpdateWeakTextField(overlay);
172     UpdateWeakText(overlay);
173     UpdateWeakImage(overlay);
174 #ifdef WEB_SUPPORTED
175     UpdateWeakWeb(overlay);
176 #endif
177     MarkNeedLayout();
178 }
179 
BindBackendEvent(const RefPtr<TextOverlayComponent> & overlay)180 void RenderTextOverlay::BindBackendEvent(const RefPtr<TextOverlayComponent>& overlay)
181 {
182     auto context = context_.Upgrade();
183     if (context && context->GetIsDeclarative() && !isUsingMouse_) {
184         BindBackendEventV2(overlay);
185     } else {
186         BackEndEventManager<void()>::GetInstance().BindBackendEvent(
187             overlay->GetCutButtonMarker(), [weak = WeakClaim(this)]() {
188                 auto overlay = weak.Upgrade();
189                 if (overlay) {
190                     overlay->HandleCut();
191                 }
192             });
193 
194         BackEndEventManager<void()>::GetInstance().BindBackendEvent(
195             overlay->GetCopyButtonMarker(), [weak = WeakClaim(this)]() {
196                 auto overlay = weak.Upgrade();
197                 if (overlay) {
198                     overlay->HandleCopy();
199                 }
200             });
201 
202         BackEndEventManager<void()>::GetInstance().BindBackendEvent(
203             overlay->GetPasteButtonMarker(), [weak = WeakClaim(this)]() {
204                 auto overlay = weak.Upgrade();
205                 if (overlay) {
206                     overlay->HandlePaste();
207                 }
208             });
209 
210         BackEndEventManager<void()>::GetInstance().BindBackendEvent(
211             overlay->GetCopyAllButtonMarker(), [weak = WeakClaim(this)]() {
212                 auto overlay = weak.Upgrade();
213                 if (overlay) {
214                     overlay->HandleCopyAll();
215                 }
216             });
217         BackEndEventManager<void()>::GetInstance().BindBackendEvent(
218             overlay->GetMoreButtonMarker(), [weak = WeakClaim(this)]() {
219                 auto overlay = weak.Upgrade();
220                 if (overlay) {
221                     overlay->HandleMoreButtonClick();
222                 }
223             });
224     }
225 }
226 
BindBackendEventV2(const RefPtr<TextOverlayComponent> & overlay)227 void RenderTextOverlay::BindBackendEventV2(const RefPtr<TextOverlayComponent>& overlay)
228 {
229     BackEndEventManager<void(const ClickInfo& info)>::GetInstance().BindBackendEvent(
230         overlay->GetCutButtonMarker(), [weak = WeakClaim(this)](const ClickInfo& info) {
231             auto overlay = weak.Upgrade();
232             if (overlay) {
233                 overlay->HandleCut();
234             }
235         });
236 
237     BackEndEventManager<void(const ClickInfo& info)>::GetInstance().BindBackendEvent(
238         overlay->GetCopyButtonMarker(), [weak = WeakClaim(this)](const ClickInfo& info) {
239             auto overlay = weak.Upgrade();
240             if (overlay) {
241                 overlay->HandleCopy();
242             }
243         });
244 
245     BackEndEventManager<void(const ClickInfo& info)>::GetInstance().BindBackendEvent(
246         overlay->GetPasteButtonMarker(), [weak = WeakClaim(this)](const ClickInfo& info) {
247             auto overlay = weak.Upgrade();
248             if (overlay) {
249                 overlay->HandlePaste();
250             }
251         });
252 
253     BackEndEventManager<void(const ClickInfo& info)>::GetInstance().BindBackendEvent(
254         overlay->GetCopyAllButtonMarker(), [weak = WeakClaim(this)](const ClickInfo& info) {
255             auto overlay = weak.Upgrade();
256             if (overlay) {
257                 overlay->HandleCopyAll();
258             }
259         });
260     BackEndEventManager<void(const ClickInfo& info)>::GetInstance().BindBackendEvent(
261         overlay->GetMoreButtonMarker(), [weak = WeakClaim(this)](const ClickInfo& info) {
262             auto overlay = weak.Upgrade();
263             if (overlay) {
264                 overlay->HandleMoreButtonClick();
265             }
266         });
267 }
268 
RemoveBackendEvent(const RefPtr<TextOverlayComponent> & overlay)269 void RenderTextOverlay::RemoveBackendEvent(const RefPtr<TextOverlayComponent>& overlay)
270 {
271     auto context = context_.Upgrade();
272     if (context && context->GetIsDeclarative() && !isUsingMouse_) {
273         BackEndEventManager<void(const ClickInfo& info)>::GetInstance().RemoveBackEndEvent(
274             overlay->GetCutButtonMarker());
275         BackEndEventManager<void(const ClickInfo& info)>::GetInstance().RemoveBackEndEvent(
276             overlay->GetCopyButtonMarker());
277         BackEndEventManager<void(const ClickInfo& info)>::GetInstance().RemoveBackEndEvent(
278             overlay->GetPasteButtonMarker());
279         BackEndEventManager<void(const ClickInfo& info)>::GetInstance().RemoveBackEndEvent(
280             overlay->GetCopyAllButtonMarker());
281         BackEndEventManager<void(const ClickInfo& info)>::GetInstance().RemoveBackEndEvent(
282             overlay->GetMoreButtonMarker());
283     } else {
284         BackEndEventManager<void()>::GetInstance().RemoveBackEndEvent(overlay->GetCutButtonMarker());
285         BackEndEventManager<void()>::GetInstance().RemoveBackEndEvent(overlay->GetCopyButtonMarker());
286         BackEndEventManager<void()>::GetInstance().RemoveBackEndEvent(overlay->GetPasteButtonMarker());
287         BackEndEventManager<void()>::GetInstance().RemoveBackEndEvent(overlay->GetCopyAllButtonMarker());
288         BackEndEventManager<void()>::GetInstance().RemoveBackEndEvent(overlay->GetMoreButtonMarker());
289     }
290 }
291 
UpdateWeakTextField(const RefPtr<TextOverlayComponent> & overlay)292 void RenderTextOverlay::UpdateWeakTextField(const RefPtr<TextOverlayComponent>& overlay)
293 {
294     if (!overlay) {
295         return;
296     }
297     weakTextField_ = overlay->GetWeakTextField();
298     auto renderTextField = weakTextField_.Upgrade();
299     if (!renderTextField) {
300         return;
301     }
302     auto callback = [weak = WeakClaim(this)](const OverlayShowOption& option) {
303         auto overlay = weak.Upgrade();
304         if (!overlay) {
305             return;
306         }
307         if (option.updateOverlayType == UpdateOverlayType::CLICK ||
308             option.updateOverlayType == UpdateOverlayType::LONG_PRESS) {
309             overlay->childRightBoundary_ = 0.0;
310         }
311         overlay->SetVisible(true);
312         overlay->showOption_ = option;
313         overlay->startHandleOffset_ = option.startHandleOffset;
314         overlay->endHandleOffset_ = option.endHandleOffset;
315         overlay->isSingleHandle_ = option.isSingleHandle;
316         if (option.updateOverlayType == UpdateOverlayType::CLICK) {
317             if (overlay->onRebuild_) {
318                 overlay->hasMenu_ = false;
319                 overlay->onRebuild_(true, false, false, false, false);
320             }
321         } else if (option.updateOverlayType == UpdateOverlayType::LONG_PRESS) {
322             if (overlay->onRebuild_) {
323                 overlay->hasMenu_ = false;
324                 overlay->onRebuild_(false, true, false, true, false);
325             }
326         }
327     };
328     renderTextField->SetUpdateHandlePosition(callback);
329 
330     auto callbackDiameter = [weak = WeakClaim(this)](const double& value) {
331         auto overlay = weak.Upgrade();
332         if (!overlay) {
333             LOGE("UpdateWeakTextField error, overlay is nullptr");
334             return;
335         }
336 
337         overlay->SetVisible(true);
338         if (overlay->onRebuild_) {
339             overlay->onRebuild_(overlay->isSingleHandle_, !overlay->isSingleHandle_, overlay->hasMenu_,
340                 !overlay->isSingleHandle_, false);
341         }
342         overlay->handleDiameter_ = Dimension(value, DimensionUnit::VP);
343         overlay->handleRadius_ = overlay->handleDiameter_ * FIFTY_PERCENT;
344     };
345     renderTextField->SetUpdateHandleDiameter(callbackDiameter);
346 
347     auto callbackDiameterInner = [weak = WeakClaim(this)](const double& value) {
348         auto overlay = weak.Upgrade();
349         if (!overlay) {
350             LOGE("UpdateWeakTextField error, overlay is nullptr");
351             return;
352         }
353 
354         overlay->SetVisible(true);
355         if (overlay->onRebuild_) {
356             overlay->onRebuild_(overlay->isSingleHandle_, !overlay->isSingleHandle_, overlay->hasMenu_,
357                 !overlay->isSingleHandle_, false);
358         }
359         overlay->handleDiameterInner_ = Dimension(value, DimensionUnit::VP);
360         overlay->handleRadiusInner_ = overlay->handleDiameterInner_ * FIFTY_PERCENT;
361     };
362     renderTextField->SetUpdateHandleDiameterInner(callbackDiameterInner);
363 
364     auto onValueChange = [weak = WeakClaim(this)] {
365         auto overlay = weak.Upgrade();
366         if (overlay) {
367             overlay->needCloseKeyboard_ = false;
368             overlay->PopOverlay();
369         }
370     };
371     renderTextField->SetOnValueChange(onValueChange);
372 
373     auto onKeyboardClose = [weak = WeakClaim(this)](bool forceCloseKeyboard) {
374         auto overlay = weak.Upgrade();
375         if (overlay) {
376             overlay->needCloseKeyboard_ = !forceCloseKeyboard;
377             overlay->needStartTwinkling_ = !forceCloseKeyboard;
378             overlay->PopOverlay();
379         }
380     };
381     renderTextField->SetOnKeyboardClose(onKeyboardClose);
382 
383     auto onClipRectChanged = [weak = WeakClaim(this)](const Rect& clipRect) {
384         auto overlay = weak.Upgrade();
385         if (overlay && (overlay->clipRect_ != clipRect)) {
386             overlay->clipRect_ = clipRect;
387             overlay->MarkNeedLayout();
388         }
389     };
390     renderTextField->SetOnClipRectChanged(onClipRectChanged);
391 }
392 
UpdateWeakText(const RefPtr<TextOverlayComponent> & overlay)393 void RenderTextOverlay::UpdateWeakText(const RefPtr<TextOverlayComponent>& overlay)
394 {
395     if (!overlay) {
396         return;
397     }
398     weakText_ = overlay->GetWeakText();
399     auto renderText = weakText_.Upgrade();
400     if (!renderText) {
401         return;
402     }
403     auto callback = [weak = WeakClaim(this)](const OverlayShowOption& option) {
404         auto overlay = weak.Upgrade();
405         if (!overlay) {
406             return;
407         }
408         if (option.updateOverlayType == UpdateOverlayType::CLICK ||
409             option.updateOverlayType == UpdateOverlayType::LONG_PRESS) {
410             overlay->childRightBoundary_ = 0.0;
411         }
412         overlay->SetVisible(true);
413         overlay->showOption_ = option;
414         overlay->startHandleOffset_ = option.startHandleOffset;
415         overlay->endHandleOffset_ = option.endHandleOffset;
416         overlay->isSingleHandle_ = option.isSingleHandle;
417         if (option.updateOverlayType == UpdateOverlayType::CLICK) {
418             if (overlay->onRebuild_) {
419                 overlay->hasMenu_ = false;
420                 overlay->onRebuild_(true, false, false, false, false);
421             }
422         } else if (option.updateOverlayType == UpdateOverlayType::LONG_PRESS) {
423             if (overlay->onRebuild_) {
424                 overlay->hasMenu_ = false;
425                 overlay->onRebuild_(false, true, false, true, false);
426             }
427         }
428     };
429     renderText->SetUpdateHandlePosition(callback);
430 
431     auto callbackDiameter = [weak = WeakClaim(this)](const double& value) {
432         auto overlay = weak.Upgrade();
433         if (!overlay) {
434             LOGE("UpdateWeakText error, overlay is nullptr");
435             return;
436         }
437 
438         overlay->SetVisible(true);
439         if (overlay->onRebuild_) {
440             overlay->onRebuild_(overlay->isSingleHandle_, !overlay->isSingleHandle_, overlay->hasMenu_,
441                 !overlay->isSingleHandle_, false);
442         }
443         overlay->handleDiameter_ = Dimension(value, DimensionUnit::VP);
444         overlay->handleRadius_ = overlay->handleDiameter_ * FIFTY_PERCENT;
445     };
446     renderText->SetUpdateHandleDiameter(callbackDiameter);
447 
448     auto callbackDiameterInner = [weak = WeakClaim(this)](const double& value) {
449         auto overlay = weak.Upgrade();
450         if (!overlay) {
451             LOGE("UpdateWeakText error, overlay is nullptr");
452             return;
453         }
454 
455         overlay->SetVisible(true);
456         if (overlay->onRebuild_) {
457             overlay->onRebuild_(overlay->isSingleHandle_, !overlay->isSingleHandle_, overlay->hasMenu_,
458                 !overlay->isSingleHandle_, false);
459         }
460         overlay->handleDiameterInner_ = Dimension(value, DimensionUnit::VP);
461         overlay->handleRadiusInner_ = overlay->handleDiameterInner_ * FIFTY_PERCENT;
462     };
463     renderText->SetUpdateHandleDiameterInner(callbackDiameterInner);
464 
465     auto onClipRectChanged = [weak = WeakClaim(this)](const Rect& clipRect) {
466         auto overlay = weak.Upgrade();
467         if (overlay && (overlay->clipRect_ != clipRect)) {
468             overlay->clipRect_ = clipRect;
469             overlay->MarkNeedLayout();
470         }
471     };
472     renderText->SetOnClipRectChanged(onClipRectChanged);
473 }
474 
UpdateWeakImage(const RefPtr<TextOverlayComponent> & overlay)475 void RenderTextOverlay::UpdateWeakImage(const RefPtr<TextOverlayComponent>& overlay)
476 {
477     if (!overlay) {
478         return;
479     }
480     weakImage_ = overlay->GetWeakImage();
481     auto renderImage = weakImage_.Upgrade();
482     if (!renderImage) {
483         return;
484     }
485     auto callback = [weak = WeakClaim(this)](const OverlayShowOption& option) {
486         auto overlay = weak.Upgrade();
487         if (!overlay) {
488             return;
489         }
490         if (option.updateOverlayType == UpdateOverlayType::CLICK ||
491             option.updateOverlayType == UpdateOverlayType::LONG_PRESS) {
492             overlay->childRightBoundary_ = 0.0;
493         }
494         overlay->SetVisible(true);
495         overlay->showOption_ = option;
496         overlay->startHandleOffset_ = option.startHandleOffset;
497         overlay->endHandleOffset_ = option.endHandleOffset;
498         overlay->isSingleHandle_ = option.isSingleHandle;
499         if (option.updateOverlayType == UpdateOverlayType::CLICK) {
500             if (overlay->onRebuild_) {
501                 overlay->hasMenu_ = false;
502                 overlay->onRebuild_(true, false, false, false, false);
503             }
504         } else if (option.updateOverlayType == UpdateOverlayType::LONG_PRESS) {
505             if (overlay->onRebuild_) {
506                 overlay->hasMenu_ = false;
507                 overlay->onRebuild_(false, true, false, true, false);
508             }
509         }
510     };
511     renderImage->SetUpdateHandlePosition(callback);
512 }
513 
514 #ifdef WEB_SUPPORTED
UpdateWeakWeb(const RefPtr<TextOverlayComponent> & overlay)515 void RenderTextOverlay::UpdateWeakWeb(const RefPtr<TextOverlayComponent>& overlay)
516 {
517     if (!overlay) {
518         return;
519     }
520     weakWeb_ = overlay->GetWeakWeb();
521     auto web = weakWeb_.Upgrade();
522     if (!touchDetector_ || !web) {
523         return;
524     }
525     isUsedByWeb_ = true;
526     auto callback = [weak = WeakClaim(this)](const OverlayShowOption& option, float startHeight, float endHeight) {
527         auto overlay = weak.Upgrade();
528         if (!overlay) {
529             return;
530         }
531         overlay->startHandleOffset_ = option.startHandleOffset;
532         overlay->endHandleOffset_ = option.endHandleOffset;
533         overlay->isSingleHandle_ = option.isSingleHandle;
534         overlay->showOption_.showMenu = option.showMenu;
535         overlay->showOption_.showStartHandle = option.showStartHandle;
536         overlay->showOption_.showEndHandle = option.showEndHandle;
537         overlay->startHandleHeight_ = startHeight;
538         overlay->endHandleHeight_ = endHeight;
539         overlay->lineHeight_ = startHeight;
540         overlay->MarkNeedLayout();
541     };
542     web->SetUpdateHandlePosition(callback);
543     showOption_.showMenu = web->TextOverlayMenuShouldShow();
544     showOption_.showStartHandle = web->GetShowStartTouchHandle();
545     showOption_.showEndHandle = web->GetShowEndTouchHandle();
546 
547     clickDetector_->SetOnClick([weak = AceType::WeakClaim(this)](const ClickInfo& clickInfo) {});
548     touchDetector_->SetOnTouchDown([weak = AceType::WeakClaim(this), weakWeb = weakWeb_](const TouchEventInfo& info) {
549         auto overlay = weak.Upgrade();
550         if (overlay) {
551             overlay->showMagnifier_ = true;
552             auto startOffset = info.GetTouches().front().GetLocalLocation();
553             if (overlay->startHandleRegion_.ContainsInRegion(startOffset.GetX(), startOffset.GetY())) {
554                 overlay->isTouchStartDrag_ = true;
555                 overlay->isTouchEndDrag_ = false;
556             } else {
557                 overlay->isTouchStartDrag_ = false;
558                 overlay->isTouchEndDrag_ =
559                     overlay->endHandleRegion_.ContainsInRegion(startOffset.GetX(), startOffset.GetY());
560             }
561             auto web = weakWeb.Upgrade();
562             if (web) {
563                 web->HandleTouchDown(info, true);
564             }
565         }
566     });
567 
568     touchDetector_->SetOnTouchMove([weak = AceType::WeakClaim(this), weakWeb = weakWeb_](const TouchEventInfo& info) {
569         auto overlay = weak.Upgrade();
570         if (overlay) {
571             auto web = weakWeb.Upgrade();
572             if (web) {
573                 web->HandleTouchMove(info, true);
574             }
575         }
576     });
577 
578     touchDetector_->SetOnTouchUp([weak = AceType::WeakClaim(this), weakWeb = weakWeb_](const TouchEventInfo& info) {
579         auto overlay = weak.Upgrade();
580         if (overlay) {
581             overlay->showMagnifier_ = false;
582             auto web = weakWeb.Upgrade();
583             if (web) {
584                 web->HandleTouchUp(info, true);
585             }
586             overlay->MarkNeedLayout();
587         }
588     });
589 }
590 #endif
591 
PerformLayout()592 void RenderTextOverlay::PerformLayout()
593 {
594     double handleRadius = NormalizeToPx(handleRadius_);
595     startHandleCenter_ = Offset(-handleRadius, handleRadius);
596     endHandleCenter_ = Offset(handleRadius, handleRadius);
597 
598     if (!GetChildren().empty()) {
599         const auto& child = GetChildren().front();
600         if (child) {
601             child->Layout(GetLayoutParam());
602             child->SetPosition(ComputeChildPosition(child));
603             if (NearZero(childRightBoundary_)) {
604                 childRightBoundary_ = child->GetPosition().GetX() + child->GetLayoutSize().Width();
605             } else {
606                 child->SetPosition(
607                     Offset(childRightBoundary_ - child->GetLayoutSize().Width(), child->GetPosition().GetY()));
608             }
609         } else {
610             LOGE("child is null");
611             return;
612         }
613         SetLayoutSize(GetLayoutParam().GetMaxSize());
614 
615         // If child size is changed, refresh animation, when there is tool bar only.
616         if (HasToolBarOnly()) {
617             double horizonOffsetForAnimation = child->GetLayoutSize().Width() - NormalizeToPx(MORE_BUTTON_SIZE);
618             if (!NearEqual(horizonOffsetForAnimation, horizonOffsetForAnimation_)) {
619                 horizonOffsetForAnimation_ = horizonOffsetForAnimation;
620                 isAnimationInited_ = false;
621             }
622         }
623         if (child && renderClip_) {
624             renderClip_->SetShadowBoxOffset(Offset(
625                 std::max(
626                     child->GetLayoutSize().Width() - horizonOffsetForAnimation_ - NormalizeToPx(MORE_BUTTON_SIZE), 0.0),
627                 0.0));
628         }
629 
630         if (textDirection_ == TextDirection::RTL) {
631             moreButtonPosition_ = child->GetGlobalOffset();
632         } else {
633             moreButtonPosition_ = child->GetGlobalOffset() + Offset(child->GetLayoutSize().Width(), 0.0);
634         }
635     }
636 
637     // Compute touch region of handle.
638     double hotZoneDiameter = NormalizeToPx(HANDLE_HOTZONE_DIAMETER);
639     double hotZoneRadius = hotZoneDiameter / 2.0;
640     if (isSingleHandle_) {
641         startHandleRegion_ = TouchRegion(startHandleOffset_ + Offset(-hotZoneRadius, 0.0),
642             startHandleOffset_ + Offset(hotZoneRadius, hotZoneDiameter));
643     } else {
644         startHandleRegion_ = TouchRegion(
645             startHandleOffset_ + Offset(-hotZoneRadius, -startHandleHeight_.value_or(lineHeight_) - hotZoneDiameter),
646             startHandleOffset_ + Offset(hotZoneRadius, -startHandleHeight_.value_or(lineHeight_)));
647         endHandleRegion_ = TouchRegion(
648             endHandleOffset_ + Offset(-hotZoneRadius, 0.0), endHandleOffset_ + Offset(hotZoneRadius, hotZoneDiameter));
649     }
650 
651     InitAnimation();
652 
653     auto context = GetContext().Upgrade();
654     if (!context) {
655         return;
656     }
657     if (!GetChildren().empty()) {
658         const auto& child = GetChildren().front();
659         if (!child) {
660             LOGE("child is null");
661             return;
662         }
663         Rect textOverlayRect(child->GetGlobalOffset(), child->GetLayoutSize());
664         auto startHandleOffset = startHandleOffset_ + Offset(-hotZoneRadius, -lineHeight_ - hotZoneDiameter);
665         auto startHandlebottomRightPoint = startHandleOffset_ + Offset(hotZoneRadius, -lineHeight_);
666         Rect startHandleRect(startHandleOffset.GetX(), startHandleOffset.GetY(),
667             startHandlebottomRightPoint.GetX() - startHandleOffset.GetX(),
668             startHandlebottomRightPoint.GetY() - startHandleOffset.GetY());
669         auto endHandleOffset = endHandleOffset_ + Offset(-hotZoneRadius, 0.0);
670         auto endHandlebottomRightPoint = endHandleOffset_ + Offset(hotZoneRadius, hotZoneDiameter);
671         Rect endHandleRect(endHandleOffset.GetX(), endHandleOffset.GetY(),
672             endHandlebottomRightPoint.GetX() - endHandleOffset.GetX(),
673             endHandlebottomRightPoint.GetY() - endHandleOffset.GetY());
674         auto textOverlayManager = context->GetTextOverlayManager();
675         if (textOverlayManager) {
676             textOverlayManager->ClearTextOverlayRect();
677             auto isContainerModal = context->GetWindowModal() == WindowModal::CONTAINER_MODAL &&
678                                     context->GetWindowManager()->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING;
679             if (isContainerModal) {
680                 textOverlayManager->SetCoordinateOffset(
681                     Offset((CONTAINER_BORDER_WIDTH.ConvertToPx() + CONTENT_PADDING.ConvertToPx()),
682                         CONTAINER_TITLE_HEIGHT.ConvertToPx()));
683             }
684             textOverlayManager->AddTextOverlayRect(textOverlayRect);
685             textOverlayManager->AddTextOverlayRect(startHandleRect);
686             textOverlayManager->AddTextOverlayRect(endHandleRect);
687         }
688     } else {
689         auto textOverlayManager = context->GetTextOverlayManager();
690         if (textOverlayManager) {
691             textOverlayManager->ClearTextOverlayRect();
692         }
693     }
694 }
695 
ComputeChildPosition(const RefPtr<RenderNode> & child)696 Offset RenderTextOverlay::ComputeChildPosition(const RefPtr<RenderNode>& child)
697 {
698     // Adjust position of overlay.
699     Offset startHandleOffset = startHandleOffset_;
700     Offset endHandleOffset = endHandleOffset_;
701     if (needClipRect_) {
702         startHandleOffset.SetX(std::clamp(startHandleOffset.GetX(), clipRect_.Left(), clipRect_.Right()));
703         startHandleOffset.SetY(std::clamp(startHandleOffset.GetY(), clipRect_.Top(), clipRect_.Bottom()));
704         endHandleOffset.SetX(std::clamp(endHandleOffset.GetX(), clipRect_.Left(), clipRect_.Right()));
705         endHandleOffset.SetY(std::clamp(endHandleOffset.GetY(), clipRect_.Top(), clipRect_.Bottom()));
706 
707         if (!NearEqual(startHandleOffset.GetY(), endHandleOffset.GetY())) {
708             startHandleOffset.SetX(clipRect_.Left());
709             endHandleOffset.SetX(clipRect_.Right());
710         }
711     }
712     // Calculate the spacing with text and handle, menu is fixed up the handle and text.
713     double menuSpacingWithText = NormalizeToPx(menuSpacingWithText_);
714     double menuSpacingWithHandle = NormalizeToPx(menuSpacingWithHandle_);
715     double menuSpacing = isSingleHandle_ ? menuSpacingWithText : menuSpacingWithHandle + menuSpacingWithText;
716 
717     Offset childPosition;
718     if (isUsingMouse_) {
719         auto context = GetContext().Upgrade();
720         auto isContainerModal = context->GetWindowModal() == WindowModal::CONTAINER_MODAL &&
721                                 context->GetWindowManager()->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING;
722         if (isContainerModal) {
723             childPosition =
724                 mouseOffset_ - Offset((CONTAINER_BORDER_WIDTH.ConvertToPx() + CONTENT_PADDING.ConvertToPx()),
725                                       CONTAINER_TITLE_HEIGHT.ConvertToPx());
726         } else {
727             childPosition = mouseOffset_;
728         }
729         if (GreatOrEqual(
730             childPosition.GetX() + child->GetLayoutSize().Width(), GetLayoutParam().GetMaxSize().Width()) &&
731             GreatOrEqual(childPosition.GetX(), child->GetLayoutSize().Width())) {
732             childPosition.SetX(childPosition.GetX() - child->GetLayoutSize().Width());
733         }
734         if (GreatOrEqual(
735             childPosition.GetY() + child->GetLayoutSize().Height(), GetLayoutParam().GetMaxSize().Height()) &&
736             GreatOrEqual(childPosition.GetY(), child->GetLayoutSize().Height())) {
737             childPosition.SetY(childPosition.GetY() - child->GetLayoutSize().Height());
738         }
739     } else {
740         childPosition =
741             Offset((startHandleOffset.GetX() + endHandleOffset.GetX() - child->GetLayoutSize().Width()) / 2.0,
742                 startHandleOffset.GetY() - lineHeight_ - menuSpacing - NormalizeToPx(TOOL_BAR_HEIGHT));
743         // Adjust position of overlay.
744         if (LessOrEqual(childPosition.GetX(), 0.0)) {
745             childPosition.SetX(DEFAULT_SPACING);
746         } else if (GreatOrEqual(
747             childPosition.GetX() + child->GetLayoutSize().Width(), GetLayoutParam().GetMaxSize().Width())) {
748             childPosition.SetX(
749                 GetLayoutParam().GetMaxSize().Width() - child->GetLayoutSize().Width() - DEFAULT_SPACING);
750         }
751         if (LessNotEqual(childPosition.GetY(), NormalizeToPx(TOOL_BAR_HEIGHT))) {
752             childPosition.SetY(endHandleOffset_.GetY() + menuSpacingWithHandle + menuSpacingWithText);
753         }
754     }
755 
756     return childPosition;
757 }
758 
InitAnimation()759 void RenderTextOverlay::InitAnimation()
760 {
761     if (isAnimationInited_) {
762         return;
763     }
764     isAnimationInited_ = true;
765 
766     // Create tween option for in.
767     // Add offset animation for outer tween.
768     auto xCurveIn = AceType::MakeRefPtr<CurveAnimation<float>>(horizonOffsetForAnimation_, 0.0f, Curves::FRICTION);
769     tweenOptionIn_.SetPropertyAnimationFloat(PropertyAnimatableType::PROPERTY_OFFSET_X, xCurveIn);
770 
771     // Add opacity animation for outer tween.
772     auto opacityKeyframeInFirst = AceType::MakeRefPtr<Keyframe<float>>(0.0f, 0.0f);
773     auto opacityKeyframeInSecond = AceType::MakeRefPtr<Keyframe<float>>(OPACITY_KEYFRAME, 1.0f);
774     opacityKeyframeInSecond->SetCurve(Curves::SHARP);
775     auto opacityKeyframeInThird = AceType::MakeRefPtr<Keyframe<float>>(1.0f, 1.0f);
776     auto opacityAnimationIn = AceType::MakeRefPtr<KeyframeAnimation<float>>();
777     opacityAnimationIn->AddKeyframe(opacityKeyframeInFirst);
778     opacityAnimationIn->AddKeyframe(opacityKeyframeInSecond);
779     opacityAnimationIn->AddKeyframe(opacityKeyframeInThird);
780     tweenOptionIn_.SetOpacityAnimation(opacityAnimationIn);
781     tweenOptionIn_.SetDuration(ANIMATION_DURATION);
782     tweenOptionIn_.SetFillMode(FillMode::FORWARDS);
783 
784     // Add translate animation for inner tween.
785     auto xTranslateIn = AceType::MakeRefPtr<CurveAnimation<DimensionOffset>>(
786         DimensionOffset(ANIMATION_OFFSET_X, 0.0_vp), DimensionOffset(0.0_vp, 0.0_vp), Curves::FRICTION);
787     innerTweenOptionIn_.SetTranslateAnimations(AnimationType::TRANSLATE_X, xTranslateIn);
788     innerTweenOptionIn_.SetDuration(ANIMATION_DURATION);
789     innerTweenOptionIn_.SetFillMode(FillMode::FORWARDS);
790 
791     // Create tween option for out.
792     // Add offset animation for outer tween.
793     auto xCurveOut = AceType::MakeRefPtr<CurveAnimation<float>>(0.0f, horizonOffsetForAnimation_, Curves::FRICTION);
794     tweenOptionOut_.SetPropertyAnimationFloat(PropertyAnimatableType::PROPERTY_OFFSET_X, xCurveOut);
795 
796     // Add opacity animation for outer tween.
797     auto opacityKeyframeOutFirst = AceType::MakeRefPtr<Keyframe<float>>(0.0f, 1.0f);
798     auto opacityKeyframeOutSecond = AceType::MakeRefPtr<Keyframe<float>>(OPACITY_KEYFRAME, 0.0f);
799     opacityKeyframeOutSecond->SetCurve(Curves::SHARP);
800     auto opacityKeyframeOutThird = AceType::MakeRefPtr<Keyframe<float>>(1.0f, 0.0f);
801     auto opacityAnimationOut = AceType::MakeRefPtr<KeyframeAnimation<float>>();
802     opacityAnimationOut->AddKeyframe(opacityKeyframeOutFirst);
803     opacityAnimationOut->AddKeyframe(opacityKeyframeOutSecond);
804     opacityAnimationOut->AddKeyframe(opacityKeyframeOutThird);
805     tweenOptionOut_.SetOpacityAnimation(opacityAnimationOut);
806     tweenOptionOut_.SetDuration(ANIMATION_DURATION);
807     tweenOptionOut_.SetFillMode(FillMode::FORWARDS);
808 
809     // Create translate animation for inner tween.
810     auto xTranslateOut = AceType::MakeRefPtr<CurveAnimation<DimensionOffset>>(
811         DimensionOffset(0.0_vp, 0.0_vp), DimensionOffset(ANIMATION_OFFSET_X, 0.0_vp), Curves::FRICTION);
812     innerTweenOptionOut_.SetTranslateAnimations(AnimationType::TRANSLATE_X, xTranslateOut);
813     innerTweenOptionOut_.SetDuration(ANIMATION_DURATION);
814     innerTweenOptionOut_.SetFillMode(FillMode::FORWARDS);
815 }
816 
TouchTest(const Point & globalPoint,const Point & parentLocalPoint,const TouchRestrict & touchRestrict,TouchTestResult & result)817 bool RenderTextOverlay::TouchTest(const Point& globalPoint, const Point& parentLocalPoint,
818     const TouchRestrict& touchRestrict, TouchTestResult& result)
819 {
820     if (GetDisableTouchEvent() || disabled_ || !isAnimationStopped_) {
821         return false;
822     }
823     const auto localPoint = parentLocalPoint - GetPaintRect().GetOffset();
824     if (!isSingleHandle_ || showOption_.showMenu) {
825         for (auto iter = GetChildren().rbegin(); iter != GetChildren().rend(); ++iter) {
826             const auto& child = *iter;
827             auto childResult = result.size();
828             if (child->TouchTest(globalPoint, localPoint, touchRestrict, result)) {
829                 return true;
830             }
831             // here is to handle some cases when the touch test doesnt bubble up.
832             if (childResult != result.size()) {
833                 return true;
834             }
835         }
836     }
837     if (startHandleRegion_.ContainsInRegion(parentLocalPoint.GetX(), parentLocalPoint.GetY()) ||
838         endHandleRegion_.ContainsInRegion(parentLocalPoint.GetX(), parentLocalPoint.GetY())) {
839         const auto coordinateOffset = globalPoint - localPoint;
840         globalPoint_ = globalPoint;
841         OnTouchTestHit(coordinateOffset, touchRestrict, result);
842         return true;
843     }
844     if (globalPoint.GetSourceType() == SourceType::MOUSE) {
845         PopOverlay();
846         return true;
847     }
848     return false;
849 }
850 
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)851 void RenderTextOverlay::OnTouchTestHit(
852     const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
853 {
854     clickDetector_->SetCoordinateOffset(coordinateOffset);
855     result.emplace_back(clickDetector_);
856     dragDetector_->SetCoordinateOffset(coordinateOffset);
857     result.emplace_back(dragDetector_);
858     touchDetector_->SetCoordinateOffset(coordinateOffset);
859     result.emplace_back(touchDetector_);
860 }
861 
HandleMouseEvent(const MouseEvent & event)862 bool RenderTextOverlay::HandleMouseEvent(const MouseEvent& event)
863 {
864     if (event.button != MouseButton::LEFT_BUTTON && event.action == MouseAction::PRESS) {
865         PopOverlay();
866         return true;
867     }
868     return event.button == MouseButton::LEFT_BUTTON;
869 }
870 
HandleClick(const Offset & clickOffset)871 void RenderTextOverlay::HandleClick(const Offset& clickOffset)
872 {
873     if (isSingleHandle_ && startHandleRegion_.ContainsInRegion(clickOffset.GetX(), clickOffset.GetY())) {
874         childRightBoundary_ = 0.0;
875         showOption_.showMenu = true;
876         auto textField = weakTextField_.Upgrade();
877         if (textField) {
878             textField->SetIsOverlayShowed(true, false);
879         }
880         if (onRebuild_) {
881             OnFocusChange(RenderStatus::FOCUS);
882             onRebuild_(true, true, hasMenu_, true, false);
883         }
884     }
885 }
886 
HandleDragStart(const Offset & startOffset)887 void RenderTextOverlay::HandleDragStart(const Offset& startOffset)
888 {
889     childRightBoundary_ = 0.0;
890     showOption_.showMenu = true;
891     auto textField = weakTextField_.Upgrade();
892     auto text = weakText_.Upgrade();
893     if (!textField && !text) {
894         LOGE("TextField or text is nullptr");
895         return;
896     }
897 
898     // Mark start and end index
899     if (textField) {
900         startIndex_ = textField->GetEditingValue().selection.GetStart();
901         endIndex_ = textField->GetEditingValue().selection.GetEnd();
902         if (startHandleRegion_.ContainsInRegion(startOffset.GetX(), startOffset.GetY())) {
903             textField->SetInitIndex(endIndex_);
904         } else {
905             textField->SetInitIndex(startIndex_);
906         }
907     } else if (text) {
908         startIndex_ = text->GetTextSelect().GetStart();
909         endIndex_ = text->GetTextSelect().GetEnd();
910     }
911 
912     // Mark start or end flag and mark the index
913     if (startHandleRegion_.ContainsInRegion(startOffset.GetX(), startOffset.GetY())) {
914         isStartDrag_ = true;
915         isEndDrag_ = false;
916     } else {
917         isStartDrag_ = false;
918         isEndDrag_ = endHandleRegion_.ContainsInRegion(startOffset.GetX(), startOffset.GetY());
919     }
920 }
921 
HandleDragUpdateAndEnd(const Offset & offset)922 void RenderTextOverlay::HandleDragUpdateAndEnd(const Offset& offset)
923 {
924     childRightBoundary_ = 0.0;
925     if (isStartDrag_) {
926         auto startCallback = [weak = WeakClaim(this)](const Offset& startHandleOffset) {
927             auto overlay = weak.Upgrade();
928             if (overlay) {
929                 overlay->startHandleOffset_ = startHandleOffset;
930                 if (overlay->isSingleHandle_) {
931                     overlay->endHandleOffset_ = startHandleOffset;
932                 }
933                 overlay->MarkNeedLayout();
934             }
935         };
936         onStartHandleMove_(endIndex_, offset - Offset(0.0, lineHeight_), startCallback, isSingleHandle_);
937         isDragging_ = true;
938     } else if (isEndDrag_) {
939         auto endCallback = [weak = WeakClaim(this)](const Offset& endHandleOffset) {
940             auto overlay = weak.Upgrade();
941             if (overlay) {
942                 overlay->endHandleOffset_ = endHandleOffset;
943                 overlay->MarkNeedLayout();
944             }
945         };
946         onEndHandleMove_(startIndex_, offset - Offset(0.0, lineHeight_), endCallback);
947         isDragging_ = true;
948     }
949 }
950 
HandleCut()951 void RenderTextOverlay::HandleCut()
952 {
953     needCloseKeyboard_ = false;
954     if (onCut_) {
955         onCut_();
956     }
957     PopOverlay();
958 }
959 
HandleCopy()960 void RenderTextOverlay::HandleCopy()
961 {
962     needCloseKeyboard_ = false;
963     if (onCopy_) {
964         onCopy_();
965     }
966     PopOverlay();
967 }
968 
HandlePaste()969 void RenderTextOverlay::HandlePaste()
970 {
971     needCloseKeyboard_ = false;
972     if (onPaste_) {
973         onPaste_();
974     }
975     PopOverlay();
976 }
977 
HandleCopyAll()978 void RenderTextOverlay::HandleCopyAll()
979 {
980     needCloseKeyboard_ = false;
981     isSingleHandle_ = false;
982     childRightBoundary_ = 0.0;
983     auto callback = [weak = WeakClaim(this)](const Offset& startHandleOffset, const Offset& endHandleOffset) {
984         auto overlay = weak.Upgrade();
985         if (overlay) {
986             if (startHandleOffset == overlay->startHandleOffset_ && endHandleOffset == overlay->endHandleOffset_) {
987                 LOGI("selection region has not changed");
988                 return;
989             }
990             overlay->startHandleOffset_ = startHandleOffset;
991             overlay->endHandleOffset_ = endHandleOffset;
992             overlay->isSingleHandle_ = false;
993             if (startHandleOffset == endHandleOffset) {
994                 overlay->isSingleHandle_ = true;
995             }
996             if (overlay->onRebuild_) {
997                 overlay->onRebuild_(overlay->isSingleHandle_, true, overlay->hasMenu_, true, false);
998             }
999         }
1000     };
1001     if (onCopyAll_) {
1002         onCopyAll_(callback);
1003     }
1004 }
1005 
HandleMoreButtonClick()1006 void RenderTextOverlay::HandleMoreButtonClick()
1007 {
1008     needCloseKeyboard_ = false;
1009     // Is animation is not stopped, do not handle click to start a new animation.
1010     if (!isAnimationStopped_) {
1011         return;
1012     }
1013     hasMenu_ = !hasMenu_;
1014     auto context = GetContext().Upgrade();
1015     if (context && context->GetIsDeclarative()) {
1016         onRebuild_(isSingleHandle_, true, true, true, false);
1017         return;
1018     }
1019     isAnimationStarted_ = false;
1020     isAnimationStopped_ = false;
1021     if (onRebuild_) {
1022         animateUntilPaint_ = hasMenu_;
1023         onRebuild_(isSingleHandle_, true, true, true, true);
1024     }
1025     if (!animateUntilPaint_) {
1026         startAnimation_(tweenOptionIn_, innerTweenOptionIn_, isSingleHandle_, true);
1027         StartMoreAnimation(reverse_);
1028     }
1029 }
1030 
OnPaintFinish()1031 void RenderTextOverlay::OnPaintFinish()
1032 {
1033     if (animateUntilPaint_) {
1034         animateUntilPaint_ = false;
1035         startAnimation_(tweenOptionOut_, innerTweenOptionOut_, isSingleHandle_, false);
1036         StartMoreAnimation(reverse_);
1037     }
1038 }
1039 
RestoreMoreButtonStyle()1040 void RenderTextOverlay::RestoreMoreButtonStyle()
1041 {
1042     if (!controller_) {
1043         return;
1044     }
1045     if (reverse_) {
1046         BuildAndStartMoreButtonAnimation();
1047         controller_->Finish();
1048     } else if (controller_->IsRunning()) {
1049         controller_->Finish();
1050     }
1051 }
1052 
StartMoreAnimation(bool reverse)1053 void RenderTextOverlay::StartMoreAnimation(bool reverse)
1054 {
1055     if (controller_ && controller_->IsRunning()) {
1056         reverse_ = !reverse_;
1057         controller_->Reverse();
1058         return;
1059     }
1060     BuildAndStartMoreButtonAnimation();
1061 }
1062 
BuildStrokeWidthAnimation(const RefPtr<KeyframeAnimation<Dimension>> & widthAnimation,const Dimension & from,const Dimension & to,bool reverse)1063 void RenderTextOverlay::BuildStrokeWidthAnimation(const RefPtr<KeyframeAnimation<Dimension>>& widthAnimation,
1064     const Dimension& from, const Dimension& to, bool reverse)
1065 {
1066     auto widthFrameStart = AceType::MakeRefPtr<Keyframe<Dimension>>(KEYFRAME_BEGINNING, from);
1067     auto widthFrameEnd = AceType::MakeRefPtr<Keyframe<Dimension>>(KEYFRAME_ENDING, to);
1068     widthAnimation->AddKeyframe(widthFrameStart);
1069     if (reverse) {
1070         widthFrameEnd->SetCurve(Curves::FRICTION);
1071     } else {
1072         auto widthFrameMid = AceType::MakeRefPtr<Keyframe<Dimension>>(KEYFRAME_PERCENT_THIRTY, to);
1073         widthFrameMid->SetCurve(Curves::FRICTION);
1074         widthFrameEnd->SetCurve(Curves::LINEAR);
1075         widthAnimation->AddKeyframe(widthFrameMid);
1076     }
1077     widthAnimation->AddKeyframe(widthFrameEnd);
1078     widthAnimation->AddListener([weakText = AceType::WeakClaim(this)](const Dimension& value) {
1079         auto overlay = weakText.Upgrade();
1080         if (overlay) {
1081             overlay->strokeWidth_ = value;
1082             overlay->clipWidth_ = std::clamp((CLIP_WIDTH - (value - STROKE_MIN_WIDTH).Value()), 0.0, CLIP_WIDTH);
1083             overlay->MarkNeedRender(true);
1084         }
1085     });
1086 }
1087 
BuildEndPointOffsetAnimation(const RefPtr<KeyframeAnimation<double>> & offsetAnimation,double from,double to,bool reverse)1088 void RenderTextOverlay::BuildEndPointOffsetAnimation(
1089     const RefPtr<KeyframeAnimation<double>>& offsetAnimation, double from, double to, bool reverse)
1090 {
1091     auto offsetFrameStart = AceType::MakeRefPtr<Keyframe<double>>(KEYFRAME_BEGINNING, from);
1092     auto offsetFrameEnd = AceType::MakeRefPtr<Keyframe<double>>(KEYFRAME_ENDING, to);
1093 
1094     offsetAnimation->AddKeyframe(offsetFrameStart);
1095     if (reverse) {
1096         offsetFrameEnd->SetCurve(Curves::FRICTION);
1097     } else {
1098         auto offsetFrameMid = AceType::MakeRefPtr<Keyframe<double>>(KEYFRAME_PERCENT_THIRTY, from);
1099         offsetFrameMid->SetCurve(Curves::LINEAR);
1100         offsetFrameEnd->SetCurve(Curves::FRICTION);
1101         offsetAnimation->AddKeyframe(offsetFrameMid);
1102     }
1103     offsetAnimation->AddKeyframe(offsetFrameEnd);
1104     offsetAnimation->AddListener([weakText = AceType::WeakClaim(this)](double value) {
1105         auto overlay = weakText.Upgrade();
1106         if (overlay) {
1107             overlay->ProcessEndPointAnimation(value);
1108             overlay->MarkNeedRender(true);
1109         }
1110     });
1111 }
1112 
BuildFrictionAnimation(const RefPtr<KeyframeAnimation<double>> & animation,double from,double to)1113 void RenderTextOverlay::BuildFrictionAnimation(
1114     const RefPtr<KeyframeAnimation<double>>& animation, double from, double to)
1115 {
1116     auto frameStart = AceType::MakeRefPtr<Keyframe<double>>(KEYFRAME_BEGINNING, from);
1117     auto frameEnd = AceType::MakeRefPtr<Keyframe<double>>(KEYFRAME_ENDING, to);
1118     frameEnd->SetCurve(Curves::FRICTION);
1119 
1120     animation->AddKeyframe(frameStart);
1121     animation->AddKeyframe(frameEnd);
1122     animation->AddListener([weakText = AceType::WeakClaim(this)](double value) {
1123         auto overlay = weakText.Upgrade();
1124         if (overlay) {
1125             overlay->ProcessFrictionAnimation(value);
1126             overlay->MarkNeedRender(true);
1127         }
1128     });
1129 }
1130 
ProcessFrictionAnimation(double value)1131 void RenderTextOverlay::ProcessFrictionAnimation(double value)
1132 {
1133     // calculate start point offset of dots
1134     dot1StartOffset_ = DOT1_OFFSET * value;
1135     dot2StartOffset_ = DOT2_OFFSET * value;
1136     dot3StartOffset_ = DOT3_OFFSET * value;
1137     dot4StartOffset_ = DOT4_OFFSET * value;
1138 
1139     // calculate rotate degree
1140     rotateDegree_ = ROTATE_DEGREE * value;
1141 }
1142 
ProcessEndPointAnimation(double value)1143 void RenderTextOverlay::ProcessEndPointAnimation(double value)
1144 {
1145     dot2Offset_ = (END_POINT - DOT2_POSITION - DOT2_OFFSET) * value;
1146     dot3Offset_ = (END_POINT - DOT3_POSITION - DOT3_OFFSET) * value;
1147     dot4Offset_ = (END_POINT - DOT4_POSITION - DOT4_OFFSET) * value;
1148 }
1149 
BuildAndStartMoreButtonAnimation()1150 void RenderTextOverlay::BuildAndStartMoreButtonAnimation()
1151 {
1152     if (!controller_) {
1153         controller_ = CREATE_ANIMATOR(GetContext());
1154     }
1155     controller_->ClearInterpolators();
1156     controller_->ClearAllListeners();
1157 
1158     RefPtr<KeyframeAnimation<Dimension>> strokeWidthAnimation = AceType::MakeRefPtr<KeyframeAnimation<Dimension>>();
1159     RefPtr<KeyframeAnimation<double>> startPointAnimation = AceType::MakeRefPtr<KeyframeAnimation<double>>();
1160     RefPtr<KeyframeAnimation<double>> endPointAnimation = AceType::MakeRefPtr<KeyframeAnimation<double>>();
1161     if (reverse_) {
1162         BuildStrokeWidthAnimation(strokeWidthAnimation, STROKE_MIN_WIDTH, STROKE_MAX_WIDTH, true);
1163         BuildFrictionAnimation(startPointAnimation, KEYFRAME_ENDING, KEYFRAME_BEGINNING);
1164         BuildEndPointOffsetAnimation(endPointAnimation, KEYFRAME_ENDING, KEYFRAME_BEGINNING, true);
1165     } else {
1166         BuildStrokeWidthAnimation(strokeWidthAnimation, STROKE_MAX_WIDTH, STROKE_MIN_WIDTH, false);
1167         BuildFrictionAnimation(startPointAnimation, KEYFRAME_BEGINNING, KEYFRAME_ENDING);
1168         BuildEndPointOffsetAnimation(endPointAnimation, KEYFRAME_BEGINNING, KEYFRAME_ENDING, false);
1169     }
1170 
1171     controller_->SetDuration(MORE_ANIMATION_DURATION);
1172     controller_->AddStopListener([more = AceType::WeakClaim(this)]() {
1173         auto textMore = more.Upgrade();
1174         if (textMore) {
1175             textMore->reverse_ = (!textMore->reverse_);
1176             textMore->renderMenu_->SetIsWattingForAnimationStart(false);
1177         }
1178     });
1179     controller_->AddStartListener([more = AceType::WeakClaim(this)]() {
1180         auto textMore = more.Upgrade();
1181         if (textMore) {
1182             textMore->reverse_ = (!textMore->reverse_);
1183             textMore->renderMenu_->SetIsWattingForAnimationStart(true);
1184         }
1185     });
1186     controller_->AddInterpolator(strokeWidthAnimation);
1187     controller_->AddInterpolator(startPointAnimation);
1188     controller_->AddInterpolator(endPointAnimation);
1189     controller_->Forward();
1190 }
1191 
PopOverlay()1192 void RenderTextOverlay::PopOverlay()
1193 {
1194     if (hasPoped_) {
1195         return;
1196     }
1197     auto textField = weakTextField_.Upgrade();
1198     if (!textField) {
1199         return;
1200     }
1201     auto stack = textField->GetStackElement().Upgrade();
1202     if (stack) {
1203         hasPoped_ = true;
1204         stack->PopTextOverlay();
1205     }
1206     textField->SetIsOverlayShowed(false, needStartTwinkling_);
1207     textField->SetTextOverlayPushed(false);
1208 }
1209 
OnFocusChange(RenderStatus renderStatus)1210 void RenderTextOverlay::OnFocusChange(RenderStatus renderStatus)
1211 {
1212     if (onFocusChange_) {
1213         onFocusChange_(renderStatus == RenderStatus::FOCUS, needCloseKeyboard_);
1214     }
1215 }
1216 
InitRenderChild(const RefPtr<RenderNode> & render)1217 void RenderTextOverlay::InitRenderChild(const RefPtr<RenderNode>& render)
1218 {
1219     if (!render) {
1220         return;
1221     }
1222 
1223     if (AceType::InstanceOf<RenderBox>(render)) {
1224         if (!renderBox_) {
1225             renderBox_ = AceType::DynamicCast<RenderBox>(render);
1226         }
1227     } else if (AceType::InstanceOf<RenderClip>(render)) {
1228         if (!renderClip_) {
1229             renderClip_ = AceType::DynamicCast<RenderClip>(render);
1230         }
1231     } else if (AceType::InstanceOf<RenderSelectPopup>(render)) {
1232         if (!renderMenu_) {
1233             renderMenu_ = AceType::DynamicCast<RenderSelectPopup>(render);
1234         }
1235     }
1236 
1237     for (const auto& child : render->GetChildren()) {
1238         InitRenderChild(child);
1239     }
1240 }
1241 
ResetRenderChild()1242 void RenderTextOverlay::ResetRenderChild()
1243 {
1244     renderBox_.Reset();
1245     renderClip_.Reset();
1246     renderMenu_.Reset();
1247 }
1248 
HasToolBarOnly() const1249 bool RenderTextOverlay::HasToolBarOnly() const
1250 {
1251     // Child of render overlay is focus collaboration.
1252     auto focusCollaboration = AceType::DynamicCast<RenderFocusCollaboration>(GetChildren().front());
1253     if (!focusCollaboration) {
1254         return false;
1255     }
1256     // Child of render focus collaboration is column.
1257     auto column = AceType::DynamicCast<RenderFlex>(focusCollaboration->GetChildren().front());
1258     if (!column) {
1259         return false;
1260     }
1261     // Column has two children at most, tool bar and menu, if there is only one, it must be tool bar.
1262     return column->GetChildren().size() == 1;
1263 }
1264 
SetOnRebuild(const std::function<void (bool,bool,bool,bool,bool)> & onRebuild)1265 void RenderTextOverlay::SetOnRebuild(const std::function<void(bool, bool, bool, bool, bool)>& onRebuild)
1266 {
1267     onRebuild_ = onRebuild;
1268 }
1269 
SetStartAnimationCallback(const StartAnimationCallback & callback)1270 void RenderTextOverlay::SetStartAnimationCallback(const StartAnimationCallback& callback)
1271 {
1272     startAnimation_ = callback;
1273 }
1274 
SetIsAnimationStarted(bool isAnimationStarted)1275 void RenderTextOverlay::SetIsAnimationStarted(bool isAnimationStarted)
1276 {
1277     isAnimationStarted_ = isAnimationStarted;
1278 }
1279 
IsAnimationStarted() const1280 bool RenderTextOverlay::IsAnimationStarted() const
1281 {
1282     return isAnimationStarted_;
1283 }
1284 
SetIsAnimationStopped(bool isAnimationStopped)1285 void RenderTextOverlay::SetIsAnimationStopped(bool isAnimationStopped)
1286 {
1287     isAnimationStopped_ = isAnimationStopped;
1288 }
1289 
GetHorizonOffsetForAnimation() const1290 double RenderTextOverlay::GetHorizonOffsetForAnimation() const
1291 {
1292     return horizonOffsetForAnimation_;
1293 }
1294 
1295 } // namespace OHOS::Ace
1296