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