• 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/layout/grid_system_manager.h"
32 #include "core/components/common/properties/placement.h"
33 #include "core/components/popup/popup_theme.h"
34 #include "core/components_ng/base/frame_node.h"
35 #include "core/components_ng/pattern/bubble/bubble_layout_property.h"
36 #include "core/components_ng/pattern/bubble/bubble_pattern.h"
37 #include "core/components_ng/pattern/text/text_pattern.h"
38 #include "core/pipeline/pipeline_base.h"
39 #include "core/pipeline_ng/pipeline_context.h"
40 
41 namespace OHOS::Ace::NG {
42 namespace {
43 constexpr double HALF = 2.0;
44 constexpr double DOUBLE = 2.0;
45 constexpr Dimension ARROW_RADIUS = 2.0_vp;
46 constexpr Dimension DRAW_EDGES_SPACE = 1.0_vp;
47 constexpr double BUBBLE_ARROW_HALF = 2.0;
48 constexpr size_t ALIGNMENT_STEP_OFFSET = 2;
49 
50 // help value to calculate p2 p4 position
51 constexpr Dimension DEFAULT_BUBBLE_ARROW_WIDTH = 16.0_vp;
52 constexpr Dimension DEFAULT_BUBBLE_ARROW_HEIGHT = 8.0_vp;
53 constexpr Dimension DEFAULT_P3_HEIGHT = 9.0_vp;
54 constexpr Dimension DEFAULT_P3_END_HEIGHT = 9.5_vp;
55 Dimension DEFAULT_P2_HEIGHT = 7.32_vp;
56 Dimension DEFAULT_P4_END_Y = 3.5_vp;
57 Dimension DEFAULT_P2_END_X = 3.2_vp;
58 
59 Dimension BUBBLE_ARROW_WIDTH = 16.0_vp;
60 Dimension BUBBLE_ARROW_HEIGHT = 8.0_vp;
61 constexpr double ARROW_OFFSET_START_VALUE = 0.0;
62 constexpr double ARROW_OFFSET_CENTER_VALUE = 0.5;
63 constexpr Dimension HORIZON_SPACING_WITH_SCREEN = 8.0_vp;
64 constexpr Dimension BEZIER_WIDTH_HALF = 8.0_vp;
65 
66 Dimension ARROW_VERTICAL_P1_OFFSET_X = 8.0_vp;
67 Dimension ARROW_VERTICAL_P2_OFFSET_X = 1.5_vp;
68 Dimension ARROW_VERTICAL_P2_OFFSET_Y = 7.32_vp;
69 Dimension ARROW_VERTICAL_P4_OFFSET_X = 1.5_vp;
70 Dimension ARROW_VERTICAL_P4_OFFSET_Y = 7.32_vp;
71 Dimension ARROW_VERTICAL_P5_OFFSET_X = 8.0_vp;
72 
73 Dimension ARROW_HORIZON_P1_OFFSET_Y = 8.0_vp;
74 Dimension ARROW_HORIZON_P2_OFFSET_Y = 1.5_vp;
75 Dimension ARROW_HORIZON_P2_OFFSET_X = 7.32_vp;
76 Dimension ARROW_HORIZON_P4_OFFSET_Y = 1.5_vp;
77 Dimension ARROW_HORIZON_P4_OFFSET_X = 7.32_vp;
78 Dimension ARROW_HORIZON_P5_OFFSET_Y = 8.0_vp;
79 
80 Dimension ARROW_REPLACE_START_VERTICAL_P1_OFFSET_X = 8.0_vp;
81 Dimension ARROW_REPLACE_START_VERTICAL_P2_OFFSET_X = 8.0_vp;
82 Dimension ARROW_REPLACE_START_VERTICAL_P2_OFFSET_Y = 6.0_vp;
83 Dimension ARROW_REPLACE_START_VERTICAL_P4_OFFSET_X = 4.8_vp;
84 Dimension ARROW_REPLACE_START_VERTICAL_P4_OFFSET_Y = 7.6_vp;
85 Dimension ARROW_REPLACE_START_VERTICAL_P5_OFFSET_X = 8.0_vp;
86 
87 Dimension ARROW_REPLACE_END_VERTICAL_P1_OFFSET_X = 8.0_vp;
88 Dimension ARROW_REPLACE_END_VERTICAL_P2_OFFSET_X = 4.8_vp;
89 Dimension ARROW_REPLACE_END_VERTICAL_P2_OFFSET_Y = 7.6_vp;
90 Dimension ARROW_REPLACE_END_VERTICAL_P4_OFFSET_X = 8.0_vp;
91 Dimension ARROW_REPLACE_END_VERTICAL_P4_OFFSET_Y = 6.0_vp;
92 Dimension ARROW_REPLACE_END_VERTICAL_P5_OFFSET_X = 8.0_vp;
93 
94 Dimension ARROW_REPLACE_START_HORIZON_P1_OFFSET_Y = 8.0_vp;
95 Dimension ARROW_REPLACE_START_HORIZON_P2_OFFSET_X = 6.0_vp;
96 Dimension ARROW_REPLACE_START_HORIZON_P2_OFFSET_Y = 8.0_vp;
97 Dimension ARROW_REPLACE_START_HORIZON_P4_OFFSET_X = 7.6_vp;
98 Dimension ARROW_REPLACE_START_HORIZON_P4_OFFSET_Y = 4.8_vp;
99 Dimension ARROW_REPLACE_START_HORIZON_P5_OFFSET_Y = 8.0_vp;
100 
101 Dimension ARROW_REPLACE_END_HORIZON_P1_OFFSET_Y = 8.0_vp;
102 Dimension ARROW_REPLACE_END_HORIZON_P2_OFFSET_X = 7.6_vp;
103 Dimension ARROW_REPLACE_END_HORIZON_P2_OFFSET_Y = 4.8_vp;
104 Dimension ARROW_REPLACE_END_HORIZON_P4_OFFSET_X = 6.0_vp;
105 Dimension ARROW_REPLACE_END_HORIZON_P4_OFFSET_Y = 8.0_vp;
106 Dimension ARROW_REPLACE_END_HORIZON_P5_OFFSET_Y = 8.0_vp;
107 
108 constexpr Dimension BUBBLE_ARROW_ZERO_PERCENT_VALUE = 0.0_pct;
109 constexpr Dimension BUBBLE_ARROW_HALF_PERCENT_VALUE = 0.5_pct;
110 constexpr Dimension BUBBLE_ARROW_ONE_HUNDRED_PERCENT_VALUE = 1.0_pct;
111 constexpr Dimension OUT_RANGE_SPACE = 40.0_vp;
112 
113 constexpr int16_t ARROW_OFFSETS_INDEX_ZERO = 0;
114 constexpr int16_t ARROW_OFFSETS_INDEX_ONE = 1;
115 constexpr int16_t ARROW_OFFSETS_INDEX_TWO = 2;
116 constexpr int16_t ARROW_OFFSETS_INDEX_THREE = 3;
117 
GetEndP2P4(const Dimension & radius)118 void GetEndP2P4(const Dimension& radius)
119 {
120     auto P3Y = DEFAULT_P3_END_HEIGHT * BUBBLE_ARROW_HEIGHT.ConvertToPx() / DEFAULT_BUBBLE_ARROW_HEIGHT.ConvertToPx();
121     float tanR = BUBBLE_ARROW_WIDTH.ConvertToPx() / P3Y.ConvertToPx();
122     float tanHalfR = std::tan(std::atan(tanR) / HALF);
123     if (tanHalfR > 0) {
124         DEFAULT_P4_END_Y = radius / tanHalfR;
125         DEFAULT_P2_END_X = DEFAULT_P4_END_Y * std::sin(std::atan(tanR));
126     }
127 }
128 
GetP2(const Dimension & radius)129 void GetP2(const Dimension& radius)
130 {
131     auto P3Y = DEFAULT_P3_HEIGHT * BUBBLE_ARROW_HEIGHT.ConvertToPx() / DEFAULT_BUBBLE_ARROW_HEIGHT.ConvertToPx();
132     float tanR = BUBBLE_ARROW_WIDTH.ConvertToPx() / P3Y.ConvertToPx() / HALF;
133     float cosR = std::cos(std::atan(tanR));
134     if (tanR > 0) {
135         DEFAULT_P2_HEIGHT = P3Y - radius / tanR * cosR;
136     }
137 }
138 
calculateArrowPoint(Dimension height,Dimension width)139 void calculateArrowPoint(Dimension height, Dimension width)
140 {
141     auto rateX = width.ConvertToPx() / BUBBLE_ARROW_WIDTH.ConvertToPx();
142     auto rateHeightWidth = height.ConvertToPx() / width.ConvertToPx();
143     BUBBLE_ARROW_WIDTH = width;
144     BUBBLE_ARROW_HEIGHT = height;
145 
146     GetEndP2P4(ARROW_RADIUS);
147     GetP2(ARROW_RADIUS);
148 
149     ARROW_VERTICAL_P1_OFFSET_X = ARROW_VERTICAL_P1_OFFSET_X * rateX;
150     ARROW_VERTICAL_P2_OFFSET_Y = DEFAULT_P2_HEIGHT;
151     ARROW_VERTICAL_P4_OFFSET_Y = DEFAULT_P2_HEIGHT;
152     ARROW_VERTICAL_P5_OFFSET_X = ARROW_VERTICAL_P5_OFFSET_X * rateX;
153 
154     ARROW_HORIZON_P1_OFFSET_Y = ARROW_HORIZON_P1_OFFSET_Y * rateX;
155     ARROW_HORIZON_P2_OFFSET_X = DEFAULT_P2_HEIGHT;
156     ARROW_HORIZON_P4_OFFSET_X = DEFAULT_P2_HEIGHT;
157     ARROW_HORIZON_P5_OFFSET_Y = ARROW_HORIZON_P5_OFFSET_Y * rateX;
158 
159     ARROW_REPLACE_START_VERTICAL_P1_OFFSET_X = ARROW_REPLACE_START_VERTICAL_P1_OFFSET_X * rateX;
160     ARROW_REPLACE_START_VERTICAL_P2_OFFSET_X = ARROW_REPLACE_START_VERTICAL_P2_OFFSET_X * rateX;
161     ARROW_REPLACE_START_VERTICAL_P2_OFFSET_Y = BUBBLE_ARROW_HEIGHT - DEFAULT_P4_END_Y;
162     ARROW_REPLACE_START_VERTICAL_P4_OFFSET_X = ARROW_REPLACE_START_VERTICAL_P1_OFFSET_X - DEFAULT_P2_END_X;
163     ARROW_REPLACE_START_VERTICAL_P4_OFFSET_Y = BUBBLE_ARROW_HEIGHT - DEFAULT_P2_END_X * rateHeightWidth;
164     ARROW_REPLACE_START_VERTICAL_P5_OFFSET_X = ARROW_REPLACE_START_VERTICAL_P5_OFFSET_X * rateX;
165 
166     ARROW_REPLACE_END_VERTICAL_P1_OFFSET_X = ARROW_REPLACE_END_VERTICAL_P1_OFFSET_X * rateX;
167     ARROW_REPLACE_END_VERTICAL_P2_OFFSET_X = ARROW_REPLACE_END_VERTICAL_P1_OFFSET_X - DEFAULT_P2_END_X;
168     ARROW_REPLACE_END_VERTICAL_P2_OFFSET_Y = BUBBLE_ARROW_HEIGHT - DEFAULT_P2_END_X * rateHeightWidth;
169     ARROW_REPLACE_END_VERTICAL_P4_OFFSET_X = ARROW_REPLACE_END_VERTICAL_P4_OFFSET_X * rateX;
170     ARROW_REPLACE_END_VERTICAL_P4_OFFSET_Y = BUBBLE_ARROW_HEIGHT - DEFAULT_P4_END_Y;
171     ARROW_REPLACE_END_VERTICAL_P5_OFFSET_X = ARROW_REPLACE_END_VERTICAL_P5_OFFSET_X * rateX;
172 
173     ARROW_REPLACE_START_HORIZON_P1_OFFSET_Y = ARROW_REPLACE_START_HORIZON_P1_OFFSET_Y * rateX;
174     ARROW_REPLACE_START_HORIZON_P2_OFFSET_X = BUBBLE_ARROW_HEIGHT - DEFAULT_P4_END_Y;
175     ARROW_REPLACE_START_HORIZON_P2_OFFSET_Y = ARROW_REPLACE_START_HORIZON_P2_OFFSET_Y * rateX;
176     ARROW_REPLACE_START_HORIZON_P4_OFFSET_X = BUBBLE_ARROW_HEIGHT - DEFAULT_P2_END_X * rateHeightWidth;
177     ARROW_REPLACE_START_HORIZON_P4_OFFSET_Y = ARROW_REPLACE_START_HORIZON_P1_OFFSET_Y - DEFAULT_P2_END_X;
178     ARROW_REPLACE_START_HORIZON_P5_OFFSET_Y = ARROW_REPLACE_START_HORIZON_P5_OFFSET_Y * rateX;
179 
180     ARROW_REPLACE_END_HORIZON_P1_OFFSET_Y = ARROW_REPLACE_END_HORIZON_P1_OFFSET_Y * rateX;
181     ARROW_REPLACE_END_HORIZON_P2_OFFSET_X = BUBBLE_ARROW_HEIGHT - DEFAULT_P2_END_X * rateHeightWidth;
182     ARROW_REPLACE_END_HORIZON_P2_OFFSET_Y = ARROW_REPLACE_END_HORIZON_P1_OFFSET_Y - DEFAULT_P2_END_X;
183     ARROW_REPLACE_END_HORIZON_P4_OFFSET_X = BUBBLE_ARROW_HEIGHT - DEFAULT_P4_END_Y;
184     ARROW_REPLACE_END_HORIZON_P4_OFFSET_Y = ARROW_REPLACE_END_HORIZON_P4_OFFSET_Y * rateX;
185     ARROW_REPLACE_END_HORIZON_P5_OFFSET_Y = ARROW_REPLACE_END_HORIZON_P5_OFFSET_Y * rateX;
186 }
187 
188 // get main window's pipeline
GetMainPipelineContext()189 RefPtr<PipelineContext> GetMainPipelineContext()
190 {
191     auto containerId = Container::CurrentId();
192     RefPtr<PipelineContext> context;
193     if (containerId >= MIN_SUBCONTAINER_ID) {
194         auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(containerId);
195         auto parentContainer = AceEngine::Get().GetContainer(parentContainerId);
196         CHECK_NULL_RETURN(parentContainer, nullptr);
197         context = AceType::DynamicCast<PipelineContext>(parentContainer->GetPipelineContext());
198     } else {
199         context = PipelineContext::GetCurrentContext();
200     }
201     return context;
202 }
203 } // namespace
204 
BubbleLayoutAlgorithm(int32_t id,const std::string & tag,const std::optional<OffsetF> & targetOffset,const std::optional<SizeF> & targetSize)205 BubbleLayoutAlgorithm::BubbleLayoutAlgorithm(int32_t id, const std::string& tag,
206     const std::optional<OffsetF>& targetOffset, const std::optional<SizeF>& targetSize)
207     : targetNodeId_(id), targetTag_(tag)
208 {
209     if (targetOffset.has_value()) {
210         targetOffset_ = targetOffset.value();
211     }
212     if (targetSize.has_value()) {
213         targetSize_ = targetSize.value();
214     }
215     placementFuncMap_[Placement::TOP] = &BubbleLayoutAlgorithm::GetPositionWithPlacementTop;
216     placementFuncMap_[Placement::TOP_LEFT] = &BubbleLayoutAlgorithm::GetPositionWithPlacementTopLeft;
217     placementFuncMap_[Placement::TOP_RIGHT] = &BubbleLayoutAlgorithm::GetPositionWithPlacementTopRight;
218     placementFuncMap_[Placement::BOTTOM] = &BubbleLayoutAlgorithm::GetPositionWithPlacementBottom;
219     placementFuncMap_[Placement::BOTTOM_LEFT] = &BubbleLayoutAlgorithm::GetPositionWithPlacementBottomLeft;
220     placementFuncMap_[Placement::BOTTOM_RIGHT] = &BubbleLayoutAlgorithm::GetPositionWithPlacementBottomRight;
221     placementFuncMap_[Placement::LEFT] = &BubbleLayoutAlgorithm::GetPositionWithPlacementLeft;
222     placementFuncMap_[Placement::LEFT_TOP] = &BubbleLayoutAlgorithm::GetPositionWithPlacementLeftTop;
223     placementFuncMap_[Placement::LEFT_BOTTOM] = &BubbleLayoutAlgorithm::GetPositionWithPlacementLeftBottom;
224     placementFuncMap_[Placement::RIGHT] = &BubbleLayoutAlgorithm::GetPositionWithPlacementRight;
225     placementFuncMap_[Placement::RIGHT_TOP] = &BubbleLayoutAlgorithm::GetPositionWithPlacementRightTop;
226     placementFuncMap_[Placement::RIGHT_BOTTOM] = &BubbleLayoutAlgorithm::GetPositionWithPlacementRightBottom;
227 
228     setHorizontal_ = { Placement::LEFT, Placement::LEFT_BOTTOM, Placement::LEFT_TOP, Placement::RIGHT,
229         Placement::RIGHT_BOTTOM, Placement::RIGHT_TOP };
230     setVertical_ = { Placement::TOP, Placement::TOP_LEFT, Placement::TOP_RIGHT, Placement::BOTTOM,
231         Placement::BOTTOM_LEFT, Placement::BOTTOM_RIGHT };
232 }
233 
Measure(LayoutWrapper * layoutWrapper)234 void BubbleLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
235 {
236     CHECK_NULL_VOID(layoutWrapper);
237     auto bubbleProp = DynamicCast<BubbleLayoutProperty>(layoutWrapper->GetLayoutProperty());
238     CHECK_NULL_VOID(bubbleProp);
239     auto bubbleLayoutProperty = AceType::DynamicCast<BubbleLayoutProperty>(layoutWrapper->GetLayoutProperty());
240     CHECK_NULL_VOID(bubbleLayoutProperty);
241     bool showInSubWindow = bubbleLayoutProperty->GetShowInSubWindowValue(false);
242     InitProps(bubbleProp, showInSubWindow);
243     auto bubbleNode = layoutWrapper->GetHostNode();
244     CHECK_NULL_VOID(bubbleNode);
245     const auto& layoutConstraint = bubbleLayoutProperty->GetLayoutConstraint();
246     if (!layoutConstraint) {
247         LOGE("fail to measure bubble due to layoutConstraint is nullptr");
248         return;
249     }
250     useCustom_ = bubbleLayoutProperty->GetUseCustom().value_or(false);
251     // bubble size fit screen.
252     layoutWrapper->GetGeometryNode()->SetFrameSize(layoutConstraint->maxSize);
253     layoutWrapper->GetGeometryNode()->SetContentSize(layoutConstraint->maxSize);
254     // update child layout constraint
255     LayoutConstraintF childLayoutConstraint = bubbleLayoutProperty->CreateChildConstraint();
256     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
257     if (children.empty()) {
258         return;
259     }
260     auto child = children.front();
261     CHECK_NULL_VOID(child);
262     measureChildSizeBefore_ = child->GetGeometryNode()->GetFrameSize();
263     // childSize_ and childOffset_ is used in Layout.
264     child->Measure(childLayoutConstraint);
265     measureChildSizeAfter_ = child->GetGeometryNode()->GetFrameSize();
266     if (!NearEqual(measureChildSizeBefore_, measureChildSizeAfter_)) {
267         auto childShowWidth = child->GetGeometryNode()->GetFrameSize().Width() + BUBBLE_ARROW_HEIGHT.ConvertToPx() * 2;
268         auto childShowHeight =
269             child->GetGeometryNode()->GetFrameSize().Height() + BUBBLE_ARROW_HEIGHT.ConvertToPx() * 2;
270         child->GetGeometryNode()->SetFrameSize(SizeF { childShowWidth, childShowHeight });
271         measureChildSizeLast_ = child->GetGeometryNode()->GetFrameSize();
272     } else {
273         measureChildSizeLast_ = child->GetGeometryNode()->GetFrameSize();
274     }
275     if (useCustom_ && !showInSubWindow) {
276         auto context = PipelineBase::GetCurrentContext();
277         CHECK_NULL_VOID(context);
278         float rootH = context->GetRootHeight();
279         float rootW = context->GetRootWidth();
280         auto childHeight = child->GetGeometryNode()->GetMarginFrameSize().Height();
281         auto childWidth = child->GetGeometryNode()->GetMarginFrameSize().Width();
282         auto scaledBubbleSpacing = scaledBubbleSpacing_ * 2;
283         auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
284         CHECK_NULL_VOID(targetNode);
285         auto geometryNode = targetNode->GetGeometryNode();
286         CHECK_NULL_VOID(geometryNode);
287         auto targetSize = geometryNode->GetFrameSize();
288         auto targetOffset = targetNode->GetPaintRectOffset();
289         auto constrainHeight = layoutWrapper->GetGeometryNode()->GetFrameSize().Height();
290         auto constrainWidth = layoutWrapper->GetGeometryNode()->GetFrameSize().Width();
291         float maxWidth = constrainWidth - targetSecurity_ * DOUBLE;
292         auto placement = bubbleLayoutProperty->GetPlacement().value_or(Placement::BOTTOM);
293         std::unordered_set<Placement> setHorizontal = { Placement::LEFT, Placement::LEFT_BOTTOM, Placement::LEFT_TOP,
294             Placement::RIGHT, Placement::RIGHT_BOTTOM, Placement::RIGHT_TOP };
295         std::unordered_set<Placement> setVertical = { Placement::TOP, Placement::TOP_LEFT, Placement::TOP_RIGHT,
296             Placement::BOTTOM, Placement::BOTTOM_LEFT, Placement::BOTTOM_RIGHT };
297         if (setHorizontal.find(placement) != setHorizontal.end()) {
298             if (childWidth + targetOffset.GetX() + targetSize.Width() + scaledBubbleSpacing <= rootW &&
299                 targetOffset.GetX() - childWidth - scaledBubbleSpacing >= 0) {
300                 return;
301             }
302             constrainWidth = rootW - scaledBubbleSpacing;
303         }
304         if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TWELVE)) {
305             if (setVertical.find(placement) != setVertical.end()) {
306                 if (childHeight + targetOffset.GetY() + targetSize.Height() + scaledBubbleSpacing <= rootH &&
307                     targetOffset.GetY() - childHeight - scaledBubbleSpacing >= 0) {
308                     return;
309                 }
310                 constrainHeight = std::max(rootH - targetOffset.GetY() - targetSize.Height() - scaledBubbleSpacing,
311                     targetOffset.GetY() - scaledBubbleSpacing);
312             }
313         }
314         constrainWidth = std::min(constrainWidth, maxWidth);
315         SizeF size = SizeF(constrainWidth, constrainHeight);
316         childLayoutConstraint.UpdateMaxSizeWithCheck(size);
317         child->Measure(childLayoutConstraint);
318     }
319 }
320 
GetMaxWith()321 Dimension GetMaxWith()
322 {
323     auto gridColumnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::BUBBLE_TYPE);
324     auto parent = gridColumnInfo->GetParent();
325     if (parent) {
326         parent->BuildColumnWidth();
327     }
328     auto maxWidth = Dimension(gridColumnInfo->GetMaxWidth());
329     return maxWidth;
330 }
331 
GetPopupMaxWidthAndHeight(bool showInSubWindow,const float & width)332 SizeF BubbleLayoutAlgorithm::GetPopupMaxWidthAndHeight(bool showInSubWindow, const float& width)
333 {
334     auto pipelineContext = PipelineContext::GetMainPipelineContext();
335     CHECK_NULL_RETURN(pipelineContext, SizeF());
336     auto windowGlobalRect = pipelineContext->GetDisplayWindowRectInfo();
337     auto safeAreaManager = pipelineContext->GetSafeAreaManager();
338     CHECK_NULL_RETURN(safeAreaManager, SizeF());
339     auto bottom = safeAreaManager->GetSystemSafeArea().bottom_.Length();
340     auto top = safeAreaManager->GetSystemSafeArea().top_.Length();
341     auto maxHeight = windowGlobalRect.Height();
342     if (showInSubWindow) {
343         maxHeight = SystemProperties::GetDeviceHeight();
344     }
345     auto popupMaxWidth = GetMaxWith().Value();
346     if (useCustom_) {
347         popupMaxWidth = width;
348     }
349     auto popupMaxHeight = maxHeight - OUT_RANGE_SPACE.ConvertToPx() - OUT_RANGE_SPACE.ConvertToPx() - bottom - top;
350     return SizeF(popupMaxWidth, popupMaxHeight);
351 }
352 
BubbleAvoidanceRule(RefPtr<LayoutWrapper> child,RefPtr<BubbleLayoutProperty> bubbleProp,RefPtr<FrameNode> bubbleNode,bool showInSubWindow)353 void BubbleLayoutAlgorithm::BubbleAvoidanceRule(RefPtr<LayoutWrapper> child, RefPtr<BubbleLayoutProperty> bubbleProp,
354     RefPtr<FrameNode> bubbleNode, bool showInSubWindow)
355 {
356     enableArrow_ = bubbleProp->GetEnableArrow().value_or(false);
357     auto bubblePattern = bubbleNode->GetPattern<BubblePattern>();
358     CHECK_NULL_VOID(bubblePattern);
359     auto bubblePaintProperty = bubbleNode->GetPaintProperty<BubbleRenderProperty>();
360     CHECK_NULL_VOID(bubblePaintProperty);
361     bool UseArrowOffset = bubblePaintProperty->GetArrowOffset().has_value();
362     if (!bubblePattern->IsExiting()) {
363         InitTargetSizeAndPosition(showInSubWindow);
364         InitCaretTargetSizeAndPosition();
365         // subtract the global offset of the overlay node,
366         // because the final node position is set relative to the overlay node.
367         auto overlayGlobalOffset = bubbleNode->GetOffsetRelativeToWindow();
368         targetOffset_ -= overlayGlobalOffset;
369     }
370     childSize_ = child->GetGeometryNode()->GetMarginFrameSize(); // bubble's size
371     auto childShowWidth = childSize_.Width() - BUBBLE_ARROW_HEIGHT.ConvertToPx() * 2;
372     auto childShowHeight = childSize_.Height() - BUBBLE_ARROW_HEIGHT.ConvertToPx() * 2;
373     childSize_ = SizeF(childShowWidth, childShowHeight);
374     auto littleSide = childSize_.Height() > childSize_.Width() ? childSize_.Width() : childSize_.Height();
375     auto littleSideHalf = littleSide / HALF;
376     if (borderRadius_.Unit() == DimensionUnit::PERCENT) {
377         auto value = borderRadius_.Value() * littleSideHalf;
378         borderRadius_.SetValue(value);
379         borderRadius_.SetUnit(DimensionUnit::PX);
380         border_.SetBorderRadius(Radius(borderRadius_));
381     }
382     auto borderRadius = ModifyBorderRadius(border_.BottomLeftRadius().GetY().ConvertToPx(), childSize_.Height() / 2);
383     auto borderRadius2 = ModifyBorderRadius(border_.BottomLeftRadius().GetY().ConvertToPx(), childSize_.Width() / 2);
384     float radiusPx = borderRadius < borderRadius2 ? borderRadius : borderRadius2;
385     borderRadius_.SetValue(radiusPx);
386     borderRadius_.SetUnit(DimensionUnit::PX);
387     border_.SetBorderRadius(Radius(borderRadius_));
388     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TWELVE)) {
389         childOffset_ = GetChildPosition(childSize_, bubbleProp, UseArrowOffset); // bubble's offset
390         placement_ = arrowPlacement_;
391         UpdateChildPosition(childOffset_);
392         if (arrowPlacement_ == Placement::TOP) {
393             if (bCaretMode_) {
394                 arrowPosition_ = OffsetF(targetOffset_.GetX(), targetOffset_.GetY() - scaledBubbleSpacing_);
395             }
396         }
397         if (arrowPlacement_ == Placement::BOTTOM) {
398             if (bCaretMode_) {
399                 arrowPosition_ =
400                     OffsetF(targetOffset_.GetX(), targetOffset_.GetY() + targetSize_.Height() + scaledBubbleSpacing_);
401             }
402         }
403     } else {
404         childOffset_ = GetChildPositionNew(childSize_, bubbleProp); // bubble's offset
405         childOffset_ = AddOffset(childOffset_);
406     }
407 }
408 
Layout(LayoutWrapper * layoutWrapper)409 void BubbleLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
410 {
411     CHECK_NULL_VOID(layoutWrapper);
412     auto bubbleProp = DynamicCast<BubbleLayoutProperty>(layoutWrapper->GetLayoutProperty());
413     CHECK_NULL_VOID(bubbleProp);
414     auto frameNode = layoutWrapper->GetHostNode();
415     CHECK_NULL_VOID(frameNode);
416     auto bubblePattern = frameNode->GetPattern<BubblePattern>();
417     CHECK_NULL_VOID(bubblePattern);
418     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
419     if (children.empty()) {
420         return;
421     }
422     selfSize_ = layoutWrapper->GetGeometryNode()->GetFrameSize(); // window's size
423     auto childWrapper = children.front();
424     bool showInSubWindow = bubbleProp->GetShowInSubWindowValue(false);
425     auto layoutChildSize = childWrapper->GetGeometryNode()->GetMarginFrameSize();
426     auto childMaxSize =
427         GetPopupMaxWidthAndHeight(showInSubWindow, childWrapper->GetGeometryNode()->GetMarginFrameSize().Width());
428     if (NearEqual(childMaxSize, layoutChildSize) || !NearEqual(measureChildSizeLast_, layoutChildSize)) {
429         auto childShowWidth =
430             childWrapper->GetGeometryNode()->GetFrameSize().Width() + BUBBLE_ARROW_HEIGHT.ConvertToPx() * 2;
431         auto childShowHeight =
432             childWrapper->GetGeometryNode()->GetFrameSize().Height() + BUBBLE_ARROW_HEIGHT.ConvertToPx() * 2;
433         childWrapper->GetGeometryNode()->SetFrameSize(SizeF { childShowWidth, childShowHeight });
434     }
435     BubbleAvoidanceRule(childWrapper, bubbleProp, frameNode, showInSubWindow);
436     UpdateTouchRegion();
437     auto childShowOffset = OffsetF(childOffset_.GetX() - BUBBLE_ARROW_HEIGHT.ConvertToPx(),
438         childOffset_.GetY() - BUBBLE_ARROW_HEIGHT.ConvertToPx());
439     childWrapper->GetGeometryNode()->SetMarginFrameOffset(childShowOffset);
440     childWrapper->Layout();
441     auto childLayoutWrapper = layoutWrapper->GetOrCreateChildByIndex(0);
442     CHECK_NULL_VOID(childLayoutWrapper);
443     const auto& columnChild = childLayoutWrapper->GetAllChildrenWithBuild();
444     if (columnChild.size() > 1 && !useCustom_) {
445         auto buttonRow = columnChild.back();
446         buttonRowSize_ = buttonRow->GetGeometryNode()->GetMarginFrameSize();
447         buttonRowOffset_ = buttonRow->GetGeometryNode()->GetMarginFrameOffset() + childOffset_;
448     }
449     // If bubble displayed in subwindow, set the hotarea of subwindow.
450     if (showInSubWindow) {
451         std::vector<Rect> rects;
452         if (!bubbleProp->GetBlockEventValue(true)) {
453             auto rect = Rect(childOffset_.GetX(), childOffset_.GetY(), childSize_.Width(), childSize_.Height());
454             rects.emplace_back(rect);
455         } else {
456             auto parentWindowRect = SubwindowManager::GetInstance()->GetParentWindowRect();
457             auto rect = Rect(childOffset_.GetX(), childOffset_.GetY(), childSize_.Width(), childSize_.Height());
458             rects.emplace_back(parentWindowRect);
459             rects.emplace_back(rect);
460         }
461         SubwindowManager::GetInstance()->SetHotAreas(rects, frameNode->GetId(), bubblePattern->GetContainerId());
462     }
463     targetOffsetForPaint_ = targetOffset_;
464     childOffsetForPaint_ = childOffset_;
465     arrowPositionForPaint_ = arrowPosition_;
466     UpdateClipOffset(frameNode);
467 }
GetIfNeedArrow(const RefPtr<BubbleLayoutProperty> & bubbleProp,const SizeF & childSize)468 bool BubbleLayoutAlgorithm::GetIfNeedArrow(const RefPtr<BubbleLayoutProperty>& bubbleProp, const SizeF& childSize)
469 {
470     auto enableArrow = bubbleProp->GetEnableArrow().value_or(true);
471     if (!enableArrow) {
472         return false;
473     }
474     double arrowWidth = BUBBLE_ARROW_WIDTH.ConvertToPx();
475     double twoRadiusPx = borderRadius_.ConvertToPx() * 2.0;
476     if (setHorizontal_.find(placement_) != setHorizontal_.end()) {
477         if (childSize.Height() >= twoRadiusPx + arrowWidth) {
478             return true;
479         }
480     }
481     if (setVertical_.find(placement_) != setVertical_.end()) {
482         if (childSize.Width() >= twoRadiusPx + arrowWidth) {
483             return true;
484         }
485     }
486     return false;
487 }
488 
InitProps(const RefPtr<BubbleLayoutProperty> & layoutProp,bool showInSubWindow)489 void BubbleLayoutAlgorithm::InitProps(const RefPtr<BubbleLayoutProperty>& layoutProp, bool showInSubWindow)
490 {
491     auto pipeline = PipelineBase::GetCurrentContext();
492     CHECK_NULL_VOID(pipeline);
493     auto popupTheme = pipeline->GetTheme<PopupTheme>();
494     CHECK_NULL_VOID(popupTheme);
495     padding_ = popupTheme->GetPadding();
496     borderRadius_ = layoutProp->GetRadius().value_or(popupTheme->GetRadius().GetX());
497     border_.SetBorderRadius(Radius(borderRadius_));
498     userSetTargetSpace_ = layoutProp->GetTargetSpace().value_or(Dimension(0.0f));
499     targetSpace_ = layoutProp->GetTargetSpace().value_or(popupTheme->GetTargetSpace());
500     placement_ = layoutProp->GetPlacement().value_or(Placement::BOTTOM);
501     auto height = layoutProp->GetArrowHeight().value_or(DEFAULT_BUBBLE_ARROW_HEIGHT);
502     auto width = layoutProp->GetArrowWidth().value_or(DEFAULT_BUBBLE_ARROW_WIDTH);
503     calculateArrowPoint(height, width);
504     arrowHeight_ = height.ConvertToPx();
505     scaledBubbleSpacing_ = arrowHeight_;
506     realArrowWidth_ = BUBBLE_ARROW_WIDTH.ConvertToPx();
507     realArrowHeight_ = BUBBLE_ARROW_HEIGHT.ConvertToPx();
508     positionOffset_ = layoutProp->GetPositionOffset().value_or(OffsetF());
509     auto constraint = layoutProp->GetLayoutConstraint();
510     enableArrow_ = layoutProp->GetEnableArrow().value_or(true);
511     auto wrapperIdealSize =
512         CreateIdealSize(constraint.value(), Axis::FREE, layoutProp->GetMeasureType(MeasureType::MATCH_PARENT), true);
513     wrapperSize_ = wrapperIdealSize;
514     targetSecurity_ = HORIZON_SPACING_WITH_SCREEN.ConvertToPx();
515     auto pipelineContext = PipelineContext::GetMainPipelineContext();
516     CHECK_NULL_VOID(pipelineContext);
517     auto safeAreaManager = pipelineContext->GetSafeAreaManager();
518     CHECK_NULL_VOID(safeAreaManager);
519     top_ = safeAreaManager->GetSystemSafeArea().top_.Length();
520     bottom_ = safeAreaManager->GetSystemSafeArea().bottom_.Length();
521     paddingStart_ = DRAW_EDGES_SPACE.ConvertToPx();
522     paddingEnd_ = DRAW_EDGES_SPACE.ConvertToPx();
523     paddingTop_ = top_ + DRAW_EDGES_SPACE.ConvertToPx();
524     if (showInSubWindow) {
525         paddingBottom_ = bottom_ + DRAW_EDGES_SPACE.ConvertToPx();
526     } else {
527         paddingBottom_ = DRAW_EDGES_SPACE.ConvertToPx();
528     }
529     showArrow_ = false;
530 }
531 
GetChildPositionNew(const SizeF & childSize,const RefPtr<BubbleLayoutProperty> & bubbleProp)532 OffsetF BubbleLayoutAlgorithm::GetChildPositionNew(
533     const SizeF& childSize, const RefPtr<BubbleLayoutProperty>& bubbleProp)
534 {
535     static std::map<Placement, std::vector<Placement>> PLACEMENT_STATES = {
536         { Placement::BOTTOM_LEFT,
537             {
538                 Placement::BOTTOM_LEFT,
539                 Placement::BOTTOM_RIGHT,
540                 Placement::TOP_LEFT,
541                 Placement::TOP_RIGHT,
542                 Placement::RIGHT_TOP,
543                 Placement::RIGHT_BOTTOM,
544                 Placement::LEFT_TOP,
545                 Placement::LEFT_BOTTOM,
546                 Placement::NONE,
547             } },
548         { Placement::BOTTOM,
549             {
550                 Placement::BOTTOM,
551                 Placement::BOTTOM_RIGHT,
552                 Placement::BOTTOM_LEFT,
553                 Placement::TOP,
554                 Placement::TOP_RIGHT,
555                 Placement::TOP_LEFT,
556                 Placement::RIGHT,
557                 Placement::RIGHT_TOP,
558                 Placement::RIGHT_BOTTOM,
559                 Placement::LEFT,
560                 Placement::LEFT_TOP,
561                 Placement::LEFT_BOTTOM,
562                 Placement::NONE,
563             } },
564         { Placement::BOTTOM_RIGHT,
565             {
566                 Placement::BOTTOM_RIGHT,
567                 Placement::BOTTOM_LEFT,
568                 Placement::TOP_RIGHT,
569                 Placement::TOP_LEFT,
570                 Placement::RIGHT_BOTTOM,
571                 Placement::RIGHT_TOP,
572                 Placement::LEFT_BOTTOM,
573                 Placement::LEFT_TOP,
574                 Placement::NONE,
575             } },
576         { Placement::TOP_LEFT,
577             {
578                 Placement::TOP_LEFT,
579                 Placement::TOP_RIGHT,
580                 Placement::BOTTOM_LEFT,
581                 Placement::BOTTOM_RIGHT,
582                 Placement::RIGHT_TOP,
583                 Placement::RIGHT_BOTTOM,
584                 Placement::LEFT_TOP,
585                 Placement::LEFT_BOTTOM,
586                 Placement::NONE,
587             } },
588         { Placement::TOP,
589             {
590                 Placement::TOP,
591                 Placement::TOP_RIGHT,
592                 Placement::TOP_LEFT,
593                 Placement::BOTTOM,
594                 Placement::BOTTOM_RIGHT,
595                 Placement::BOTTOM_LEFT,
596                 Placement::RIGHT,
597                 Placement::RIGHT_TOP,
598                 Placement::RIGHT_BOTTOM,
599                 Placement::LEFT,
600                 Placement::LEFT_TOP,
601                 Placement::LEFT_BOTTOM,
602                 Placement::NONE,
603             } },
604         { Placement::TOP_RIGHT,
605             {
606                 Placement::TOP_RIGHT,
607                 Placement::TOP_LEFT,
608                 Placement::BOTTOM_RIGHT,
609                 Placement::BOTTOM_LEFT,
610                 Placement::RIGHT_BOTTOM,
611                 Placement::RIGHT_TOP,
612                 Placement::LEFT_BOTTOM,
613                 Placement::LEFT_TOP,
614                 Placement::NONE,
615             } },
616         { Placement::LEFT_TOP,
617             {
618                 Placement::LEFT_TOP,
619                 Placement::LEFT_BOTTOM,
620                 Placement::RIGHT_TOP,
621                 Placement::RIGHT_BOTTOM,
622                 Placement::BOTTOM_LEFT,
623                 Placement::BOTTOM_RIGHT,
624                 Placement::TOP_LEFT,
625                 Placement::TOP_RIGHT,
626                 Placement::NONE,
627             } },
628         { Placement::LEFT,
629             {
630                 Placement::LEFT,
631                 Placement::LEFT_TOP,
632                 Placement::LEFT_BOTTOM,
633                 Placement::RIGHT,
634                 Placement::RIGHT_TOP,
635                 Placement::RIGHT_BOTTOM,
636                 Placement::BOTTOM,
637                 Placement::BOTTOM_RIGHT,
638                 Placement::BOTTOM_LEFT,
639                 Placement::TOP,
640                 Placement::TOP_RIGHT,
641                 Placement::TOP_LEFT,
642                 Placement::NONE,
643             } },
644         { Placement::LEFT_BOTTOM,
645             {
646                 Placement::LEFT_BOTTOM,
647                 Placement::LEFT_TOP,
648                 Placement::RIGHT_BOTTOM,
649                 Placement::RIGHT_TOP,
650                 Placement::BOTTOM_RIGHT,
651                 Placement::BOTTOM_LEFT,
652                 Placement::TOP_RIGHT,
653                 Placement::TOP_LEFT,
654                 Placement::NONE,
655             } },
656         { Placement::RIGHT_TOP,
657             {
658                 Placement::RIGHT_TOP,
659                 Placement::RIGHT_BOTTOM,
660                 Placement::LEFT_TOP,
661                 Placement::LEFT_BOTTOM,
662                 Placement::BOTTOM_LEFT,
663                 Placement::BOTTOM_RIGHT,
664                 Placement::TOP_LEFT,
665                 Placement::TOP_RIGHT,
666                 Placement::NONE,
667             } },
668         { Placement::RIGHT,
669             {
670                 Placement::RIGHT,
671                 Placement::RIGHT_TOP,
672                 Placement::RIGHT_BOTTOM,
673                 Placement::LEFT,
674                 Placement::LEFT_TOP,
675                 Placement::LEFT_BOTTOM,
676                 Placement::BOTTOM,
677                 Placement::BOTTOM_RIGHT,
678                 Placement::BOTTOM_LEFT,
679                 Placement::TOP,
680                 Placement::TOP_RIGHT,
681                 Placement::TOP_LEFT,
682                 Placement::NONE,
683             } },
684         { Placement::RIGHT_BOTTOM,
685             {
686                 Placement::RIGHT_BOTTOM,
687                 Placement::RIGHT_TOP,
688                 Placement::LEFT_BOTTOM,
689                 Placement::LEFT_TOP,
690                 Placement::BOTTOM_RIGHT,
691                 Placement::BOTTOM_LEFT,
692                 Placement::TOP_RIGHT,
693                 Placement::TOP_LEFT,
694                 Placement::NONE,
695             } },
696     };
697     OffsetF bottomPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
698         targetOffset_.GetY() + targetSize_.Height() + targetSpace_.ConvertToPx() + arrowHeight_);
699     OffsetF topPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
700         targetOffset_.GetY() - childSize.Height() - targetSpace_.ConvertToPx() - arrowHeight_);
701     OffsetF defaultPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
702         targetOffset_.GetY() + (targetSize_.Height() - childSize.Height()) / 2.0);
703     OffsetF childPosition;
704     OffsetF ArrowOffset;
705     OffsetF position = defaultPosition;
706     auto positionOffset = positionOffset_;
707     bool didNeedArrow = false;
708     std::vector<Placement> currentPlacementStates = PLACEMENT_STATES.find(Placement::BOTTOM)->second;
709     if (PLACEMENT_STATES.find(placement_) != PLACEMENT_STATES.end()) {
710         currentPlacementStates = PLACEMENT_STATES.find(placement_)->second;
711     }
712     size_t step = ALIGNMENT_STEP_OFFSET;
713     if (placement_ <= Placement::BOTTOM) {
714         step += 1;
715     }
716     bVertical_ = false;
717     bHorizontal_ = false;
718     for (size_t i = 0, len = currentPlacementStates.size(); i < len;) {
719         placement_ = currentPlacementStates[i];
720         if (placement_ == Placement::NONE) {
721             break;
722         }
723         if (bCaretMode_) { // Caret mode
724             if ((placement_ != Placement::BOTTOM) && (placement_ != Placement::TOP)) {
725                 i++;
726                 continue;
727             }
728         }
729         if (bVertical_) {
730             if (setHorizontal_.find(placement_) != setHorizontal_.end()) {
731                 i++;
732                 continue;
733             }
734         }
735         if (bHorizontal_) {
736             if (setVertical_.find(placement_) != setVertical_.end()) {
737                 i++;
738                 continue;
739             }
740         }
741         if (i >= step) {
742             positionOffset_ = OffsetF(0.0f, 0.0f);
743         }
744         childPosition = GetPositionWithPlacementNew(childSize, topPosition, bottomPosition, ArrowOffset);
745         UpdateChildPosition(childPosition);
746         didNeedArrow = GetIfNeedArrow(bubbleProp, childSize_);
747         position = FitToScreenNew(childPosition, step, i, childSize, didNeedArrow);
748         if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
749             continue;
750         }
751         break;
752     }
753     if (placement_ == Placement::NONE) {
754         bVertical_ = false;
755         bHorizontal_ = false;
756         position = GetAdjustPosition(currentPlacementStates, step, childSize, topPosition, bottomPosition, ArrowOffset);
757         if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
758             showArrow_ = false;
759             position = AdjustPosition(defaultPosition, childSize.Width(), childSize.Height(), targetSecurity_);
760             if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
761                 auto x = std::clamp(
762                     defaultPosition.GetX(), paddingStart_, wrapperSize_.Width() - childSize.Width() - paddingEnd_);
763                 auto y = paddingTop_;
764                 position = OffsetF(x, y);
765             }
766         }
767     }
768     positionOffset_ = positionOffset;
769     arrowPlacement_ = placement_;
770     arrowPosition_ = ArrowOffset;
771     return position;
772 }
773 
GetAdjustPosition(std::vector<Placement> & currentPlacementStates,size_t step,const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)774 OffsetF BubbleLayoutAlgorithm::GetAdjustPosition(std::vector<Placement>& currentPlacementStates, size_t step,
775     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
776 {
777     OffsetF childPosition;
778     OffsetF position;
779     float width = 0.0f;
780     float height = 0.0f;
781     for (size_t i = 0, len = currentPlacementStates.size(); i < len;) {
782         placement_ = currentPlacementStates[i];
783         if (placement_ == Placement::NONE) {
784             break;
785         }
786         if (bCaretMode_) { // Caret mode
787             if ((placement_ != Placement::BOTTOM) && (placement_ != Placement::TOP)) {
788                 i++;
789                 continue;
790             }
791         }
792         if (bVertical_) {
793             if (setHorizontal_.find(placement_) != setHorizontal_.end()) {
794                 i++;
795                 continue;
796             }
797         }
798         if (bHorizontal_) {
799             if (setVertical_.find(placement_) != setVertical_.end()) {
800                 i++;
801                 continue;
802             }
803         }
804         childPosition = GetPositionWithPlacementNew(childSize, topPosition, bottomPosition, arrowPosition);
805         UpdateChildPosition(childPosition);
806         width = childSize.Width();
807         height = childSize.Height();
808         if (showArrow_) {
809             if (setHorizontal_.find(placement_) != setHorizontal_.end()) {
810                 width += BUBBLE_ARROW_HEIGHT.ConvertToPx();
811             }
812             if (setVertical_.find(placement_) != setVertical_.end()) {
813                 height += BUBBLE_ARROW_HEIGHT.ConvertToPx();
814             }
815         }
816         position = AdjustPosition(childPosition, width, height, targetSecurity_);
817         if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
818             i += step;
819             continue;
820         }
821         break;
822     }
823     return position;
824 }
825 
AdjustPosition(const OffsetF & position,float width,float height,float space)826 OffsetF BubbleLayoutAlgorithm::AdjustPosition(const OffsetF& position, float width, float height, float space)
827 {
828     float xMax = 0.0f;
829     float yMax = 0.0f;
830     float xMin = 1.0f;
831     float yMin = 1.0f;
832     switch (placement_) {
833         case Placement::LEFT_TOP:
834         case Placement::LEFT_BOTTOM:
835         case Placement::LEFT: {
836             xMin = paddingStart_;
837             xMax = std::min(targetOffset_.GetX() - width - space, wrapperSize_.Width() - paddingEnd_ - width);
838             yMin = paddingTop_;
839             yMax = wrapperSize_.Height() - height - paddingBottom_;
840             break;
841         }
842         case Placement::RIGHT_TOP:
843         case Placement::RIGHT_BOTTOM:
844         case Placement::RIGHT: {
845             if (showArrow_) {
846                 space = space + BUBBLE_ARROW_HEIGHT.ConvertToPx();
847             }
848             xMin = std::max(targetOffset_.GetX() + targetSize_.Width() + space, paddingStart_);
849             xMax = wrapperSize_.Width() - width - paddingEnd_;
850             yMin = paddingTop_;
851             yMax = wrapperSize_.Height() - height - paddingBottom_;
852             break;
853         }
854         case Placement::TOP_LEFT:
855         case Placement::TOP_RIGHT:
856         case Placement::TOP: {
857             xMin = paddingStart_;
858             xMax = wrapperSize_.Width() - width - paddingEnd_;
859             yMin = paddingTop_;
860             yMax = std::min(targetOffset_.GetY() - height - space, wrapperSize_.Height() - paddingBottom_ - height);
861             break;
862         }
863         case Placement::BOTTOM_LEFT:
864         case Placement::BOTTOM_RIGHT:
865         case Placement::BOTTOM: {
866             if (showArrow_) {
867                 space = space + BUBBLE_ARROW_HEIGHT.ConvertToPx();
868             }
869             xMin = paddingStart_;
870             xMax = wrapperSize_.Width() - width - paddingEnd_;
871             yMin = std::max(targetOffset_.GetY() + targetSize_.Height() + space, paddingTop_);
872             yMax = wrapperSize_.Height() - height - paddingBottom_;
873             break;
874         }
875         case Placement::NONE: {
876             xMin = paddingStart_;
877             xMax = wrapperSize_.Width() - width - paddingEnd_;
878             yMin = paddingTop_;
879             yMax = wrapperSize_.Height() - height - paddingBottom_;
880             break;
881         }
882         default:
883             break;
884     }
885     if (xMax < xMin || yMax < yMin) {
886         return OffsetF(0.0f, 0.0f);
887     }
888     auto x = std::clamp(position.GetX(), xMin, xMax);
889     auto y = std::clamp(position.GetY(), yMin, yMax);
890     return OffsetF(x, y);
891 }
892 
GetPositionWithPlacementNew(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)893 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementNew(
894     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
895 {
896     OffsetF childPosition;
897     auto func = placementFuncMap_.find(placement_);
898     if (func != placementFuncMap_.end()) {
899         auto placementFunc = func->second;
900         if (placementFunc != nullptr) {
901             childPosition = (this->*placementFunc)(childSize, topPosition, bottomPosition, arrowPosition);
902         }
903     }
904     return childPosition;
905 }
906 
FitToScreenNew(const OffsetF & position,size_t step,size_t & i,const SizeF & childSize,bool didNeedArrow)907 OffsetF BubbleLayoutAlgorithm::FitToScreenNew(
908     const OffsetF& position, size_t step, size_t& i, const SizeF& childSize, bool didNeedArrow)
909 {
910     OffsetF afterOffsetPosition;
911     auto originPosition = position;
912     if (NearEqual(positionOffset_, OffsetF(0.0f, 0.0f)) && (!didNeedArrow || arrowPlacement_ == Placement::NONE)) {
913         afterOffsetPosition = AddTargetSpace(originPosition);
914     } else {
915         afterOffsetPosition = originPosition;
916     }
917     if (!CheckPosition(afterOffsetPosition, childSize, step, i)) {
918         return OffsetF(0.0f, 0.0f);
919     }
920 
921     return afterOffsetPosition;
922 }
923 
AddTargetSpace(const OffsetF & position)924 OffsetF BubbleLayoutAlgorithm::AddTargetSpace(const OffsetF& position)
925 {
926     auto x = position.GetX();
927     auto y = position.GetY();
928     switch (placement_) {
929         case Placement::BOTTOM_LEFT:
930         case Placement::BOTTOM_RIGHT:
931         case Placement::BOTTOM: {
932             y += targetSecurity_;
933             break;
934         }
935         case Placement::TOP_LEFT:
936         case Placement::TOP_RIGHT:
937         case Placement::TOP: {
938             y -= targetSecurity_;
939             break;
940         }
941         case Placement::RIGHT_TOP:
942         case Placement::RIGHT_BOTTOM:
943         case Placement::RIGHT: {
944             x += targetSecurity_;
945             break;
946         }
947         case Placement::LEFT_TOP:
948         case Placement::LEFT_BOTTOM:
949         case Placement::LEFT: {
950             x -= targetSecurity_;
951             break;
952         }
953         default: {
954             y += targetSecurity_;
955             break;
956         }
957     }
958     return OffsetF(x, y);
959 }
960 
AddOffset(const OffsetF & position)961 OffsetF BubbleLayoutAlgorithm::AddOffset(const OffsetF& position)
962 {
963     auto x = position.GetX();
964     auto y = position.GetY();
965     x += positionOffset_.GetX();
966     y += positionOffset_.GetY();
967     return OffsetF(x, y);
968 }
969 
UpdateChildPosition(OffsetF & childOffset)970 void BubbleLayoutAlgorithm::UpdateChildPosition(OffsetF& childOffset)
971 {
972     double arrowWidth = BUBBLE_ARROW_WIDTH.ConvertToPx();
973     double twoRadiusPx = borderRadius_.ConvertToPx() * 2.0;
974     float movingDistance = BUBBLE_ARROW_HEIGHT.ConvertToPx() + BUBBLE_ARROW_HEIGHT.ConvertToPx();
975     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TWELVE)) {
976         movingDistance = BUBBLE_ARROW_HEIGHT.ConvertToPx();
977     }
978     switch (placement_) {
979         case Placement::TOP:
980         case Placement::TOP_LEFT:
981         case Placement::TOP_RIGHT:
982             showArrow_ = GreatOrEqual(childSize_.Width() - twoRadiusPx, arrowWidth);
983             if (!showArrow_ || !enableArrow_) {
984                 childOffset += OffsetF(0.0, movingDistance);
985             }
986             break;
987         case Placement::BOTTOM:
988         case Placement::BOTTOM_LEFT:
989         case Placement::BOTTOM_RIGHT:
990             showArrow_ = GreatOrEqual(childSize_.Width() - twoRadiusPx, arrowWidth);
991             if (!showArrow_ || !enableArrow_) {
992                 childOffset += OffsetF(0.0, -(movingDistance));
993             }
994             break;
995         case Placement::LEFT:
996         case Placement::LEFT_TOP:
997         case Placement::LEFT_BOTTOM:
998             showArrow_ = GreatOrEqual(childSize_.Height() - twoRadiusPx, arrowWidth);
999             if (!showArrow_ || !enableArrow_) {
1000                 childOffset += OffsetF(movingDistance, 0.0);
1001             }
1002             break;
1003         case Placement::RIGHT:
1004         case Placement::RIGHT_TOP:
1005         case Placement::RIGHT_BOTTOM:
1006             showArrow_ = GreatOrEqual(childSize_.Height() - twoRadiusPx, arrowWidth);
1007             if (!showArrow_ || !enableArrow_) {
1008                 childOffset += OffsetF(-(movingDistance), 0.0);
1009             }
1010             break;
1011         default:
1012             break;
1013     }
1014     if (!enableArrow_) {
1015         showArrow_ = false;
1016     }
1017 }
1018 
UpdateTouchRegion()1019 void BubbleLayoutAlgorithm::UpdateTouchRegion()
1020 {
1021     OffsetF topLeft;
1022     OffsetF bottomRight;
1023     switch (arrowPlacement_) {
1024         case Placement::TOP:
1025         case Placement::TOP_LEFT:
1026         case Placement::TOP_RIGHT:
1027             topLeft = childOffset_;
1028             bottomRight = OffsetF(childSize_.Width(), targetSpace_.ConvertToPx() + childSize_.Height());
1029             if (showArrow_) {
1030                 bottomRight += OffsetF(0.0, BUBBLE_ARROW_HEIGHT.ConvertToPx());
1031             }
1032             break;
1033         case Placement::BOTTOM:
1034         case Placement::BOTTOM_LEFT:
1035         case Placement::BOTTOM_RIGHT:
1036             topLeft = childOffset_ + OffsetF(0.0, -targetSpace_.ConvertToPx());
1037             bottomRight = OffsetF(childSize_.Width(), targetSpace_.ConvertToPx() + childSize_.Height());
1038             if (showArrow_) {
1039                 topLeft += OffsetF(0.0, -BUBBLE_ARROW_HEIGHT.ConvertToPx());
1040                 bottomRight += OffsetF(0.0, BUBBLE_ARROW_HEIGHT.ConvertToPx());
1041             }
1042             break;
1043         case Placement::LEFT:
1044         case Placement::LEFT_TOP:
1045         case Placement::LEFT_BOTTOM:
1046             topLeft = childOffset_;
1047             bottomRight = OffsetF(targetSpace_.ConvertToPx() + childSize_.Width(), childSize_.Height());
1048             if (showArrow_) {
1049                 bottomRight += OffsetF(BUBBLE_ARROW_HEIGHT.ConvertToPx(), 0.0);
1050             }
1051             break;
1052         case Placement::RIGHT:
1053         case Placement::RIGHT_TOP:
1054         case Placement::RIGHT_BOTTOM:
1055             topLeft = childOffset_ + OffsetF(-targetSpace_.ConvertToPx(), 0.0);
1056             bottomRight = OffsetF(targetSpace_.ConvertToPx() + childSize_.Width(), childSize_.Height());
1057             if (showArrow_) {
1058                 topLeft += OffsetF(-BUBBLE_ARROW_HEIGHT.ConvertToPx(), 0.0);
1059                 bottomRight += OffsetF(BUBBLE_ARROW_HEIGHT.ConvertToPx(), 0.0);
1060             }
1061             break;
1062         default:
1063             break;
1064     }
1065     touchRegion_ = RectF(topLeft, topLeft + bottomRight);
1066 }
1067 
InitCaretTargetSizeAndPosition()1068 void BubbleLayoutAlgorithm::InitCaretTargetSizeAndPosition()
1069 {
1070     static std::vector<std::string> TEXT_STATES = { V2::TEXTAREA_ETS_TAG, V2::TEXTINPUT_ETS_TAG,
1071         V2::RICH_EDITOR_ETS_TAG, V2::SEARCH_ETS_TAG };
1072     auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
1073     CHECK_NULL_VOID(targetNode);
1074     auto it = std::find(TEXT_STATES.begin(), TEXT_STATES.end(), targetTag_);
1075     bCaretMode_ = false;
1076     CaretMetricsF caretMetrics;
1077     if (it != TEXT_STATES.end()) {
1078         bCaretMode_ = true;
1079         positionOffset_ = OffsetF(0.0f, 0.0f);
1080         if ((placement_ != Placement::BOTTOM) && (placement_ != Placement::TOP)) {
1081             placement_ = Placement::BOTTOM;
1082         }
1083         GetTextCaretMetrics<TextBase>(targetNode, caretMetrics);
1084         targetOffset_ = caretMetrics.offset;
1085         targetSize_.SetHeight(caretMetrics.height);
1086         targetSize_.SetWidth(0.0f);
1087     }
1088 }
1089 
InitTargetSizeAndPosition(bool showInSubWindow)1090 void BubbleLayoutAlgorithm::InitTargetSizeAndPosition(bool showInSubWindow)
1091 {
1092     auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
1093     CHECK_NULL_VOID(targetNode);
1094     if (!targetNode->IsOnMainTree() && !targetNode->IsVisible()) {
1095         return;
1096     }
1097     auto geometryNode = targetNode->GetGeometryNode();
1098     CHECK_NULL_VOID(geometryNode);
1099     targetSize_ = geometryNode->GetFrameSize();
1100     auto pipelineContext = GetMainPipelineContext();
1101     CHECK_NULL_VOID(pipelineContext);
1102     targetOffset_ = targetNode->GetPaintRectOffset();
1103     // Show in SubWindow
1104     if (showInSubWindow) {
1105         auto displayWindowOffset = OffsetF(pipelineContext->GetDisplayWindowRectInfo().GetOffset().GetX(),
1106             pipelineContext->GetDisplayWindowRectInfo().GetOffset().GetY());
1107         targetOffset_ += displayWindowOffset;
1108         auto currentSubwindow = SubwindowManager::GetInstance()->GetCurrentWindow();
1109         if (currentSubwindow) {
1110             auto subwindowRect = currentSubwindow->GetRect();
1111             targetOffset_ -= subwindowRect.GetOffset();
1112         }
1113     }
1114 }
1115 
CheckPositionInPlacementRect(const Rect & rect,const OffsetF & position,const SizeF & childSize)1116 bool BubbleLayoutAlgorithm::CheckPositionInPlacementRect(
1117     const Rect& rect, const OffsetF& position, const SizeF& childSize)
1118 {
1119     auto x = position.GetX();
1120     auto y = position.GetY();
1121     if (x < rect.Left() || (x + childSize.Width()) > rect.Right() || y < rect.Top() ||
1122         (y + childSize.Height()) > rect.Bottom()) {
1123         return false;
1124     }
1125     return true;
1126 }
1127 
CheckPosition(const OffsetF & position,const SizeF & childSize,size_t step,size_t & i)1128 bool BubbleLayoutAlgorithm::CheckPosition(const OffsetF& position, const SizeF& childSize, size_t step, size_t& i)
1129 {
1130     float targetOffsetX = targetOffset_.GetX();
1131     float targetOffsetY = targetOffset_.GetY();
1132     Rect rect;
1133     switch (placement_) {
1134         case Placement::BOTTOM_LEFT:
1135         case Placement::BOTTOM_RIGHT:
1136         case Placement::BOTTOM: {
1137             targetOffsetY += (userSetTargetSpace_.ConvertToPx());
1138             auto y = std::max(targetOffsetY + targetSize_.Height(), paddingTop_);
1139             auto height = std::min(wrapperSize_.Height() - paddingBottom_ - targetOffsetY - targetSize_.Height(),
1140                 wrapperSize_.Height() - paddingBottom_ - paddingTop_);
1141             rect.SetRect(paddingStart_, y, wrapperSize_.Width() - paddingEnd_ - paddingStart_, height);
1142             if (childSize.Height() > height) {
1143                 i += step;
1144                 return false;
1145             } else {
1146                 bVertical_ = true;
1147             }
1148             break;
1149         }
1150         case Placement::TOP_LEFT:
1151         case Placement::TOP_RIGHT:
1152         case Placement::TOP: {
1153             targetOffsetY += (-userSetTargetSpace_.ConvertToPx());
1154             auto height = std::min(targetOffsetY - paddingTop_, wrapperSize_.Height() - paddingTop_ - paddingBottom_);
1155             rect.SetRect(paddingStart_, paddingTop_, wrapperSize_.Width() - paddingEnd_ - paddingStart_, height);
1156             if (childSize.Height() > height) {
1157                 i += step;
1158                 return false;
1159             } else {
1160                 bVertical_ = true;
1161             }
1162             break;
1163         }
1164         case Placement::RIGHT_TOP:
1165         case Placement::RIGHT_BOTTOM:
1166         case Placement::RIGHT: {
1167             targetOffsetX += (userSetTargetSpace_.ConvertToPx());
1168             auto x = std::max(targetOffsetX + targetSize_.Width(), paddingStart_);
1169             auto width = std::min(wrapperSize_.Width() - targetOffsetX - targetSize_.Width() - paddingEnd_,
1170                 wrapperSize_.Width() - paddingStart_ - paddingEnd_);
1171             rect.SetRect(x, paddingTop_, width, wrapperSize_.Height() - paddingBottom_ - paddingTop_);
1172             if (childSize.Width() > width) {
1173                 i += step;
1174                 return false;
1175             } else {
1176                 bHorizontal_ = true;
1177             }
1178             break;
1179         }
1180         case Placement::LEFT_TOP:
1181         case Placement::LEFT_BOTTOM:
1182         case Placement::LEFT: {
1183             targetOffsetX += (-userSetTargetSpace_.ConvertToPx());
1184             auto width = std::min(targetOffsetX - paddingStart_, wrapperSize_.Width() - paddingEnd_ - paddingStart_);
1185             rect.SetRect(paddingStart_, paddingTop_, width, wrapperSize_.Height() - paddingBottom_ - paddingTop_);
1186             if (childSize.Width() > width) {
1187                 i += step;
1188                 return false;
1189             } else {
1190                 bHorizontal_ = true;
1191             }
1192             break;
1193         }
1194         default:
1195             return false;
1196     }
1197     i++;
1198     return CheckPositionInPlacementRect(rect, position, childSize);
1199 }
1200 
GetPositionWithPlacementTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1201 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementTop(
1202     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1203 {
1204     float bubbleSpacing = scaledBubbleSpacing_;
1205     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1206     float radius = borderRadius_.ConvertToPx();
1207     arrowPosition = topPosition + OffsetF(radius + arrowHalfWidth, childSize.Height() + bubbleSpacing);
1208     if (bCaretMode_) {
1209         arrowPosition = OffsetF(targetOffset_.GetX(), targetOffset_.GetY() - bubbleSpacing);
1210     }
1211     return topPosition;
1212 }
1213 
GetPositionWithPlacementTopLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1214 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementTopLeft(
1215     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1216 {
1217     OffsetF childPosition;
1218     float marginRight = 0.0f;
1219     float marginBottom = 0.0f;
1220     float bubbleSpacing = scaledBubbleSpacing_;
1221     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1222     float radius = borderRadius_.ConvertToPx();
1223     childPosition = OffsetF(targetOffset_.GetX() - marginRight,
1224         targetOffset_.GetY() - childSize.Height() - bubbleSpacing - marginBottom - targetSpace_.ConvertToPx());
1225     arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, childSize.Height() + bubbleSpacing);
1226     return childPosition;
1227 }
1228 
GetPositionWithPlacementTopRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1229 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementTopRight(
1230     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1231 {
1232     OffsetF childPosition;
1233     float marginBottom = 0.0f;
1234     float marginLeft = 0.0f;
1235     float bubbleSpacing = scaledBubbleSpacing_;
1236     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1237     float radius = borderRadius_.ConvertToPx();
1238     childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
1239         targetOffset_.GetY() - childSize.Height() - targetSpace_.ConvertToPx() - bubbleSpacing - marginBottom);
1240     arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, childSize.Height() + bubbleSpacing);
1241     return childPosition;
1242 }
1243 
GetPositionWithPlacementBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1244 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementBottom(
1245     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1246 {
1247     float bubbleSpacing = scaledBubbleSpacing_;
1248     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1249     float radius = borderRadius_.ConvertToPx();
1250     arrowPosition = bottomPosition + OffsetF(radius + arrowHalfWidth, -bubbleSpacing);
1251     if (bCaretMode_) {
1252         arrowPosition = OffsetF(targetOffset_.GetX(), targetOffset_.GetY() + targetSize_.Height() + bubbleSpacing);
1253     }
1254     return bottomPosition;
1255 }
1256 
GetPositionWithPlacementBottomLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1257 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementBottomLeft(
1258     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1259 {
1260     OffsetF childPosition;
1261     float marginRight = 0.0f;
1262     float marginTop = 0.0f;
1263     float bubbleSpacing = scaledBubbleSpacing_;
1264     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1265     float radius = borderRadius_.ConvertToPx();
1266     childPosition = OffsetF(targetOffset_.GetX() - marginRight,
1267         targetOffset_.GetY() + targetSize_.Height() + targetSpace_.ConvertToPx() + bubbleSpacing + marginTop);
1268     arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, -bubbleSpacing);
1269     return childPosition;
1270 }
1271 
GetPositionWithPlacementBottomRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1272 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementBottomRight(
1273     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1274 {
1275     OffsetF childPosition;
1276     float marginTop = 0.0f;
1277     float marginLeft = 0.0f;
1278     float bubbleSpacing = scaledBubbleSpacing_;
1279     float radius = borderRadius_.ConvertToPx();
1280     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1281     childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
1282         targetOffset_.GetY() + targetSize_.Height() + targetSpace_.ConvertToPx() + bubbleSpacing + marginTop);
1283     arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, -bubbleSpacing);
1284     return childPosition;
1285 }
1286 
GetPositionWithPlacementLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1287 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementLeft(
1288     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1289 {
1290     OffsetF childPosition;
1291     float marginRight = 0.0f;
1292     float bubbleSpacing = scaledBubbleSpacing_;
1293     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1294     float radius = borderRadius_.ConvertToPx();
1295     childPosition =
1296         OffsetF(targetOffset_.GetX() - targetSpace_.ConvertToPx() - bubbleSpacing - childSize.Width() - marginRight,
1297             targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
1298     arrowPosition = childPosition + OffsetF(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
1299     return childPosition;
1300 }
1301 
GetPositionWithPlacementLeftTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1302 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementLeftTop(
1303     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1304 {
1305     OffsetF childPosition;
1306     float marginRight = 0.0f;
1307     float marginBottom = 0.0f;
1308     float bubbleSpacing = scaledBubbleSpacing_;
1309     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1310     float radius = borderRadius_.ConvertToPx();
1311     childPosition =
1312         OffsetF(targetOffset_.GetX() - targetSpace_.ConvertToPx() - bubbleSpacing - childSize.Width() - marginRight,
1313             targetOffset_.GetY() - marginBottom);
1314     arrowPosition = childPosition + OffsetF(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
1315     return childPosition;
1316 }
1317 
GetPositionWithPlacementLeftBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1318 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementLeftBottom(
1319     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1320 {
1321     OffsetF childPosition;
1322     float marginRight = 0.0f;
1323     float marginTop = 0.0f;
1324     float bubbleSpacing = scaledBubbleSpacing_;
1325     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1326     float radius = borderRadius_.ConvertToPx();
1327     childPosition =
1328         OffsetF(targetOffset_.GetX() - targetSpace_.ConvertToPx() - bubbleSpacing - childSize.Width() - marginRight,
1329             targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
1330     arrowPosition = childPosition + OffsetF(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
1331     return childPosition;
1332 }
1333 
GetPositionWithPlacementRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1334 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementRight(
1335     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1336 {
1337     OffsetF childPosition;
1338     float marginLeft = 0.0f;
1339     float bubbleSpacing = scaledBubbleSpacing_;
1340     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1341     float radius = borderRadius_.ConvertToPx();
1342     childPosition =
1343         OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_.ConvertToPx() + bubbleSpacing + marginLeft,
1344             targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
1345     arrowPosition = childPosition + OffsetF(-bubbleSpacing, radius + arrowHalfWidth);
1346     return childPosition;
1347 }
1348 
GetPositionWithPlacementRightTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1349 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementRightTop(
1350     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1351 {
1352     OffsetF childPosition;
1353     float marginBottom = 0.0f;
1354     float marginLeft = 0.0f;
1355     float bubbleSpacing = scaledBubbleSpacing_;
1356     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1357     float radius = borderRadius_.ConvertToPx();
1358     childPosition =
1359         OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_.ConvertToPx() + bubbleSpacing + marginLeft,
1360             targetOffset_.GetY() - marginBottom);
1361     arrowPosition = childPosition + OffsetF(-bubbleSpacing, radius + arrowHalfWidth);
1362     return childPosition;
1363 }
1364 
GetPositionWithPlacementRightBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1365 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementRightBottom(
1366     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1367 {
1368     OffsetF childPosition;
1369     float marginTop = 0.0f;
1370     float marginLeft = 0.0f;
1371     float bubbleSpacing = scaledBubbleSpacing_;
1372     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1373     float radius = borderRadius_.ConvertToPx();
1374     childPosition =
1375         OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_.ConvertToPx() + bubbleSpacing + marginLeft,
1376             targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
1377     arrowPosition = childPosition + OffsetF(-bubbleSpacing, radius + arrowHalfWidth);
1378     return childPosition;
1379 }
1380 
MoveTo(double x,double y)1381 std::string BubbleLayoutAlgorithm::MoveTo(double x, double y)
1382 {
1383     return "M" + std::to_string(x) + " " + std::to_string(y) + " ";
1384 }
1385 
LineTo(double x,double y)1386 std::string BubbleLayoutAlgorithm::LineTo(double x, double y)
1387 {
1388     return "L" + std::to_string(x) + " " + std::to_string(y) + " ";
1389 }
1390 
ArcTo(double rx,double ry,double rotation,int32_t arc_flag,double x,double y)1391 std::string BubbleLayoutAlgorithm::ArcTo(double rx, double ry, double rotation, int32_t arc_flag, double x, double y)
1392 {
1393     int32_t sweep_flag = 1;
1394     return "A" + std::to_string(rx) + " " + std::to_string(ry) + " " + std::to_string(rotation) + " " +
1395            std::to_string(arc_flag) + " " + std::to_string(sweep_flag) + " " + std::to_string(x) + " " +
1396            std::to_string(y) + " ";
1397 }
1398 
UpdateClipOffset(const RefPtr<FrameNode> & frameNode)1399 void BubbleLayoutAlgorithm::UpdateClipOffset(const RefPtr<FrameNode>& frameNode)
1400 {
1401     auto paintProperty = frameNode->GetPaintProperty<BubbleRenderProperty>();
1402     auto childNode = AceType::DynamicCast<FrameNode>(frameNode->GetFirstChild());
1403     if (!bCaretMode_) {
1404         arrowPosition_ =
1405             OffsetF(BUBBLE_ARROW_HEIGHT.ConvertToPx() * DOUBLE, BUBBLE_ARROW_HEIGHT.ConvertToPx() * DOUBLE);
1406         UpdateArrowOffset(paintProperty->GetArrowOffset(), arrowPlacement_);
1407     } else {
1408         arrowPosition_ = arrowPosition_ - childOffset_ + OffsetF(BUBBLE_ARROW_HEIGHT.ConvertToPx(), 0.0f);
1409     }
1410     targetOffset_ = targetOffset_ - childOffset_;
1411     childOffset_ = OffsetF(BUBBLE_ARROW_HEIGHT.ConvertToPx(), BUBBLE_ARROW_HEIGHT.ConvertToPx());
1412     clipFrameNode_ = childNode;
1413     clipPath_.clear();
1414     clipPath_ = ClipBubbleWithPath();
1415 }
1416 
ClipBubbleWithPath()1417 std::string BubbleLayoutAlgorithm::ClipBubbleWithPath()
1418 {
1419     std::string path;
1420     float arrowOffset = 0.0;
1421     if (!bCaretMode_) {
1422         arrowOffset = GetArrowOffset(arrowPlacement_);
1423     }
1424     float radiusPx = borderRadius_.ConvertToPx();
1425     Placement arrowBuildplacement = Placement::NONE;
1426     if (enableArrow_ && showArrow_) {
1427         GetArrowBuildPlacement(arrowBuildplacement);
1428     }
1429     if ((arrowBuildplacement == Placement::TOP_LEFT) || (arrowBuildplacement == Placement::LEFT_TOP)) {
1430         path += MoveTo(childOffset_.GetX(), childOffset_.GetY());
1431     } else {
1432         path += MoveTo(childOffset_.GetX() + radiusPx, childOffset_.GetY());
1433     }
1434     path += BuildTopLinePath(arrowOffset, radiusPx, arrowBuildplacement);
1435     if ((arrowBuildplacement != Placement::TOP_RIGHT) && (arrowBuildplacement != Placement::RIGHT_TOP)) {
1436         path += BuildCornerPath(Placement::TOP_RIGHT, radiusPx);
1437     }
1438     path += BuildRightLinePath(arrowOffset, radiusPx, arrowBuildplacement);
1439     if ((arrowBuildplacement != Placement::RIGHT_BOTTOM) && (arrowBuildplacement != Placement::BOTTOM_RIGHT)) {
1440         path += BuildCornerPath(Placement::BOTTOM_RIGHT, radiusPx);
1441     }
1442     path += BuildBottomLinePath(arrowOffset, radiusPx, arrowBuildplacement);
1443     if ((arrowBuildplacement != Placement::BOTTOM_LEFT) && (arrowBuildplacement != Placement::LEFT_BOTTOM)) {
1444         path += BuildCornerPath(Placement::BOTTOM_LEFT, radiusPx);
1445     }
1446     path += BuildLeftLinePath(arrowOffset, radiusPx, arrowBuildplacement);
1447     if ((arrowBuildplacement != Placement::LEFT_TOP) && (arrowBuildplacement != Placement::TOP_LEFT)) {
1448         path += BuildCornerPath(Placement::TOP_LEFT, radiusPx);
1449     }
1450     return path + "Z";
1451 }
1452 
GetArrowOffset(const Placement & placement)1453 float BubbleLayoutAlgorithm::GetArrowOffset(const Placement& placement)
1454 {
1455     Edge edge;
1456     double arrowOffset;
1457     double edgeValue = 0.0;
1458     double maxMotionRange = 0.0;
1459     double minMotionRange = 0.0;
1460     double targetOffsetXOrY = 0.0;
1461     double childOffsetsetXOrY = 0.0;
1462     double childSizeWidthOrHeight = 0.0;
1463     double targetSizeWidthOrHeight = 0.0;
1464     bool bHorizontal = false;
1465 
1466     InitEdgeSize(edge);
1467     switch (placement) {
1468         case Placement::TOP:
1469         case Placement::TOP_LEFT:
1470         case Placement::TOP_RIGHT:
1471             edgeValue = edge.Top().Value();
1472             bHorizontal = true;
1473             break;
1474         case Placement::BOTTOM:
1475         case Placement::BOTTOM_LEFT:
1476         case Placement::BOTTOM_RIGHT:
1477             edgeValue = edge.Bottom().Value();
1478             bHorizontal = true;
1479             break;
1480         case Placement::LEFT:
1481         case Placement::LEFT_TOP:
1482         case Placement::LEFT_BOTTOM:
1483             edgeValue = edge.Left().Value();
1484             break;
1485         case Placement::RIGHT:
1486         case Placement::RIGHT_TOP:
1487         case Placement::RIGHT_BOTTOM:
1488             edgeValue = edge.Right().Value();
1489             break;
1490         default:
1491             break;
1492     }
1493     if (bHorizontal) {
1494         targetOffsetXOrY = targetOffset_.GetX();
1495         targetSizeWidthOrHeight = targetSize_.Width();
1496         childOffsetsetXOrY = childOffset_.GetX();
1497         childSizeWidthOrHeight = childSize_.Width();
1498     } else {
1499         targetOffsetXOrY = targetOffset_.GetY();
1500         targetSizeWidthOrHeight = targetSize_.Height();
1501         childOffsetsetXOrY = childOffset_.GetY();
1502         childSizeWidthOrHeight = childSize_.Height();
1503     }
1504     maxMotionRange = childSizeWidthOrHeight;
1505     if (arrowOfTargetOffset_ == ArrowOfTargetOffset::START) {
1506         arrowOffset = (targetOffsetXOrY + (arrowOffset_.Value() * targetSizeWidthOrHeight)) - childOffsetsetXOrY +
1507                       BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1508         return arrowOffset;
1509     }
1510     if (arrowOfTargetOffset_ == ArrowOfTargetOffset::CENTER) {
1511         arrowOffset = (targetOffsetXOrY + (arrowOffset_.Value() * targetSizeWidthOrHeight)) - childOffsetsetXOrY;
1512         return arrowOffset;
1513     }
1514     if (arrowOfTargetOffset_ == ArrowOfTargetOffset::END) {
1515         arrowOffset = (targetOffsetXOrY + (arrowOffset_.Value() * targetSizeWidthOrHeight)) - childOffsetsetXOrY -
1516                       BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1517         return arrowOffset;
1518     }
1519     return std::clamp(arrowOffset_.Unit() == DimensionUnit::PERCENT ? arrowOffset_.Value() * maxMotionRange
1520                                                                     : arrowOffset_.ConvertToPx(),
1521         minMotionRange, maxMotionRange);
1522 }
1523 
UpdateArrowOffset(const std::optional<Dimension> & offset,const Placement & placement)1524 void BubbleLayoutAlgorithm::UpdateArrowOffset(const std::optional<Dimension>& offset, const Placement& placement)
1525 {
1526     if (offset.has_value()) {
1527         arrowOffset_ = offset.value();
1528         arrowOfTargetOffset_ = ArrowOfTargetOffset::NONE;
1529         if (arrowOffset_.Unit() == DimensionUnit::PERCENT) {
1530             if (arrowOffset_.Value() == ARROW_OFFSET_START_VALUE) {
1531                 arrowOfTargetOffset_ = ArrowOfTargetOffset::START;
1532             } else if (arrowOffset_.Value() == ARROW_OFFSET_CENTER_VALUE) {
1533                 arrowOfTargetOffset_ = ArrowOfTargetOffset::CENTER;
1534             } else {
1535                 arrowOfTargetOffset_ = ArrowOfTargetOffset::END;
1536             }
1537             arrowOffset_.SetValue(std::clamp(arrowOffset_.Value(), 0.0, 1.0));
1538         }
1539         return;
1540     }
1541     arrowOfTargetOffset_ = ArrowOfTargetOffset::NONE;
1542     switch (placement) {
1543         case Placement::LEFT:
1544         case Placement::RIGHT:
1545         case Placement::TOP:
1546         case Placement::BOTTOM:
1547             arrowOffset_ = BUBBLE_ARROW_HALF_PERCENT_VALUE;
1548             arrowOfTargetOffset_ = ArrowOfTargetOffset::CENTER;
1549             break;
1550         case Placement::TOP_LEFT:
1551         case Placement::BOTTOM_LEFT:
1552         case Placement::LEFT_TOP:
1553         case Placement::RIGHT_TOP:
1554             arrowOffset_ = BUBBLE_ARROW_ZERO_PERCENT_VALUE;
1555             arrowOfTargetOffset_ = ArrowOfTargetOffset::START;
1556             break;
1557         case Placement::TOP_RIGHT:
1558         case Placement::BOTTOM_RIGHT:
1559         case Placement::LEFT_BOTTOM:
1560         case Placement::RIGHT_BOTTOM:
1561             arrowOffset_ = BUBBLE_ARROW_ONE_HUNDRED_PERCENT_VALUE;
1562             arrowOfTargetOffset_ = ArrowOfTargetOffset::END;
1563             break;
1564         default:
1565             break;
1566     }
1567 }
1568 
InitEdgeSize(Edge & edge)1569 void BubbleLayoutAlgorithm::InitEdgeSize(Edge& edge)
1570 {
1571     edge.SetTop(Dimension(std::max(padding_.Left().ConvertToPx(), border_.TopLeftRadius().GetX().ConvertToPx()) +
1572                           std::max(padding_.Right().ConvertToPx(), border_.TopRightRadius().GetX().ConvertToPx())));
1573     edge.SetBottom(
1574         Dimension(std::max(padding_.Left().ConvertToPx(), border_.BottomLeftRadius().GetX().ConvertToPx()) +
1575                   std::max(padding_.Right().ConvertToPx(), border_.BottomRightRadius().GetX().ConvertToPx())));
1576     edge.SetLeft(
1577         Dimension(std::max(padding_.Top().ConvertToPx(), border_.TopRightRadius().GetY().ConvertToPx()) +
1578                   std::max(padding_.Bottom().ConvertToPx(), border_.BottomRightRadius().GetY().ConvertToPx())));
1579     edge.SetRight(
1580         Dimension(std::max(padding_.Top().ConvertToPx(), border_.TopLeftRadius().GetY().ConvertToPx()) +
1581                   std::max(padding_.Bottom().ConvertToPx(), border_.BottomLeftRadius().GetY().ConvertToPx())));
1582 }
1583 
ModifyBorderRadius(float borderRadius,float halfChildHeight)1584 float BubbleLayoutAlgorithm::ModifyBorderRadius(float borderRadius, float halfChildHeight)
1585 {
1586     return GreatOrEqual(borderRadius, halfChildHeight) ? halfChildHeight : borderRadius;
1587 }
1588 
GetArrowBuildPlacement(Placement & arrowBuildplacement)1589 void BubbleLayoutAlgorithm::GetArrowBuildPlacement(Placement& arrowBuildplacement)
1590 {
1591     auto radius = borderRadius_.ConvertToPx();
1592     float maxOffset = 0.0;
1593     switch (arrowPlacement_) {
1594         case Placement::BOTTOM:
1595         case Placement::BOTTOM_LEFT:
1596         case Placement::BOTTOM_RIGHT: // TOP
1597             maxOffset = childOffset_.GetX() + childSize_.Width() - radius -
1598                         BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1599             if ((!bCaretMode_) && (arrowOffset_.Unit() != DimensionUnit::PERCENT)) {
1600                 if ((arrowPosition_.GetX() + arrowOffset_.ConvertToPx()) > maxOffset) {
1601                     arrowBuildplacement = Placement::TOP_RIGHT;
1602                     break;
1603                 }
1604                 if ((arrowOffset_.ConvertToPx()) < radius) {
1605                     arrowBuildplacement = Placement::TOP_LEFT;
1606                     break;
1607                 }
1608                 arrowBuildplacement = Placement::TOP;
1609             } else {
1610                 arrowBuildplacement = Placement::TOP;
1611             }
1612             break;
1613         case Placement::LEFT:
1614         case Placement::LEFT_TOP:
1615         case Placement::LEFT_BOTTOM: // Right
1616             maxOffset = childOffset_.GetY() + childSize_.Height() - radius -
1617                         BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1618             if ((!bCaretMode_) && (arrowOffset_.Unit() != DimensionUnit::PERCENT)) {
1619                 if ((arrowPosition_.GetY() + arrowOffset_.ConvertToPx()) > maxOffset) {
1620                     arrowBuildplacement = Placement::RIGHT_BOTTOM;
1621                     break;
1622                 }
1623                 if ((arrowOffset_.ConvertToPx()) < radius) {
1624                     arrowBuildplacement = Placement::RIGHT_TOP;
1625                     break;
1626                 }
1627                 arrowBuildplacement = Placement::RIGHT;
1628             } else {
1629                 arrowBuildplacement = Placement::RIGHT;
1630             }
1631             break;
1632         case Placement::TOP:
1633         case Placement::TOP_LEFT:
1634         case Placement::TOP_RIGHT: // Bottom
1635             maxOffset = childOffset_.GetX() + childSize_.Width() - radius -
1636                         BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1637             if ((!bCaretMode_) && (arrowOffset_.Unit() != DimensionUnit::PERCENT)) {
1638                 if ((arrowPosition_.GetX() + arrowOffset_.ConvertToPx()) > maxOffset) {
1639                     arrowBuildplacement = Placement::BOTTOM_RIGHT; // replace
1640                     break;
1641                 }
1642                 if ((arrowOffset_.ConvertToPx()) < radius) {
1643                     arrowBuildplacement = Placement::BOTTOM_LEFT; // replace
1644                     break;
1645                 }
1646                 arrowBuildplacement = Placement::BOTTOM; // nomal
1647             } else {
1648                 arrowBuildplacement = Placement::BOTTOM; // nomal
1649             }
1650             break;
1651         case Placement::RIGHT:
1652         case Placement::RIGHT_TOP:
1653         case Placement::RIGHT_BOTTOM: // Left
1654             maxOffset = childOffset_.GetY() + childSize_.Height() - radius -
1655                         BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1656             if ((!bCaretMode_) && (arrowOffset_.Unit() != DimensionUnit::PERCENT)) {
1657                 if ((arrowPosition_.GetY() + arrowOffset_.ConvertToPx()) > maxOffset) {
1658                     arrowBuildplacement = Placement::LEFT_BOTTOM;
1659                     break;
1660                 }
1661                 if ((arrowOffset_.ConvertToPx()) < radius) {
1662                     arrowBuildplacement = Placement::LEFT_TOP;
1663                     break;
1664                 }
1665                 arrowBuildplacement = Placement::LEFT;
1666             } else {
1667                 arrowBuildplacement = Placement::LEFT;
1668             }
1669             break;
1670         default:
1671             break;
1672     }
1673     if (arrowBuildplacement > Placement::BOTTOM) {
1674         arrowPosition_ += OffsetF(-BUBBLE_ARROW_HEIGHT.ConvertToPx(), -BUBBLE_ARROW_HEIGHT.ConvertToPx());
1675     }
1676 }
1677 
SetArrowOffsetsFromClip(const int16_t index,const float offsetX,const float offsetY)1678 void BubbleLayoutAlgorithm::SetArrowOffsetsFromClip(const int16_t index, const float offsetX, const float offsetY)
1679 {
1680     arrowOffsetsFromClip_[index] = { offsetX, offsetY };
1681 }
1682 
BuildTopLinePath(float arrowOffset,float radius,Placement & arrowBuildplacement)1683 std::string BubbleLayoutAlgorithm::BuildTopLinePath(float arrowOffset, float radius, Placement& arrowBuildplacement)
1684 {
1685     std::string path;
1686     float childOffsetY = childOffset_.GetY();
1687     auto leftOffset =
1688         childOffset_.GetX() + radius + BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1689     auto rightOffset = childOffset_.GetX() + childSize_.Width() - radius -
1690                        BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1691     auto arrowTopOffset = std::clamp(
1692         arrowPosition_.GetX() + arrowOffset, static_cast<float>(leftOffset), static_cast<float>(rightOffset));
1693     switch (arrowPlacement_) {
1694         case Placement::BOTTOM:
1695         case Placement::BOTTOM_LEFT:
1696         case Placement::BOTTOM_RIGHT:
1697             if (arrowBuildplacement == Placement::TOP_RIGHT) {
1698                 path += ReplaceArrowTopRight(
1699                     arrowPosition_.GetX() + childSize_.Width() - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF, childOffsetY);
1700             }
1701             if (arrowBuildplacement == Placement::TOP_LEFT) {
1702                 path +=
1703                     ReplaceArrowTopLeft(arrowPosition_.GetX() + BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF, childOffsetY);
1704             }
1705             if (arrowBuildplacement == Placement::TOP) {
1706                 path += LineTo(arrowTopOffset - ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffsetY); // P1
1707                 path += LineTo(arrowTopOffset - ARROW_VERTICAL_P2_OFFSET_X.ConvertToPx(),
1708                     childOffsetY - ARROW_VERTICAL_P2_OFFSET_Y.ConvertToPx()); // P2
1709                 path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
1710                     arrowTopOffset + ARROW_VERTICAL_P4_OFFSET_X.ConvertToPx(),
1711                     childOffsetY - ARROW_VERTICAL_P4_OFFSET_Y.ConvertToPx());                            // P4
1712                 path += LineTo(arrowTopOffset + ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffsetY); // P5
1713                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
1714                     arrowTopOffset - ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffsetY);
1715                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
1716                     arrowTopOffset - ARROW_VERTICAL_P2_OFFSET_X.ConvertToPx(),
1717                     childOffsetY - ARROW_VERTICAL_P2_OFFSET_Y.ConvertToPx());
1718                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
1719                     arrowTopOffset + ARROW_VERTICAL_P4_OFFSET_X.ConvertToPx(),
1720                     childOffsetY - ARROW_VERTICAL_P4_OFFSET_Y.ConvertToPx());
1721                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
1722                     arrowTopOffset + ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffsetY);
1723             }
1724             break;
1725         default:
1726             break;
1727     }
1728     if (arrowBuildplacement != Placement::TOP_RIGHT) {
1729         path += LineTo(childOffset_.GetX() + childSize_.Width() - radius, childOffsetY);
1730     }
1731     return path;
1732 }
1733 
BuildRightLinePath(float arrowOffset,float radius,Placement & arrowBuildplacement)1734 std::string BubbleLayoutAlgorithm::BuildRightLinePath(float arrowOffset, float radius, Placement& arrowBuildplacement)
1735 {
1736     std::string path;
1737     float childOffsetY = childOffset_.GetY();
1738     auto topOffset = childOffset_.GetY() + radius + BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1739     auto bottomOffset = childOffset_.GetY() + childSize_.Height() - radius - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1740     auto arrowRightOffset = std::clamp(
1741         arrowPosition_.GetY() + arrowOffset, static_cast<float>(topOffset), static_cast<float>(bottomOffset));
1742     switch (arrowPlacement_) {
1743         case Placement::LEFT:
1744         case Placement::LEFT_TOP:
1745         case Placement::LEFT_BOTTOM:
1746             if (arrowBuildplacement == Placement::RIGHT_BOTTOM) {
1747                 path += ReplaceArrowRightBottom(arrowPosition_.GetY() + childSize_.Height()
1748                     - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF, childOffset_.GetX() + childSize_.Width());
1749             }
1750             if (arrowBuildplacement == Placement::RIGHT_TOP) {
1751                 path += ReplaceArrowRightTop(arrowPosition_.GetY() + BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF,
1752                     childOffset_.GetX() + childSize_.Width());
1753             }
1754             if (arrowBuildplacement == Placement::RIGHT) {
1755                 path += LineTo(childOffset_.GetX() + childSize_.Width(),
1756                     arrowRightOffset - ARROW_HORIZON_P1_OFFSET_Y.ConvertToPx()); // P1
1757                 path += LineTo(childOffset_.GetX() + childSize_.Width() + ARROW_HORIZON_P2_OFFSET_X.ConvertToPx(),
1758                     arrowRightOffset - ARROW_HORIZON_P2_OFFSET_Y.ConvertToPx()); // P2
1759                 path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
1760                     childOffset_.GetX() + childSize_.Width() + ARROW_HORIZON_P4_OFFSET_X.ConvertToPx(),
1761                     arrowRightOffset + ARROW_HORIZON_P4_OFFSET_Y.ConvertToPx()); // P4
1762                 path += LineTo(childOffset_.GetX() + childSize_.Width(),
1763                     arrowRightOffset + ARROW_HORIZON_P5_OFFSET_Y.ConvertToPx()); // P5
1764                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO, childOffset_.GetX() + childSize_.Width(),
1765                     arrowRightOffset - ARROW_HORIZON_P1_OFFSET_Y.ConvertToPx());
1766                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
1767                     childOffset_.GetX() + childSize_.Width() + ARROW_HORIZON_P2_OFFSET_X.ConvertToPx(),
1768                     arrowRightOffset - ARROW_HORIZON_P2_OFFSET_Y.ConvertToPx());
1769                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
1770                     childOffset_.GetX() + childSize_.Width() + ARROW_HORIZON_P4_OFFSET_X.ConvertToPx(),
1771                     arrowRightOffset + ARROW_HORIZON_P4_OFFSET_Y.ConvertToPx());
1772                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE, childOffset_.GetX() + childSize_.Width(),
1773                     arrowRightOffset + ARROW_HORIZON_P5_OFFSET_Y.ConvertToPx());
1774             }
1775             break;
1776         default:
1777             break;
1778     }
1779     if (arrowBuildplacement != Placement::RIGHT_BOTTOM) {
1780         path += LineTo(childOffset_.GetX() + childSize_.Width(), childOffsetY + childSize_.Height() - radius);
1781     }
1782     return path;
1783 }
1784 
BuildBottomLinePath(float arrowOffset,float radius,Placement & arrowBuildplacement)1785 std::string BubbleLayoutAlgorithm::BuildBottomLinePath(float arrowOffset, float radius, Placement& arrowBuildplacement)
1786 {
1787     std::string path;
1788     float childOffsetY = childOffset_.GetY();
1789     auto leftOffset = childOffset_.GetX() + radius + BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1790     auto rightOffset = childOffset_.GetX() + childSize_.Width() - radius - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1791     auto arrowBottomOffset = std::clamp(
1792         arrowPosition_.GetX() + arrowOffset, static_cast<float>(leftOffset), static_cast<float>(rightOffset));
1793     switch (arrowPlacement_) {
1794         case Placement::TOP:
1795         case Placement::TOP_LEFT:
1796         case Placement::TOP_RIGHT:
1797             if (arrowBuildplacement == Placement::BOTTOM_RIGHT) {
1798                 path += ReplaceArrowBottomRight(arrowPosition_.GetX() + childSize_.Width()
1799                     - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF, childOffsetY + childSize_.Height());
1800             }
1801             if (arrowBuildplacement == Placement::BOTTOM_LEFT) {
1802                 path += ReplaceArrowBottomLeft(arrowPosition_.GetX() + BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF,
1803                     childOffsetY + childSize_.Height());
1804             }
1805             if (arrowBuildplacement == Placement::BOTTOM) {
1806                 path += LineTo(arrowBottomOffset + ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(),
1807                     childOffsetY + childSize_.Height()); // P1
1808                 path += LineTo(arrowBottomOffset + ARROW_VERTICAL_P2_OFFSET_X.ConvertToPx(),
1809                     childOffsetY + childSize_.Height() + ARROW_VERTICAL_P2_OFFSET_Y.ConvertToPx()); // P2
1810                 path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
1811                     arrowBottomOffset - ARROW_VERTICAL_P4_OFFSET_X.ConvertToPx(),
1812                     childOffsetY + childSize_.Height() + ARROW_VERTICAL_P4_OFFSET_Y.ConvertToPx()); // P4
1813                 path += LineTo(arrowBottomOffset - ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx(),
1814                     childOffsetY + childSize_.Height()); // P5
1815                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
1816                     arrowBottomOffset + ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffsetY + childSize_.Height());
1817                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
1818                     arrowBottomOffset + ARROW_VERTICAL_P2_OFFSET_X.ConvertToPx(),
1819                     childOffsetY + childSize_.Height() + ARROW_VERTICAL_P2_OFFSET_Y.ConvertToPx());
1820                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
1821                     arrowBottomOffset - ARROW_VERTICAL_P4_OFFSET_X.ConvertToPx(),
1822                     childOffsetY + childSize_.Height() + ARROW_VERTICAL_P4_OFFSET_Y.ConvertToPx());
1823                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE, arrowBottomOffset
1824                     - ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffsetY + childSize_.Height());
1825             }
1826             break;
1827         default:
1828             break;
1829     }
1830     if (arrowBuildplacement != Placement::BOTTOM_LEFT) {
1831         path += LineTo(childOffset_.GetX() + radius, childOffsetY + childSize_.Height());
1832     }
1833     return path;
1834 }
1835 
BuildLeftLinePath(float arrowOffset,float radius,Placement & arrowBuildplacement)1836 std::string BubbleLayoutAlgorithm::BuildLeftLinePath(float arrowOffset, float radius, Placement& arrowBuildplacement)
1837 {
1838     std::string path;
1839     float childOffsetY = childOffset_.GetY();
1840     auto topOffset =
1841         childOffset_.GetY() + radius + BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1842     auto bottomOffset = childOffset_.GetY() + childSize_.Height() - radius -
1843                         BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1844     auto arrowLeftOffset = std::clamp(
1845         arrowPosition_.GetY() + arrowOffset, static_cast<float>(topOffset), static_cast<float>(bottomOffset));
1846     switch (arrowPlacement_) {
1847         case Placement::RIGHT:
1848         case Placement::RIGHT_TOP:
1849         case Placement::RIGHT_BOTTOM:
1850             if (arrowBuildplacement == Placement::LEFT_BOTTOM) {
1851                 path += ReplaceArrowLeftBottom(
1852                     arrowPosition_.GetY() + childSize_.Height() - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF,
1853                     childOffset_.GetX());
1854             }
1855             if (arrowBuildplacement == Placement::LEFT_TOP) {
1856                 path += ReplaceArrowLeftTop(
1857                     arrowPosition_.GetY() + BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF, childOffset_.GetX());
1858             }
1859             if (arrowBuildplacement == Placement::LEFT) {
1860                 path += LineTo(childOffset_.GetX(), arrowLeftOffset + ARROW_HORIZON_P1_OFFSET_Y.ConvertToPx()); // P1
1861                 path += LineTo(childOffset_.GetX() - ARROW_HORIZON_P2_OFFSET_X.ConvertToPx(),
1862                     arrowLeftOffset + ARROW_HORIZON_P2_OFFSET_Y.ConvertToPx()); // P2
1863                 path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
1864                     childOffset_.GetX() - ARROW_HORIZON_P4_OFFSET_X.ConvertToPx(),
1865                     arrowLeftOffset - ARROW_HORIZON_P4_OFFSET_Y.ConvertToPx());                                 // P4
1866                 path += LineTo(childOffset_.GetX(), arrowLeftOffset - ARROW_HORIZON_P5_OFFSET_Y.ConvertToPx()); // P5
1867                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
1868                     childOffset_.GetX(), arrowLeftOffset + ARROW_HORIZON_P1_OFFSET_Y.ConvertToPx());
1869                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
1870                     childOffset_.GetX() - ARROW_HORIZON_P2_OFFSET_X.ConvertToPx(),
1871                     arrowLeftOffset + ARROW_HORIZON_P2_OFFSET_Y.ConvertToPx());
1872                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
1873                     childOffset_.GetX() - ARROW_HORIZON_P4_OFFSET_X.ConvertToPx(),
1874                     arrowLeftOffset - ARROW_HORIZON_P4_OFFSET_Y.ConvertToPx());
1875                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
1876                     childOffset_.GetX(), arrowLeftOffset - ARROW_HORIZON_P5_OFFSET_Y.ConvertToPx());
1877             }
1878             break;
1879         default:
1880             break;
1881     }
1882     if (arrowBuildplacement != Placement::LEFT_TOP) {
1883         path += LineTo(childOffset_.GetX(), childOffsetY + radius);
1884     }
1885     return path;
1886 }
1887 
ReplaceArrowTopLeft(const float arrowOffset,const float childOffset)1888 std::string BubbleLayoutAlgorithm::ReplaceArrowTopLeft(const float arrowOffset, const float childOffset)
1889 {
1890     std::string path;
1891     path += LineTo(arrowOffset - ARROW_REPLACE_START_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffset); // P1
1892     path += LineTo(arrowOffset - ARROW_REPLACE_START_VERTICAL_P2_OFFSET_X.ConvertToPx(),
1893         childOffset - ARROW_REPLACE_START_VERTICAL_P2_OFFSET_Y.ConvertToPx()); // P2
1894     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
1895         arrowOffset - ARROW_REPLACE_START_VERTICAL_P4_OFFSET_X.ConvertToPx(),
1896         childOffset - ARROW_REPLACE_START_VERTICAL_P4_OFFSET_Y.ConvertToPx());                         // P4
1897     path += LineTo(arrowOffset + ARROW_REPLACE_START_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffset); // P5
1898     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
1899         arrowOffset - ARROW_REPLACE_START_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffset);
1900     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
1901         arrowOffset - ARROW_REPLACE_START_VERTICAL_P2_OFFSET_X.ConvertToPx(),
1902         childOffset - ARROW_REPLACE_START_VERTICAL_P2_OFFSET_Y.ConvertToPx());
1903     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
1904         arrowOffset - ARROW_REPLACE_START_VERTICAL_P4_OFFSET_X.ConvertToPx(),
1905         childOffset - ARROW_REPLACE_START_VERTICAL_P4_OFFSET_Y.ConvertToPx());
1906     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
1907         arrowOffset + ARROW_REPLACE_START_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffset);
1908     return path;
1909 }
1910 
ReplaceArrowTopRight(const float arrowOffset,const float childOffset)1911 std::string BubbleLayoutAlgorithm::ReplaceArrowTopRight(const float arrowOffset, const float childOffset)
1912 {
1913     std::string path;
1914     path += LineTo((arrowOffset - ARROW_REPLACE_END_VERTICAL_P1_OFFSET_X.ConvertToPx()) / HALF, childOffset); // P1
1915     path += LineTo(arrowOffset - ARROW_REPLACE_END_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffset);          // P1
1916     path += LineTo(arrowOffset + ARROW_REPLACE_END_VERTICAL_P2_OFFSET_X.ConvertToPx(),
1917         childOffset - ARROW_REPLACE_END_VERTICAL_P2_OFFSET_Y.ConvertToPx()); // P2
1918     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
1919         arrowOffset + ARROW_REPLACE_END_VERTICAL_P4_OFFSET_X.ConvertToPx(),
1920         childOffset - ARROW_REPLACE_END_VERTICAL_P4_OFFSET_Y.ConvertToPx());                         // P4
1921     path += LineTo(arrowOffset + ARROW_REPLACE_END_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffset); // P5
1922     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
1923         arrowOffset - ARROW_REPLACE_END_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffset);
1924     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
1925         arrowOffset + ARROW_REPLACE_END_VERTICAL_P2_OFFSET_X.ConvertToPx(),
1926         childOffset - ARROW_REPLACE_END_VERTICAL_P2_OFFSET_Y.ConvertToPx());
1927     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
1928         arrowOffset + ARROW_REPLACE_END_VERTICAL_P4_OFFSET_X.ConvertToPx(),
1929         childOffset - ARROW_REPLACE_END_VERTICAL_P4_OFFSET_Y.ConvertToPx());
1930     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
1931         arrowOffset + ARROW_REPLACE_END_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffset);
1932     return path;
1933 }
1934 
ReplaceArrowRightTop(const float arrowOffset,const float childOffset)1935 std::string BubbleLayoutAlgorithm::ReplaceArrowRightTop(const float arrowOffset, const float childOffset)
1936 {
1937     std::string path;
1938     path += LineTo(childOffset, arrowOffset - ARROW_REPLACE_START_HORIZON_P1_OFFSET_Y.ConvertToPx()); // P1
1939     path += LineTo(childOffset + ARROW_REPLACE_START_HORIZON_P2_OFFSET_X.ConvertToPx(),
1940         arrowOffset - ARROW_REPLACE_START_HORIZON_P2_OFFSET_Y.ConvertToPx()); // P2
1941     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
1942         childOffset + ARROW_REPLACE_START_HORIZON_P4_OFFSET_X.ConvertToPx(),
1943         arrowOffset - ARROW_REPLACE_START_HORIZON_P4_OFFSET_Y.ConvertToPx());                         // P4
1944     path += LineTo(childOffset, arrowOffset + ARROW_REPLACE_START_HORIZON_P5_OFFSET_Y.ConvertToPx()); // P5
1945     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO, childOffset,
1946         arrowOffset - ARROW_REPLACE_START_HORIZON_P1_OFFSET_Y.ConvertToPx());
1947     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
1948         childOffset + ARROW_REPLACE_START_HORIZON_P2_OFFSET_X.ConvertToPx(),
1949         arrowOffset - ARROW_REPLACE_START_HORIZON_P2_OFFSET_Y.ConvertToPx());
1950     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
1951         childOffset + ARROW_REPLACE_START_HORIZON_P4_OFFSET_X.ConvertToPx(),
1952         arrowOffset - ARROW_REPLACE_START_HORIZON_P4_OFFSET_Y.ConvertToPx());
1953     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
1954         childOffset, arrowOffset + ARROW_REPLACE_START_HORIZON_P5_OFFSET_Y.ConvertToPx());
1955     return path;
1956 }
1957 
ReplaceArrowRightBottom(const float arrowOffset,const float childOffset)1958 std::string BubbleLayoutAlgorithm::ReplaceArrowRightBottom(const float arrowOffset, const float childOffset)
1959 {
1960     std::string path;
1961     path += LineTo(childOffset, arrowOffset - ARROW_REPLACE_END_HORIZON_P1_OFFSET_Y.ConvertToPx()); // P1
1962     path += LineTo(childOffset + ARROW_REPLACE_END_HORIZON_P2_OFFSET_X.ConvertToPx(),
1963         arrowOffset + ARROW_REPLACE_END_HORIZON_P2_OFFSET_Y.ConvertToPx()); // P2
1964     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
1965         childOffset + ARROW_REPLACE_END_HORIZON_P4_OFFSET_X.ConvertToPx(),
1966         arrowOffset + ARROW_REPLACE_END_HORIZON_P4_OFFSET_Y.ConvertToPx());                         // P4
1967     path += LineTo(childOffset, arrowOffset + ARROW_REPLACE_END_HORIZON_P5_OFFSET_Y.ConvertToPx()); // P5
1968     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO, childOffset,
1969         arrowOffset - ARROW_REPLACE_END_HORIZON_P1_OFFSET_Y.ConvertToPx());
1970     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
1971         childOffset + ARROW_REPLACE_END_HORIZON_P2_OFFSET_X.ConvertToPx(),
1972         arrowOffset + ARROW_REPLACE_END_HORIZON_P2_OFFSET_Y.ConvertToPx());
1973     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
1974         childOffset + ARROW_REPLACE_END_HORIZON_P4_OFFSET_X.ConvertToPx(),
1975         arrowOffset + ARROW_REPLACE_END_HORIZON_P4_OFFSET_Y.ConvertToPx());
1976     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
1977         childOffset, arrowOffset + ARROW_REPLACE_END_HORIZON_P5_OFFSET_Y.ConvertToPx());
1978     return path;
1979 }
1980 
ReplaceArrowBottomLeft(const float arrowOffset,const float childOffset)1981 std::string BubbleLayoutAlgorithm::ReplaceArrowBottomLeft(const float arrowOffset, const float childOffset)
1982 {
1983     std::string path;
1984     path += LineTo(arrowOffset + ARROW_REPLACE_START_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffset); // P1
1985     path += LineTo(arrowOffset - ARROW_REPLACE_START_VERTICAL_P4_OFFSET_X.ConvertToPx(),
1986         childOffset + ARROW_REPLACE_START_VERTICAL_P4_OFFSET_Y.ConvertToPx()); // P2
1987     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
1988         arrowOffset - ARROW_REPLACE_START_VERTICAL_P2_OFFSET_X.ConvertToPx(),
1989         childOffset + ARROW_REPLACE_START_VERTICAL_P2_OFFSET_Y.ConvertToPx());                         // P4
1990     path += LineTo(arrowOffset - ARROW_REPLACE_START_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffset); // P5
1991     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
1992         arrowOffset + ARROW_REPLACE_START_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffset);
1993     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
1994         arrowOffset - ARROW_REPLACE_START_VERTICAL_P4_OFFSET_X.ConvertToPx(),
1995         childOffset + ARROW_REPLACE_START_VERTICAL_P4_OFFSET_Y.ConvertToPx());
1996     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
1997         arrowOffset - ARROW_REPLACE_START_VERTICAL_P2_OFFSET_X.ConvertToPx(),
1998         childOffset + ARROW_REPLACE_START_VERTICAL_P2_OFFSET_Y.ConvertToPx());
1999     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
2000         arrowOffset - ARROW_REPLACE_START_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffset);
2001     return path;
2002 }
2003 
ReplaceArrowBottomRight(const float arrowOffset,const float childOffset)2004 std::string BubbleLayoutAlgorithm::ReplaceArrowBottomRight(const float arrowOffset, const float childOffset)
2005 {
2006     std::string path;
2007     path += LineTo(arrowOffset + ARROW_REPLACE_END_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffset); // P1
2008     path += LineTo(arrowOffset + ARROW_REPLACE_END_VERTICAL_P4_OFFSET_X.ConvertToPx(),
2009         childOffset + ARROW_REPLACE_END_VERTICAL_P4_OFFSET_Y.ConvertToPx()); // P2
2010     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
2011         arrowOffset + ARROW_REPLACE_END_VERTICAL_P2_OFFSET_X.ConvertToPx(),
2012         childOffset + ARROW_REPLACE_END_VERTICAL_P2_OFFSET_Y.ConvertToPx());                         // P4
2013     path += LineTo(arrowOffset - ARROW_REPLACE_END_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffset); // P5
2014     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
2015         arrowOffset + ARROW_REPLACE_END_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffset);
2016     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
2017         arrowOffset + ARROW_REPLACE_END_VERTICAL_P4_OFFSET_X.ConvertToPx(),
2018         childOffset + ARROW_REPLACE_END_VERTICAL_P4_OFFSET_Y.ConvertToPx());
2019     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
2020         arrowOffset + ARROW_REPLACE_END_VERTICAL_P2_OFFSET_X.ConvertToPx(),
2021         childOffset + ARROW_REPLACE_END_VERTICAL_P2_OFFSET_Y.ConvertToPx());
2022     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
2023         arrowOffset - ARROW_REPLACE_END_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffset);
2024     return path;
2025 }
2026 
ReplaceArrowLeftTop(const float arrowOffset,const float childOffset)2027 std::string BubbleLayoutAlgorithm::ReplaceArrowLeftTop(const float arrowOffset, const float childOffset)
2028 {
2029     std::string path;
2030     path += LineTo(childOffset, arrowOffset + ARROW_REPLACE_START_HORIZON_P1_OFFSET_Y.ConvertToPx()); // P1
2031     path += LineTo(childOffset - ARROW_REPLACE_START_HORIZON_P4_OFFSET_X.ConvertToPx(),
2032         arrowOffset - ARROW_REPLACE_START_HORIZON_P4_OFFSET_Y.ConvertToPx()); // P2
2033     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
2034         childOffset - ARROW_REPLACE_START_HORIZON_P2_OFFSET_X.ConvertToPx(),
2035         arrowOffset - ARROW_REPLACE_START_HORIZON_P2_OFFSET_Y.ConvertToPx());                         // P4
2036     path += LineTo(childOffset, arrowOffset - ARROW_REPLACE_START_HORIZON_P5_OFFSET_Y.ConvertToPx()); // P5
2037     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
2038         childOffset, arrowOffset + ARROW_REPLACE_START_HORIZON_P1_OFFSET_Y.ConvertToPx());
2039     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
2040         childOffset - ARROW_REPLACE_START_HORIZON_P4_OFFSET_X.ConvertToPx(),
2041         arrowOffset - ARROW_REPLACE_START_HORIZON_P4_OFFSET_Y.ConvertToPx());
2042     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
2043         childOffset - ARROW_REPLACE_START_HORIZON_P2_OFFSET_X.ConvertToPx(),
2044         arrowOffset - ARROW_REPLACE_START_HORIZON_P2_OFFSET_Y.ConvertToPx());
2045     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
2046         childOffset, arrowOffset - ARROW_REPLACE_START_HORIZON_P5_OFFSET_Y.ConvertToPx());
2047     return path;
2048 }
2049 
ReplaceArrowLeftBottom(const float arrowOffset,const float childOffset)2050 std::string BubbleLayoutAlgorithm::ReplaceArrowLeftBottom(const float arrowOffset, const float childOffset)
2051 {
2052     std::string path;
2053     path += LineTo(childOffset, arrowOffset + ARROW_REPLACE_END_HORIZON_P1_OFFSET_Y.ConvertToPx()); // P1
2054     path += LineTo(childOffset - ARROW_REPLACE_END_HORIZON_P4_OFFSET_X.ConvertToPx(),
2055         arrowOffset + ARROW_REPLACE_END_HORIZON_P4_OFFSET_Y.ConvertToPx()); // P2
2056     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
2057         childOffset - ARROW_REPLACE_END_HORIZON_P2_OFFSET_X.ConvertToPx(),
2058         arrowOffset + ARROW_REPLACE_END_HORIZON_P2_OFFSET_Y.ConvertToPx());                         // P4
2059     path += LineTo(childOffset, arrowOffset - ARROW_REPLACE_END_HORIZON_P5_OFFSET_Y.ConvertToPx()); // P5
2060     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
2061         childOffset, arrowOffset + ARROW_REPLACE_END_HORIZON_P1_OFFSET_Y.ConvertToPx());
2062     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
2063         childOffset - ARROW_REPLACE_END_HORIZON_P4_OFFSET_X.ConvertToPx(),
2064         arrowOffset + ARROW_REPLACE_END_HORIZON_P4_OFFSET_Y.ConvertToPx());
2065     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
2066         childOffset - ARROW_REPLACE_END_HORIZON_P2_OFFSET_X.ConvertToPx(),
2067         arrowOffset + ARROW_REPLACE_END_HORIZON_P2_OFFSET_Y.ConvertToPx());
2068     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
2069         childOffset, arrowOffset - ARROW_REPLACE_END_HORIZON_P5_OFFSET_Y.ConvertToPx());
2070     return path;
2071 }
2072 
BuildCornerPath(const Placement & placement,float radius)2073 std::string BubbleLayoutAlgorithm::BuildCornerPath(const Placement& placement, float radius)
2074 {
2075     std::string path;
2076     float childOffsetY = childOffset_.GetY();
2077     switch (placement) {
2078         case Placement::TOP_LEFT:
2079             path += ArcTo(radius, radius, 0.0f, 0, childOffset_.GetX() + radius, childOffsetY);
2080             break;
2081         case Placement::TOP_RIGHT:
2082             path += ArcTo(radius, radius, 0.0f, 0, childOffset_.GetX() + childSize_.Width(), childOffsetY + radius);
2083             break;
2084         case Placement::BOTTOM_RIGHT:
2085             path += ArcTo(radius, radius, 0.0f, 0, childOffset_.GetX() + childSize_.Width() - radius,
2086                 childOffsetY + childSize_.Height());
2087             break;
2088         case Placement::BOTTOM_LEFT:
2089             path += ArcTo(radius, radius, 0.0f, 0, childOffset_.GetX(), childOffsetY + childSize_.Height() - radius);
2090             break;
2091         default:
2092             break;
2093     }
2094     return path;
2095 }
2096 
GetChildPosition(const SizeF & childSize,const RefPtr<BubbleLayoutProperty> & layoutProp,bool UseArrowOffset)2097 OffsetF BubbleLayoutAlgorithm::GetChildPosition(
2098     const SizeF& childSize, const RefPtr<BubbleLayoutProperty>& layoutProp, bool UseArrowOffset)
2099 {
2100     OffsetF bottomPosition;
2101     OffsetF topPosition;
2102     OffsetF topArrowPosition;
2103     OffsetF bottomArrowPosition;
2104     OffsetF fitPosition;
2105     OffsetF originOffset;
2106     OffsetF originArrowOffset;
2107     OffsetF childPosition;
2108 
2109     InitArrowTopAndBottomPosition(topArrowPosition, bottomArrowPosition, topPosition, bottomPosition, childSize);
2110     GetPositionWithPlacement(originOffset, originArrowOffset, childSize, placement_);
2111     originOffset = originOffset + positionOffset_;
2112     originArrowOffset += positionOffset_;
2113     arrowPlacement_ = placement_;
2114 
2115     // Fit popup to screen range.
2116     ErrorPositionType errorType = GetErrorPositionType(originOffset, childSize);
2117     if (errorType == ErrorPositionType::NORMAL) {
2118         arrowPosition_ = originArrowOffset;
2119         return originOffset;
2120     }
2121 
2122     if (placement_ == Placement::TOP || placement_ == Placement::TOP_LEFT || placement_ == Placement::TOP_RIGHT) {
2123         fitPosition = topPosition;
2124         arrowPosition_ = topArrowPosition;
2125         arrowPlacement_ = Placement::TOP;
2126     } else {
2127         placement_ = Placement::BOTTOM;
2128         fitPosition = bottomPosition;
2129         arrowPosition_ = bottomArrowPosition;
2130         arrowPlacement_ = Placement::BOTTOM;
2131     }
2132 
2133     childPosition = FitToScreen(fitPosition, childSize);
2134     if (UseArrowOffset) {
2135         arrowPosition_.SetX(
2136             childPosition.GetX() + border_.TopLeftRadius().GetX().ConvertToPx() + BEZIER_WIDTH_HALF.ConvertToPx());
2137     }
2138 
2139     if (GetErrorPositionType(childPosition, childSize) == ErrorPositionType::NORMAL) {
2140         return childPosition;
2141     }
2142 
2143     // Fit popup to opposite position
2144     fitPosition = fitPosition == topPosition ? bottomPosition : topPosition;
2145     arrowPosition_ = arrowPlacement_ == Placement::TOP ? bottomArrowPosition : topArrowPosition;
2146     arrowPlacement_ = arrowPlacement_ == Placement::TOP ? Placement::BOTTOM : Placement::TOP;
2147     placement_ = arrowPlacement_ == Placement::TOP ? Placement::BOTTOM : Placement::TOP;
2148 
2149     childPosition = FitToScreen(fitPosition, childSize);
2150     if (UseArrowOffset) {
2151         arrowPosition_.SetX(
2152             childPosition.GetX() + border_.TopLeftRadius().GetX().ConvertToPx() + BEZIER_WIDTH_HALF.ConvertToPx());
2153     }
2154 
2155     if (GetErrorPositionType(childPosition, childSize) == ErrorPositionType::NORMAL) {
2156         return childPosition;
2157     }
2158 
2159     // If childPosition is error, adjust bubble to origin position.
2160     arrowPlacement_ = placement_;
2161     arrowPosition_ = originArrowOffset;
2162 
2163     return originOffset;
2164 }
2165 
InitArrowTopAndBottomPosition(OffsetF & topArrowPosition,OffsetF & bottomArrowPosition,OffsetF & topPosition,OffsetF & bottomPosition,const SizeF & childSize)2166 void BubbleLayoutAlgorithm::InitArrowTopAndBottomPosition(OffsetF& topArrowPosition, OffsetF& bottomArrowPosition,
2167     OffsetF& topPosition, OffsetF& bottomPosition, const SizeF& childSize)
2168 {
2169     auto arrowCenter = targetOffset_.GetX() + targetSize_.Width() / 2.0;
2170     auto horizonSpacing = static_cast<float>(HORIZON_SPACING_WITH_SCREEN.ConvertToPx());
2171     double arrowWidth = ARROW_WIDTH.ConvertToPx();
2172     float radius = borderRadius_.ConvertToPx();
2173     auto safePosition = horizonSpacing + radius + arrowWidth / 2.0;
2174 
2175     GetPositionWithPlacement(topPosition, topArrowPosition, childSize, Placement::TOP);
2176     GetPositionWithPlacement(bottomPosition, bottomArrowPosition, childSize, Placement::BOTTOM);
2177 
2178     // move the arrow to safe position while arrow too close to window
2179     // In order not to separate the bubble from the arrow
2180     // If ArrowOffset is not set, arrow always point to the middle of the targetNode
2181     if (arrowCenter < safePosition) {
2182         topArrowPosition = topArrowPosition + OffsetF(safePosition - arrowCenter, 0);
2183         bottomArrowPosition = bottomArrowPosition + OffsetF(safePosition - arrowCenter, 0);
2184     }
2185     if (arrowCenter > selfSize_.Width() - safePosition) {
2186         topArrowPosition = topArrowPosition - OffsetF(arrowCenter + safePosition - selfSize_.Width(), 0);
2187         bottomArrowPosition = bottomArrowPosition - OffsetF(arrowCenter + safePosition - selfSize_.Width(), 0);
2188     }
2189 }
2190 
GetPositionWithPlacement(OffsetF & childPosition,OffsetF & arrowPosition,const SizeF & childSize,Placement placement)2191 void BubbleLayoutAlgorithm::GetPositionWithPlacement(
2192     OffsetF& childPosition, OffsetF& arrowPosition, const SizeF& childSize, Placement placement)
2193 {
2194     float bubbleSpacing = scaledBubbleSpacing_;
2195     float marginRight = 0.0f;
2196     float marginBottom = 0.0f;
2197     float marginTop = 0.0f;
2198     float marginLeft = 0.0f;
2199     float arrowHalfWidth = ARROW_WIDTH.ConvertToPx() / 2.0;
2200     float radius = borderRadius_.ConvertToPx();
2201     float targetSpace = targetSpace_.ConvertToPx();
2202     switch (placement) {
2203         case Placement::TOP:
2204             childPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
2205                 targetOffset_.GetY() - childSize.Height() - targetSpace - arrowHeight_);
2206             arrowPosition = childPosition + OffsetF(std::max(padding_.Left().ConvertToPx(),
2207             border_.TopLeftRadius().GetX().ConvertToPx()) +
2208             BEZIER_WIDTH_HALF.ConvertToPx(), childSize.Height() + arrowHeight_);
2209             break;
2210         case Placement::TOP_LEFT:
2211             childPosition = OffsetF(targetOffset_.GetX() - marginRight,
2212                 targetOffset_.GetY() - childSize.Height() - bubbleSpacing - marginBottom - targetSpace - arrowHeight_);
2213             arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, childSize.Height() + bubbleSpacing);
2214             break;
2215         case Placement::TOP_RIGHT:
2216             childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
2217                 targetOffset_.GetY() - childSize.Height() - targetSpace - bubbleSpacing - marginBottom - arrowHeight_);
2218             arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, childSize.Height() + bubbleSpacing);
2219             break;
2220         case Placement::BOTTOM:
2221             childPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
2222                 targetOffset_.GetY() + targetSize_.Height() + targetSpace + arrowHeight_);
2223             arrowPosition = childPosition + OffsetF(std::max(padding_.Left().ConvertToPx(),
2224             border_.BottomLeftRadius().GetX().ConvertToPx()) +
2225             BEZIER_WIDTH_HALF.ConvertToPx(), -arrowHeight_);
2226             break;
2227         case Placement::BOTTOM_LEFT:
2228             childPosition = OffsetF(targetOffset_.GetX() - marginRight,
2229                 targetOffset_.GetY() + targetSize_.Height() + targetSpace + bubbleSpacing + marginTop + arrowHeight_);
2230             arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, -bubbleSpacing);
2231             break;
2232         case Placement::BOTTOM_RIGHT:
2233             childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
2234                 targetOffset_.GetY() + targetSize_.Height() + targetSpace + bubbleSpacing + marginTop + arrowHeight_);
2235             arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, -bubbleSpacing);
2236             break;
2237         case Placement::LEFT:
2238             childPosition = OffsetF(
2239                 targetOffset_.GetX() - targetSpace - bubbleSpacing - childSize.Width() - marginRight - arrowHeight_,
2240                 targetOffset_.GetY() + targetSize_.Height() / HALF - childSize.Height() / HALF);
2241             arrowPosition = childPosition + OffsetF(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
2242             break;
2243         case Placement::LEFT_TOP:
2244             childPosition = OffsetF(
2245                 targetOffset_.GetX() - targetSpace - bubbleSpacing - childSize.Width() - marginRight - arrowHeight_,
2246                 targetOffset_.GetY() - marginBottom);
2247             arrowPosition = childPosition + OffsetF(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
2248             break;
2249         case Placement::LEFT_BOTTOM:
2250             childPosition = OffsetF(
2251                 targetOffset_.GetX() - targetSpace - bubbleSpacing - childSize.Width() - marginRight - arrowHeight_,
2252                 targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
2253             arrowPosition = childPosition + OffsetF(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
2254             break;
2255         case Placement::RIGHT:
2256             childPosition = OffsetF(
2257                 targetOffset_.GetX() + targetSize_.Width() + targetSpace + bubbleSpacing + marginLeft + arrowHeight_,
2258                 targetOffset_.GetY() + targetSize_.Height() / HALF - childSize.Height() / HALF);
2259             arrowPosition = childPosition + OffsetF(-bubbleSpacing, radius + arrowHalfWidth);
2260             break;
2261         case Placement::RIGHT_TOP:
2262             childPosition = OffsetF(
2263                 targetOffset_.GetX() + targetSize_.Width() + targetSpace + bubbleSpacing + marginLeft + arrowHeight_,
2264                 targetOffset_.GetY() - marginBottom);
2265             arrowPosition = childPosition + OffsetF(-bubbleSpacing, radius + arrowHalfWidth);
2266             break;
2267         case Placement::RIGHT_BOTTOM:
2268             childPosition = OffsetF(
2269                 targetOffset_.GetX() + targetSize_.Width() + targetSpace + bubbleSpacing + marginLeft + arrowHeight_,
2270                 targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
2271             arrowPosition = childPosition + OffsetF(-bubbleSpacing, radius + arrowHalfWidth);
2272             break;
2273         default:
2274             break;
2275     }
2276 }
2277 
GetErrorPositionType(const OffsetF & childOffset,const SizeF & childSize)2278 BubbleLayoutAlgorithm::ErrorPositionType BubbleLayoutAlgorithm::GetErrorPositionType(
2279     const OffsetF& childOffset, const SizeF& childSize)
2280 {
2281     auto horizonSpacing = static_cast<float>(HORIZON_SPACING_WITH_SCREEN.ConvertToPx());
2282     RectF validRegion =
2283         RectF(OffsetF(horizonSpacing, top_), OffsetF(selfSize_.Width() - horizonSpacing, selfSize_.Height() - bottom_));
2284     PointF childPoint(childOffset.GetX(), childOffset.GetY());
2285     if (!validRegion.IsInRegion(childPoint)) {
2286         return ErrorPositionType::TOP_LEFT_ERROR;
2287     }
2288     if (!validRegion.IsInRegion(
2289             PointF(childOffset.GetX() + childSize.Width(), childOffset.GetY() + childSize.Height()))) {
2290         return ErrorPositionType::BOTTOM_RIGHT_ERROR;
2291     }
2292     return ErrorPositionType::NORMAL;
2293 }
2294 
FitToScreen(const OffsetF & fitPosition,const SizeF & childSize)2295 OffsetF BubbleLayoutAlgorithm::FitToScreen(const OffsetF& fitPosition, const SizeF& childSize)
2296 {
2297     auto validation = GetErrorPositionType(fitPosition, childSize);
2298     if (validation == ErrorPositionType::NORMAL) {
2299         return fitPosition;
2300     }
2301     OffsetF childPosition = fitPosition;
2302     auto horizonSpacing = static_cast<float>(HORIZON_SPACING_WITH_SCREEN.ConvertToPx());
2303     if (validation == ErrorPositionType::TOP_LEFT_ERROR) {
2304         childPosition.SetX(horizonSpacing);
2305     } else {
2306         childPosition.SetX(selfSize_.Width() - childSize.Width() - horizonSpacing);
2307     }
2308     return childPosition;
2309 }
2310 
2311 } // namespace OHOS::Ace::NG
2312