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