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->GetOrCreateEventHub<EventHub>();
117 CHECK_NULL_VOID(eventHub);
118 OnAreaChangedFunc onAreaChangedFunc = [popupNodeWk = WeakPtr<FrameNode>(host), weak = WeakClaim(this)](
119 const RectF& /* oldRect */, const OffsetF& /* oldOrigin */,
120 const RectF& /* rect */, const OffsetF& /* origin */) {
121 auto popupNode = popupNodeWk.Upgrade();
122 CHECK_NULL_VOID(popupNode);
123 popupNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
124 auto pattern = weak.Upgrade();
125 if (pattern) {
126 pattern->PopBubble(true);
127 }
128 };
129 eventHub->AddInnerOnAreaChangedCallback(host->GetId(), std::move(onAreaChangedFunc));
130
131 halfFoldHoverCallbackId_ =
132 pipelineContext->RegisterHalfFoldHoverChangedCallback([weak = WeakClaim(this)](bool isHalfFoldHover) {
133 auto pattern = weak.Upgrade();
134 CHECK_NULL_VOID(pattern);
135 auto host = pattern->GetHost();
136 CHECK_NULL_VOID(host);
137 AnimationOption option;
138 auto curve = AceType::MakeRefPtr<ResponsiveSpringMotion>(0.35f, 1.0f, 0.0f);
139 option.SetCurve(curve);
140 auto context = host->GetContext();
141 CHECK_NULL_VOID(context);
142 AnimationUtils::Animate(option, [host, context]() {
143 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
144 context->FlushUITasks();
145 }, nullptr, nullptr, host->GetContextRefPtr());
146 });
147 }
148
OnDetachFromFrameNode(FrameNode * frameNode)149 void BubblePattern::OnDetachFromFrameNode(FrameNode* frameNode)
150 {
151 auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
152 CHECK_NULL_VOID(pipeline);
153 pipeline->RemoveWindowSizeChangeCallback(frameNode->GetId());
154 pipeline->RemoveWindowStateChangedCallback(frameNode->GetId());
155 auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
156 CHECK_NULL_VOID(targetNode);
157 if (!hasOnAreaChange_) {
158 pipeline->RemoveOnAreaChangeNode(targetNode->GetId());
159 }
160 pipeline->UnRegisterHalfFoldHoverChangedCallback(halfFoldHoverCallbackId_);
161 }
162
InitTouchEvent()163 void BubblePattern::InitTouchEvent()
164 {
165 auto host = GetHost();
166 CHECK_NULL_VOID(host);
167 auto hub = host->GetOrCreateEventHub<EventHub>();
168 CHECK_NULL_VOID(hub);
169 auto gestureHub = hub->GetOrCreateGestureEventHub();
170 CHECK_NULL_VOID(gestureHub);
171 if (touchEvent_) {
172 return;
173 }
174 auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
175 auto pattern = weak.Upgrade();
176 if (pattern) {
177 pattern->HandleTouchEvent(info);
178 }
179 };
180 touchEvent_ = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
181 gestureHub->AddTouchEvent(touchEvent_);
182 }
183
HandleTouchEvent(const TouchEventInfo & info)184 void BubblePattern::HandleTouchEvent(const TouchEventInfo& info)
185 {
186 if (info.GetTouches().empty()) {
187 return;
188 }
189 auto touchType = info.GetTouches().front().GetTouchType();
190 auto clickPos = info.GetTouches().front().GetLocalLocation();
191 if (touchType == TouchType::DOWN) {
192 HandleTouchDown(clickPos);
193 }
194 }
195
HandleTouchDown(const Offset & clickPosition)196 void BubblePattern::HandleTouchDown(const Offset& clickPosition)
197 {
198 auto host = GetHost();
199 CHECK_NULL_VOID(host);
200 auto bubbleRenderProp = host->GetPaintProperty<BubbleRenderProperty>();
201 CHECK_NULL_VOID(bubbleRenderProp);
202 if (touchRegion_.IsInRegion(PointF(clickPosition.GetX(), clickPosition.GetY()))) {
203 return;
204 }
205 auto autoCancel = bubbleRenderProp->GetAutoCancel().value_or(true);
206 if (autoCancel) {
207 TAG_LOGD(AceLogTag::ACE_DIALOG, "handle popup touch down event");
208 if (!GetInteractiveDismiss()) {
209 return;
210 }
211 if (HasOnWillDismiss()) {
212 auto pipelineNg = host->GetContextRefPtr();
213 CHECK_NULL_VOID(pipelineNg);
214 auto overlayManager = pipelineNg->GetOverlayManager();
215 CHECK_NULL_VOID(overlayManager);
216 overlayManager->SetDismissPopupId(targetNodeId_);
217 CallOnWillDismiss(static_cast<int32_t>(DismissReason::TOUCH_OUTSIDE));
218 return;
219 }
220 PopBubble();
221 }
222 }
223
RegisterButtonOnHover()224 void BubblePattern::RegisterButtonOnHover()
225 {
226 if (mouseEventInitFlag_) {
227 return;
228 }
229 auto paintProps = GetPaintProperty<BubbleRenderProperty>();
230 CHECK_NULL_VOID(paintProps);
231 auto primaryButtonShow = paintProps->GetPrimaryButtonShow().value_or(false);
232 auto secondaryButtonShow = paintProps->GetSecondaryButtonShow().value_or(false);
233 auto custom = paintProps->GetUseCustom().value_or(false);
234 if (custom) {
235 return;
236 }
237 if (!primaryButtonShow && !secondaryButtonShow) {
238 return;
239 }
240 auto buttonRowNode = GetButtonRowNode();
241 for (const auto& child : buttonRowNode->GetChildren()) {
242 auto buttonNode = AceType::DynamicCast<FrameNode>(child);
243 CHECK_NULL_VOID(buttonNode);
244 if (buttonNode->GetTag() != V2::BUTTON_ETS_TAG) {
245 return;
246 }
247 auto inputHub = buttonNode->GetOrCreateInputEventHub();
248 CHECK_NULL_VOID(inputHub);
249 auto mouseTask = [weak = WeakClaim(this), buttonNodeWK = WeakPtr<FrameNode>(buttonNode)](bool isHover) {
250 auto pattern = weak.Upgrade();
251 CHECK_NULL_VOID(pattern);
252 auto buttonNode = buttonNodeWK.Upgrade();
253 CHECK_NULL_VOID(buttonNode);
254 pattern->ButtonOnHover(isHover, buttonNode);
255 };
256 auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
257 inputHub->AddOnHoverEvent(mouseEvent);
258 }
259 mouseEventInitFlag_ = true;
260 }
261
ButtonOnHover(bool isHover,const RefPtr<NG::FrameNode> & buttonNode)262 void BubblePattern::ButtonOnHover(bool isHover, const RefPtr<NG::FrameNode>& buttonNode)
263 {
264 auto renderContext = buttonNode->GetRenderContext();
265 CHECK_NULL_VOID(renderContext);
266 auto theme = GetPopupTheme();
267 CHECK_NULL_VOID(theme);
268 isHover_ = isHover;
269 auto hoverColor = theme->GetButtonHoverColor();
270 auto backgroundColor = theme->GetButtonBackgroundColor();
271 if (isHover) {
272 // normal to hover
273 Animation(renderContext, hoverColor, theme->GetHoverAnimationDuration(), Curves::FRICTION);
274 } else {
275 // hover to normal
276 Animation(renderContext, backgroundColor, theme->GetHoverAnimationDuration(), Curves::FRICTION);
277 }
278 }
279
RegisterButtonOnTouch()280 void BubblePattern::RegisterButtonOnTouch()
281 {
282 if (touchEventInitFlag_) {
283 return;
284 }
285 auto paintProps = GetPaintProperty<BubbleRenderProperty>();
286 CHECK_NULL_VOID(paintProps);
287 auto primaryButtonShow = paintProps->GetPrimaryButtonShow().value_or(false);
288 auto secondaryButtonShow = paintProps->GetSecondaryButtonShow().value_or(false);
289 auto custom = paintProps->GetUseCustom().value_or(false);
290 if (custom) {
291 return;
292 }
293 if (!primaryButtonShow && !secondaryButtonShow) {
294 return;
295 }
296
297 auto buttonRowNode = GetButtonRowNode();
298 for (const auto& child : buttonRowNode->GetChildren()) {
299 auto buttonNode = AceType::DynamicCast<FrameNode>(child);
300 CHECK_NULL_VOID(buttonNode);
301 if (buttonNode->GetTag() != V2::BUTTON_ETS_TAG) {
302 return;
303 }
304 auto gestureHub = buttonNode->GetOrCreateGestureEventHub();
305 CHECK_NULL_VOID(gestureHub);
306 auto touchCallback = [weak = WeakClaim(this), buttonNodeWK = WeakPtr<FrameNode>(buttonNode)]
307 (const TouchEventInfo& info) {
308 auto pattern = weak.Upgrade();
309 CHECK_NULL_VOID(pattern);
310 auto buttonNode = buttonNodeWK.Upgrade();
311 CHECK_NULL_VOID(buttonNode);
312 pattern->ButtonOnPress(info, buttonNode);
313 };
314 auto touchEvent = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
315 gestureHub->AddTouchEvent(touchEvent);
316 }
317 touchEventInitFlag_ = true;
318 }
319
ButtonOnPress(const TouchEventInfo & info,const RefPtr<NG::FrameNode> & buttonNode)320 void BubblePattern::ButtonOnPress(const TouchEventInfo& info, const RefPtr<NG::FrameNode>& buttonNode)
321 {
322 if (info.GetTouches().empty()) {
323 return;
324 }
325 auto touchType = info.GetTouches().front().GetTouchType();
326 auto renderContext = buttonNode->GetRenderContext();
327 CHECK_NULL_VOID(renderContext);
328 auto theme = GetPopupTheme();
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(bool tips)377 void BubblePattern::PopBubble(bool tips)
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 auto instanceId = pipelineNg->GetInstanceId();
385 CHECK_NULL_VOID(overlayManager);
386 auto popupInfo = overlayManager->GetPopupInfo(targetNodeId_);
387 if (!popupInfo.isCurrentOnShow || (tips && !popupInfo.isTips)) {
388 return;
389 }
390 popupInfo.markNeedUpdate = true;
391 CHECK_NULL_VOID(host);
392 auto layoutProp = host->GetLayoutProperty<BubbleLayoutProperty>();
393 CHECK_NULL_VOID(layoutProp);
394 auto showInSubWindow = layoutProp->GetShowInSubWindow().value_or(false);
395 auto isTips = layoutProp->GetIsTips().value_or(false);
396 if (showInSubWindow) {
397 if (isTips) {
398 SubwindowManager::GetInstance()->HideTipsNG(targetNodeId_, 0, instanceId);
399 } else {
400 SubwindowManager::GetInstance()->HidePopupNG(targetNodeId_, instanceId);
401 }
402 } else {
403 if (isTips) {
404 overlayManager->HideTips(targetNodeId_, popupInfo, 0);
405 } else {
406 overlayManager->HidePopup(targetNodeId_, popupInfo);
407 }
408 }
409 }
410
GetPopupTheme()411 RefPtr<PopupTheme> BubblePattern::GetPopupTheme()
412 {
413 auto host = GetHost();
414 CHECK_NULL_RETURN(host, nullptr);
415 auto pipelineContext = host->GetContext();
416 CHECK_NULL_RETURN(pipelineContext, nullptr);
417 auto popupTheme = pipelineContext->GetTheme<PopupTheme>();
418 CHECK_NULL_RETURN(popupTheme, nullptr);
419 return popupTheme;
420 }
421
Animation(RefPtr<RenderContext> & renderContext,const Color & endColor,int32_t duration,const RefPtr<Curve> & curve)422 void BubblePattern::Animation(
423 RefPtr<RenderContext>& renderContext, const Color& endColor, int32_t duration, const RefPtr<Curve>& curve)
424 {
425 auto host = GetHost();
426 CHECK_NULL_VOID(host);
427 AnimationOption option = AnimationOption();
428 option.SetCurve(curve);
429 option.SetDuration(duration);
430 option.SetFillMode(FillMode::FORWARDS);
431 AnimationUtils::Animate(
432 option, [buttonContext = renderContext, color = endColor]() { buttonContext->UpdateBackgroundColor(color); },
433 nullptr, nullptr, host->GetContextRefPtr());
434 }
435
PostTask(const TaskExecutor::Task & task,const std::string & name)436 bool BubblePattern::PostTask(const TaskExecutor::Task& task, const std::string& name)
437 {
438 auto host = GetHost();
439 CHECK_NULL_RETURN(host, false);
440 auto pipeline = host->GetContext();
441 CHECK_NULL_RETURN(pipeline, false);
442 auto taskExecutor = pipeline->GetTaskExecutor();
443 CHECK_NULL_RETURN(taskExecutor, false);
444 return taskExecutor->PostTask(task, TaskExecutor::TaskType::UI, name);
445 }
446
StartEnteringTransitionEffects(const RefPtr<FrameNode> & popupNode,const std::function<void ()> & finish)447 void BubblePattern::StartEnteringTransitionEffects(
448 const RefPtr<FrameNode>& popupNode, const std::function<void()>& finish)
449 {
450 auto popupId = popupNode->GetId();
451 auto pattern = popupNode->GetPattern<BubblePattern>();
452 pattern->transitionStatus_ = TransitionStatus::ENTERING;
453 auto layoutProp = popupNode->GetLayoutProperty<BubbleLayoutProperty>();
454 CHECK_NULL_VOID(layoutProp);
455 layoutProp->UpdateVisibility(VisibleType::VISIBLE, false);
456 auto showInSubWindow = layoutProp->GetShowInSubWindow().value_or(false);
457 auto isBlock = layoutProp->GetBlockEventValue(true);
458 auto& renderContext = popupNode->GetRenderContext();
459 auto isTips = layoutProp->GetIsTips().value_or(false);
460 renderContext->SetTransitionInCallback(
461 [weak = WeakClaim(this), finish, showInSubWindow, popupId, isBlock, isTips]() {
462 auto pattern = weak.Upgrade();
463 CHECK_NULL_VOID(pattern);
464 if (pattern->transitionStatus_ != TransitionStatus::ENTERING) {
465 return;
466 }
467 pattern->transitionStatus_ = TransitionStatus::NORMAL;
468 if (showInSubWindow && !isTips) {
469 std::vector<Rect> rects;
470 if (!isBlock) {
471 auto rect = Rect(pattern->GetChildOffset().GetX(), pattern->GetChildOffset().GetY(),
472 pattern->GetChildSize().Width(), pattern->GetChildSize().Height());
473 rects.emplace_back(rect);
474 } else {
475 auto parentWindowRect = pattern->GetHostWindowRect();
476 auto rect = Rect(pattern->GetChildOffset().GetX(), pattern->GetChildOffset().GetY(),
477 pattern->GetChildSize().Width(), pattern->GetChildSize().Height());
478 rects.emplace_back(parentWindowRect);
479 rects.emplace_back(rect);
480 }
481 auto subWindowMgr = SubwindowManager::GetInstance();
482 subWindowMgr->SetHotAreas(rects, SubwindowType::TYPE_POPUP, popupId, pattern->GetContainerId());
483 }
484 if (finish) {
485 finish();
486 }
487 });
488 }
489
StartExitingTransitionEffects(const RefPtr<FrameNode> & popupNode,const std::function<void ()> & finish)490 void BubblePattern::StartExitingTransitionEffects(
491 const RefPtr<FrameNode>& popupNode, const std::function<void()>& finish)
492 {
493 auto pattern = popupNode->GetPattern<BubblePattern>();
494 pattern->transitionStatus_ = TransitionStatus::EXITING;
495 auto layoutProperty = popupNode->GetLayoutProperty();
496 layoutProperty->UpdateVisibility(VisibleType::INVISIBLE, true);
497 auto renderContext = popupNode->GetRenderContext();
498 renderContext->SetTransitionOutCallback(
499 [weak = WeakClaim(this), finish]() {
500 auto pattern = weak.Upgrade();
501 CHECK_NULL_VOID(pattern);
502 if (pattern->transitionStatus_ != TransitionStatus::EXITING) {
503 return;
504 }
505 pattern->transitionStatus_ = TransitionStatus::INVISIABLE;
506 if (finish) {
507 finish();
508 }
509 });
510 }
511
StartEnteringAnimation(std::function<void ()> finish)512 void BubblePattern::StartEnteringAnimation(std::function<void()> finish)
513 {
514 if (!arrowPlacement_.has_value()) {
515 delayShow_ = true;
516 finish_ = finish;
517 return;
518 }
519 if (IsOnShow()) {
520 return;
521 }
522
523 if (transitionStatus_ == TransitionStatus::INVISIABLE) {
524 ResetToInvisible();
525 }
526
527 StartOffsetEnteringAnimation();
528 if (finish) {
529 StartAlphaEnteringAnimation(finish);
530 } else {
531 StartAlphaEnteringAnimation(finish_);
532 }
533 }
534
StartOffsetEnteringAnimation()535 void BubblePattern::StartOffsetEnteringAnimation()
536 {
537 auto host = GetHost();
538 CHECK_NULL_VOID(host);
539 AnimationOption optionPosition;
540 optionPosition.SetDuration(ENTRY_ANIMATION_DURATION);
541 optionPosition.SetCurve(Curves::FRICTION);
542 AnimationUtils::Animate(
543 optionPosition,
544 [weak = WeakClaim(this)]() {
545 auto pattern = weak.Upgrade();
546 CHECK_NULL_VOID(pattern);
547 auto renderContext = pattern->GetRenderContext();
548 CHECK_NULL_VOID(renderContext);
549 renderContext->UpdateOffset(OffsetT<Dimension>());
550 renderContext->SyncGeometryProperties(nullptr);
551 },
552 nullptr, nullptr, host->GetContextRefPtr());
553 }
554
StartAlphaEnteringAnimation(std::function<void ()> finish)555 void BubblePattern::StartAlphaEnteringAnimation(std::function<void()> finish)
556 {
557 AnimationOption optionAlpha;
558 optionAlpha.SetDuration(ENTRY_ANIMATION_DURATION);
559 optionAlpha.SetCurve(Curves::SHARP);
560 auto host = GetHost();
561 CHECK_NULL_VOID(host);
562 auto popupId = host->GetId();
563 auto layoutProp = host->GetLayoutProperty<BubbleLayoutProperty>();
564 CHECK_NULL_VOID(layoutProp);
565 auto showInSubWindow = layoutProp->GetShowInSubWindow().value_or(false);
566 auto isBlock = layoutProp->GetBlockEventValue(true);
567 auto isTips = layoutProp->GetIsTips().value_or(false);
568 AnimationUtils::Animate(
569 optionAlpha,
570 [weak = WeakClaim(this)]() {
571 auto pattern = weak.Upgrade();
572 CHECK_NULL_VOID(pattern);
573 pattern->transitionStatus_ = TransitionStatus::ENTERING;
574 auto renderContext = pattern->GetRenderContext();
575 CHECK_NULL_VOID(renderContext);
576 renderContext->UpdateOpacity(VISIABLE_ALPHA);
577 },
578 [weak = WeakClaim(this), finish, showInSubWindow, popupId, isBlock, isTips]() {
579 auto pattern = weak.Upgrade();
580 CHECK_NULL_VOID(pattern);
581 if (pattern->transitionStatus_ != TransitionStatus::ENTERING) {
582 return;
583 }
584 pattern->transitionStatus_ = TransitionStatus::NORMAL;
585 if (showInSubWindow && !isTips) {
586 std::vector<Rect> rects;
587 if (!isBlock) {
588 auto rect = Rect(pattern->GetChildOffset().GetX(), pattern->GetChildOffset().GetY(),
589 pattern->GetChildSize().Width(), pattern->GetChildSize().Height());
590 rects.emplace_back(rect);
591 } else {
592 auto parentWindowRect = pattern->GetHostWindowRect();
593 auto rect = Rect(pattern->GetChildOffset().GetX(), pattern->GetChildOffset().GetY(),
594 pattern->GetChildSize().Width(), pattern->GetChildSize().Height());
595 rects.emplace_back(parentWindowRect);
596 rects.emplace_back(rect);
597 }
598 auto subWindowMgr = SubwindowManager::GetInstance();
599 subWindowMgr->SetHotAreas(rects, SubwindowType::TYPE_POPUP, popupId, pattern->GetContainerId());
600 }
601 if (finish) {
602 finish();
603 }
604 }, nullptr, host->GetContextRefPtr());
605 }
606
StartExitingAnimation(std::function<void ()> finish)607 void BubblePattern::StartExitingAnimation(std::function<void()> finish)
608 {
609 StartOffsetExitingAnimation();
610 StartAlphaExitingAnimation(finish);
611 }
612
StartOffsetExitingAnimation()613 void BubblePattern::StartOffsetExitingAnimation()
614 {
615 auto host = GetHost();
616 CHECK_NULL_VOID(host);
617 AnimationOption optionPosition;
618 optionPosition.SetDuration(EXIT_ANIMATION_DURATION);
619 optionPosition.SetCurve(Curves::FRICTION);
620 AnimationUtils::Animate(
621 optionPosition,
622 [weak = WeakClaim(this)]() {
623 auto pattern = weak.Upgrade();
624 CHECK_NULL_VOID(pattern);
625 auto renderContext = pattern->GetRenderContext();
626 CHECK_NULL_VOID(renderContext);
627 renderContext->UpdateOffset(pattern->GetInvisibleOffset());
628 renderContext->SyncGeometryProperties(nullptr);
629 },
630 nullptr, nullptr, host->GetContextRefPtr());
631 }
632
StartAlphaExitingAnimation(std::function<void ()> finish)633 void BubblePattern::StartAlphaExitingAnimation(std::function<void()> finish)
634 {
635 auto host = GetHost();
636 CHECK_NULL_VOID(host);
637 AnimationOption optionAlpha;
638 optionAlpha.SetDuration(EXIT_ANIMATION_DURATION);
639 optionAlpha.SetCurve(Curves::SHARP);
640 AnimationUtils::Animate(
641 optionAlpha,
642 [weak = WeakClaim(this)]() {
643 auto pattern = weak.Upgrade();
644 CHECK_NULL_VOID(pattern);
645 pattern->transitionStatus_ = TransitionStatus::EXITING;
646 auto renderContext = pattern->GetRenderContext();
647 CHECK_NULL_VOID(renderContext);
648 renderContext->UpdateOpacity(INVISIABLE_ALPHA);
649 },
650 [weak = WeakClaim(this), finish]() {
651 auto pattern = weak.Upgrade();
652 CHECK_NULL_VOID(pattern);
653 if (pattern->transitionStatus_ != TransitionStatus::EXITING) {
654 return;
655 }
656 pattern->transitionStatus_ = TransitionStatus::INVISIABLE;
657 if (finish) {
658 finish();
659 }
660 }, nullptr, host->GetContextRefPtr());
661 }
662
IsOnShow()663 bool BubblePattern::IsOnShow()
664 {
665 return (transitionStatus_ == TransitionStatus::ENTERING) || (transitionStatus_ == TransitionStatus::NORMAL);
666 }
667
IsExiting()668 bool BubblePattern::IsExiting()
669 {
670 return transitionStatus_ == TransitionStatus::EXITING;
671 }
672
GetInvisibleOffset()673 OffsetT<Dimension> BubblePattern::GetInvisibleOffset()
674 {
675 if (!arrowPlacement_.has_value()) {
676 return OffsetT<Dimension>();
677 }
678
679 OffsetT<Dimension> offset;
680 switch (arrowPlacement_.value()) {
681 case Placement::LEFT:
682 case Placement::LEFT_TOP:
683 case Placement::LEFT_BOTTOM:
684 offset.AddX(INVISIABLE_OFFSET);
685 break;
686 case Placement::RIGHT:
687 case Placement::RIGHT_TOP:
688 case Placement::RIGHT_BOTTOM:
689 offset.AddX(INVISIABLE_OFFSET * -1);
690 break;
691 case Placement::TOP:
692 case Placement::TOP_LEFT:
693 case Placement::TOP_RIGHT:
694 offset.AddY(INVISIABLE_OFFSET);
695 break;
696 case Placement::BOTTOM:
697 case Placement::BOTTOM_LEFT:
698 case Placement::BOTTOM_RIGHT:
699 offset.AddY(INVISIABLE_OFFSET * -1);
700 break;
701 default:
702 break;
703 }
704 return offset;
705 }
706
GetRenderContext()707 RefPtr<RenderContext> BubblePattern::GetRenderContext()
708 {
709 auto frameNode = GetHost();
710 CHECK_NULL_RETURN(frameNode, nullptr);
711 return frameNode->GetRenderContext();
712 }
713
ResetToInvisible()714 void BubblePattern::ResetToInvisible()
715 {
716 auto renderContext = GetRenderContext();
717 CHECK_NULL_VOID(renderContext);
718
719 renderContext->UpdateOpacity(INVISIABLE_ALPHA);
720 renderContext->UpdateOffset(GetInvisibleOffset());
721 renderContext->SyncGeometryProperties(nullptr);
722 }
723
OnWindowSizeChanged(int32_t width,int32_t height,WindowSizeChangeReason type)724 void BubblePattern::OnWindowSizeChanged(int32_t width, int32_t height, WindowSizeChangeReason type)
725 {
726 TAG_LOGI(AceLogTag::ACE_OVERLAY, "Popup OnWindowSizeChanged, reason: %{public}d", type);
727 if (type == WindowSizeChangeReason::MAXIMIZE || type == WindowSizeChangeReason::RECOVER ||
728 type == WindowSizeChangeReason::ROTATION || type == WindowSizeChangeReason::HIDE ||
729 type == WindowSizeChangeReason::TRANSFORM || type == WindowSizeChangeReason::CUSTOM_ANIMATION) {
730 auto host = GetHost();
731 CHECK_NULL_VOID(host);
732 auto pipelineNg = host->GetContextRefPtr();
733 CHECK_NULL_VOID(pipelineNg);
734 auto overlayManager = pipelineNg->GetOverlayManager();
735 CHECK_NULL_VOID(overlayManager);
736 overlayManager->HideAllPopups();
737 auto layoutProp = host->GetLayoutProperty<BubbleLayoutProperty>();
738 CHECK_NULL_VOID(layoutProp);
739 auto showInSubWindow = layoutProp->GetShowInSubWindow().value_or(false);
740 if (showInSubWindow) {
741 auto subwindow = SubwindowManager::GetInstance()->GetSubwindowByType(
742 pipelineNg->GetInstanceId(), SubwindowType::TYPE_POPUP);
743 CHECK_NULL_VOID(subwindow);
744 subwindow->HidePopupNG(targetNodeId_);
745 }
746 }
747 }
748
OnWindowHide()749 void BubblePattern::OnWindowHide()
750 {
751 TAG_LOGI(AceLogTag::ACE_OVERLAY, "Popup OnWindowHide start");
752 auto host = GetHost();
753 CHECK_NULL_VOID(host);
754 auto pipelineNg = host->GetContextRefPtr();
755 CHECK_NULL_VOID(pipelineNg);
756 auto overlayManager = pipelineNg->GetOverlayManager();
757 CHECK_NULL_VOID(overlayManager);
758 overlayManager->HideAllPopups();
759 CHECK_NULL_VOID(host);
760 auto layoutProp = host->GetLayoutProperty<BubbleLayoutProperty>();
761 CHECK_NULL_VOID(layoutProp);
762 auto showInSubWindow = layoutProp->GetShowInSubWindow().value_or(false);
763 if (showInSubWindow) {
764 auto subwindow = SubwindowManager::GetInstance()->GetSubwindowByType(
765 pipelineNg->GetInstanceId(), SubwindowType::TYPE_POPUP);
766 CHECK_NULL_VOID(subwindow);
767 subwindow->HidePopupNG(targetNodeId_);
768 }
769 }
770
771
UpdateText(const RefPtr<UINode> & node,const RefPtr<PopupTheme> & popupTheme)772 void BubblePattern::UpdateText(const RefPtr<UINode>& node, const RefPtr<PopupTheme>& popupTheme)
773 {
774 if (node->GetTag() == V2::TEXT_ETS_TAG) {
775 auto textNode = DynamicCast<FrameNode>(node);
776 CHECK_NULL_VOID(textNode);
777 auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
778 CHECK_NULL_VOID(textLayoutProperty);
779 auto parentNode = node->GetParent();
780 if (parentNode && parentNode->GetTag() == V2::BUTTON_ETS_TAG &&
781 !(Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN))) {
782 if (popupTheme->GetPopupDoubleButtonIsSameStyle()) {
783 textLayoutProperty->UpdateTextColor(popupTheme->GetButtonFontColor());
784 }
785 } else if (!isSetMessageColor_) {
786 if ((Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN))) {
787 textLayoutProperty->UpdateTextColor(popupTheme->GetFontColor());
788 } else {
789 textLayoutProperty->UpdateTextColor(popupTheme->GetFontPrimaryColor());
790 }
791 }
792 textNode->MarkModifyDone();
793 textNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
794 } else {
795 for (const auto& childNode : node->GetChildren()) {
796 UpdateText(childNode, popupTheme);
797 }
798 }
799 }
800
DumpInfo()801 void BubblePattern::DumpInfo()
802 {
803 DumpLog::GetInstance().AddDesc("enableArrow: " + std::to_string(dumpInfo_.enableArrow));
804 DumpLog::GetInstance().AddDesc("mask: " + std::to_string(dumpInfo_.mask));
805 DumpLog::GetInstance().AddDesc("targetTag: " + dumpInfo_.targetNode + ", targetID: "
806 + std::to_string(dumpInfo_.targetID));
807 DumpLog::GetInstance().AddDesc("targetOffset: " + dumpInfo_.targetOffset.ToString());
808 DumpLog::GetInstance().AddDesc("targetSize: " + dumpInfo_.targetSize.ToString());
809 DumpLog::GetInstance().AddDesc("touchRegion: " + dumpInfo_.touchRegion.ToString());
810 DumpLog::GetInstance().AddDesc("avoid top: " + std::to_string(dumpInfo_.top)
811 + ", bottom: " + std::to_string(dumpInfo_.bottom));
812 DumpLog::GetInstance().AddDesc("userOffset: " + dumpInfo_.userOffset.ToString());
813 DumpLog::GetInstance().AddDesc("targetSpace: " + dumpInfo_.targetSpace.ToString());
814 DumpLog::GetInstance().AddDesc("originPlacement: " + dumpInfo_.originPlacement);
815 DumpLog::GetInstance().AddDesc("finalPlacement: " + dumpInfo_.finalPlacement);
816 DumpLog::GetInstance().AddDesc("enableHoverMode: " + std::to_string(dumpInfo_.enableHoverMode));
817 DumpLog::GetInstance().AddDesc("avoidKeyboard: " + std::to_string(dumpInfo_.avoidKeyboard));
818 }
819
UpdateBubbleText()820 void BubblePattern::UpdateBubbleText()
821 {
822 auto host = GetHost();
823 CHECK_NULL_VOID(host);
824 host->SetNeedCallChildrenUpdate(false);
825 auto context = host->GetContext();
826 CHECK_NULL_VOID(context);
827 auto popupTheme = context->GetTheme<PopupTheme>();
828 CHECK_NULL_VOID(popupTheme);
829 UpdateText(host, popupTheme);
830 host->MarkDirtyNode();
831 }
832
UpdateStyleOption(BlurStyle blurStyle,bool needUpdateShadow)833 void BubblePattern::UpdateStyleOption(BlurStyle blurStyle, bool needUpdateShadow)
834 {
835 auto host = GetHost();
836 CHECK_NULL_VOID(host);
837 auto popupTheme = GetPopupTheme();
838 CHECK_NULL_VOID(popupTheme);
839 auto childNode = AceType::DynamicCast<FrameNode>(host->GetFirstChild());
840 CHECK_NULL_VOID(childNode);
841 auto popupPaintProp = host->GetPaintProperty<BubbleRenderProperty>();
842 CHECK_NULL_VOID(popupPaintProp);
843 auto renderContext = childNode->GetRenderContext();
844 CHECK_NULL_VOID(renderContext);
845 auto defaultBGcolor = popupTheme->GetDefaultBGColor();
846 auto backgroundColor = popupPaintProp->GetBackgroundColor().value_or(defaultBGcolor);
847 renderContext->UpdateBackgroundColor(backgroundColor);
848 BlurStyleOption styleOption;
849 styleOption.blurStyle = blurStyle;
850 styleOption.colorMode = static_cast<ThemeColorMode>(popupTheme->GetBgThemeColorMode());
851 renderContext->UpdateBackBlurStyle(styleOption);
852 if (needUpdateShadow) {
853 auto shadow = Shadow::CreateShadow(ShadowStyle::OuterDefaultSM);
854 renderContext->UpdateBackShadow(shadow);
855 }
856 }
857
OnColorConfigurationUpdate()858 void BubblePattern::OnColorConfigurationUpdate()
859 {
860 // Tips: Color mode changes are already adapted, so ConfigChangePerform() control is not required.
861 if (isTips_) {
862 UpdateStyleOption(BlurStyle::COMPONENT_REGULAR, true);
863 } else if (SystemProperties::ConfigChangePerform()) {
864 UpdateStyleOption(popupParam_->GetBlurStyle(), false);
865 }
866 if (isCustomPopup_) {
867 return;
868 }
869 auto context = GetContext();
870 CHECK_NULL_VOID(context);
871 colorMode_ = context->GetColorMode();
872 UpdateBubbleText();
873 }
874
UpdateAgingTextSize()875 void BubblePattern::UpdateAgingTextSize()
876 {
877 if (isCustomPopup_) {
878 return;
879 }
880 CHECK_NULL_VOID(messageNode_);
881 messageNode_->MarkDirtyNode();
882 }
883
UpdateBubbleText(const Color & value)884 void BubblePattern::UpdateBubbleText(const Color& value)
885 {
886 auto messagenode = GetMessageNode();
887 CHECK_NULL_VOID(messagenode);
888 auto textLayoutProps = messagenode->GetLayoutProperty<TextLayoutProperty>();
889 CHECK_NULL_VOID(textLayoutProps);
890 textLayoutProps->UpdateTextColor(value);
891 messagenode->MarkModifyDone();
892 messagenode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
893 }
894
UpdateBubbleBackGroundColor(const Color & value)895 void BubblePattern::UpdateBubbleBackGroundColor(const Color& value)
896 {
897 auto host = GetHost();
898 CHECK_NULL_VOID(host);
899 auto popupPaintProp = host->GetPaintProperty<BubbleRenderProperty>();
900 CHECK_NULL_VOID(popupPaintProp);
901 popupPaintProp->UpdateBackgroundColor(value);
902 CHECK_NULL_VOID(popupParam_);
903 UpdateStyleOption(popupParam_->GetBlurStyle(), false);
904 host->MarkModifyDone();
905 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
906 }
907
UpdateMaskColor(const Color & value)908 void BubblePattern::UpdateMaskColor(const Color& value)
909 {
910 auto host = GetHost();
911 CHECK_NULL_VOID(host);
912 auto popupPaintProp = host->GetPaintProperty<BubbleRenderProperty>();
913 CHECK_NULL_VOID(popupPaintProp);
914 popupPaintProp->UpdateMaskColor(value);
915 host->MarkModifyDone();
916 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
917 }
918
UpdateMask(bool maskValue)919 void BubblePattern::UpdateMask(bool maskValue)
920 {
921 auto host = GetHost();
922 CHECK_NULL_VOID(host);
923 auto popupPaintProp = host->GetPaintProperty<BubbleLayoutProperty>();
924 CHECK_NULL_VOID(popupPaintProp);
925 popupPaintProp->UpdateBlockEvent(maskValue);
926 host->MarkModifyDone();
927 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
928 }
929
UpdateArrowWidth(const CalcDimension & dimension)930 void BubblePattern::UpdateArrowWidth(const CalcDimension& dimension)
931 {
932 auto host = GetHost();
933 CHECK_NULL_VOID(host);
934 auto popupLayoutProp = host->GetLayoutProperty<BubbleLayoutProperty>();
935 CHECK_NULL_VOID(popupLayoutProp);
936 if (dimension.Value() > 0 && dimension.Unit() != DimensionUnit::PERCENT) {
937 popupLayoutProp->UpdateArrowWidth(dimension);
938 }
939
940 host->MarkModifyDone();
941 host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
942 }
943
UpdateArrowHeight(const CalcDimension & dimension)944 void BubblePattern::UpdateArrowHeight(const CalcDimension& dimension)
945 {
946 auto host = GetHost();
947 CHECK_NULL_VOID(host);
948 auto popupLayoutProp = host->GetLayoutProperty<BubbleLayoutProperty>();
949 CHECK_NULL_VOID(popupLayoutProp);
950 if (dimension.Value() > 0 && dimension.Unit() != DimensionUnit::PERCENT) {
951 popupLayoutProp->UpdateArrowHeight(dimension);
952 }
953 host->MarkModifyDone();
954 host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
955 }
956
UpdateWidth(const CalcDimension & dimension)957 void BubblePattern::UpdateWidth(const CalcDimension& dimension)
958 {
959 auto host = GetHost();
960 CHECK_NULL_VOID(host);
961 auto childNode = AceType::DynamicCast<FrameNode>(host->GetFirstChild());
962 CHECK_NULL_VOID(childNode);
963 auto childLayoutProperty = childNode->GetLayoutProperty();
964 CHECK_NULL_VOID(childLayoutProperty);
965 if (dimension.Value() > 0) {
966 childLayoutProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(dimension), std::nullopt));
967 }
968 host->MarkModifyDone();
969 host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
970 }
971
UpdateRadius(const CalcDimension & dimension)972 void BubblePattern::UpdateRadius(const CalcDimension& dimension)
973 {
974 auto host = GetHost();
975 CHECK_NULL_VOID(host);
976 auto layoutProps = host->GetLayoutProperty<BubbleLayoutProperty>();
977 if (dimension.Value() >= 0) {
978 layoutProps->UpdateRadius(dimension);
979 }
980 host->MarkModifyDone();
981 host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
982 }
983
984
985 } // namespace OHOS::Ace::NG
986