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