• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 "core/components/focus_collaboration/render_focus_collaboration.h"
19 #include "core/components/stack/stack_element.h"
20 
21 namespace OHOS::Ace {
22 namespace {
23 
24 const Offset DOT1_OFFSET = Offset(-0.790737726, -1.290737726);
25 const Offset DOT2_OFFSET = Offset(-0.623475836, -1.290737726);
26 const Offset DOT3_OFFSET = Offset(-0.790737726, -0.123475836);
27 const Offset DOT4_OFFSET = Offset(0.790737726, 1.290737726);
28 const Offset END_POINT = DOT1_POSITION + DOT1_OFFSET;
29 constexpr Dimension TOOL_BAR_HEIGHT = 40.0_vp;
30 constexpr Dimension HANDLE_HOTZONE_DIAMETER = 20.0_vp;
31 constexpr Dimension MORE_BUTTON_SIZE = 40.0_vp;
32 constexpr Dimension ANIMATION_OFFSET_X = 16.0_vp;
33 constexpr double DEFAULT_SPACING = 10.0;
34 constexpr double FIFTY_PERCENT = 0.5;
35 constexpr double ROTATE_DEGREE = -45.0;
36 constexpr double CLIP_WIDTH = 1.0;
37 constexpr double OPACITY_KEYFRAME = 250.0 / 350.0; // Clip and translate animation is 350ms, opacity animation is 250ms.
38 constexpr float KEYFRAME_PERCENT_THIRTY = 0.33f;
39 constexpr float KEYFRAME_BEGINNING = 0.0f;
40 constexpr float KEYFRAME_ENDING = 1.0f;
41 constexpr int32_t MORE_ANIMATION_DURATION = 300; // Duration of more icon animation.
42 constexpr int32_t ANIMATION_DURATION = 350;      // Duration of clip and translate animation
43 
44 } // namespace
45 
RenderTextOverlay()46 RenderTextOverlay::RenderTextOverlay()
47 {
48     clickDetector_ = AceType::MakeRefPtr<ClickRecognizer>();
49     clickDetector_->SetOnClick([weak = AceType::WeakClaim(this)](const ClickInfo& clickInfo) {
50         auto overlay = weak.Upgrade();
51         if (overlay) {
52             overlay->HandleClick(clickInfo.GetLocalLocation());
53         }
54     });
55 
56     dragDetector_ = AceType::MakeRefPtr<DragRecognizer>(Axis::FREE);
57     dragDetector_->SetOnDragStart([weak = AceType::WeakClaim(this)](const DragStartInfo& startInfo) {
58         auto overlay = weak.Upgrade();
59         if (overlay) {
60             overlay->HandleDragStart(startInfo.GetLocalLocation());
61         }
62     });
63 
64     dragDetector_->SetOnDragUpdate([weak = AceType::WeakClaim(this)](const DragUpdateInfo& updateInfo) {
65         auto overlay = weak.Upgrade();
66         if (overlay) {
67             overlay->HandleDragUpdateAndEnd(updateInfo.GetLocalLocation());
68         }
69     });
70 
71     dragDetector_->SetOnDragEnd([weak = AceType::WeakClaim(this)](const DragEndInfo& endInfo) {
72         auto overlay = weak.Upgrade();
73         if (overlay) {
74             overlay->HandleDragUpdateAndEnd(endInfo.GetLocalLocation());
75             overlay->isDragging_ = false;
76         }
77     });
78 
79     touchDetector_ = AceType::MakeRefPtr<RawRecognizer>();
80     touchDetector_->SetOnTouchDown([weak = AceType::WeakClaim(this)](const TouchEventInfo& info) {
81         auto overlay = weak.Upgrade();
82         if (overlay) {
83             overlay->showMagnifier_ = true;
84             auto startOffset = info.GetTouches().front().GetLocalLocation();
85             if (overlay->startHandleRegion_.ContainsInRegion(startOffset.GetX(), startOffset.GetY())) {
86                 overlay->isTouchStartDrag_ = true;
87                 overlay->isTouchEndDrag_ = false;
88             } else {
89                 overlay->isTouchStartDrag_ = false;
90                 overlay->isTouchEndDrag_ =
91                     overlay->endHandleRegion_.ContainsInRegion(startOffset.GetX(), startOffset.GetY());
92             }
93         }
94     });
95 
96     touchDetector_->SetOnTouchUp([weak = AceType::WeakClaim(this)](const TouchEventInfo&) {
97         auto overlay = weak.Upgrade();
98         if (overlay) {
99             overlay->showMagnifier_ = false;
100         }
101     });
102 }
103 
~RenderTextOverlay()104 RenderTextOverlay::~RenderTextOverlay()
105 {
106     auto renderTextField = weakTextField_.Upgrade();
107     if (renderTextField) {
108         renderTextField->SetIsOverlayShowed(false, needStartTwinkling_);
109     }
110     auto spOverlayComponent = overlayComponent_.Upgrade();
111     if (spOverlayComponent) {
112         RemoveBackendEvent(spOverlayComponent);
113     }
114 }
115 
Update(const RefPtr<Component> & component)116 void RenderTextOverlay::Update(const RefPtr<Component>& component)
117 {
118     auto overlay = AceType::DynamicCast<TextOverlayComponent>(component);
119     if (!overlay) {
120         return;
121     }
122     overlayComponent_ = overlay;
123     overlay->SetPopOverlay([weak = WeakClaim(this)]() {
124         auto overlay = weak.Upgrade();
125         if (overlay) {
126             overlay->needCloseKeyboard_ = false;
127             overlay->PopOverlay();
128         }
129     });
130     onFocusChange_ = overlay->GetOnFocusChange();
131     onCut_ = overlay->GetOnCut();
132     onCopy_ = overlay->GetOnCopy();
133     onPaste_ = overlay->GetOnPaste();
134     onCopyAll_ = overlay->GetOnCopyAll();
135     startHandleOffset_ = overlay->GetStartHandleOffset();
136     endHandleOffset_ = overlay->GetEndHandleOffset();
137     onStartHandleMove_ = overlay->GetOnStartHandleMove();
138     onEndHandleMove_ = overlay->GetOnEndHandleMove();
139     isSingleHandle_ = overlay->GetIsSingleHandle() || (startHandleOffset_ == endHandleOffset_);
140     lineHeight_ = overlay->GetLineHeight();
141     handleDiameter_ = overlay->GetHandleDiameter();
142     menuSpacingWithHandle_ = handleDiameter_;
143     handleDiameterInner_ = overlay->GetHandleDiameterInner();
144     handleRadius_ = handleDiameter_ / 2.0;
145     handleRadiusInner_ = handleDiameterInner_ / 2.0;
146     menuSpacingWithText_ = overlay->GetMenuSpacingWithText();
147     handleColor_ = overlay->GetHandleColor();
148     handleColorInner_ = overlay->GetHandleColorInner();
149     clipRect_ = overlay->GetClipRect();
150     textDirection_ = overlay->GetTextDirection();
151     realTextDirection_ = overlay->GetRealTextDirection();
152     BindBackendEvent(overlay);
153     UpdateWeakTextField(overlay);
154     MarkNeedLayout();
155 }
156 
BindBackendEvent(const RefPtr<TextOverlayComponent> & overlay)157 void RenderTextOverlay::BindBackendEvent(const RefPtr<TextOverlayComponent>& overlay)
158 {
159     auto context = context_.Upgrade();
160     if (context->GetIsDeclarative()) {
161         BindBackendEventV2(overlay);
162     } else {
163         BackEndEventManager<void()>::GetInstance().BindBackendEvent(
164             overlay->GetCutButtonMarker(), [weak = WeakClaim(this)]() {
165                 auto overlay = weak.Upgrade();
166                 if (overlay) {
167                     overlay->HandleCut();
168                 }
169             });
170 
171         BackEndEventManager<void()>::GetInstance().BindBackendEvent(
172             overlay->GetCopyButtonMarker(), [weak = WeakClaim(this)]() {
173                 auto overlay = weak.Upgrade();
174                 if (overlay) {
175                     overlay->HandleCopy();
176                 }
177             });
178 
179         BackEndEventManager<void()>::GetInstance().BindBackendEvent(
180             overlay->GetPasteButtonMarker(), [weak = WeakClaim(this)]() {
181                 auto overlay = weak.Upgrade();
182                 if (overlay) {
183                     overlay->HandlePaste();
184                 }
185             });
186 
187         BackEndEventManager<void()>::GetInstance().BindBackendEvent(
188             overlay->GetCopyAllButtonMarker(), [weak = WeakClaim(this)]() {
189                 auto overlay = weak.Upgrade();
190                 if (overlay) {
191                     overlay->HandleCopyAll();
192                 }
193             });
194     }
195 
196     BackEndEventManager<void()>::GetInstance().BindBackendEvent(
197         overlay->GetMoreButtonMarker(), [weak = WeakClaim(this)]() {
198             auto overlay = weak.Upgrade();
199             if (overlay) {
200                 overlay->HandleMoreButtonClick();
201             }
202         });
203 }
204 
BindBackendEventV2(const RefPtr<TextOverlayComponent> & overlay)205 void RenderTextOverlay::BindBackendEventV2(const RefPtr<TextOverlayComponent>& overlay)
206 {
207     BackEndEventManager<void(const ClickInfo& info)>::GetInstance().BindBackendEvent(
208         overlay->GetCutButtonMarker(), [weak = WeakClaim(this)](const ClickInfo& info) {
209             auto overlay = weak.Upgrade();
210             if (overlay) {
211                 overlay->HandleCut();
212             }
213         });
214 
215     BackEndEventManager<void(const ClickInfo& info)>::GetInstance().BindBackendEvent(
216         overlay->GetCopyButtonMarker(), [weak = WeakClaim(this)](const ClickInfo& info) {
217             auto overlay = weak.Upgrade();
218             if (overlay) {
219                 overlay->HandleCopy();
220             }
221         });
222 
223     BackEndEventManager<void(const ClickInfo& info)>::GetInstance().BindBackendEvent(
224         overlay->GetPasteButtonMarker(), [weak = WeakClaim(this)](const ClickInfo& info) {
225             auto overlay = weak.Upgrade();
226             if (overlay) {
227                 overlay->HandlePaste();
228             }
229         });
230 
231     BackEndEventManager<void(const ClickInfo& info)>::GetInstance().BindBackendEvent(
232         overlay->GetCopyAllButtonMarker(), [weak = WeakClaim(this)](const ClickInfo& info) {
233             auto overlay = weak.Upgrade();
234             if (overlay) {
235                 overlay->HandleCopyAll();
236             }
237         });
238 }
239 
RemoveBackendEvent(const RefPtr<TextOverlayComponent> & overlay)240 void RenderTextOverlay::RemoveBackendEvent(const RefPtr<TextOverlayComponent>& overlay)
241 {
242     BackEndEventManager<void()>::GetInstance().RemoveBackEndEvent(overlay->GetCutButtonMarker());
243     BackEndEventManager<void()>::GetInstance().RemoveBackEndEvent(overlay->GetCopyButtonMarker());
244     BackEndEventManager<void()>::GetInstance().RemoveBackEndEvent(overlay->GetPasteButtonMarker());
245     BackEndEventManager<void()>::GetInstance().RemoveBackEndEvent(overlay->GetCopyAllButtonMarker());
246     BackEndEventManager<void()>::GetInstance().RemoveBackEndEvent(overlay->GetMoreButtonMarker());
247 }
248 
UpdateWeakTextField(const RefPtr<TextOverlayComponent> & overlay)249 void RenderTextOverlay::UpdateWeakTextField(const RefPtr<TextOverlayComponent>& overlay)
250 {
251     if (!overlay) {
252         return;
253     }
254     weakTextField_ = overlay->GetWeakTextField();
255     auto renderTextField = weakTextField_.Upgrade();
256     if (!renderTextField) {
257         return;
258     }
259     auto callback = [weak = WeakClaim(this)](const OverlayShowOption& option) {
260         auto overlay = weak.Upgrade();
261         if (!overlay) {
262             return;
263         }
264         if (option.updateOverlayType == UpdateOverlayType::CLICK ||
265             option.updateOverlayType == UpdateOverlayType::LONG_PRESS) {
266             overlay->childRightBoundary_ = 0.0;
267         }
268         overlay->SetVisible(true);
269         overlay->showOption_ = option;
270         overlay->startHandleOffset_ = option.startHandleOffset;
271         overlay->endHandleOffset_ = option.endHandleOffset;
272         overlay->isSingleHandle_ = option.isSingleHandle;
273         if (option.updateOverlayType == UpdateOverlayType::CLICK) {
274             if (overlay->onRebuild_) {
275                 overlay->hasMenu_ = false;
276                 overlay->onRebuild_(true, false, false, false, false);
277             }
278         } else if (option.updateOverlayType == UpdateOverlayType::LONG_PRESS) {
279             if (overlay->onRebuild_) {
280                 overlay->hasMenu_ = false;
281                 overlay->onRebuild_(false, true, false, true, false);
282             }
283         }
284     };
285     renderTextField->SetUpdateHandlePosition(callback);
286 
287     auto callbackDiameter = [weak = WeakClaim(this)](const double& value) {
288         auto overlay = weak.Upgrade();
289         if (!overlay) {
290             LOGE("UpdateWeakTextField error, overlay is nullptr");
291             return;
292         }
293 
294         overlay->SetVisible(true);
295         if (overlay->onRebuild_) {
296             overlay->onRebuild_(overlay->isSingleHandle_, !overlay->isSingleHandle_, overlay->hasMenu_,
297                 !overlay->isSingleHandle_, false);
298         }
299         overlay->handleDiameter_ = Dimension(value, DimensionUnit::VP);
300         overlay->handleRadius_ = overlay->handleDiameter_ * FIFTY_PERCENT;
301     };
302     renderTextField->SetUpdateHandleDiameter(callbackDiameter);
303 
304     auto callbackDiameterInner = [weak = WeakClaim(this)](const double& value) {
305         auto overlay = weak.Upgrade();
306         if (!overlay) {
307             LOGE("UpdateWeakTextField error, overlay is nullptr");
308             return;
309         }
310 
311         overlay->SetVisible(true);
312         if (overlay->onRebuild_) {
313             overlay->onRebuild_(overlay->isSingleHandle_, !overlay->isSingleHandle_, overlay->hasMenu_,
314                 !overlay->isSingleHandle_, false);
315         }
316         overlay->handleDiameterInner_ = Dimension(value, DimensionUnit::VP);
317         overlay->handleRadiusInner_ = overlay->handleDiameterInner_ * FIFTY_PERCENT;
318     };
319     renderTextField->SetUpdateHandleDiameterInner(callbackDiameterInner);
320 
321     auto onValueChange = [weak = WeakClaim(this)] {
322         auto overlay = weak.Upgrade();
323         if (overlay) {
324             overlay->needCloseKeyboard_ = false;
325             overlay->PopOverlay();
326         }
327     };
328     renderTextField->SetOnValueChange(onValueChange);
329 
330     auto onKeyboardClose = [weak = WeakClaim(this)](bool forceCloseKeyboard) {
331         auto overlay = weak.Upgrade();
332         if (overlay) {
333             overlay->needCloseKeyboard_ = !forceCloseKeyboard;
334             overlay->needStartTwinkling_ = !forceCloseKeyboard;
335             overlay->PopOverlay();
336         }
337     };
338     renderTextField->SetOnKeyboardClose(onKeyboardClose);
339 
340     auto onClipRectChanged = [weak = WeakClaim(this)](const Rect& clipRect) {
341         auto overlay = weak.Upgrade();
342         if (overlay && (overlay->clipRect_ != clipRect)) {
343             overlay->clipRect_ = clipRect;
344             overlay->MarkNeedLayout();
345         }
346     };
347     renderTextField->SetOnClipRectChanged(onClipRectChanged);
348 }
349 
PerformLayout()350 void RenderTextOverlay::PerformLayout()
351 {
352     double handleRadius = NormalizeToPx(handleRadius_);
353     startHandleCenter_ = Offset(-handleRadius, handleRadius);
354     endHandleCenter_ = Offset(handleRadius, handleRadius);
355 
356     if (!GetChildren().empty()) {
357         const auto& child = GetChildren().front();
358         if (child) {
359             child->Layout(GetLayoutParam());
360             child->SetPosition(ComputeChildPosition(child));
361             if (NearZero(childRightBoundary_)) {
362                 childRightBoundary_ = child->GetPosition().GetX() + child->GetLayoutSize().Width();
363             } else {
364                 child->SetPosition(
365                     Offset(childRightBoundary_ - child->GetLayoutSize().Width(), child->GetPosition().GetY()));
366             }
367         }
368         SetLayoutSize(GetLayoutParam().GetMaxSize());
369 
370         // If child size is changed, refresh animation, when there is tool bar only.
371         if (HasToolBarOnly()) {
372             double horizonOffsetForAnimation = child->GetLayoutSize().Width() - NormalizeToPx(MORE_BUTTON_SIZE);
373             if (!NearEqual(horizonOffsetForAnimation, horizonOffsetForAnimation_)) {
374                 horizonOffsetForAnimation_ = horizonOffsetForAnimation;
375                 isAnimationInited_ = false;
376             }
377         }
378         if (child && renderClip_) {
379             renderClip_->SetShadowBoxOffset(Offset(
380                 std::max(
381                     child->GetLayoutSize().Width() - horizonOffsetForAnimation_ - NormalizeToPx(MORE_BUTTON_SIZE), 0.0),
382                 0.0));
383         }
384 
385         if (textDirection_ == TextDirection::RTL) {
386             moreButtonPosition_ = child->GetGlobalOffset();
387         } else {
388             moreButtonPosition_ = child->GetGlobalOffset() + Offset(child->GetLayoutSize().Width(), 0.0);
389         }
390     }
391 
392     // Compute touch region of handle.
393     double hotZoneDiameter = NormalizeToPx(HANDLE_HOTZONE_DIAMETER);
394     double hotZoneRadius = hotZoneDiameter / 2.0;
395     if (isSingleHandle_) {
396         startHandleRegion_ = TouchRegion(startHandleOffset_ + Offset(-hotZoneRadius, 0.0),
397             startHandleOffset_ + Offset(hotZoneRadius, hotZoneDiameter));
398     } else {
399         startHandleRegion_ = TouchRegion(startHandleOffset_ + Offset(-hotZoneRadius, -lineHeight_ - hotZoneDiameter),
400             startHandleOffset_ + Offset(hotZoneRadius, -lineHeight_));
401         endHandleRegion_ = TouchRegion(
402             endHandleOffset_ + Offset(-hotZoneRadius, 0.0), endHandleOffset_ + Offset(hotZoneRadius, hotZoneDiameter));
403     }
404 
405     InitAnimation();
406 }
407 
ComputeChildPosition(const RefPtr<RenderNode> & child)408 Offset RenderTextOverlay::ComputeChildPosition(const RefPtr<RenderNode>& child)
409 {
410     Offset startHandleOffset = startHandleOffset_;
411     startHandleOffset.SetX(std::clamp(startHandleOffset.GetX(), clipRect_.Left(), clipRect_.Right()));
412     startHandleOffset.SetY(std::clamp(startHandleOffset.GetY(), clipRect_.Top(), clipRect_.Bottom()));
413     Offset endHandleOffset = endHandleOffset_;
414     endHandleOffset.SetX(std::clamp(endHandleOffset.GetX(), clipRect_.Left(), clipRect_.Right()));
415     endHandleOffset.SetY(std::clamp(endHandleOffset.GetY(), clipRect_.Top(), clipRect_.Bottom()));
416     if (!NearEqual(startHandleOffset.GetY(), endHandleOffset.GetY())) {
417         startHandleOffset.SetX(clipRect_.Left());
418         endHandleOffset.SetX(clipRect_.Right());
419     }
420 
421     // Calculate the spacing with text and handle, menu is fixed up the handle and text.
422     double menuSpacingWithText = NormalizeToPx(menuSpacingWithText_);
423     double menuSpacingWithHandle = NormalizeToPx(menuSpacingWithHandle_);
424     double menuSpacing = isSingleHandle_ ? menuSpacingWithText : menuSpacingWithHandle + menuSpacingWithText;
425 
426     Offset childPosition =
427         Offset((startHandleOffset.GetX() + endHandleOffset.GetX() - child->GetLayoutSize().Width()) / 2.0,
428             startHandleOffset.GetY() - lineHeight_ - menuSpacing - NormalizeToPx(TOOL_BAR_HEIGHT));
429     // Adjust position of overlay.
430     if (LessOrEqual(childPosition.GetX(), 0.0)) {
431         childPosition.SetX(DEFAULT_SPACING);
432     } else if (GreatOrEqual(
433         childPosition.GetX() + child->GetLayoutSize().Width(), GetLayoutParam().GetMaxSize().Width())) {
434         childPosition.SetX(GetLayoutParam().GetMaxSize().Width() - child->GetLayoutSize().Width() - DEFAULT_SPACING);
435     }
436     if (LessNotEqual(childPosition.GetY(), 0.0)) {
437         childPosition.SetY(endHandleOffset_.GetY() + menuSpacingWithHandle + menuSpacingWithText);
438     }
439     return childPosition;
440 }
441 
InitAnimation()442 void RenderTextOverlay::InitAnimation()
443 {
444     if (isAnimationInited_) {
445         return;
446     }
447     isAnimationInited_ = true;
448 
449     // Create tween option for in.
450     // Add offset animation for outer tween.
451     auto xCurveIn = AceType::MakeRefPtr<CurveAnimation<float>>(horizonOffsetForAnimation_, 0.0f, Curves::FRICTION);
452     tweenOptionIn_.SetPropertyAnimationFloat(PropertyAnimatableType::PROPERTY_OFFSET_X, xCurveIn);
453 
454     // Add opacity animation for outer tween.
455     auto opacityKeyframeInFirst = AceType::MakeRefPtr<Keyframe<float>>(0.0f, 0.0f);
456     auto opacityKeyframeInSecond = AceType::MakeRefPtr<Keyframe<float>>(OPACITY_KEYFRAME, 1.0f);
457     opacityKeyframeInSecond->SetCurve(Curves::SHARP);
458     auto opacityKeyframeInThird = AceType::MakeRefPtr<Keyframe<float>>(1.0f, 1.0f);
459     auto opacityAnimationIn = AceType::MakeRefPtr<KeyframeAnimation<float>>();
460     opacityAnimationIn->AddKeyframe(opacityKeyframeInFirst);
461     opacityAnimationIn->AddKeyframe(opacityKeyframeInSecond);
462     opacityAnimationIn->AddKeyframe(opacityKeyframeInThird);
463     tweenOptionIn_.SetOpacityAnimation(opacityAnimationIn);
464     tweenOptionIn_.SetDuration(ANIMATION_DURATION);
465     tweenOptionIn_.SetFillMode(FillMode::FORWARDS);
466 
467     // Add translate animation for inner tween.
468     auto xTranslateIn = AceType::MakeRefPtr<CurveAnimation<DimensionOffset>>(
469         DimensionOffset(ANIMATION_OFFSET_X, 0.0_vp), DimensionOffset(0.0_vp, 0.0_vp), Curves::FRICTION);
470     innerTweenOptionIn_.SetTranslateAnimations(AnimationType::TRANSLATE_X, xTranslateIn);
471     innerTweenOptionIn_.SetDuration(ANIMATION_DURATION);
472     innerTweenOptionIn_.SetFillMode(FillMode::FORWARDS);
473 
474     // Create tween option for out.
475     // Add offset animation for outer tween.
476     auto xCurveOut = AceType::MakeRefPtr<CurveAnimation<float>>(0.0f, horizonOffsetForAnimation_, Curves::FRICTION);
477     tweenOptionOut_.SetPropertyAnimationFloat(PropertyAnimatableType::PROPERTY_OFFSET_X, xCurveOut);
478 
479     // Add opacity animation for outer tween.
480     auto opacityKeyframeOutFirst = AceType::MakeRefPtr<Keyframe<float>>(0.0f, 1.0f);
481     auto opacityKeyframeOutSecond = AceType::MakeRefPtr<Keyframe<float>>(OPACITY_KEYFRAME, 0.0f);
482     opacityKeyframeOutSecond->SetCurve(Curves::SHARP);
483     auto opacityKeyframeOutThird = AceType::MakeRefPtr<Keyframe<float>>(1.0f, 0.0f);
484     auto opacityAnimationOut = AceType::MakeRefPtr<KeyframeAnimation<float>>();
485     opacityAnimationOut->AddKeyframe(opacityKeyframeOutFirst);
486     opacityAnimationOut->AddKeyframe(opacityKeyframeOutSecond);
487     opacityAnimationOut->AddKeyframe(opacityKeyframeOutThird);
488     tweenOptionOut_.SetOpacityAnimation(opacityAnimationOut);
489     tweenOptionOut_.SetDuration(ANIMATION_DURATION);
490     tweenOptionOut_.SetFillMode(FillMode::FORWARDS);
491 
492     // Create translate animation for inner tween.
493     auto xTranslateOut = AceType::MakeRefPtr<CurveAnimation<DimensionOffset>>(
494         DimensionOffset(0.0_vp, 0.0_vp), DimensionOffset(ANIMATION_OFFSET_X, 0.0_vp), Curves::FRICTION);
495     innerTweenOptionOut_.SetTranslateAnimations(AnimationType::TRANSLATE_X, xTranslateOut);
496     innerTweenOptionOut_.SetDuration(ANIMATION_DURATION);
497     innerTweenOptionOut_.SetFillMode(FillMode::FORWARDS);
498 }
499 
TouchTest(const Point & globalPoint,const Point & parentLocalPoint,const TouchRestrict & touchRestrict,TouchTestResult & result)500 bool RenderTextOverlay::TouchTest(const Point& globalPoint, const Point& parentLocalPoint,
501     const TouchRestrict& touchRestrict, TouchTestResult& result)
502 {
503     if (GetDisableTouchEvent() || disabled_ || !isAnimationStopped_) {
504         return false;
505     }
506     const auto localPoint = parentLocalPoint - GetPaintRect().GetOffset();
507     if (!isSingleHandle_ || showOption_.showMenu) {
508         for (auto iter = GetChildren().rbegin(); iter != GetChildren().rend(); ++iter) {
509             const auto& child = *iter;
510             if (child->TouchTest(globalPoint, localPoint, touchRestrict, result)) {
511                 return true;
512             }
513         }
514     }
515     if (startHandleRegion_.ContainsInRegion(parentLocalPoint.GetX(), parentLocalPoint.GetY()) ||
516         endHandleRegion_.ContainsInRegion(parentLocalPoint.GetX(), parentLocalPoint.GetY())) {
517         const auto coordinateOffset = globalPoint - localPoint;
518         globalPoint_ = globalPoint;
519         OnTouchTestHit(coordinateOffset, touchRestrict, result);
520         return true;
521     }
522     if (globalPoint.GetSourceType() == SourceType::MOUSE) {
523         PopOverlay();
524         return true;
525     }
526     return false;
527 }
528 
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)529 void RenderTextOverlay::OnTouchTestHit(
530     const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
531 {
532     clickDetector_->SetCoordinateOffset(coordinateOffset);
533     result.emplace_back(clickDetector_);
534     dragDetector_->SetCoordinateOffset(coordinateOffset);
535     result.emplace_back(dragDetector_);
536     touchDetector_->SetCoordinateOffset(coordinateOffset);
537     result.emplace_back(touchDetector_);
538 }
539 
HandleMouseEvent(const MouseEvent & event)540 bool RenderTextOverlay::HandleMouseEvent(const MouseEvent& event)
541 {
542     if (event.button != MouseButton::LEFT_BUTTON && event.action == MouseAction::PRESS) {
543         PopOverlay();
544         return true;
545     }
546     return false;
547 }
548 
HandleClick(const Offset & clickOffset)549 void RenderTextOverlay::HandleClick(const Offset& clickOffset)
550 {
551     if (isSingleHandle_ && startHandleRegion_.ContainsInRegion(clickOffset.GetX(), clickOffset.GetY())) {
552         childRightBoundary_ = 0.0;
553         showOption_.showMenu = true;
554         auto textField = weakTextField_.Upgrade();
555         if (textField) {
556             textField->SetIsOverlayShowed(true, false);
557         }
558         if (onRebuild_) {
559             OnFocusChange(RenderStatus::FOCUS);
560             onRebuild_(true, true, hasMenu_, true, false);
561         }
562     }
563 }
564 
HandleDragStart(const Offset & startOffset)565 void RenderTextOverlay::HandleDragStart(const Offset& startOffset)
566 {
567     childRightBoundary_ = 0.0;
568     showOption_.showMenu = true;
569     auto textField = weakTextField_.Upgrade();
570     if (!textField) {
571         LOGE("TextField is nullptr");
572         return;
573     }
574 
575     // Mark start and end index
576     startIndex_ = textField->GetEditingValue().selection.GetStart();
577     endIndex_ = textField->GetEditingValue().selection.GetEnd();
578 
579     // Mark start or end flag and mark the index
580     if (startHandleRegion_.ContainsInRegion(startOffset.GetX(), startOffset.GetY())) {
581         isStartDrag_ = true;
582         isEndDrag_ = false;
583         textField->SetInitIndex(endIndex_);
584     } else {
585         isStartDrag_ = false;
586         isEndDrag_ = endHandleRegion_.ContainsInRegion(startOffset.GetX(), startOffset.GetY());
587         textField->SetInitIndex(startIndex_);
588     }
589 }
590 
HandleDragUpdateAndEnd(const Offset & offset)591 void RenderTextOverlay::HandleDragUpdateAndEnd(const Offset& offset)
592 {
593     childRightBoundary_ = 0.0;
594     if (isStartDrag_) {
595         auto startCallback = [weak = WeakClaim(this)](const Offset& startHandleOffset) {
596             auto overlay = weak.Upgrade();
597             if (overlay) {
598                 overlay->startHandleOffset_ = startHandleOffset;
599                 if (overlay->isSingleHandle_) {
600                     overlay->endHandleOffset_ = startHandleOffset;
601                 }
602                 overlay->MarkNeedLayout();
603             }
604         };
605         onStartHandleMove_(endIndex_, offset - Offset(0.0, lineHeight_), startCallback, isSingleHandle_);
606         isDragging_ = true;
607     } else if (isEndDrag_) {
608         auto endCallback = [weak = WeakClaim(this)](const Offset& endHandleOffset) {
609             auto overlay = weak.Upgrade();
610             if (overlay) {
611                 overlay->endHandleOffset_ = endHandleOffset;
612                 overlay->MarkNeedLayout();
613             }
614         };
615         onEndHandleMove_(startIndex_, offset - Offset(0.0, lineHeight_), endCallback);
616         isDragging_ = true;
617     }
618 }
619 
HandleCut()620 void RenderTextOverlay::HandleCut()
621 {
622     needCloseKeyboard_ = false;
623     if (onCut_) {
624         onCut_();
625     }
626     PopOverlay();
627 }
628 
HandleCopy()629 void RenderTextOverlay::HandleCopy()
630 {
631     needCloseKeyboard_ = false;
632     if (onCopy_) {
633         onCopy_();
634     }
635     PopOverlay();
636 }
637 
HandlePaste()638 void RenderTextOverlay::HandlePaste()
639 {
640     needCloseKeyboard_ = false;
641     if (onPaste_) {
642         onPaste_();
643     }
644     PopOverlay();
645 }
646 
HandleCopyAll()647 void RenderTextOverlay::HandleCopyAll()
648 {
649     needCloseKeyboard_ = false;
650     isSingleHandle_ = false;
651     childRightBoundary_ = 0.0;
652     auto callback = [weak = WeakClaim(this)](const Offset& startHandleOffset, const Offset& endHandleOffset) {
653         auto overlay = weak.Upgrade();
654         if (overlay) {
655             overlay->startHandleOffset_ = startHandleOffset;
656             overlay->endHandleOffset_ = endHandleOffset;
657             overlay->isSingleHandle_ = false;
658             if (startHandleOffset == endHandleOffset) {
659                 overlay->isSingleHandle_ = true;
660             }
661             if (overlay->onRebuild_) {
662                 overlay->onRebuild_(overlay->isSingleHandle_, true, overlay->hasMenu_, true, false);
663             }
664         }
665     };
666     if (onCopyAll_) {
667         onCopyAll_(callback);
668     }
669 }
670 
HandleMoreButtonClick()671 void RenderTextOverlay::HandleMoreButtonClick()
672 {
673     needCloseKeyboard_ = false;
674     // Is animation is not stopped, do not handle click to start a new animation.
675     if (!isAnimationStopped_) {
676         return;
677     }
678 
679     hasMenu_ = !hasMenu_;
680     isAnimationStarted_ = false;
681     isAnimationStopped_ = false;
682     if (onRebuild_) {
683         animateUntilPaint_ = hasMenu_;
684         onRebuild_(isSingleHandle_, true, true, true, true);
685     }
686 
687     if (!animateUntilPaint_) {
688         startAnimation_(tweenOptionIn_, innerTweenOptionIn_, isSingleHandle_, true);
689         StartMoreAnimation(reverse_);
690     }
691 }
692 
OnPaintFinish()693 void RenderTextOverlay::OnPaintFinish()
694 {
695     if (animateUntilPaint_) {
696         animateUntilPaint_ = false;
697         startAnimation_(tweenOptionOut_, innerTweenOptionOut_, isSingleHandle_, false);
698         StartMoreAnimation(reverse_);
699     }
700 }
701 
RestoreMoreButtonStyle()702 void RenderTextOverlay::RestoreMoreButtonStyle()
703 {
704     if (!controller_) {
705         return;
706     }
707     if (reverse_) {
708         BuildAndStartMoreButtonAnimation();
709         controller_->Finish();
710     } else if (controller_->IsRunning()) {
711         controller_->Finish();
712     }
713 }
714 
StartMoreAnimation(bool reverse)715 void RenderTextOverlay::StartMoreAnimation(bool reverse)
716 {
717     if (controller_ && controller_->IsRunning()) {
718         reverse_ = !reverse_;
719         controller_->Reverse();
720         return;
721     }
722     BuildAndStartMoreButtonAnimation();
723 }
724 
BuildStrokeWidthAnimation(const RefPtr<KeyframeAnimation<Dimension>> & widthAnimation,const Dimension & from,const Dimension & to,bool reverse)725 void RenderTextOverlay::BuildStrokeWidthAnimation(const RefPtr<KeyframeAnimation<Dimension>>& widthAnimation,
726     const Dimension& from, const Dimension& to, bool reverse)
727 {
728     auto widthFrameStart = AceType::MakeRefPtr<Keyframe<Dimension>>(KEYFRAME_BEGINNING, from);
729     auto widthFrameEnd = AceType::MakeRefPtr<Keyframe<Dimension>>(KEYFRAME_ENDING, to);
730     widthAnimation->AddKeyframe(widthFrameStart);
731     if (reverse) {
732         widthFrameEnd->SetCurve(Curves::FRICTION);
733     } else {
734         auto widthFrameMid = AceType::MakeRefPtr<Keyframe<Dimension>>(KEYFRAME_PERCENT_THIRTY, to);
735         widthFrameMid->SetCurve(Curves::FRICTION);
736         widthFrameEnd->SetCurve(Curves::LINEAR);
737         widthAnimation->AddKeyframe(widthFrameMid);
738     }
739     widthAnimation->AddKeyframe(widthFrameEnd);
740     widthAnimation->AddListener([weakText = AceType::WeakClaim(this)](const Dimension& value) {
741         auto overlay = weakText.Upgrade();
742         if (overlay) {
743             overlay->strokeWidth_ = value;
744             overlay->clipWidth_ = std::clamp((CLIP_WIDTH - (value - STROKE_MIN_WIDTH).Value()), 0.0, CLIP_WIDTH);
745             overlay->MarkNeedRender(true);
746         }
747     });
748 }
749 
BuildEndPointOffsetAnimation(const RefPtr<KeyframeAnimation<double>> & offsetAnimation,double from,double to,bool reverse)750 void RenderTextOverlay::BuildEndPointOffsetAnimation(
751     const RefPtr<KeyframeAnimation<double>>& offsetAnimation, double from, double to, bool reverse)
752 {
753     auto offsetFrameStart = AceType::MakeRefPtr<Keyframe<double>>(KEYFRAME_BEGINNING, from);
754     auto offsetFrameEnd = AceType::MakeRefPtr<Keyframe<double>>(KEYFRAME_ENDING, to);
755 
756     offsetAnimation->AddKeyframe(offsetFrameStart);
757     if (reverse) {
758         offsetFrameEnd->SetCurve(Curves::FRICTION);
759     } else {
760         auto offsetFrameMid = AceType::MakeRefPtr<Keyframe<double>>(KEYFRAME_PERCENT_THIRTY, from);
761         offsetFrameMid->SetCurve(Curves::LINEAR);
762         offsetFrameEnd->SetCurve(Curves::FRICTION);
763         offsetAnimation->AddKeyframe(offsetFrameMid);
764     }
765     offsetAnimation->AddKeyframe(offsetFrameEnd);
766     offsetAnimation->AddListener([weakText = AceType::WeakClaim(this)](double value) {
767         auto overlay = weakText.Upgrade();
768         if (overlay) {
769             overlay->ProcessEndPointAnimation(value);
770             overlay->MarkNeedRender(true);
771         }
772     });
773 }
774 
BuildFrictionAnimation(const RefPtr<KeyframeAnimation<double>> & animation,double from,double to)775 void RenderTextOverlay::BuildFrictionAnimation(
776     const RefPtr<KeyframeAnimation<double>>& animation, double from, double to)
777 {
778     auto frameStart = AceType::MakeRefPtr<Keyframe<double>>(KEYFRAME_BEGINNING, from);
779     auto frameEnd = AceType::MakeRefPtr<Keyframe<double>>(KEYFRAME_ENDING, to);
780     frameEnd->SetCurve(Curves::FRICTION);
781 
782     animation->AddKeyframe(frameStart);
783     animation->AddKeyframe(frameEnd);
784     animation->AddListener([weakText = AceType::WeakClaim(this)](double value) {
785         auto overlay = weakText.Upgrade();
786         if (overlay) {
787             overlay->ProcessFrictionAnimation(value);
788             overlay->MarkNeedRender(true);
789         }
790     });
791 }
792 
ProcessFrictionAnimation(double value)793 void RenderTextOverlay::ProcessFrictionAnimation(double value)
794 {
795     // calculate start point offset of dots
796     dot1StartOffset_ = DOT1_OFFSET * value;
797     dot2StartOffset_ = DOT2_OFFSET * value;
798     dot3StartOffset_ = DOT3_OFFSET * value;
799     dot4StartOffset_ = DOT4_OFFSET * value;
800 
801     // calculate rotate degree
802     rotateDegree_ = ROTATE_DEGREE * value;
803 }
804 
ProcessEndPointAnimation(double value)805 void RenderTextOverlay::ProcessEndPointAnimation(double value)
806 {
807     dot2Offset_ = (END_POINT - DOT2_POSITION - DOT2_OFFSET) * value;
808     dot3Offset_ = (END_POINT - DOT3_POSITION - DOT3_OFFSET) * value;
809     dot4Offset_ = (END_POINT - DOT4_POSITION - DOT4_OFFSET) * value;
810 }
811 
BuildAndStartMoreButtonAnimation()812 void RenderTextOverlay::BuildAndStartMoreButtonAnimation()
813 {
814     if (!controller_) {
815         controller_ = AceType::MakeRefPtr<Animator>(GetContext());
816     }
817     controller_->ClearInterpolators();
818     controller_->ClearAllListeners();
819 
820     RefPtr<KeyframeAnimation<Dimension>> strokeWidthAnimation = AceType::MakeRefPtr<KeyframeAnimation<Dimension>>();
821     RefPtr<KeyframeAnimation<double>> startPointAnimation = AceType::MakeRefPtr<KeyframeAnimation<double>>();
822     RefPtr<KeyframeAnimation<double>> endPointAnimation = AceType::MakeRefPtr<KeyframeAnimation<double>>();
823     if (reverse_) {
824         BuildStrokeWidthAnimation(strokeWidthAnimation, STROKE_MIN_WIDTH, STROKE_MAX_WIDTH, true);
825         BuildFrictionAnimation(startPointAnimation, KEYFRAME_ENDING, KEYFRAME_BEGINNING);
826         BuildEndPointOffsetAnimation(endPointAnimation, KEYFRAME_ENDING, KEYFRAME_BEGINNING, true);
827     } else {
828         BuildStrokeWidthAnimation(strokeWidthAnimation, STROKE_MAX_WIDTH, STROKE_MIN_WIDTH, false);
829         BuildFrictionAnimation(startPointAnimation, KEYFRAME_BEGINNING, KEYFRAME_ENDING);
830         BuildEndPointOffsetAnimation(endPointAnimation, KEYFRAME_BEGINNING, KEYFRAME_ENDING, false);
831     }
832 
833     controller_->SetDuration(MORE_ANIMATION_DURATION);
834     controller_->AddStopListener([more = AceType::WeakClaim(this)]() {
835         auto textMore = more.Upgrade();
836         if (textMore) {
837             textMore->reverse_ = (!textMore->reverse_);
838         }
839     });
840     controller_->AddInterpolator(strokeWidthAnimation);
841     controller_->AddInterpolator(startPointAnimation);
842     controller_->AddInterpolator(endPointAnimation);
843     controller_->Forward();
844 }
845 
PopOverlay()846 void RenderTextOverlay::PopOverlay()
847 {
848     if (hasPoped_) {
849         return;
850     }
851     auto textField = weakTextField_.Upgrade();
852     if (!textField) {
853         return;
854     }
855     auto stack = textField->GetStackElement().Upgrade();
856     if (stack) {
857         hasPoped_ = true;
858         stack->PopTextOverlay();
859     }
860     textField->SetIsOverlayShowed(false, needStartTwinkling_);
861     textField->SetTextOverlayPushed(false);
862 }
863 
OnFocusChange(RenderStatus renderStatus)864 void RenderTextOverlay::OnFocusChange(RenderStatus renderStatus)
865 {
866     if (onFocusChange_) {
867         onFocusChange_(renderStatus == RenderStatus::FOCUS, needCloseKeyboard_);
868     }
869 }
870 
InitRenderChild(const RefPtr<RenderNode> & render)871 void RenderTextOverlay::InitRenderChild(const RefPtr<RenderNode>& render)
872 {
873     if (!render) {
874         return;
875     }
876 
877     if (AceType::InstanceOf<RenderBox>(render)) {
878         if (!renderBox_) {
879             renderBox_ = AceType::DynamicCast<RenderBox>(render);
880         }
881     } else if (AceType::InstanceOf<RenderClip>(render)) {
882         if (!renderClip_) {
883             renderClip_ = AceType::DynamicCast<RenderClip>(render);
884         }
885     } else if (AceType::InstanceOf<RenderSelectPopup>(render)) {
886         if (!renderMenu_) {
887             renderMenu_ = AceType::DynamicCast<RenderSelectPopup>(render);
888         }
889     }
890 
891     for (const auto& child : render->GetChildren()) {
892         InitRenderChild(child);
893     }
894 }
895 
ResetRenderChild()896 void RenderTextOverlay::ResetRenderChild()
897 {
898     renderBox_.Reset();
899     renderClip_.Reset();
900     renderMenu_.Reset();
901 }
902 
HasToolBarOnly() const903 bool RenderTextOverlay::HasToolBarOnly() const
904 {
905     // Child of render overlay is focus collaboration.
906     auto focusCollaboration = AceType::DynamicCast<RenderFocusCollaboration>(GetChildren().front());
907     if (!focusCollaboration) {
908         return false;
909     }
910     // Child of render focus collaboration is column.
911     auto column = AceType::DynamicCast<RenderFlex>(focusCollaboration->GetChildren().front());
912     if (!column) {
913         return false;
914     }
915     // Column has two children at most, tool bar and menu, if there is only one, it must be tool bar.
916     return column->GetChildren().size() == 1;
917 }
918 
SetOnRebuild(const std::function<void (bool,bool,bool,bool,bool)> & onRebuild)919 void RenderTextOverlay::SetOnRebuild(const std::function<void(bool, bool, bool, bool, bool)>& onRebuild)
920 {
921     onRebuild_ = onRebuild;
922 }
923 
SetStartAnimationCallback(const StartAnimationCallback & callback)924 void RenderTextOverlay::SetStartAnimationCallback(const StartAnimationCallback& callback)
925 {
926     startAnimation_ = callback;
927 }
928 
SetIsAnimationStarted(bool isAnimationStarted)929 void RenderTextOverlay::SetIsAnimationStarted(bool isAnimationStarted)
930 {
931     isAnimationStarted_ = isAnimationStarted;
932 }
933 
IsAnimationStarted() const934 bool RenderTextOverlay::IsAnimationStarted() const
935 {
936     return isAnimationStarted_;
937 }
938 
SetIsAnimationStopped(bool isAnimationStopped)939 void RenderTextOverlay::SetIsAnimationStopped(bool isAnimationStopped)
940 {
941     isAnimationStopped_ = isAnimationStopped;
942 }
943 
GetHorizonOffsetForAnimation() const944 double RenderTextOverlay::GetHorizonOffsetForAnimation() const
945 {
946     return horizonOffsetForAnimation_;
947 }
948 
949 } // namespace OHOS::Ace
950