• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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_ng/pattern/bubble/bubble_layout_algorithm.h"
17 
18 #include <algorithm>
19 
20 #include "base/geometry/dimension.h"
21 #include "base/geometry/ng/offset_t.h"
22 #include "base/geometry/ng/point_t.h"
23 #include "base/geometry/ng/size_t.h"
24 #include "base/memory/ace_type.h"
25 #include "base/subwindow/subwindow_manager.h"
26 #include "base/utils/device_config.h"
27 #include "base/utils/system_properties.h"
28 #include "base/utils/utils.h"
29 #include "core/common/ace_engine.h"
30 #include "core/common/container.h"
31 #include "core/components/common/properties/placement.h"
32 #include "core/components/popup/popup_theme.h"
33 #include "core/components_ng/base/frame_node.h"
34 #include "core/components_ng/pattern/bubble/bubble_layout_property.h"
35 #include "core/components_ng/pattern/bubble/bubble_pattern.h"
36 #include "core/pipeline/pipeline_base.h"
37 #include "core/pipeline_ng/pipeline_context.h"
38 
39 namespace OHOS::Ace::NG {
40 namespace {
41 constexpr Dimension ARROW_WIDTH = 32.0_vp;
42 constexpr Dimension ARROW_HEIGHT = 8.0_vp;
43 constexpr Dimension HORIZON_SPACING_WITH_SCREEN = 6.0_vp;
44 constexpr Dimension BEZIER_WIDTH_HALF = 16.0_vp;
45 
46 // get main window's pipeline
GetMainPipelineContext()47 RefPtr<PipelineContext> GetMainPipelineContext()
48 {
49     auto containerId = Container::CurrentId();
50     RefPtr<PipelineContext> context;
51     if (containerId >= MIN_SUBCONTAINER_ID) {
52         auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(containerId);
53         auto parentContainer = AceEngine::Get().GetContainer(parentContainerId);
54         CHECK_NULL_RETURN(parentContainer, nullptr);
55         context = AceType::DynamicCast<PipelineContext>(parentContainer->GetPipelineContext());
56     } else {
57         context = PipelineContext::GetCurrentContext();
58     }
59     return context;
60 }
61 } // namespace
62 
Measure(LayoutWrapper * layoutWrapper)63 void BubbleLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
64 {
65     CHECK_NULL_VOID(layoutWrapper);
66     auto bubbleProp = DynamicCast<BubbleLayoutProperty>(layoutWrapper->GetLayoutProperty());
67     CHECK_NULL_VOID(bubbleProp);
68     InitProps(bubbleProp);
69     auto bubbleLayoutProperty = AceType::DynamicCast<BubbleLayoutProperty>(layoutWrapper->GetLayoutProperty());
70     CHECK_NULL_VOID(bubbleLayoutProperty);
71 
72     const auto& layoutConstraint = bubbleLayoutProperty->GetLayoutConstraint();
73     if (!layoutConstraint) {
74         LOGE("fail to measure bubble due to layoutConstraint is nullptr");
75         return;
76     }
77     bool useCustom = bubbleLayoutProperty->GetUseCustom().value_or(false);
78     // bubble size fit screen.
79     layoutWrapper->GetGeometryNode()->SetFrameSize(layoutConstraint->maxSize);
80     layoutWrapper->GetGeometryNode()->SetContentSize(layoutConstraint->maxSize);
81 
82     // update child layout constraint
83     LayoutConstraintF childLayoutConstraint = bubbleLayoutProperty->CreateChildConstraint();
84     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
85     if (children.empty()) {
86         return;
87     }
88     auto child = children.front();
89     // childSize_ and childOffset_ is used in Layout.
90     child->Measure(childLayoutConstraint);
91     bool showInSubWindow = bubbleLayoutProperty->GetShowInSubWindowValue(false);
92     if (useCustom && !showInSubWindow) {
93         auto context = PipelineBase::GetCurrentContext();
94         CHECK_NULL_VOID(context);
95         float rootH = context->GetRootHeight();
96         float rootW = context->GetRootWidth();
97         auto childHeight = child->GetGeometryNode()->GetMarginFrameSize().Height();
98         auto childWidth = child->GetGeometryNode()->GetMarginFrameSize().Width();
99         auto scaledBubbleSpacing = scaledBubbleSpacing_ * 2;
100         auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
101         CHECK_NULL_VOID(targetNode);
102         auto geometryNode = targetNode->GetGeometryNode();
103         CHECK_NULL_VOID(geometryNode);
104         auto targetSize = geometryNode->GetFrameSize();
105         auto targetOffset = targetNode->GetPaintRectOffset();
106         auto constrainHeight = layoutWrapper->GetGeometryNode()->GetFrameSize().Height();
107         auto constrainWidth = layoutWrapper->GetGeometryNode()->GetFrameSize().Width();
108         auto placement = bubbleLayoutProperty->GetPlacement().value_or(Placement::BOTTOM);
109         std::unordered_set<Placement> setHorizontal = { Placement::LEFT, Placement::LEFT_BOTTOM, Placement::LEFT_TOP,
110             Placement::RIGHT, Placement::RIGHT_BOTTOM, Placement::RIGHT_TOP };
111         std::unordered_set<Placement> setVertical = { Placement::TOP, Placement::TOP_LEFT, Placement::TOP_RIGHT,
112             Placement::BOTTOM, Placement::BOTTOM_LEFT, Placement::BOTTOM_RIGHT };
113         if (setHorizontal.find(placement) != setHorizontal.end()) {
114             if (childWidth + targetOffset.GetX() + targetSize.Width() + scaledBubbleSpacing <= rootW &&
115                 targetOffset.GetX() - childWidth - scaledBubbleSpacing >= 0) {
116                 return;
117             }
118             constrainWidth = rootW - scaledBubbleSpacing;
119         }
120         if (setVertical.find(placement) != setVertical.end()) {
121             if (childHeight + targetOffset.GetY() + targetSize.Height() + scaledBubbleSpacing <= rootH &&
122                 targetOffset.GetY() - childHeight - scaledBubbleSpacing >= 0) {
123                 return;
124             }
125             constrainHeight = std::max(rootH - targetOffset.GetY() - targetSize.Height() - scaledBubbleSpacing,
126                 targetOffset.GetY() - scaledBubbleSpacing);
127         }
128         SizeF size = SizeF(constrainWidth, constrainHeight);
129         childLayoutConstraint.UpdateMaxSizeWithCheck(size);
130         child->Measure(childLayoutConstraint);
131     }
132 }
133 
Layout(LayoutWrapper * layoutWrapper)134 void BubbleLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
135 {
136     CHECK_NULL_VOID(layoutWrapper);
137     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
138     if (children.empty()) {
139         return;
140     }
141     auto child = children.front();
142     child->Layout();
143 }
144 
145 /*
146     Because of bubble's position depends on targetNode,
147     The position of the bubble needs to be calculated after the targetNode layout is complete.
148 */
GetChildOffsetAfterLayout(const RefPtr<LayoutWrapper> & layoutWrapper)149 OffsetT<Dimension> BubbleLayoutAlgorithm::GetChildOffsetAfterLayout(const RefPtr<LayoutWrapper>& layoutWrapper)
150 {
151     CHECK_NULL_RETURN(layoutWrapper, OffsetT<Dimension> {});
152     auto bubbleProp = DynamicCast<BubbleLayoutProperty>(layoutWrapper->GetLayoutProperty());
153     CHECK_NULL_RETURN(bubbleProp, OffsetT<Dimension> {});
154     auto frameNode = layoutWrapper->GetHostNode();
155     CHECK_NULL_RETURN(frameNode, OffsetT<Dimension> {});
156     auto bubblePattern = frameNode->GetPattern<BubblePattern>();
157     CHECK_NULL_RETURN(bubblePattern, OffsetT<Dimension> {});
158     auto bubblePaintProperty = frameNode->GetPaintProperty<BubbleRenderProperty>();
159     CHECK_NULL_RETURN(bubblePaintProperty, OffsetT<Dimension> {});
160     bool UseArrowOffset = bubblePaintProperty->GetArrowOffset().has_value();
161     auto ShowInSubWindow = bubbleProp->GetShowInSubWindow().value_or(false);
162     if (!bubblePattern->IsExiting()) {
163         InitTargetSizeAndPosition(ShowInSubWindow);
164         // subtract the global offset of the overlay node,
165         // because the final node position is set relative to the overlay node.
166         auto overlayGlobalOffset = frameNode->GetPaintRectOffset();
167         targetOffset_ -= overlayGlobalOffset;
168     }
169     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
170     auto childWrapper = children.front();
171     if (children.empty()) {
172         return OffsetT<Dimension> {};
173     }
174     selfSize_ = layoutWrapper->GetGeometryNode()->GetFrameSize();       // window's size
175     childSize_ = childWrapper->GetGeometryNode()->GetMarginFrameSize(); // bubble's size
176     childOffset_ = GetChildPosition(childSize_, bubbleProp, UseArrowOffset);            // bubble's offset
177     UpdateChildPosition(bubbleProp);
178     UpdateTouchRegion();
179 
180     // If bubble displayed in subwindow, set the hotarea of subwindow.
181     if (bubbleProp->GetShowInSubWindowValue(false) && (!bubblePattern->IsSkipHotArea())) {
182         std::vector<Rect> rects;
183         if (!bubbleProp->GetBlockEventValue(true)) {
184             auto rect = Rect(childOffset_.GetX(), childOffset_.GetY(), childSize_.Width(), childSize_.Height());
185             rects.emplace_back(rect);
186         } else {
187             auto parentWindowRect = SubwindowManager::GetInstance()->GetParentWindowRect();
188             auto rect = Rect(childOffset_.GetX(), childOffset_.GetY(), childSize_.Width(), childSize_.Height());
189             rects.emplace_back(parentWindowRect);
190             rects.emplace_back(rect);
191         }
192         SubwindowManager::GetInstance()->SetHotAreas(rects, frameNode->GetId(), bubblePattern->GetContainerId());
193     }
194     bubblePattern->SetSkipHotArea(false);
195 
196     Dimension childOffsetX(childOffset_.GetX());
197     Dimension childOffsetY(childOffset_.GetY());
198     OffsetT<Dimension> childRenderOffset(childOffsetX, childOffsetY);
199     return childRenderOffset;
200 }
201 
InitProps(const RefPtr<BubbleLayoutProperty> & layoutProp)202 void BubbleLayoutAlgorithm::InitProps(const RefPtr<BubbleLayoutProperty>& layoutProp)
203 {
204     auto pipeline = PipelineBase::GetCurrentContext();
205     CHECK_NULL_VOID(pipeline);
206     auto popupTheme = pipeline->GetTheme<PopupTheme>();
207     CHECK_NULL_VOID(popupTheme);
208     padding_ = popupTheme->GetPadding();
209     borderRadius_ = popupTheme->GetRadius().GetX();
210     border_.SetBorderRadius(popupTheme->GetRadius());
211     targetSpace_ = layoutProp->GetTargetSpace().value_or(popupTheme->GetTargetSpace());
212     placement_ = layoutProp->GetPlacement().value_or(Placement::BOTTOM);
213     scaledBubbleSpacing_ = static_cast<float>(popupTheme->GetBubbleSpacing().ConvertToPx());
214     arrowHeight_ = static_cast<float>(popupTheme->GetArrowHeight().ConvertToPx());
215     positionOffset_ = layoutProp->GetPositionOffset().value_or(OffsetF());
216 }
217 
GetChildPosition(const SizeF & childSize,const RefPtr<BubbleLayoutProperty> & layoutProp,bool UseArrowOffset)218 OffsetF BubbleLayoutAlgorithm::GetChildPosition(
219     const SizeF& childSize, const RefPtr<BubbleLayoutProperty>& layoutProp, bool UseArrowOffset)
220 {
221     OffsetF bottomPosition;
222     OffsetF topPosition;
223     OffsetF topArrowPosition;
224     OffsetF bottomArrowPosition;
225     OffsetF fitPosition;
226     OffsetF originOffset;
227     OffsetF originArrowOffset;
228     OffsetF childPosition;
229 
230     InitArrowTopAndBottomPosition(topArrowPosition, bottomArrowPosition, topPosition, bottomPosition, childSize);
231     GetPositionWithPlacement(originOffset, originArrowOffset, childSize, placement_);
232     originOffset = originOffset + positionOffset_;
233     originArrowOffset += positionOffset_;
234     arrowPlacement_ = placement_;
235 
236     // Fit popup to screen range.
237     ErrorPositionType errorType = GetErrorPositionType(originOffset, childSize);
238     if (errorType == ErrorPositionType::NORMAL) {
239         arrowPosition_ = originArrowOffset;
240         return originOffset;
241     }
242 
243     if (placement_ == Placement::TOP || placement_ == Placement::TOP_LEFT || placement_ == Placement::TOP_RIGHT) {
244         fitPosition = topPosition;
245         arrowPosition_ = topArrowPosition;
246         arrowPlacement_ = Placement::TOP;
247     } else {
248         fitPosition = bottomPosition;
249         arrowPosition_ = bottomArrowPosition;
250         arrowPlacement_ = Placement::BOTTOM;
251     }
252 
253     childPosition = FitToScreen(fitPosition, childSize);
254     if (UseArrowOffset) {
255         arrowPosition_.SetX(
256             childPosition.GetX() + border_.TopLeftRadius().GetX().ConvertToPx() + BEZIER_WIDTH_HALF.ConvertToPx());
257     }
258 
259     if (GetErrorPositionType(childPosition, childSize) == ErrorPositionType::NORMAL) {
260         return childPosition;
261     }
262 
263     // Fit popup to opposite position
264     fitPosition = fitPosition == topPosition ? bottomPosition : topPosition;
265     arrowPosition_ = arrowPosition_ == topArrowPosition ? bottomArrowPosition : topArrowPosition;
266     arrowPlacement_ = arrowPlacement_ == Placement::TOP ? Placement::BOTTOM : Placement::TOP;
267 
268     childPosition = FitToScreen(fitPosition, childSize);
269     if (UseArrowOffset) {
270         arrowPosition_.SetX(
271             childPosition.GetX() + border_.TopLeftRadius().GetX().ConvertToPx() + BEZIER_WIDTH_HALF.ConvertToPx());
272         arrowPosition_.SetY(childPosition.GetY() - arrowHeight_);
273     }
274 
275     if (GetErrorPositionType(childPosition, childSize) == ErrorPositionType::NORMAL) {
276         return childPosition;
277     }
278 
279     // If childPosition is error, adjust bubble to origin position.
280     arrowPlacement_ = placement_;
281     arrowPosition_ = originArrowOffset;
282 
283     return originOffset;
284 }
285 
InitArrowTopAndBottomPosition(OffsetF & topArrowPosition,OffsetF & bottomArrowPosition,OffsetF & topPosition,OffsetF & bottomPosition,const SizeF & childSize)286 void BubbleLayoutAlgorithm::InitArrowTopAndBottomPosition(OffsetF& topArrowPosition, OffsetF& bottomArrowPosition,
287     OffsetF& topPosition, OffsetF& bottomPosition, const SizeF& childSize)
288 {
289     auto arrowCenter = targetOffset_.GetX() + targetSize_.Width() / 2.0;
290     auto horizonSpacing = static_cast<float>(HORIZON_SPACING_WITH_SCREEN.ConvertToPx());
291     double arrowWidth = ARROW_WIDTH.ConvertToPx();
292     float radius = borderRadius_.ConvertToPx();
293     auto safePosition = horizonSpacing + radius + arrowWidth / 2.0;
294 
295     GetPositionWithPlacement(topPosition, topArrowPosition, childSize, Placement::TOP);
296     GetPositionWithPlacement(bottomPosition, bottomArrowPosition, childSize, Placement::BOTTOM);
297 
298     // move the arrow to safe position while arrow too close to window
299     // In order not to separate the bubble from the arrow
300     // If ArrowOffset is not set, arrow always point to the middle of the targetNode
301     if (arrowCenter < safePosition) {
302         topArrowPosition = topArrowPosition + OffsetF(safePosition - arrowCenter, 0);
303         bottomArrowPosition = bottomArrowPosition + OffsetF(safePosition - arrowCenter, 0);
304     }
305     if (arrowCenter > selfSize_.Width() - safePosition) {
306         topArrowPosition = topArrowPosition - OffsetF(arrowCenter + safePosition - selfSize_.Width(), 0);
307         bottomArrowPosition = bottomArrowPosition - OffsetF(arrowCenter + safePosition - selfSize_.Width(), 0);
308     }
309 }
310 
GetPositionWithPlacement(OffsetF & childPosition,OffsetF & arrowPosition,const SizeF & childSize,Placement placement)311 void BubbleLayoutAlgorithm::GetPositionWithPlacement(
312     OffsetF& childPosition, OffsetF& arrowPosition, const SizeF& childSize, Placement placement)
313 {
314     float bubbleSpacing = scaledBubbleSpacing_;
315     float marginRight = 0.0f;
316     float marginBottom = 0.0f;
317     float marginTop = 0.0f;
318     float marginLeft = 0.0f;
319     float arrowHalfWidth = ARROW_WIDTH.ConvertToPx() / 2.0;
320     float radius = borderRadius_.ConvertToPx();
321     float targetSpace = targetSpace_.ConvertToPx();
322     switch (placement) {
323         case Placement::TOP:
324             childPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
325                 targetOffset_.GetY() - childSize.Height() - targetSpace - arrowHeight_);
326             arrowPosition = childPosition + OffsetF(std::max(padding_.Left().ConvertToPx(),
327                 border_.TopLeftRadius().GetX().ConvertToPx()) + BEZIER_WIDTH_HALF.ConvertToPx(),
328                     childSize.Height() + arrowHeight_);
329             break;
330         case Placement::TOP_LEFT:
331             childPosition = OffsetF(targetOffset_.GetX() - marginRight,
332                 targetOffset_.GetY() - childSize.Height() - bubbleSpacing - marginBottom - targetSpace);
333             arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, childSize.Height() + bubbleSpacing);
334             break;
335         case Placement::TOP_RIGHT:
336             childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
337                 targetOffset_.GetY() - childSize.Height() - targetSpace - bubbleSpacing - marginBottom);
338             arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, childSize.Height() + bubbleSpacing);
339             break;
340         case Placement::BOTTOM:
341             childPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
342                 targetOffset_.GetY() + targetSize_.Height() + targetSpace + arrowHeight_);
343             arrowPosition = childPosition + OffsetF(std::max(padding_.Left().ConvertToPx(),
344                 border_.BottomLeftRadius().GetX().ConvertToPx()) + BEZIER_WIDTH_HALF.ConvertToPx(), -arrowHeight_);
345             break;
346         case Placement::BOTTOM_LEFT:
347             childPosition = OffsetF(targetOffset_.GetX() - marginRight,
348                 targetOffset_.GetY() + targetSize_.Height() + targetSpace + bubbleSpacing + marginTop);
349             arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, -bubbleSpacing);
350             break;
351         case Placement::BOTTOM_RIGHT:
352             childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
353                 targetOffset_.GetY() + targetSize_.Height() + targetSpace + bubbleSpacing + marginTop);
354             arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, -bubbleSpacing);
355             break;
356         case Placement::LEFT:
357             childPosition =
358                 OffsetF(targetOffset_.GetX() - targetSpace - bubbleSpacing - childSize.Width() - marginRight,
359                     targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
360             arrowPosition = childPosition + OffsetF(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
361             break;
362         case Placement::LEFT_TOP:
363             childPosition =
364                 OffsetF(targetOffset_.GetX() - targetSpace - bubbleSpacing - childSize.Width() - marginRight,
365                     targetOffset_.GetY() - marginBottom);
366             arrowPosition = childPosition + OffsetF(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
367             break;
368         case Placement::LEFT_BOTTOM:
369             childPosition =
370                 OffsetF(targetOffset_.GetX() - targetSpace - bubbleSpacing - childSize.Width() - marginRight,
371                     targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
372             arrowPosition = childPosition + OffsetF(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
373             break;
374         case Placement::RIGHT:
375             childPosition =
376                 OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace + bubbleSpacing + marginLeft,
377                     targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
378             arrowPosition = childPosition + OffsetF(-bubbleSpacing, radius + arrowHalfWidth);
379             break;
380         case Placement::RIGHT_TOP:
381             childPosition =
382                 OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace + bubbleSpacing + marginLeft,
383                     targetOffset_.GetY() - marginBottom);
384             arrowPosition = childPosition + OffsetF(-bubbleSpacing, radius + arrowHalfWidth);
385             break;
386         case Placement::RIGHT_BOTTOM:
387             childPosition =
388                 OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace + bubbleSpacing + marginLeft,
389                     targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
390             arrowPosition = childPosition + OffsetF(-bubbleSpacing, radius + arrowHalfWidth);
391             break;
392         default:
393             break;
394     }
395 }
396 
GetErrorPositionType(const OffsetF & childOffset,const SizeF & childSize)397 BubbleLayoutAlgorithm::ErrorPositionType BubbleLayoutAlgorithm::GetErrorPositionType(
398     const OffsetF& childOffset, const SizeF& childSize)
399 {
400     auto horizonSpacing = static_cast<float>(HORIZON_SPACING_WITH_SCREEN.ConvertToPx());
401     RectF validRegion =
402         RectF(OffsetF(horizonSpacing, 0.0), OffsetF(selfSize_.Width() - horizonSpacing, selfSize_.Height()));
403     PointF childPoint(childOffset.GetX(), childOffset.GetY());
404     if (!validRegion.IsInRegion(childPoint)) {
405         return ErrorPositionType::TOP_LEFT_ERROR;
406     }
407     if (!validRegion.IsInRegion(
408             PointF(childOffset.GetX() + childSize.Width(), childOffset.GetY() + childSize.Height()))) {
409         return ErrorPositionType::BOTTOM_RIGHT_ERROR;
410     }
411     return ErrorPositionType::NORMAL;
412 }
413 
FitToScreen(const OffsetF & fitPosition,const SizeF & childSize)414 OffsetF BubbleLayoutAlgorithm::FitToScreen(const OffsetF& fitPosition, const SizeF& childSize)
415 {
416     auto validation = GetErrorPositionType(fitPosition, childSize);
417     if (validation == ErrorPositionType::NORMAL) {
418         return fitPosition;
419     }
420     OffsetF childPosition = fitPosition;
421     auto horizonSpacing = static_cast<float>(HORIZON_SPACING_WITH_SCREEN.ConvertToPx());
422     if (validation == ErrorPositionType::TOP_LEFT_ERROR) {
423         childPosition.SetX(horizonSpacing);
424     } else {
425         childPosition.SetX(selfSize_.Width() - childSize.Width() - horizonSpacing);
426     }
427     return childPosition;
428 }
429 
UpdateChildPosition(const RefPtr<BubbleLayoutProperty> & layoutProp)430 void BubbleLayoutAlgorithm::UpdateChildPosition(const RefPtr<BubbleLayoutProperty>& layoutProp)
431 {
432     auto enableArrow = layoutProp->GetEnableArrow().value_or(true);
433     double arrowWidth = ARROW_WIDTH.ConvertToPx();
434     double twoRadiusPx = borderRadius_.ConvertToPx() * 2.0;
435     switch (arrowPlacement_) {
436         case Placement::TOP:
437         case Placement::TOP_LEFT:
438         case Placement::TOP_RIGHT:
439             showArrow_ = GreatOrEqual(childSize_.Width() - twoRadiusPx, arrowWidth);
440             if (!showArrow_ || !enableArrow) {
441                 childOffset_ += OffsetF(0.0, ARROW_HEIGHT.ConvertToPx());
442             }
443             break;
444         case Placement::BOTTOM:
445         case Placement::BOTTOM_LEFT:
446         case Placement::BOTTOM_RIGHT:
447             showArrow_ = GreatOrEqual(childSize_.Width() - twoRadiusPx, arrowWidth);
448             if (!showArrow_ || !enableArrow) {
449                 childOffset_ += OffsetF(0.0, -ARROW_HEIGHT.ConvertToPx());
450             }
451             break;
452         case Placement::LEFT:
453         case Placement::LEFT_TOP:
454         case Placement::LEFT_BOTTOM:
455             showArrow_ = GreatOrEqual(childSize_.Height() - twoRadiusPx, arrowWidth);
456             if (!showArrow_ || !enableArrow) {
457                 childOffset_ += OffsetF(ARROW_HEIGHT.ConvertToPx(), 0.0);
458             }
459             break;
460         case Placement::RIGHT:
461         case Placement::RIGHT_TOP:
462         case Placement::RIGHT_BOTTOM:
463             showArrow_ = GreatOrEqual(childSize_.Height() - twoRadiusPx, arrowWidth);
464             if (!showArrow_ || !enableArrow) {
465                 childOffset_ += OffsetF(-ARROW_HEIGHT.ConvertToPx(), 0.0);
466             }
467             break;
468         default:
469             break;
470     }
471 }
472 
UpdateTouchRegion()473 void BubbleLayoutAlgorithm::UpdateTouchRegion()
474 {
475     OffsetF topLeft;
476     OffsetF bottomRight;
477     switch (arrowPlacement_) {
478         case Placement::TOP:
479         case Placement::TOP_LEFT:
480         case Placement::TOP_RIGHT:
481             topLeft = childOffset_;
482             bottomRight = OffsetF(childSize_.Width(), targetSpace_.ConvertToPx() + childSize_.Height());
483             if (showArrow_) {
484                 bottomRight += OffsetF(0.0, ARROW_HEIGHT.ConvertToPx());
485             }
486             break;
487         case Placement::BOTTOM:
488         case Placement::BOTTOM_LEFT:
489         case Placement::BOTTOM_RIGHT:
490             topLeft = childOffset_ + OffsetF(0.0, -targetSpace_.ConvertToPx());
491             bottomRight = OffsetF(childSize_.Width(), targetSpace_.ConvertToPx() + childSize_.Height());
492             if (showArrow_) {
493                 topLeft += OffsetF(0.0, -ARROW_HEIGHT.ConvertToPx());
494                 bottomRight += OffsetF(0.0, ARROW_HEIGHT.ConvertToPx());
495             }
496             break;
497         case Placement::LEFT:
498         case Placement::LEFT_TOP:
499         case Placement::LEFT_BOTTOM:
500             topLeft = childOffset_;
501             bottomRight = OffsetF(targetSpace_.ConvertToPx() + childSize_.Width(), childSize_.Height());
502             if (showArrow_) {
503                 bottomRight += OffsetF(ARROW_HEIGHT.ConvertToPx(), 0.0);
504             }
505             break;
506         case Placement::RIGHT:
507         case Placement::RIGHT_TOP:
508         case Placement::RIGHT_BOTTOM:
509             topLeft = childOffset_ + OffsetF(-targetSpace_.ConvertToPx(), 0.0);
510             bottomRight = OffsetF(targetSpace_.ConvertToPx() + childSize_.Width(), childSize_.Height());
511             if (showArrow_) {
512                 topLeft += OffsetF(-ARROW_HEIGHT.ConvertToPx(), 0.0);
513                 bottomRight += OffsetF(ARROW_HEIGHT.ConvertToPx(), 0.0);
514             }
515             break;
516         default:
517             break;
518     }
519     touchRegion_ = RectF(topLeft, topLeft + bottomRight);
520 }
521 
InitTargetSizeAndPosition(bool showInSubWindow)522 void BubbleLayoutAlgorithm::InitTargetSizeAndPosition(bool showInSubWindow)
523 {
524     auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
525     CHECK_NULL_VOID(targetNode);
526     if (!targetNode->IsOnMainTree() || !targetNode->IsVisible()) {
527         return;
528     }
529     auto geometryNode = targetNode->GetGeometryNode();
530     CHECK_NULL_VOID(geometryNode);
531     targetSize_ = geometryNode->GetFrameSize();
532     auto pipelineContext = GetMainPipelineContext();
533     CHECK_NULL_VOID(pipelineContext);
534     targetOffset_ = targetNode->GetPaintRectOffset();
535     // Show in SubWindow
536     if (showInSubWindow) {
537         auto displayWindowOffset = OffsetF(pipelineContext->GetDisplayWindowRectInfo().GetOffset().GetX(),
538             pipelineContext->GetDisplayWindowRectInfo().GetOffset().GetY());
539         targetOffset_ += displayWindowOffset;
540         auto currentSubwindow = SubwindowManager::GetInstance()->GetCurrentWindow();
541         if (currentSubwindow) {
542             auto subwindowRect = currentSubwindow->GetRect();
543             targetOffset_ -= subwindowRect.GetOffset();
544         }
545     }
546 }
547 } // namespace OHOS::Ace::NG
548