1 /*
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components/bubble/render_bubble.h"
17 
18 #include "base/log/event_report.h"
19 #include "core/components/stack/stack_element.h"
20 #ifdef USE_ROSEN_DRAWING
21 #include "core/components_ng/render/drawing.h"
22 #endif
23 #include "core/pipeline/base/composed_element.h"
24 
25 namespace OHOS::Ace {
26 namespace {
27 
28 constexpr Dimension ARROW_WIDTH = 32.0_vp;
29 constexpr Dimension ARROW_HEIGHT = 8.0_vp;
30 constexpr Dimension GRID_MARGIN_PORTRAIT = 48.0_vp;
31 constexpr Dimension GRID_SPACING = 24.0_vp;
32 constexpr Dimension GRID_SPACING_TOTAL = 232.0_vp;
33 constexpr Dimension HORIZON_SPACING_WITH_SCREEN = 6.0_vp;
34 constexpr int32_t GRID_NUMBER_LANDSCAPE = 8;
35 constexpr int32_t BUBBLR_GRID_MAX_LANDSCAPE = 6;
36 constexpr Dimension BUBBLE_RADIUS = 16.0_vp;
37 constexpr Dimension ARROW_ZERO_PERCENT_VALUE = Dimension(0.0, DimensionUnit::PERCENT);
38 constexpr Dimension ARROW_HALF_PERCENT_VALUE = Dimension(0.5, DimensionUnit::PERCENT);
39 constexpr Dimension ARROW_ONE_HUNDRED_PERCENT_VALUE = Dimension(1.0, DimensionUnit::PERCENT);
40 } // namespace
41 
42 const Dimension RenderBubble::BUBBLE_SPACING(8.0, DimensionUnit::VP);
43 
RenderBubble()44 RenderBubble::RenderBubble()
45 {
46     rawDetector_ = AceType::MakeRefPtr<RawRecognizer>();
47     rawDetector_->SetOnTouchDown([weak = WeakClaim(this)](const TouchEventInfo& info) {
48         if (info.GetTouches().empty()) {
49             LOGE("RenderBubble touch event info is empty!");
50             return;
51         }
52 
53         auto bubble = weak.Upgrade();
54         if (bubble) {
55             bubble->HandleTouch(info.GetTouches().front().GetLocalLocation());
56         }
57     });
58 }
59 
Update(const RefPtr<Component> & component)60 void RenderBubble::Update(const RefPtr<Component>& component)
61 {
62     const auto bubble = AceType::DynamicCast<BubbleComponent>(component);
63     if (!bubble) {
64         LOGE("RenderBubble update with nullptr");
65         EventReport::SendRenderException(RenderExcepType::RENDER_COMPONENT_ERR);
66         return;
67     }
68     if (!bubble->GetPopupParam()) {
69         return;
70     }
71     hoverAnimationType_ = HoverAnimationType::NONE;
72     bubbleComponent_ = bubble;
73     maskColor_ = bubble->GetPopupParam()->GetMaskColor();
74     backgroundColor_ = bubble->GetPopupParam()->GetBackgroundColor();
75     placement_ = bubble->GetPopupParam()->GetPlacement();
76     onVisibilityChange_ =
77         AceAsyncEvent<void(const std::string&)>::Create(bubble->GetPopupParam()->GetOnVisibilityChange(), context_);
78     isShow_ = bubble->GetPopupParam()->IsShow();
79     enableArrow_ = bubble->GetPopupParam()->EnableArrow();
80     padding_ = bubble->GetPopupParam()->GetPadding();
81     margin_ = bubble->GetPopupParam()->GetMargin();
82     border_ = bubble->GetPopupParam()->GetBorder();
83     UpdateArrowOffset(bubble, placement_);
84     targetId_ = bubble->GetPopupParam()->GetTargetId();
85     weakStack_ = bubble->GetWeakStack();
86     useCustom_ = bubble->GetPopupParam()->IsUseCustom();
87     targetSpace_ = bubble->GetPopupParam()->GetTargetSpace().value_or(Dimension());
88     isShowInSubWindow_ = bubble->GetPopupParam()->IsShowInSubWindow();
89     if (isShowInSubWindow_) {
90         targetSize_ = bubble->GetPopupParam()->GetTargetSize();
91         targetOffset_ = bubble->GetPopupParam()->GetTargetOffset();
92     }
93     SetDisableTouchEvent(bubble->IsDisabledStatus());
94     SetInterceptTouchEvent(bubbleComponent_->GetPopupParam()->HasAction() || bubble->IsDisabledStatus());
95 
96     // When app is hide and there is no button in popup, pop popup.
97     auto context = context_.Upgrade();
98     if (context) {
99         context->SetPopupEventHandler([weak = WeakClaim(this)] {
100             auto bubble = weak.Upgrade();
101             if (bubble) {
102                 auto bubbleComponent = bubble->bubbleComponent_;
103                 if (bubbleComponent && !bubbleComponent->GetPopupParam()->HasAction()) {
104                     bubble->PopBubble();
105                 }
106             }
107         });
108     }
109 
110     MarkNeedLayout();
111 }
112 
UpdateArrowOffset(const RefPtr<BubbleComponent> & bubble,const Placement & placement)113 void RenderBubble::UpdateArrowOffset(const RefPtr<BubbleComponent>& bubble, const Placement& placement)
114 {
115     if (bubble->GetPopupParam()->GetArrowOffset().has_value()) {
116         arrowOffset_ = bubble->GetPopupParam()->GetArrowOffset().value();
117         auto context = context_.Upgrade();
118         if (context && context->GetIsDeclarative() && arrowOffset_.Unit() == DimensionUnit::PERCENT) {
119             arrowOffset_.SetValue(std::clamp(arrowOffset_.Value(), 0.0, 1.0));
120         }
121         return;
122     }
123     switch (placement_) {
124         case Placement::LEFT:
125         case Placement::RIGHT:
126         case Placement::TOP:
127         case Placement::BOTTOM:
128             arrowOffset_ = ARROW_HALF_PERCENT_VALUE;
129             break;
130         case Placement::TOP_LEFT:
131         case Placement::BOTTOM_LEFT:
132         case Placement::LEFT_TOP:
133         case Placement::RIGHT_TOP:
134             arrowOffset_ = ARROW_ZERO_PERCENT_VALUE;
135             break;
136         case Placement::TOP_RIGHT:
137         case Placement::BOTTOM_RIGHT:
138         case Placement::LEFT_BOTTOM:
139         case Placement::RIGHT_BOTTOM:
140             arrowOffset_ = ARROW_ONE_HUNDRED_PERCENT_VALUE;
141             break;
142         default:
143             break;
144     }
145 }
146 
UpdateAccessibilityInfo(Size size,Offset offset)147 void RenderBubble::UpdateAccessibilityInfo(Size size, Offset offset)
148 {
149     if (!bubbleComponent_) {
150         return;
151     }
152     auto context = context_.Upgrade();
153     if (!context) {
154         LOGE("RenderBubble context is null");
155         return;
156     }
157     auto viewScale = context->GetViewScale();
158     if (NearZero(viewScale)) {
159         LOGE("RenderBubble viewScale is zero.");
160         return;
161     }
162     auto accessibilityManager = context->GetAccessibilityManager();
163     if (!accessibilityManager) {
164         LOGE("RenderBubble accessibilityManager is null");
165         return;
166     }
167     auto nodeId = StringUtils::StringToInt(bubbleComponent_->GetId());
168     auto accessibilityNode = accessibilityManager->GetAccessibilityNodeById(nodeId);
169     if (!accessibilityNode) {
170         LOGE("RenderBubble accessibilityNode is null.");
171         return;
172     }
173     accessibilityNode->SetWidth((size.Width()) * viewScale);
174     accessibilityNode->SetHeight((size.Height()) * viewScale);
175     accessibilityNode->SetLeft((offset.GetX()) * viewScale);
176     accessibilityNode->SetTop((offset.GetY()) * viewScale);
177     accessibilityNode->SetLongClickableState(true);
178     accessibilityNode->SetClickableState(false);
179 
180     accessibilityNode->AddSupportAction(AceAction::ACTION_LONG_CLICK);
181     accessibilityNode->SetActionLongClickImpl([weakPtr = WeakClaim(this)]() {
182         const auto& bubble = weakPtr.Upgrade();
183         if (bubble) {
184             bubble->PopBubble();
185         }
186     });
187 }
188 
PerformLayout()189 void RenderBubble::PerformLayout()
190 {
191     InitTargetSizeAndPosition();
192     SetLayoutSize(GetLayoutParam().GetMaxSize());
193     LayoutParam innerLayout = GetLayoutParam();
194     if (!useCustom_) {
195         if (SystemProperties::GetDeviceOrientation() == DeviceOrientation::PORTRAIT) {
196             innerLayout.SetMaxSize(Size(innerLayout.GetMaxSize().Width() - NormalizeToPx(GRID_MARGIN_PORTRAIT),
197                 innerLayout.GetMaxSize().Height()));
198         } else {
199             static const int32_t gridGaps = 5;
200             double colWidth =
201                 (innerLayout.GetMaxSize().Width() - NormalizeToPx(GRID_SPACING_TOTAL)) / GRID_NUMBER_LANDSCAPE;
202             innerLayout.SetMaxSize(Size(colWidth * BUBBLR_GRID_MAX_LANDSCAPE + NormalizeToPx(GRID_SPACING) * gridGaps,
203                 innerLayout.GetMaxSize().Height()));
204         }
205     }
206     if (!GetChildren().empty()) {
207         const auto& child = GetChildren().front();
208         child->Layout(innerLayout);
209         childSize_ = child->GetLayoutSize();
210         childOffset_ = GetChildPosition(childSize_);
211         if (useCustom_) {
212             UpdateCustomChildPosition();
213             UpdateTouchRegion();
214         }
215         child->SetPosition(childOffset_);
216         UpdateAccessibilityInfo(childSize_, childOffset_);
217     }
218 }
219 
UpdateCustomChildPosition()220 void RenderBubble::UpdateCustomChildPosition()
221 {
222     double arrowWidth = NormalizeToPx(ARROW_WIDTH);
223     double twoRadiusPx = NormalizeToPx(BUBBLE_RADIUS) * 2.0;
224     switch (arrowPlacement_) {
225         case Placement::TOP:
226             showCustomArrow_ = GreatOrEqual(childSize_.Width() - twoRadiusPx, arrowWidth);
227             break;
228         case Placement::TOP_LEFT:
229         case Placement::TOP_RIGHT:
230             showCustomArrow_ = GreatOrEqual(childSize_.Width() - twoRadiusPx, arrowWidth);
231             if (!showCustomArrow_ || !enableArrow_) {
232                 childOffset_ += Offset(0.0, NormalizeToPx(ARROW_HEIGHT));
233             }
234             break;
235         case Placement::BOTTOM:
236             showCustomArrow_ = GreatOrEqual(childSize_.Width() - twoRadiusPx, arrowWidth);
237             break;
238         case Placement::BOTTOM_LEFT:
239         case Placement::BOTTOM_RIGHT:
240             showCustomArrow_ = GreatOrEqual(childSize_.Width() - twoRadiusPx, arrowWidth);
241             if (!showCustomArrow_ || !enableArrow_) {
242                 childOffset_ += Offset(0.0, -NormalizeToPx(ARROW_HEIGHT));
243             }
244             break;
245         case Placement::LEFT:
246         case Placement::LEFT_TOP:
247         case Placement::LEFT_BOTTOM:
248             showCustomArrow_ = GreatOrEqual(childSize_.Height() - twoRadiusPx, arrowWidth);
249             if (!showCustomArrow_ || !enableArrow_) {
250                 childOffset_ += Offset(NormalizeToPx(ARROW_HEIGHT), 0.0);
251             }
252             break;
253         case Placement::RIGHT:
254         case Placement::RIGHT_TOP:
255         case Placement::RIGHT_BOTTOM:
256             showCustomArrow_ = GreatOrEqual(childSize_.Height() - twoRadiusPx, arrowWidth);
257             if (!showCustomArrow_ || !enableArrow_) {
258                 childOffset_ += Offset(-NormalizeToPx(ARROW_HEIGHT), 0.0);
259             }
260             break;
261         default:
262             break;
263     }
264 }
265 
UpdateTouchRegion()266 void RenderBubble::UpdateTouchRegion()
267 {
268     Offset topLeft;
269     Offset bottomRight;
270     switch (arrowPlacement_) {
271         case Placement::TOP:
272         case Placement::TOP_LEFT:
273         case Placement::TOP_RIGHT:
274             topLeft = childOffset_;
275             bottomRight = Offset(0.0, NormalizeToPx(targetSpace_)) + childSize_;
276             if (showCustomArrow_) {
277                 bottomRight += Offset(0.0, NormalizeToPx(ARROW_HEIGHT));
278             }
279             break;
280         case Placement::BOTTOM:
281         case Placement::BOTTOM_LEFT:
282         case Placement::BOTTOM_RIGHT:
283             topLeft = childOffset_ + Offset(0.0, -NormalizeToPx(targetSpace_));
284             bottomRight = Offset(0.0, NormalizeToPx(targetSpace_)) + childSize_;
285             if (showCustomArrow_) {
286                 topLeft += Offset(0.0, -NormalizeToPx(ARROW_HEIGHT));
287                 bottomRight += Offset(0.0, NormalizeToPx(ARROW_HEIGHT));
288             }
289             break;
290         case Placement::LEFT:
291         case Placement::LEFT_TOP:
292         case Placement::LEFT_BOTTOM:
293             topLeft = childOffset_;
294             bottomRight = Offset(NormalizeToPx(targetSpace_), 0.0) + childSize_;
295             if (showCustomArrow_) {
296                 bottomRight += Offset(NormalizeToPx(ARROW_HEIGHT), 0.0);
297             }
298             break;
299         case Placement::RIGHT:
300         case Placement::RIGHT_TOP:
301         case Placement::RIGHT_BOTTOM:
302             topLeft = childOffset_ + Offset(-NormalizeToPx(targetSpace_), 0.0);
303             bottomRight = Offset(NormalizeToPx(targetSpace_), 0.0) + childSize_;
304             if (showCustomArrow_) {
305                 topLeft += Offset(-NormalizeToPx(ARROW_HEIGHT), 0.0);
306                 bottomRight += Offset(NormalizeToPx(ARROW_HEIGHT), 0.0);
307             }
308             break;
309         default:
310             break;
311     }
312     touchRegion_ = TouchRegion(topLeft, topLeft + bottomRight);
313 }
314 
InitTargetSizeAndPosition()315 void RenderBubble::InitTargetSizeAndPosition()
316 {
317     auto context = context_.Upgrade();
318     if (!context) {
319         return;
320     }
321     if (!isShowInSubWindow_) {
322         auto targetElement = context->GetComposedElementById(targetId_);
323         if (!targetElement) {
324             LOGE("Get target element by target id return null");
325             isShow_ = false;
326             return;
327         }
328         auto targetRender = targetElement->GetRenderNode();
329         if (!targetRender) {
330             return;
331         }
332         targetSize_ = targetRender->GetLayoutSize();
333         targetOffset_ = targetRender->GetOffsetToPage();
334     }
335     if (bubbleComponent_ && bubbleComponent_->GetPopupParam()) {
336         auto targetMargin = bubbleComponent_->GetPopupParam()->GetTargetMargin();
337         targetSize_ -= targetMargin.GetLayoutSizeInPx(context->GetDipScale());
338         targetOffset_ += targetMargin.GetOffsetInPx(context->GetDipScale());
339     }
340 }
341 
InitArrowState()342 void RenderBubble::InitArrowState()
343 {
344     if (!enableArrow_) {
345         showTopArrow_ = false;
346         showBottomArrow_ = false;
347         return;
348     }
349 
350     double arrowWidth = NormalizeToPx(ARROW_WIDTH);
351     showTopArrow_ = GreatOrEqual(
352         childSize_.Width() -
353             std::max(NormalizePercentToPx(padding_.Left(), false), NormalizeToPx(border_.TopLeftRadius().GetX())) -
354             std::max(NormalizePercentToPx(padding_.Right(), false), NormalizeToPx(border_.TopRightRadius().GetX())),
355         arrowWidth);
356     showBottomArrow_ = GreatOrEqual(
357         childSize_.Width() -
358             std::max(NormalizePercentToPx(padding_.Left(), false), NormalizeToPx(border_.BottomLeftRadius().GetX())) -
359             std::max(NormalizePercentToPx(padding_.Right(), false), NormalizeToPx(border_.BottomRightRadius().GetX())),
360         arrowWidth);
361 }
362 
InitArrowTopAndBottomPosition(Offset & topArrowPosition,Offset & bottomArrowPosition,Offset & topPosition,Offset & bottomPosition,const Size & childSize)363 void RenderBubble::InitArrowTopAndBottomPosition(Offset& topArrowPosition, Offset& bottomArrowPosition,
364     Offset& topPosition, Offset& bottomPosition, const Size& childSize)
365 {
366     double scaledBubbleSpacing = NormalizeToPx(BUBBLE_SPACING);
367     auto context = context_.Upgrade();
368     if (context && context->GetIsDeclarative()) {
369         topArrowPosition = topPosition + Offset(std::max(NormalizeToPx(padding_.Left()),
370                                                     NormalizeToPx(border_.TopLeftRadius().GetX())) +
371                                                     NormalizeToPx(BEZIER_WIDTH_HALF),
372                                              childSize.Height() + NormalizeToPx(BUBBLE_SPACING));
373         bottomArrowPosition = bottomPosition + Offset(std::max(NormalizeToPx(padding_.Left()),
374                                                           NormalizeToPx(border_.BottomLeftRadius().GetX())) +
375                                                           NormalizeToPx(BEZIER_WIDTH_HALF),
376                                                    -NormalizeToPx(BUBBLE_SPACING));
377         return;
378     }
379     topArrowPosition = Offset(targetOffset_.GetX() + targetSize_.Width() / 2.0,
380         targetOffset_.GetY() - scaledBubbleSpacing - NormalizePercentToPx(margin_.Bottom(), true));
381     bottomArrowPosition = Offset(targetOffset_.GetX() + targetSize_.Width() / 2.0,
382         targetOffset_.GetY() + targetSize_.Height() + scaledBubbleSpacing + NormalizePercentToPx(margin_.Top(), true));
383 }
384 
GetChildPosition(const Size & childSize)385 Offset RenderBubble::GetChildPosition(const Size& childSize)
386 {
387     InitArrowState();
388     double scaledBubbleSpacing = NormalizeToPx(BUBBLE_SPACING);
389     Offset bottomPosition = Offset(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
390         targetOffset_.GetY() + targetSize_.Height() + scaledBubbleSpacing + NormalizePercentToPx(margin_.Top(), true));
391     if (showBottomArrow_) {
392         bottomPosition += Offset(0.0, scaledBubbleSpacing);
393     }
394     Offset topPosition = Offset(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
395         targetOffset_.GetY() - childSize.Height() - scaledBubbleSpacing - NormalizePercentToPx(margin_.Bottom(), true));
396     if (showTopArrow_) {
397         topPosition += Offset(0.0, -scaledBubbleSpacing);
398     }
399     Offset topArrowPosition;
400     Offset bottomArrowPosition;
401     InitArrowTopAndBottomPosition(topArrowPosition, bottomArrowPosition, topPosition, bottomPosition, childSize);
402     Offset originOffset =
403         GetPositionWithPlacement(childSize, topPosition, bottomPosition, topArrowPosition, bottomArrowPosition);
404     Offset childPosition = originOffset;
405     arrowPlacement_ = placement_;
406 
407     // Fit popup to screen range.
408     ErrorPositionType errorType = GetErrorPositionType(childPosition, childSize);
409     if (errorType == ErrorPositionType::NORMAL) {
410         return childPosition;
411     }
412     // If childPosition is error, adjust bubble to bottom.
413     if (placement_ != Placement::TOP || errorType == ErrorPositionType::TOP_LEFT_ERROR) {
414         childPosition = FitToScreen(bottomPosition, childSize);
415         arrowPosition_ = bottomArrowPosition;
416         arrowPlacement_ = Placement::BOTTOM;
417         if (GetErrorPositionType(childPosition, childSize) == ErrorPositionType::NORMAL) {
418             return childPosition;
419         }
420     }
421     // If childPosition is error, adjust bubble to top.
422     childPosition = FitToScreen(topPosition, childSize);
423     arrowPosition_ = topArrowPosition;
424     arrowPlacement_ = Placement::TOP;
425     if (GetErrorPositionType(childPosition, childSize) == ErrorPositionType::NORMAL) {
426         return childPosition;
427     }
428     // If childPosition is error, adjust bubble to origin position.
429     arrowPlacement_ = placement_;
430     arrowPosition_ = arrowPlacement_ == Placement::TOP ? topArrowPosition : bottomArrowPosition;
431     return originOffset;
432 }
433 
GetPositionWithPlacement(const Size & childSize,const Offset & topPosition,const Offset & bottomPosition,const Offset & topArrowPosition,const Offset & bottomArrowPosition)434 Offset RenderBubble::GetPositionWithPlacement(const Size& childSize, const Offset& topPosition,
435     const Offset& bottomPosition, const Offset& topArrowPosition, const Offset& bottomArrowPosition)
436 {
437     Offset childPosition;
438     double bubbleSpacing = NormalizeToPx(BUBBLE_SPACING);
439     double marginRight = NormalizePercentToPx(margin_.Right(), false);
440     double marginBottom = NormalizePercentToPx(margin_.Bottom(), true);
441     double marginTop = NormalizePercentToPx(margin_.Top(), true);
442     double marginLeft = NormalizePercentToPx(margin_.Left(), false);
443     double arrowHalfWidth = NormalizeToPx(ARROW_WIDTH) / 2.0;
444     double radius = NormalizeToPx(BUBBLE_RADIUS);
445     double targetSpace = NormalizeToPx(targetSpace_);
446     switch (placement_) {
447         case Placement::TOP:
448             childPosition = topPosition;
449             arrowPosition_ = topArrowPosition;
450             break;
451         case Placement::TOP_LEFT:
452             childPosition = Offset(targetOffset_.GetX() - marginRight,
453                 targetOffset_.GetY() - childSize.Height() - bubbleSpacing * 2.0 - marginBottom);
454             arrowPosition_ = childPosition + Offset(radius + arrowHalfWidth, childSize.Height() + bubbleSpacing);
455             break;
456         case Placement::TOP_RIGHT:
457             childPosition = Offset(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
458                 targetOffset_.GetY() - childSize.Height() - targetSpace - bubbleSpacing - marginBottom);
459             arrowPosition_ = childPosition + Offset(radius + arrowHalfWidth, childSize.Height() + bubbleSpacing);
460             break;
461         case Placement::BOTTOM:
462             childPosition = bottomPosition;
463             arrowPosition_ = bottomArrowPosition;
464             break;
465         case Placement::BOTTOM_LEFT:
466             childPosition = Offset(targetOffset_.GetX() - marginRight,
467                 targetOffset_.GetY() + targetSize_.Height() + targetSpace + bubbleSpacing + marginTop);
468             arrowPosition_ = childPosition + Offset(radius + arrowHalfWidth, -bubbleSpacing);
469             break;
470         case Placement::BOTTOM_RIGHT:
471             childPosition = Offset(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
472                 targetOffset_.GetY() + targetSize_.Height() + targetSpace + bubbleSpacing + marginTop);
473             arrowPosition_ = childPosition + Offset(radius + arrowHalfWidth, -bubbleSpacing);
474             break;
475         case Placement::LEFT:
476             childPosition = Offset(targetOffset_.GetX() - targetSpace - bubbleSpacing - childSize.Width() - marginRight,
477                 targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
478             arrowPosition_ = childPosition + Offset(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
479             break;
480         case Placement::LEFT_TOP:
481             childPosition = Offset(targetOffset_.GetX() - targetSpace - bubbleSpacing - childSize.Width() - marginRight,
482                 targetOffset_.GetY() - marginBottom);
483             arrowPosition_ = childPosition + Offset(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
484             break;
485         case Placement::LEFT_BOTTOM:
486             childPosition = Offset(targetOffset_.GetX() - targetSpace - bubbleSpacing - childSize.Width() - marginRight,
487                 targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
488             arrowPosition_ = childPosition + Offset(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
489             break;
490         case Placement::RIGHT:
491             childPosition =
492                 Offset(targetOffset_.GetX() + targetSize_.Width() + targetSpace + bubbleSpacing + marginLeft,
493                     targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
494             arrowPosition_ = childPosition + Offset(-bubbleSpacing, radius + arrowHalfWidth);
495             break;
496         case Placement::RIGHT_TOP:
497             childPosition =
498                 Offset(targetOffset_.GetX() + targetSize_.Width() + targetSpace + bubbleSpacing + marginLeft,
499                     targetOffset_.GetY() - marginBottom);
500             arrowPosition_ = childPosition + Offset(-bubbleSpacing, radius + arrowHalfWidth);
501             break;
502         case Placement::RIGHT_BOTTOM:
503             childPosition =
504                 Offset(targetOffset_.GetX() + targetSize_.Width() + targetSpace + bubbleSpacing + marginLeft,
505                     targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
506             arrowPosition_ = childPosition + Offset(-bubbleSpacing, radius + arrowHalfWidth);
507             break;
508         default:
509             break;
510     }
511     return childPosition;
512 }
513 
FitToScreen(const Offset & fitPosition,const Size & childSize)514 Offset RenderBubble::FitToScreen(const Offset& fitPosition, const Size& childSize)
515 {
516     auto validation = GetErrorPositionType(fitPosition, childSize);
517     if (validation == ErrorPositionType::NORMAL) {
518         return fitPosition;
519     }
520     Offset childPosition = fitPosition;
521     double horizonSpacing = NormalizeToPx(HORIZON_SPACING_WITH_SCREEN);
522     if (validation == ErrorPositionType::TOP_LEFT_ERROR) {
523         childPosition.SetX(horizonSpacing);
524     } else {
525         childPosition.SetX(GetLayoutSize().Width() - childSize.Width() - horizonSpacing);
526     }
527     return childPosition;
528 }
529 
GetErrorPositionType(const Offset & childOffset,const Size & childSize)530 RenderBubble::ErrorPositionType RenderBubble::GetErrorPositionType(const Offset& childOffset, const Size& childSize)
531 {
532     double horizonSpacing = NormalizeToPx(HORIZON_SPACING_WITH_SCREEN);
533     TouchRegion validRegion = TouchRegion(
534         Offset(horizonSpacing, 0.0), Offset(GetLayoutSize().Width() - horizonSpacing, GetLayoutSize().Height()));
535     if (!validRegion.ContainsInRegion(childOffset.GetX(), childOffset.GetY())) {
536         return ErrorPositionType::TOP_LEFT_ERROR;
537     }
538     if (!validRegion.ContainsInRegion(
539             childOffset.GetX() + childSize.Width(), childOffset.GetY() + childSize.Height())) {
540         return ErrorPositionType::BOTTOM_RIGHT_ERROR;
541     }
542     return ErrorPositionType::NORMAL;
543 }
544 
OnHiddenChanged(bool hidden)545 void RenderBubble::OnHiddenChanged(bool hidden)
546 {
547     if (!bubbleComponent_ || !bubbleComponent_->GetPopupParam()) {
548         return;
549     }
550     // When page is hidden and there is no button in popup, pop bubble.
551     if (hidden && !bubbleComponent_->GetPopupParam()->HasAction()) {
552         PopBubble();
553     }
554 }
555 
HandleTouch(const Offset & clickPosition)556 void RenderBubble::HandleTouch(const Offset& clickPosition)
557 {
558     if (!bubbleComponent_ || !bubbleComponent_->GetPopupParam()) {
559         return;
560     }
561 
562     if (touchRegion_.ContainsInRegion(clickPosition.GetX(), clickPosition.GetY())) {
563         LOGI("Contains the touch region.");
564         return;
565     }
566 
567     if (!bubbleComponent_->GetPopupParam()->HasAction()) {
568         PopBubble();
569         UpdateAccessibilityInfo(Size(), Offset());
570     }
571 }
572 
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)573 void RenderBubble::OnTouchTestHit(
574     const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
575 {
576     rawDetector_->SetCoordinateOffset(coordinateOffset);
577     result.emplace_back(rawDetector_);
578 }
579 
PopBubble()580 bool RenderBubble::PopBubble()
581 {
582     auto stackElement = weakStack_.Upgrade();
583     if (!stackElement) {
584         return false;
585     }
586     stackElement->PopPopup(bubbleComponent_->GetId());
587     auto stateChangeEvent = bubbleComponent_->GetStateChangeEvent();
588     if (stateChangeEvent) {
589         stateChangeEvent(false);
590     }
591 
592     auto context = context_.Upgrade();
593     if (!context) {
594         return false;
595     }
596 #if !defined(PREVIEW)
597     const auto& accessibilityManager = context->GetAccessibilityManager();
598     if (accessibilityManager) {
599         accessibilityManager->RemoveAccessibilityNodeById(StringUtils::StringToInt(bubbleComponent_->GetId()));
600     }
601 #else
602     const auto& accessibilityManager = context->GetAccessibilityManager();
603     if (accessibilityManager) {
604         auto bubbleNodeId = StringUtils::StringToInt(bubbleComponent_->GetId());
605         auto node = accessibilityManager->GetAccessibilityNodeById(bubbleNodeId);
606         if (node) {
607             auto children = node->GetChildList();
608             for (auto& child : children) {
609                 child->SetVisible(false);
610                 child->ClearRect();
611             }
612         }
613     }
614 #endif
615     return true;
616 }
617 
FirePopEvent()618 void RenderBubble::FirePopEvent()
619 {
620     if (onVisibilityChange_) {
621         std::string param = std::string("\"visibilitychange\",{\"visibility\":").append("false}");
622         onVisibilityChange_(param);
623     }
624 }
625 
HandleMouseEvent(const MouseEvent & event)626 bool RenderBubble::HandleMouseEvent(const MouseEvent& event)
627 {
628     if (event.button != MouseButton::NONE_BUTTON && event.button != MouseButton::LEFT_BUTTON &&
629         event.action == MouseAction::PRESS) {
630         HandleTouch(event.GetOffset());
631     }
632     return true;
633 }
634 
635 #ifndef USE_ROSEN_DRAWING
BuildCornerPath(SkPath & path,Placement placement,double radius)636 void RenderBubble::BuildCornerPath(SkPath& path, Placement placement, double radius)
637 {
638     switch (placement) {
639         case Placement::TOP_LEFT:
640             path.arcTo(radius, radius, 0.0f, SkPath::ArcSize::kSmall_ArcSize, SkPathDirection::kCW,
641                 childOffset_.GetX() + radius, childOffset_.GetY());
642             break;
643         case Placement::TOP_RIGHT:
644             path.arcTo(radius, radius, 0.0f, SkPath::ArcSize::kSmall_ArcSize, SkPathDirection::kCW,
645                 childOffset_.GetX() + childSize_.Width(), childOffset_.GetY() + radius);
646             break;
647         case Placement::BOTTOM_RIGHT:
648             path.arcTo(radius, radius, 0.0f, SkPath::ArcSize::kSmall_ArcSize, SkPathDirection::kCW,
649                 childOffset_.GetX() + childSize_.Width() - radius, childOffset_.GetY() + childSize_.Height());
650             break;
651         case Placement::BOTTOM_LEFT:
652             path.arcTo(radius, radius, 0.0f, SkPath::ArcSize::kSmall_ArcSize, SkPathDirection::kCW, childOffset_.GetX(),
653                 childOffset_.GetY() + childSize_.Height() - radius);
654             break;
655         default:
656             break;
657     }
658 }
659 #else  // USE_ROSEN_DRAWING
BuildCornerPath(RSPath & path,Placement placement,double radius)660 void RenderBubble::BuildCornerPath(RSPath& path, Placement placement, double radius)
661 {
662     switch (placement) {
663         case Placement::TOP_LEFT:
664             path.ArcTo(
665                 radius, radius, 0.0f, RSPathDirection::CW_DIRECTION, childOffset_.GetX() + radius, childOffset_.GetY());
666             break;
667         case Placement::TOP_RIGHT:
668             path.ArcTo(radius, radius, 0.0f, RSPathDirection::CW_DIRECTION, childOffset_.GetX() + childSize_.Width(),
669                 childOffset_.GetY() + radius);
670             break;
671         case Placement::BOTTOM_RIGHT:
672             path.ArcTo(radius, radius, 0.0f, RSPathDirection::CW_DIRECTION,
673                 childOffset_.GetX() + childSize_.Width() - radius, childOffset_.GetY() + childSize_.Height());
674             break;
675         case Placement::BOTTOM_LEFT:
676             path.ArcTo(radius, radius, 0.0f, RSPathDirection::CW_DIRECTION, childOffset_.GetX(),
677                 childOffset_.GetY() + childSize_.Height() - radius);
678             break;
679         default:
680             break;
681     }
682 }
683 #endif // USE_ROSEN_DRAWING
684 
685 #ifndef USE_ROSEN_DRAWING
BuildTopLinePath(SkPath & path,double arrowOffset,double radius)686 void RenderBubble::BuildTopLinePath(SkPath& path, double arrowOffset, double radius)
687 {
688     switch (arrowPlacement_) {
689         case Placement::BOTTOM:
690         case Placement::BOTTOM_LEFT:
691         case Placement::BOTTOM_RIGHT:
692             path.lineTo(arrowPosition_.GetX() + arrowOffset - NormalizeToPx(BEZIER_WIDTH_HALF), childOffset_.GetY());
693             path.quadTo(arrowPosition_.GetX() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_THIRD),
694                 arrowPosition_.GetY() + NormalizeToPx(BEZIER_VERTICAL_OFFSET_THIRD),
695                 arrowPosition_.GetX() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_SECOND),
696                 arrowPosition_.GetY() + NormalizeToPx(BEZIER_VERTICAL_OFFSET_SECOND));
697             path.quadTo(arrowPosition_.GetX() - NormalizeToPx(BEZIER_HORIZON_OFFSET_FIRST) + arrowOffset,
698                 arrowPosition_.GetY() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_FIRST),
699                 arrowPosition_.GetX() + arrowOffset, arrowPosition_.GetY());
700             path.quadTo(arrowPosition_.GetX() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_FIRST),
701                 arrowPosition_.GetY() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_FIRST),
702                 arrowPosition_.GetX() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_SECOND),
703                 arrowPosition_.GetY() + NormalizeToPx(BEZIER_VERTICAL_OFFSET_SECOND));
704             path.quadTo(arrowPosition_.GetX() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_THIRD),
705                 arrowPosition_.GetY() + NormalizeToPx(BEZIER_VERTICAL_OFFSET_THIRD),
706                 arrowPosition_.GetX() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_FOURTH),
707                 arrowPosition_.GetY() + NormalizeToPx(BEZIER_VERTICAL_OFFSET_THIRD));
708             break;
709         default:
710             break;
711     }
712     path.lineTo(childOffset_.GetX() + childSize_.Width() - radius, childOffset_.GetY());
713 }
714 #else
BuildTopLinePath(RSPath & path,double arrowOffset,double radius)715 void RenderBubble::BuildTopLinePath(RSPath& path, double arrowOffset, double radius)
716 {
717     switch (arrowPlacement_) {
718         case Placement::BOTTOM:
719         case Placement::BOTTOM_LEFT:
720         case Placement::BOTTOM_RIGHT:
721             path.LineTo(arrowPosition_.GetX() + arrowOffset - NormalizeToPx(BEZIER_WIDTH_HALF), childOffset_.GetY());
722             path.QuadTo(arrowPosition_.GetX() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_THIRD),
723                 arrowPosition_.GetY() + NormalizeToPx(BEZIER_VERTICAL_OFFSET_THIRD),
724                 arrowPosition_.GetX() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_SECOND),
725                 arrowPosition_.GetY() + NormalizeToPx(BEZIER_VERTICAL_OFFSET_SECOND));
726             path.QuadTo(arrowPosition_.GetX() - NormalizeToPx(BEZIER_HORIZON_OFFSET_FIRST) + arrowOffset,
727                 arrowPosition_.GetY() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_FIRST),
728                 arrowPosition_.GetX() + arrowOffset, arrowPosition_.GetY());
729             path.QuadTo(arrowPosition_.GetX() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_FIRST),
730                 arrowPosition_.GetY() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_FIRST),
731                 arrowPosition_.GetX() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_SECOND),
732                 arrowPosition_.GetY() + NormalizeToPx(BEZIER_VERTICAL_OFFSET_SECOND));
733             path.QuadTo(arrowPosition_.GetX() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_THIRD),
734                 arrowPosition_.GetY() + NormalizeToPx(BEZIER_VERTICAL_OFFSET_THIRD),
735                 arrowPosition_.GetX() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_FOURTH),
736                 arrowPosition_.GetY() + NormalizeToPx(BEZIER_VERTICAL_OFFSET_THIRD));
737             break;
738         default:
739             break;
740     }
741     path.LineTo(childOffset_.GetX() + childSize_.Width() - radius, childOffset_.GetY());
742 }
743 #endif
744 
745 #ifndef USE_ROSEN_DRAWING
BuildRightLinePath(SkPath & path,double arrowOffset,double radius)746 void RenderBubble::BuildRightLinePath(SkPath& path, double arrowOffset, double radius)
747 {
748     switch (arrowPlacement_) {
749         case Placement::LEFT:
750         case Placement::LEFT_TOP:
751         case Placement::LEFT_BOTTOM:
752             path.lineTo(childOffset_.GetX() + childSize_.Width(),
753                 arrowPosition_.GetY() + arrowOffset - NormalizeToPx(BEZIER_WIDTH_HALF));
754             path.quadTo(arrowPosition_.GetX() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_THIRD),
755                 arrowPosition_.GetY() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_THIRD),
756                 arrowPosition_.GetX() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_SECOND),
757                 arrowPosition_.GetY() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_SECOND));
758             path.quadTo(arrowPosition_.GetX() + NormalizeToPx(BEZIER_VERTICAL_OFFSET_FIRST),
759                 arrowPosition_.GetY() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_FIRST), arrowPosition_.GetX(),
760                 arrowPosition_.GetY() + arrowOffset);
761             path.quadTo(arrowPosition_.GetX() + NormalizeToPx(BEZIER_VERTICAL_OFFSET_FIRST),
762                 arrowPosition_.GetY() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_FIRST),
763                 arrowPosition_.GetX() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_SECOND),
764                 arrowPosition_.GetY() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_SECOND));
765             path.quadTo(arrowPosition_.GetX() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_THIRD),
766                 arrowPosition_.GetY() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_THIRD),
767                 arrowPosition_.GetX() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_THIRD),
768                 arrowPosition_.GetY() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_FOURTH));
769             break;
770         default:
771             break;
772     }
773     path.lineTo(childOffset_.GetX() + childSize_.Width(), childOffset_.GetY() + childSize_.Height() - radius);
774 }
775 #else
BuildRightLinePath(RSPath & path,double arrowOffset,double radius)776 void RenderBubble::BuildRightLinePath(RSPath& path, double arrowOffset, double radius)
777 {
778     switch (arrowPlacement_) {
779         case Placement::LEFT:
780         case Placement::LEFT_TOP:
781         case Placement::LEFT_BOTTOM:
782             path.LineTo(childOffset_.GetX() + childSize_.Width(),
783                 arrowPosition_.GetY() + arrowOffset - NormalizeToPx(BEZIER_WIDTH_HALF));
784             path.QuadTo(arrowPosition_.GetX() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_THIRD),
785                 arrowPosition_.GetY() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_THIRD),
786                 arrowPosition_.GetX() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_SECOND),
787                 arrowPosition_.GetY() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_SECOND));
788             path.QuadTo(arrowPosition_.GetX() + NormalizeToPx(BEZIER_VERTICAL_OFFSET_FIRST),
789                 arrowPosition_.GetY() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_FIRST), arrowPosition_.GetX(),
790                 arrowPosition_.GetY() + arrowOffset);
791             path.QuadTo(arrowPosition_.GetX() + NormalizeToPx(BEZIER_VERTICAL_OFFSET_FIRST),
792                 arrowPosition_.GetY() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_FIRST),
793                 arrowPosition_.GetX() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_SECOND),
794                 arrowPosition_.GetY() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_SECOND));
795             path.QuadTo(arrowPosition_.GetX() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_THIRD),
796                 arrowPosition_.GetY() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_THIRD),
797                 arrowPosition_.GetX() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_THIRD),
798                 arrowPosition_.GetY() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_FOURTH));
799             break;
800         default:
801             break;
802     }
803     path.LineTo(childOffset_.GetX() + childSize_.Width(), childOffset_.GetY() + childSize_.Height() - radius);
804 }
805 #endif
806 
807 #ifndef USE_ROSEN_DRAWING
BuildBottomLinePath(SkPath & path,double arrowOffset,double radius)808 void RenderBubble::BuildBottomLinePath(SkPath& path, double arrowOffset, double radius)
809 {
810     switch (arrowPlacement_) {
811         case Placement::TOP:
812         case Placement::TOP_LEFT:
813         case Placement::TOP_RIGHT:
814             path.lineTo(arrowPosition_.GetX() + arrowOffset + NormalizeToPx(BEZIER_WIDTH_HALF),
815                 childOffset_.GetY() + childSize_.Height());
816             path.quadTo(arrowPosition_.GetX() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_THIRD),
817                 arrowPosition_.GetY() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_THIRD),
818                 arrowPosition_.GetX() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_SECOND),
819                 arrowPosition_.GetY() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_SECOND));
820             path.quadTo(arrowPosition_.GetX() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_FIRST),
821                 arrowPosition_.GetY() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_FIRST),
822                 arrowPosition_.GetX() + arrowOffset, arrowPosition_.GetY());
823             path.quadTo(arrowPosition_.GetX() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_FIRST),
824                 arrowPosition_.GetY() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_FIRST),
825                 arrowPosition_.GetX() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_SECOND),
826                 arrowPosition_.GetY() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_SECOND));
827             path.quadTo(arrowPosition_.GetX() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_THIRD),
828                 arrowPosition_.GetY() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_THIRD),
829                 arrowPosition_.GetX() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_FOURTH),
830                 arrowPosition_.GetY() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_THIRD));
831             break;
832         default:
833             break;
834     }
835     path.lineTo(childOffset_.GetX() + radius, childOffset_.GetY() + childSize_.Height());
836 }
837 #else
BuildBottomLinePath(RSPath & path,double arrowOffset,double radius)838 void RenderBubble::BuildBottomLinePath(RSPath& path, double arrowOffset, double radius)
839 {
840     switch (arrowPlacement_) {
841         case Placement::TOP:
842         case Placement::TOP_LEFT:
843         case Placement::TOP_RIGHT:
844             path.LineTo(arrowPosition_.GetX() + arrowOffset + NormalizeToPx(BEZIER_WIDTH_HALF),
845                 childOffset_.GetY() + childSize_.Height());
846             path.QuadTo(arrowPosition_.GetX() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_THIRD),
847                 arrowPosition_.GetY() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_THIRD),
848                 arrowPosition_.GetX() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_SECOND),
849                 arrowPosition_.GetY() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_SECOND));
850             path.QuadTo(arrowPosition_.GetX() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_FIRST),
851                 arrowPosition_.GetY() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_FIRST),
852                 arrowPosition_.GetX() + arrowOffset, arrowPosition_.GetY());
853             path.QuadTo(arrowPosition_.GetX() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_FIRST),
854                 arrowPosition_.GetY() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_FIRST),
855                 arrowPosition_.GetX() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_SECOND),
856                 arrowPosition_.GetY() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_SECOND));
857             path.QuadTo(arrowPosition_.GetX() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_THIRD),
858                 arrowPosition_.GetY() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_THIRD),
859                 arrowPosition_.GetX() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_FOURTH),
860                 arrowPosition_.GetY() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_THIRD));
861             break;
862         default:
863             break;
864     }
865     path.LineTo(childOffset_.GetX() + radius, childOffset_.GetY() + childSize_.Height());
866 }
867 #endif
868 
869 #ifndef USE_ROSEN_DRAWING
BuildLeftLinePath(SkPath & path,double arrowOffset,double radius)870 void RenderBubble::BuildLeftLinePath(SkPath& path, double arrowOffset, double radius)
871 {
872     switch (arrowPlacement_) {
873         case Placement::RIGHT:
874         case Placement::RIGHT_TOP:
875         case Placement::RIGHT_BOTTOM:
876             path.lineTo(childOffset_.GetX(), arrowPosition_.GetY() + arrowOffset + NormalizeToPx(BEZIER_WIDTH_HALF));
877             path.quadTo(arrowPosition_.GetX() + NormalizeToPx(BEZIER_VERTICAL_OFFSET_THIRD),
878                 arrowPosition_.GetY() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_THIRD),
879                 arrowPosition_.GetX() + NormalizeToPx(BEZIER_VERTICAL_OFFSET_SECOND),
880                 arrowPosition_.GetY() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_SECOND));
881             path.quadTo(arrowPosition_.GetX() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_FIRST),
882                 arrowPosition_.GetY() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_FIRST), arrowPosition_.GetX(),
883                 arrowPosition_.GetY() + arrowOffset);
884             path.quadTo(arrowPosition_.GetX() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_FIRST),
885                 arrowPosition_.GetY() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_FIRST),
886                 arrowPosition_.GetX() + NormalizeToPx(BEZIER_VERTICAL_OFFSET_SECOND),
887                 arrowPosition_.GetY() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_SECOND));
888             path.quadTo(arrowPosition_.GetX() + NormalizeToPx(BEZIER_VERTICAL_OFFSET_THIRD),
889                 arrowPosition_.GetY() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_THIRD),
890                 arrowPosition_.GetX() + NormalizeToPx(BEZIER_VERTICAL_OFFSET_THIRD),
891                 arrowPosition_.GetY() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_FOURTH));
892             break;
893         default:
894             break;
895     }
896     path.lineTo(childOffset_.GetX(), childOffset_.GetY() + radius);
897 }
898 #else
BuildLeftLinePath(RSPath & path,double arrowOffset,double radius)899 void RenderBubble::BuildLeftLinePath(RSPath& path, double arrowOffset, double radius)
900 {
901     switch (arrowPlacement_) {
902         case Placement::RIGHT:
903         case Placement::RIGHT_TOP:
904         case Placement::RIGHT_BOTTOM:
905             path.LineTo(childOffset_.GetX(), arrowPosition_.GetY() + arrowOffset + NormalizeToPx(BEZIER_WIDTH_HALF));
906             path.QuadTo(arrowPosition_.GetX() + NormalizeToPx(BEZIER_VERTICAL_OFFSET_THIRD),
907                 arrowPosition_.GetY() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_THIRD),
908                 arrowPosition_.GetX() + NormalizeToPx(BEZIER_VERTICAL_OFFSET_SECOND),
909                 arrowPosition_.GetY() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_SECOND));
910             path.QuadTo(arrowPosition_.GetX() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_FIRST),
911                 arrowPosition_.GetY() + arrowOffset + NormalizeToPx(BEZIER_HORIZON_OFFSET_FIRST), arrowPosition_.GetX(),
912                 arrowPosition_.GetY() + arrowOffset);
913             path.QuadTo(arrowPosition_.GetX() - NormalizeToPx(BEZIER_VERTICAL_OFFSET_FIRST),
914                 arrowPosition_.GetY() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_FIRST),
915                 arrowPosition_.GetX() + NormalizeToPx(BEZIER_VERTICAL_OFFSET_SECOND),
916                 arrowPosition_.GetY() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_SECOND));
917             path.QuadTo(arrowPosition_.GetX() + NormalizeToPx(BEZIER_VERTICAL_OFFSET_THIRD),
918                 arrowPosition_.GetY() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_THIRD),
919                 arrowPosition_.GetX() + NormalizeToPx(BEZIER_VERTICAL_OFFSET_THIRD),
920                 arrowPosition_.GetY() + arrowOffset - NormalizeToPx(BEZIER_HORIZON_OFFSET_FOURTH));
921             break;
922         default:
923             break;
924     }
925     path.LineTo(childOffset_.GetX(), childOffset_.GetY() + radius);
926 }
927 #endif
928 
929 #ifndef USE_ROSEN_DRAWING
BuildCompletePath(SkPath & path)930 void RenderBubble::BuildCompletePath(SkPath& path)
931 #else
932 void RenderBubble::BuildCompletePath(RSPath& path)
933 #endif
934 {
935     double arrowOffset = GetArrowOffset(placement_);
936     double radiusPx = NormalizeToPx(border_.BottomLeftRadius().GetY());
937 #ifndef USE_ROSEN_DRAWING
938     path.reset();
939     path.moveTo(childOffset_.GetX() + radiusPx, childOffset_.GetY());
940 #else
941     path.Reset();
942     path.MoveTo(childOffset_.GetX() + radiusPx, childOffset_.GetY());
943 #endif
944     BuildTopLinePath(path, arrowOffset, radiusPx);
945     BuildCornerPath(path, Placement::TOP_RIGHT, radiusPx);
946     BuildRightLinePath(path, arrowOffset, radiusPx);
947     BuildCornerPath(path, Placement::BOTTOM_RIGHT, radiusPx);
948     BuildBottomLinePath(path, arrowOffset, radiusPx);
949     BuildCornerPath(path, Placement::BOTTOM_LEFT, radiusPx);
950     BuildLeftLinePath(path, arrowOffset, radiusPx);
951     BuildCornerPath(path, Placement::TOP_LEFT, radiusPx);
952 
953 #ifndef USE_ROSEN_DRAWING
954     path.close();
955 #else
956     path.Close();
957 #endif
958 }
959 
InitEdgeSize(Edge & edge)960 void RenderBubble::InitEdgeSize(Edge& edge)
961 {
962     edge.SetTop(Dimension(std::max(NormalizeToPx(padding_.Left()), NormalizeToPx(border_.TopLeftRadius().GetX())) +
963                           std::max(NormalizeToPx(padding_.Right()), NormalizeToPx(border_.TopRightRadius().GetX()))));
964     edge.SetBottom(
965         Dimension(std::max(NormalizeToPx(padding_.Left()), NormalizeToPx(border_.BottomLeftRadius().GetX())) +
966                   std::max(NormalizeToPx(padding_.Right()), NormalizeToPx(border_.BottomRightRadius().GetX()))));
967     edge.SetLeft(
968         Dimension(std::max(NormalizeToPx(padding_.Top()), NormalizeToPx(border_.TopRightRadius().GetY())) +
969                   std::max(NormalizeToPx(padding_.Bottom()), NormalizeToPx(border_.BottomRightRadius().GetY()))));
970     edge.SetRight(
971         Dimension(std::max(NormalizeToPx(padding_.Top()), NormalizeToPx(border_.TopLeftRadius().GetY())) +
972                   std::max(NormalizeToPx(padding_.Bottom()), NormalizeToPx(border_.BottomLeftRadius().GetY()))));
973 }
974 
GetArrowOffset(const Placement & placement)975 double RenderBubble::GetArrowOffset(const Placement& placement)
976 {
977     double motionRange = 0.0;
978     Edge edge;
979     InitEdgeSize(edge);
980     switch (placement) {
981         case Placement::TOP_LEFT:
982         case Placement::TOP_RIGHT:
983             motionRange = childSize_.Width() - edge.Top().Value() - NormalizeToPx(ARROW_WIDTH);
984             break;
985         case Placement::TOP:
986             motionRange = childSize_.Width() - edge.Top().Value() - NormalizeToPx(ARROW_WIDTH);
987             break;
988         case Placement::BOTTOM:
989             motionRange = childSize_.Width() - edge.Bottom().Value() - NormalizeToPx(ARROW_WIDTH);
990             break;
991         case Placement::LEFT:
992         case Placement::LEFT_TOP:
993         case Placement::LEFT_BOTTOM:
994             motionRange = childSize_.Height() - edge.Left().Value() - NormalizeToPx(ARROW_WIDTH);
995             break;
996         case Placement::RIGHT:
997         case Placement::RIGHT_TOP:
998         case Placement::RIGHT_BOTTOM:
999             motionRange = childSize_.Height() - edge.Right().Value() - NormalizeToPx(ARROW_WIDTH);
1000             break;
1001         case Placement::BOTTOM_LEFT:
1002         case Placement::BOTTOM_RIGHT:
1003             motionRange = childSize_.Width() - edge.Bottom().Value() - NormalizeToPx(ARROW_WIDTH);
1004             break;
1005         default:
1006             break;
1007     }
1008     return std::clamp(arrowOffset_.Unit() == DimensionUnit::PERCENT ? arrowOffset_.Value() * motionRange
1009                                                                     : NormalizeToPx(arrowOffset_),
1010         0.0, motionRange);
1011 }
1012 
OnPaintFinish()1013 void RenderBubble::OnPaintFinish()
1014 {
1015     if (isShowInSubWindow_) {
1016         if (bubbleComponent_->GetPopupParam()->HasAction()) {
1017             std::vector<Rect> rects;
1018             rects.emplace_back(GetRectBasedWindowTopLeft());
1019             SubwindowManager::GetInstance()->SetHotAreas(rects);
1020             return;
1021         }
1022         std::vector<Rect> rects;
1023         rects.emplace_back(Rect(childOffset_, childSize_));
1024         SubwindowManager::GetInstance()->SetHotAreas(rects);
1025     }
1026 }
1027 
1028 } // namespace OHOS::Ace
1029