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