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