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