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