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