1 /*
2  * Copyright (c) 2022-2024 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 #include "core/components_ng/pattern/bubble/bubble_pattern.h"
16 
17 #include "base/memory/ace_type.h"
18 #include "base/subwindow/subwindow.h"
19 #include "base/subwindow/subwindow_manager.h"
20 #include "base/utils/utils.h"
21 #include "core/common/container.h"
22 #include "core/common/container_scope.h"
23 #include "core/common/window_animation_config.h"
24 #include "core/components/common/properties/shadow_config.h"
25 #include "core/components/container_modal/container_modal_constants.h"
26 #include "core/components_ng/base/frame_node.h"
27 #include "core/components_ng/base/ui_node.h"
28 #include "core/components_ng/pattern/bubble/bubble_layout_property.h"
29 #include "core/components_ng/pattern/bubble/bubble_render_property.h"
30 #include "core/components_ng/pattern/text/text_layout_property.h"
31 #include "core/components_ng/property/property.h"
32 #include "core/components_v2/inspector/inspector_constants.h"
33 #include "core/event/touch_event.h"
34 #include "core/pipeline/pipeline_base.h"
35 #include "core/pipeline/pipeline_context.h"
36 #include "core/pipeline_ng/pipeline_context.h"
37 
38 namespace OHOS::Ace::NG {
39 namespace {
40 constexpr float VISIABLE_ALPHA = 1.0f;
41 constexpr float INVISIABLE_ALPHA = 0.0f;
42 constexpr int32_t ENTRY_ANIMATION_DURATION = 250;
43 constexpr int32_t EXIT_ANIMATION_DURATION = 100;
44 const Dimension INVISIABLE_OFFSET = 8.0_px;
45 } // namespace
46 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,bool skipMeasure,bool skipLayout)47 bool BubblePattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, bool skipMeasure, bool skipLayout)
48 {
49     if (skipMeasure && skipLayout) {
50         return false;
51     }
52     auto host = GetHost();
53     CHECK_NULL_RETURN(host, false);
54     auto paintProperty = host->GetPaintProperty<BubbleRenderProperty>();
55     CHECK_NULL_RETURN(paintProperty, false);
56     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
57     CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
58     auto bubbleLayoutAlgorithm = DynamicCast<BubbleLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
59     CHECK_NULL_RETURN(bubbleLayoutAlgorithm, false);
60 
61     showArrow_ = bubbleLayoutAlgorithm->ShowArrow();
62     arrowPosition_ = bubbleLayoutAlgorithm->GetArrowPosition();
63     childOffset_ = bubbleLayoutAlgorithm->GetChildOffset();
64     childSize_ = bubbleLayoutAlgorithm->GetChildSize();
65     touchRegion_ = bubbleLayoutAlgorithm->GetTouchRegion();
66     hostWindowRect_ = bubbleLayoutAlgorithm->GetHostWindowRect();
67     targetOffset_ = bubbleLayoutAlgorithm->GetTargetOffset();
68     targetSize_ = bubbleLayoutAlgorithm->GetTargetSize();
69     arrowPlacement_ = bubbleLayoutAlgorithm->GetArrowPlacement();
70     clipPath_ = bubbleLayoutAlgorithm->GetClipPath();
71     clipFrameNode_ = bubbleLayoutAlgorithm->GetClipFrameNode();
72     arrowOffsetsFromClip_ = bubbleLayoutAlgorithm->GetArrowOffsetsFromClip();
73     arrowWidth_ = bubbleLayoutAlgorithm->GetArrowWidth();
74     arrowHeight_ = bubbleLayoutAlgorithm->GetArrowHeight();
75     border_ = bubbleLayoutAlgorithm->GetBorder();
76     paintProperty->UpdatePlacement(bubbleLayoutAlgorithm->GetArrowPlacement());
77     if (delayShow_) {
78         delayShow_ = false;
79         if (transitionStatus_ == TransitionStatus::INVISIABLE) {
80             StartEnteringAnimation(nullptr);
81         }
82     }
83     return true;
84 }
85 
OnModifyDone()86 void BubblePattern::OnModifyDone()
87 {
88     if (SystemProperties::GetColorMode() != colorMode_ && !isCustomPopup_) {
89         colorMode_ = SystemProperties::GetColorMode();
90         UpdateBubbleText();
91     }
92     UpdateAgingTextSize();
93     Pattern::OnModifyDone();
94     InitTouchEvent();
95     RegisterButtonOnHover();
96     RegisterButtonOnTouch();
97 }
98 
AddPipelineCallBack()99 void BubblePattern::AddPipelineCallBack()
100 {
101     auto host = GetHost();
102     CHECK_NULL_VOID(host);
103     auto pipelineContext = PipelineContext::GetCurrentContext();
104     CHECK_NULL_VOID(pipelineContext);
105     pipelineContext->AddWindowSizeChangeCallback(host->GetId());
106     pipelineContext->AddWindowStateChangedCallback(host->GetId());
107 }
108 
OnAttachToFrameNode()109 void BubblePattern::OnAttachToFrameNode()
110 {
111     auto host = GetHost();
112     CHECK_NULL_VOID(host);
113     host->GetRenderContext()->SetClipToFrame(true);
114 
115     auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
116     CHECK_NULL_VOID(targetNode);
117     auto pipelineContext = host->GetContextRefPtr();
118     CHECK_NULL_VOID(pipelineContext);
119     hasOnAreaChange_ = pipelineContext->HasOnAreaChangeNode(targetNode->GetId());
120     auto eventHub = targetNode->GetEventHub<EventHub>();
121     CHECK_NULL_VOID(eventHub);
122     OnAreaChangedFunc onAreaChangedFunc = [popupNodeWk = WeakPtr<FrameNode>(host)](const RectF& oldRect,
123                                               const OffsetF& oldOrigin, const RectF& /* rect */,
124                                               const OffsetF& /* origin */) {
125         auto popupNode = popupNodeWk.Upgrade();
126         CHECK_NULL_VOID(popupNode);
127         popupNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
128     };
129     eventHub->AddInnerOnAreaChangedCallback(host->GetId(), std::move(onAreaChangedFunc));
130 }
131 
OnDetachFromFrameNode(FrameNode * frameNode)132 void BubblePattern::OnDetachFromFrameNode(FrameNode* frameNode)
133 {
134     auto pipeline = PipelineContext::GetCurrentContext();
135     CHECK_NULL_VOID(pipeline);
136     pipeline->RemoveWindowSizeChangeCallback(frameNode->GetId());
137     pipeline->RemoveWindowStateChangedCallback(frameNode->GetId());
138     auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
139     CHECK_NULL_VOID(targetNode);
140     if (!hasOnAreaChange_) {
141         pipeline->RemoveOnAreaChangeNode(targetNode->GetId());
142     }
143 }
144 
InitTouchEvent()145 void BubblePattern::InitTouchEvent()
146 {
147     auto host = GetHost();
148     CHECK_NULL_VOID(host);
149     auto hub = host->GetEventHub<EventHub>();
150     CHECK_NULL_VOID(hub);
151     auto gestureHub = hub->GetOrCreateGestureEventHub();
152     CHECK_NULL_VOID(gestureHub);
153     if (touchEvent_) {
154         return;
155     }
156     auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
157         auto pattern = weak.Upgrade();
158         if (pattern) {
159             pattern->HandleTouchEvent(info);
160         }
161     };
162     touchEvent_ = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
163     gestureHub->AddTouchEvent(touchEvent_);
164 }
165 
HandleTouchEvent(const TouchEventInfo & info)166 void BubblePattern::HandleTouchEvent(const TouchEventInfo& info)
167 {
168     if (info.GetTouches().empty()) {
169         return;
170     }
171     auto touchType = info.GetTouches().front().GetTouchType();
172     auto clickPos = info.GetTouches().front().GetLocalLocation();
173     if (touchType == TouchType::DOWN) {
174         HandleTouchDown(clickPos);
175     }
176 }
177 
HandleTouchDown(const Offset & clickPosition)178 void BubblePattern::HandleTouchDown(const Offset& clickPosition)
179 {
180     auto host = GetHost();
181     CHECK_NULL_VOID(host);
182     auto bubbleRenderProp = host->GetPaintProperty<BubbleRenderProperty>();
183     CHECK_NULL_VOID(bubbleRenderProp);
184     if (touchRegion_.IsInRegion(PointF(clickPosition.GetX(), clickPosition.GetY()))) {
185         return;
186     }
187     auto autoCancel = bubbleRenderProp->GetAutoCancel().value_or(true);
188     if (autoCancel) {
189         if (!GetInteractiveDismiss()) {
190             return;
191         }
192         if (HasOnWillDismiss()) {
193             auto pipelineNg = PipelineContext::GetCurrentContext();
194             CHECK_NULL_VOID(pipelineNg);
195             auto overlayManager = pipelineNg->GetOverlayManager();
196             CHECK_NULL_VOID(overlayManager);
197             overlayManager->SetDismissPopupId(targetNodeId_);
198             CallOnWillDismiss(static_cast<int32_t>(DismissReason::TOUCH_OUTSIDE));
199             return;
200         }
201         PopBubble();
202     }
203 }
204 
RegisterButtonOnHover()205 void BubblePattern::RegisterButtonOnHover()
206 {
207     if (mouseEventInitFlag_) {
208         return;
209     }
210     auto paintProps = GetPaintProperty<BubbleRenderProperty>();
211     CHECK_NULL_VOID(paintProps);
212     auto primaryButtonShow = paintProps->GetPrimaryButtonShow().value_or(false);
213     auto secondaryButtonShow = paintProps->GetSecondaryButtonShow().value_or(false);
214     auto custom = paintProps->GetUseCustom().value_or(false);
215     if (custom) {
216         return;
217     }
218     if (!primaryButtonShow && !secondaryButtonShow) {
219         return;
220     }
221     auto buttonRowNode = GetButtonRowNode();
222     for (const auto& child : buttonRowNode->GetChildren()) {
223         auto buttonNode = AceType::DynamicCast<FrameNode>(child);
224         CHECK_NULL_VOID(buttonNode);
225         if (buttonNode->GetTag() != V2::BUTTON_ETS_TAG) {
226             return;
227         }
228         auto inputHub = buttonNode->GetOrCreateInputEventHub();
229         CHECK_NULL_VOID(inputHub);
230         auto mouseTask = [weak = WeakClaim(this), buttonNodeWK = WeakPtr<FrameNode>(buttonNode)](bool isHover) {
231             auto pattern = weak.Upgrade();
232             CHECK_NULL_VOID(pattern);
233             auto buttonNode = buttonNodeWK.Upgrade();
234             CHECK_NULL_VOID(buttonNode);
235             pattern->ButtonOnHover(isHover, buttonNode);
236         };
237         auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
238         inputHub->AddOnHoverEvent(mouseEvent);
239     }
240     mouseEventInitFlag_ = true;
241 }
242 
ButtonOnHover(bool isHover,const RefPtr<NG::FrameNode> & buttonNode)243 void BubblePattern::ButtonOnHover(bool isHover, const RefPtr<NG::FrameNode>& buttonNode)
244 {
245     auto renderContext = buttonNode->GetRenderContext();
246     CHECK_NULL_VOID(renderContext);
247     auto pipeline = PipelineBase::GetCurrentContext();
248     CHECK_NULL_VOID(pipeline);
249     auto theme = pipeline->GetTheme<PopupTheme>();
250     isHover_ = isHover;
251     auto hoverColor = theme->GetButtonHoverColor();
252     auto backgroundColor = theme->GetButtonBackgroundColor();
253     if (isHover) {
254         // normal to hover
255         Animation(renderContext, hoverColor, theme->GetHoverAnimationDuration(), Curves::FRICTION);
256     } else {
257         // hover to normal
258         Animation(renderContext, backgroundColor, theme->GetHoverAnimationDuration(), Curves::FRICTION);
259     }
260 }
261 
RegisterButtonOnTouch()262 void BubblePattern::RegisterButtonOnTouch()
263 {
264     if (touchEventInitFlag_) {
265         return;
266     }
267     auto paintProps = GetPaintProperty<BubbleRenderProperty>();
268     CHECK_NULL_VOID(paintProps);
269     auto primaryButtonShow = paintProps->GetPrimaryButtonShow().value_or(false);
270     auto secondaryButtonShow = paintProps->GetSecondaryButtonShow().value_or(false);
271     auto custom = paintProps->GetUseCustom().value_or(false);
272     if (custom) {
273         return;
274     }
275     if (!primaryButtonShow && !secondaryButtonShow) {
276         return;
277     }
278 
279     auto buttonRowNode = GetButtonRowNode();
280     for (const auto& child : buttonRowNode->GetChildren()) {
281         auto buttonNode = AceType::DynamicCast<FrameNode>(child);
282         CHECK_NULL_VOID(buttonNode);
283         if (buttonNode->GetTag() != V2::BUTTON_ETS_TAG) {
284             return;
285         }
286         auto gestureHub = buttonNode->GetOrCreateGestureEventHub();
287         CHECK_NULL_VOID(gestureHub);
288         auto touchCallback = [weak = WeakClaim(this), buttonNodeWK = WeakPtr<FrameNode>(buttonNode)]
289             (const TouchEventInfo& info) {
290             auto pattern = weak.Upgrade();
291             CHECK_NULL_VOID(pattern);
292             auto buttonNode = buttonNodeWK.Upgrade();
293             CHECK_NULL_VOID(buttonNode);
294             pattern->ButtonOnPress(info, buttonNode);
295         };
296         auto touchEvent = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
297         gestureHub->AddTouchEvent(touchEvent);
298     }
299     touchEventInitFlag_ = true;
300 }
301 
ButtonOnPress(const TouchEventInfo & info,const RefPtr<NG::FrameNode> & buttonNode)302 void BubblePattern::ButtonOnPress(const TouchEventInfo& info, const RefPtr<NG::FrameNode>& buttonNode)
303 {
304     auto touchType = info.GetTouches().front().GetTouchType();
305     auto renderContext = buttonNode->GetRenderContext();
306     CHECK_NULL_VOID(renderContext);
307     auto pipeline = PipelineBase::GetCurrentContext();
308     CHECK_NULL_VOID(pipeline);
309     auto theme = pipeline->GetTheme<PopupTheme>();
310     CHECK_NULL_VOID(theme);
311     auto pressColor = theme->GetButtonPressColor();
312     auto hoverColor = theme->GetButtonHoverColor();
313     auto backgroundColor = theme->GetButtonBackgroundColor();
314     if (touchType == TouchType::DOWN) {
315         if (isHover_) {
316             // hover to press
317             Animation(renderContext, pressColor, theme->GetHoverToPressAnimationDuration(), Curves::SHARP);
318         } else {
319             // normal to press
320             Animation(renderContext, pressColor, theme->GetHoverAnimationDuration(), Curves::SHARP);
321         }
322     } else if (touchType == TouchType::UP) {
323         if (isHover_) {
324             // press to hover
325             Animation(renderContext, hoverColor, theme->GetHoverToPressAnimationDuration(), Curves::SHARP);
326         } else {
327             // press to normal
328             Animation(renderContext, backgroundColor, theme->GetHoverAnimationDuration(), Curves::SHARP);
329         }
330     }
331 }
332 
GetButtonRowNode()333 RefPtr<FrameNode> BubblePattern::GetButtonRowNode()
334 {
335     auto host = GetHost();
336     CHECK_NULL_RETURN(host, nullptr);
337     auto columnNode = AceType::DynamicCast<FrameNode>(host->GetLastChild());
338     CHECK_NULL_RETURN(columnNode, nullptr);
339     auto lastColumnNode = AceType::DynamicCast<FrameNode>(columnNode->GetLastChild());
340     CHECK_NULL_RETURN(lastColumnNode, nullptr);
341     auto buttonRowNode = AceType::DynamicCast<FrameNode>(lastColumnNode->GetLastChild());
342     CHECK_NULL_RETURN(buttonRowNode, nullptr);
343     if ((Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN))) {
344         if (buttonRowNode->GetTag() != V2::ROW_ETS_TAG) {
345             return nullptr;
346         }
347     } else {
348         if (buttonRowNode->GetTag() != V2::FLEX_ETS_TAG) {
349             return nullptr;
350         }
351     }
352     if (buttonRowNode->GetChildren().empty()) {
353         return nullptr;
354     }
355     return buttonRowNode;
356 }
357 
PopBubble()358 void BubblePattern::PopBubble()
359 {
360     auto pipelineNg = PipelineContext::GetCurrentContext();
361     CHECK_NULL_VOID(pipelineNg);
362     auto overlayManager = pipelineNg->GetOverlayManager();
363     CHECK_NULL_VOID(overlayManager);
364     auto popupInfo = overlayManager->GetPopupInfo(targetNodeId_);
365     if (!popupInfo.isCurrentOnShow) {
366         return;
367     }
368     popupInfo.markNeedUpdate = true;
369     auto host = GetHost();
370     CHECK_NULL_VOID(host);
371     auto layoutProp = host->GetLayoutProperty<BubbleLayoutProperty>();
372     CHECK_NULL_VOID(layoutProp);
373     auto showInSubWindow = layoutProp->GetShowInSubWindow().value_or(false);
374     if (showInSubWindow) {
375         SubwindowManager::GetInstance()->HidePopupNG(targetNodeId_);
376     } else {
377         overlayManager->HidePopup(targetNodeId_, popupInfo);
378     }
379 }
380 
GetPopupTheme()381 RefPtr<PopupTheme> BubblePattern::GetPopupTheme()
382 {
383     auto pipelineContext = PipelineBase::GetCurrentContext();
384     CHECK_NULL_RETURN(pipelineContext, nullptr);
385     auto popupTheme = pipelineContext->GetTheme<PopupTheme>();
386     CHECK_NULL_RETURN(popupTheme, nullptr);
387     return popupTheme;
388 }
389 
Animation(RefPtr<RenderContext> & renderContext,const Color & endColor,int32_t duration,const RefPtr<Curve> & curve)390 void BubblePattern::Animation(
391     RefPtr<RenderContext>& renderContext, const Color& endColor, int32_t duration, const RefPtr<Curve>& curve)
392 {
393     AnimationOption option = AnimationOption();
394     option.SetCurve(curve);
395     option.SetDuration(duration);
396     option.SetFillMode(FillMode::FORWARDS);
397     AnimationUtils::Animate(
398         option, [buttonContext = renderContext, color = endColor]() { buttonContext->UpdateBackgroundColor(color); });
399 }
400 
PostTask(const TaskExecutor::Task & task,const std::string & name)401 bool BubblePattern::PostTask(const TaskExecutor::Task& task, const std::string& name)
402 {
403     auto pipeline = PipelineBase::GetCurrentContext();
404     CHECK_NULL_RETURN(pipeline, false);
405     auto taskExecutor = pipeline->GetTaskExecutor();
406     CHECK_NULL_RETURN(taskExecutor, false);
407     return taskExecutor->PostTask(task, TaskExecutor::TaskType::UI, name);
408 }
409 
StartEnteringTransitionEffects(const RefPtr<FrameNode> & popupNode,const std::function<void ()> & finish)410 void BubblePattern::StartEnteringTransitionEffects(
411     const RefPtr<FrameNode>& popupNode, const std::function<void()>& finish)
412 {
413     auto popupId = popupNode->GetId();
414     auto pattern = popupNode->GetPattern<BubblePattern>();
415     pattern->transitionStatus_ = TransitionStatus::ENTERING;
416     auto layoutProp = popupNode->GetLayoutProperty<BubbleLayoutProperty>();
417     CHECK_NULL_VOID(layoutProp);
418     layoutProp->UpdateVisibility(VisibleType::VISIBLE, false);
419     auto showInSubWindow = layoutProp->GetShowInSubWindow().value_or(false);
420     auto isBlock = layoutProp->GetBlockEventValue(true);
421     auto& renderContext = popupNode->GetRenderContext();
422     renderContext->SetTransitionInCallback(
423         [weak = WeakClaim(this), finish, showInSubWindow, popupId, isBlock]() {
424             auto pattern = weak.Upgrade();
425             CHECK_NULL_VOID(pattern);
426             if (pattern->transitionStatus_ != TransitionStatus::ENTERING) {
427                 return;
428             }
429             pattern->transitionStatus_ = TransitionStatus::NORMAL;
430             if (showInSubWindow) {
431                 std::vector<Rect> rects;
432                 if (!isBlock) {
433                     auto rect = Rect(pattern->GetChildOffset().GetX(), pattern->GetChildOffset().GetY(),
434                         pattern->GetChildSize().Width(), pattern->GetChildSize().Height());
435                     rects.emplace_back(rect);
436                 } else {
437                     auto parentWindowRect = pattern->GetHostWindowRect();
438                     auto rect = Rect(pattern->GetChildOffset().GetX(), pattern->GetChildOffset().GetY(),
439                         pattern->GetChildSize().Width(), pattern->GetChildSize().Height());
440                     rects.emplace_back(parentWindowRect);
441                     rects.emplace_back(rect);
442                 }
443                 auto subWindowMgr = SubwindowManager::GetInstance();
444                 subWindowMgr->SetHotAreas(rects, popupId, pattern->GetContainerId());
445             }
446             if (finish) {
447                 finish();
448             }
449         });
450 }
451 
StartExitingTransitionEffects(const RefPtr<FrameNode> & popupNode,const std::function<void ()> & finish)452 void BubblePattern::StartExitingTransitionEffects(
453     const RefPtr<FrameNode>& popupNode, const std::function<void()>& finish)
454 {
455     auto pattern = popupNode->GetPattern<BubblePattern>();
456     pattern->transitionStatus_ = TransitionStatus::EXITING;
457     auto layoutProperty = popupNode->GetLayoutProperty();
458     layoutProperty->UpdateVisibility(VisibleType::INVISIBLE, true);
459     auto renderContext = popupNode->GetRenderContext();
460     renderContext->SetTransitionOutCallback(
461         [weak = WeakClaim(this), finish]() {
462             auto pattern = weak.Upgrade();
463             CHECK_NULL_VOID(pattern);
464             if (pattern->transitionStatus_ != TransitionStatus::EXITING) {
465                 return;
466             }
467             pattern->transitionStatus_ = TransitionStatus::INVISIABLE;
468             if (finish) {
469                 finish();
470             }
471         });
472 }
473 
StartEnteringAnimation(std::function<void ()> finish)474 void BubblePattern::StartEnteringAnimation(std::function<void()> finish)
475 {
476     if (!arrowPlacement_.has_value()) {
477         delayShow_ = true;
478         finish_ = finish;
479         return;
480     }
481     if (IsOnShow()) {
482         return;
483     }
484 
485     if (transitionStatus_ == TransitionStatus::INVISIABLE) {
486         ResetToInvisible();
487     }
488 
489     StartOffsetEnteringAnimation();
490     if (finish) {
491         StartAlphaEnteringAnimation(finish);
492     } else {
493         StartAlphaEnteringAnimation(finish_);
494     }
495 }
496 
StartOffsetEnteringAnimation()497 void BubblePattern::StartOffsetEnteringAnimation()
498 {
499     AnimationOption optionPosition;
500     optionPosition.SetDuration(ENTRY_ANIMATION_DURATION);
501     optionPosition.SetCurve(Curves::FRICTION);
502     AnimationUtils::Animate(
503         optionPosition,
504         [weak = WeakClaim(this)]() {
505             auto pattern = weak.Upgrade();
506             CHECK_NULL_VOID(pattern);
507             auto renderContext = pattern->GetRenderContext();
508             CHECK_NULL_VOID(renderContext);
509             renderContext->UpdateOffset(OffsetT<Dimension>());
510             renderContext->SyncGeometryProperties(nullptr);
511         },
512         nullptr);
513 }
514 
StartAlphaEnteringAnimation(std::function<void ()> finish)515 void BubblePattern::StartAlphaEnteringAnimation(std::function<void()> finish)
516 {
517     AnimationOption optionAlpha;
518     optionAlpha.SetDuration(ENTRY_ANIMATION_DURATION);
519     optionAlpha.SetCurve(Curves::SHARP);
520     auto host = GetHost();
521     auto popupId = host->GetId();
522     CHECK_NULL_VOID(host);
523     auto layoutProp = host->GetLayoutProperty<BubbleLayoutProperty>();
524     CHECK_NULL_VOID(layoutProp);
525     auto showInSubWindow = layoutProp->GetShowInSubWindow().value_or(false);
526     auto isBlock = layoutProp->GetBlockEventValue(true);
527     AnimationUtils::Animate(
528         optionAlpha,
529         [weak = WeakClaim(this)]() {
530             auto pattern = weak.Upgrade();
531             CHECK_NULL_VOID(pattern);
532             pattern->transitionStatus_ = TransitionStatus::ENTERING;
533             auto renderContext = pattern->GetRenderContext();
534             CHECK_NULL_VOID(renderContext);
535             renderContext->UpdateOpacity(VISIABLE_ALPHA);
536         },
537         [weak = WeakClaim(this), finish, showInSubWindow, popupId, isBlock]() {
538             auto pattern = weak.Upgrade();
539             CHECK_NULL_VOID(pattern);
540             if (pattern->transitionStatus_ != TransitionStatus::ENTERING) {
541                 return;
542             }
543             pattern->transitionStatus_ = TransitionStatus::NORMAL;
544             if (showInSubWindow) {
545                 std::vector<Rect> rects;
546                 if (!isBlock) {
547                     auto rect = Rect(pattern->GetChildOffset().GetX(), pattern->GetChildOffset().GetY(),
548                         pattern->GetChildSize().Width(), pattern->GetChildSize().Height());
549                     rects.emplace_back(rect);
550                 } else {
551                     auto parentWindowRect = pattern->GetHostWindowRect();
552                     auto rect = Rect(pattern->GetChildOffset().GetX(), pattern->GetChildOffset().GetY(),
553                         pattern->GetChildSize().Width(), pattern->GetChildSize().Height());
554                     rects.emplace_back(parentWindowRect);
555                     rects.emplace_back(rect);
556                 }
557                 auto subWindowMgr = SubwindowManager::GetInstance();
558                 subWindowMgr->SetHotAreas(rects, popupId, pattern->GetContainerId());
559             }
560             if (finish) {
561                 finish();
562             }
563         });
564 }
565 
StartExitingAnimation(std::function<void ()> finish)566 void BubblePattern::StartExitingAnimation(std::function<void()> finish)
567 {
568     StartOffsetExitingAnimation();
569     StartAlphaExitingAnimation(finish);
570 }
571 
StartOffsetExitingAnimation()572 void BubblePattern::StartOffsetExitingAnimation()
573 {
574     AnimationOption optionPosition;
575     optionPosition.SetDuration(EXIT_ANIMATION_DURATION);
576     optionPosition.SetCurve(Curves::FRICTION);
577     AnimationUtils::Animate(
578         optionPosition,
579         [weak = WeakClaim(this)]() {
580             auto pattern = weak.Upgrade();
581             CHECK_NULL_VOID(pattern);
582             auto renderContext = pattern->GetRenderContext();
583             CHECK_NULL_VOID(renderContext);
584             renderContext->UpdateOffset(pattern->GetInvisibleOffset());
585             renderContext->SyncGeometryProperties(nullptr);
586         },
587         nullptr);
588 }
589 
StartAlphaExitingAnimation(std::function<void ()> finish)590 void BubblePattern::StartAlphaExitingAnimation(std::function<void()> finish)
591 {
592     AnimationOption optionAlpha;
593     optionAlpha.SetDuration(EXIT_ANIMATION_DURATION);
594     optionAlpha.SetCurve(Curves::SHARP);
595     AnimationUtils::Animate(
596         optionAlpha,
597         [weak = WeakClaim(this)]() {
598             auto pattern = weak.Upgrade();
599             CHECK_NULL_VOID(pattern);
600             pattern->transitionStatus_ = TransitionStatus::EXITING;
601             auto renderContext = pattern->GetRenderContext();
602             CHECK_NULL_VOID(renderContext);
603             renderContext->UpdateOpacity(INVISIABLE_ALPHA);
604         },
605         [weak = WeakClaim(this), finish]() {
606             auto pattern = weak.Upgrade();
607             CHECK_NULL_VOID(pattern);
608             if (pattern->transitionStatus_ != TransitionStatus::EXITING) {
609                 return;
610             }
611             pattern->transitionStatus_ = TransitionStatus::INVISIABLE;
612             if (finish) {
613                 finish();
614             }
615         });
616 }
617 
IsOnShow()618 bool BubblePattern::IsOnShow()
619 {
620     return (transitionStatus_ == TransitionStatus::ENTERING) || (transitionStatus_ == TransitionStatus::NORMAL);
621 }
622 
IsExiting()623 bool BubblePattern::IsExiting()
624 {
625     return transitionStatus_ == TransitionStatus::EXITING;
626 }
627 
GetInvisibleOffset()628 OffsetT<Dimension> BubblePattern::GetInvisibleOffset()
629 {
630     if (!arrowPlacement_.has_value()) {
631         return OffsetT<Dimension>();
632     }
633 
634     OffsetT<Dimension> offset;
635     switch (arrowPlacement_.value()) {
636         case Placement::LEFT:
637         case Placement::LEFT_TOP:
638         case Placement::LEFT_BOTTOM:
639             offset.AddX(INVISIABLE_OFFSET);
640             break;
641         case Placement::RIGHT:
642         case Placement::RIGHT_TOP:
643         case Placement::RIGHT_BOTTOM:
644             offset.AddX(INVISIABLE_OFFSET * -1);
645             break;
646         case Placement::TOP:
647         case Placement::TOP_LEFT:
648         case Placement::TOP_RIGHT:
649             offset.AddY(INVISIABLE_OFFSET);
650             break;
651         case Placement::BOTTOM:
652         case Placement::BOTTOM_LEFT:
653         case Placement::BOTTOM_RIGHT:
654             offset.AddY(INVISIABLE_OFFSET * -1);
655             break;
656         default:
657             break;
658     }
659     return offset;
660 }
661 
GetRenderContext()662 RefPtr<RenderContext> BubblePattern::GetRenderContext()
663 {
664     auto frameNode = GetHost();
665     CHECK_NULL_RETURN(frameNode, nullptr);
666     return frameNode->GetRenderContext();
667 }
668 
ResetToInvisible()669 void BubblePattern::ResetToInvisible()
670 {
671     auto renderContext = GetRenderContext();
672     CHECK_NULL_VOID(renderContext);
673 
674     renderContext->UpdateOpacity(INVISIABLE_ALPHA);
675     renderContext->UpdateOffset(GetInvisibleOffset());
676     renderContext->SyncGeometryProperties(nullptr);
677 }
678 
OnWindowSizeChanged(int32_t width,int32_t height,WindowSizeChangeReason type)679 void BubblePattern::OnWindowSizeChanged(int32_t width, int32_t height, WindowSizeChangeReason type)
680 {
681     switch (type) {
682         case WindowSizeChangeReason::UNDEFINED:
683         case WindowSizeChangeReason::MOVE:
684         case WindowSizeChangeReason::RESIZE:
685         case WindowSizeChangeReason::DRAG_START:
686         case WindowSizeChangeReason::DRAG:
687         case WindowSizeChangeReason::DRAG_END: {
688             break;
689         }
690         default: {
691             auto pipelineNg = PipelineContext::GetCurrentContext();
692             CHECK_NULL_VOID(pipelineNg);
693             auto overlayManager = pipelineNg->GetOverlayManager();
694             CHECK_NULL_VOID(overlayManager);
695             overlayManager->HideAllPopups();
696             auto host = GetHost();
697             CHECK_NULL_VOID(host);
698             auto layoutProp = host->GetLayoutProperty<BubbleLayoutProperty>();
699             CHECK_NULL_VOID(layoutProp);
700             auto showInSubWindow = layoutProp->GetShowInSubWindow().value_or(false);
701             if (showInSubWindow) {
702                 auto subwindow = SubwindowManager::GetInstance()->GetSubwindow(Container::CurrentId());
703                 CHECK_NULL_VOID(subwindow);
704                 subwindow->HidePopupNG(targetNodeId_);
705             }
706         }
707     }
708 }
709 
OnWindowHide()710 void BubblePattern::OnWindowHide()
711 {
712     auto pipelineNg = PipelineContext::GetCurrentContext();
713     CHECK_NULL_VOID(pipelineNg);
714     auto overlayManager = pipelineNg->GetOverlayManager();
715     CHECK_NULL_VOID(overlayManager);
716     overlayManager->HideAllPopups();
717     auto host = GetHost();
718     CHECK_NULL_VOID(host);
719     auto layoutProp = host->GetLayoutProperty<BubbleLayoutProperty>();
720     CHECK_NULL_VOID(layoutProp);
721     auto showInSubWindow = layoutProp->GetShowInSubWindow().value_or(false);
722     if (showInSubWindow) {
723         auto subwindow = SubwindowManager::GetInstance()->GetSubwindow(Container::CurrentId());
724         CHECK_NULL_VOID(subwindow);
725         subwindow->HidePopupNG(targetNodeId_);
726     }
727 }
728 
729 
UpdateText(const RefPtr<UINode> & node,const RefPtr<PopupTheme> & popupTheme)730 void BubblePattern::UpdateText(const RefPtr<UINode>& node, const RefPtr<PopupTheme>& popupTheme)
731 {
732     if (node->GetTag() == V2::TEXT_ETS_TAG) {
733         auto textNode = DynamicCast<FrameNode>(node);
734         CHECK_NULL_VOID(textNode);
735         auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
736         CHECK_NULL_VOID(textLayoutProperty);
737         auto parentNode = node->GetParent();
738         if (parentNode && parentNode->GetTag() == V2::BUTTON_ETS_TAG &&
739             !(Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN))) {
740             textLayoutProperty->UpdateTextColor(popupTheme->GetButtonFontColor());
741         } else if (!isSetMessageColor_) {
742             if ((Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN))) {
743                 textLayoutProperty->UpdateTextColor(popupTheme->GetFontColor());
744             } else {
745                 textLayoutProperty->UpdateTextColor(popupTheme->GetFontPrimaryColor());
746             }
747         }
748         textNode->MarkModifyDone();
749     } else {
750         for (const auto& childNode : node->GetChildren()) {
751             UpdateText(childNode, popupTheme);
752         }
753     }
754 }
755 
UpdateBubbleText()756 void BubblePattern::UpdateBubbleText()
757 {
758     auto host = GetHost();
759     CHECK_NULL_VOID(host);
760     host->SetNeedCallChildrenUpdate(false);
761     auto context = host->GetContext();
762     CHECK_NULL_VOID(context);
763     auto popupTheme = context->GetTheme<PopupTheme>();
764     CHECK_NULL_VOID(popupTheme);
765     UpdateText(host, popupTheme);
766     host->MarkDirtyNode();
767 }
768 
OnColorConfigurationUpdate()769 void BubblePattern::OnColorConfigurationUpdate()
770 {
771     if (isCustomPopup_) {
772         return;
773     }
774     colorMode_ = SystemProperties::GetColorMode();
775     UpdateBubbleText();
776 }
777 
UpdateAgingTextSize()778 void BubblePattern::UpdateAgingTextSize()
779 {
780     if (isCustomPopup_) {
781         return;
782     }
783     CHECK_NULL_VOID(messageNode_);
784     messageNode_->MarkDirtyNode();
785 }
786 } // namespace OHOS::Ace::NG
787