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