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     UpdateHostWindowRect();
482     SetHotAreas(showInSubWindow, isBlock, frameNode, bubblePattern->GetContainerId());
483     UpdateClipOffset(frameNode);
484 }
485 
UpdateHostWindowRect()486 void BubbleLayoutAlgorithm::UpdateHostWindowRect()
487 {
488     hostWindowRect_ = SubwindowManager::GetInstance()->GetParentWindowRect();
489     auto currentId = Container::CurrentId();
490     auto container = Container::Current();
491     CHECK_NULL_VOID(container);
492     if (container->IsSubContainer()) {
493         currentId = SubwindowManager::GetInstance()->GetParentContainerId(currentId);
494         container = AceEngine::Get().GetContainer(currentId);
495         CHECK_NULL_VOID(container);
496     }
497     if (container->IsUIExtensionWindow()) {
498         auto subwindow = SubwindowManager::GetInstance()->GetSubwindow(currentId);
499         CHECK_NULL_VOID(subwindow);
500         hostWindowRect_ = subwindow->GetUIExtensionHostWindowRect();
501     }
502 }
503 
SetHotAreas(bool showInSubWindow,bool isBlock,RefPtr<FrameNode> frameNode,int32_t containerId)504 void BubbleLayoutAlgorithm::SetHotAreas(bool showInSubWindow, bool isBlock,
505     RefPtr<FrameNode> frameNode, int32_t containerId)
506 {
507     if (showInSubWindow) {
508         std::vector<Rect> rects;
509         if (!isBlock) {
510             auto rect = Rect(childOffsetForPaint_.GetX(), childOffsetForPaint_.GetY(),
511                 childSize_.Width(), childSize_.Height());
512             rects.emplace_back(rect);
513         } else {
514             auto rect = Rect(childOffsetForPaint_.GetX(), childOffsetForPaint_.GetY(),
515                 childSize_.Width(), childSize_.Height());
516             rects.emplace_back(hostWindowRect_);
517             rects.emplace_back(rect);
518         }
519         auto context = PipelineContext::GetCurrentContext();
520         CHECK_NULL_VOID(context);
521         auto taskExecutor = context->GetTaskExecutor();
522         CHECK_NULL_VOID(taskExecutor);
523         taskExecutor->PostTask(
524             [rects, containerId, frameNodeWK = WeakClaim(RawPtr(frameNode))]() {
525                 auto frameNode = frameNodeWK.Upgrade();
526                 CHECK_NULL_VOID(frameNode);
527                 auto subWindowMgr = SubwindowManager::GetInstance();
528                 subWindowMgr->SetHotAreas(rects, frameNode->GetId(), containerId);
529             },
530             TaskExecutor::TaskType::UI, "ArkUIPopupSetHotAreas");
531     }
532 }
533 
GetIfNeedArrow(const RefPtr<BubbleLayoutProperty> & bubbleProp,const SizeF & childSize)534 bool BubbleLayoutAlgorithm::GetIfNeedArrow(const RefPtr<BubbleLayoutProperty>& bubbleProp, const SizeF& childSize)
535 {
536     auto enableArrow = bubbleProp->GetEnableArrow().value_or(true);
537     if (!enableArrow) {
538         return false;
539     }
540     double arrowWidth = BUBBLE_ARROW_WIDTH.ConvertToPx();
541     double twoRadiusPx = borderRadius_.ConvertToPx() * 2.0;
542     if (setHorizontal_.find(placement_) != setHorizontal_.end()) {
543         if (childSize.Height() >= twoRadiusPx + arrowWidth) {
544             return true;
545         }
546     }
547     if (setVertical_.find(placement_) != setVertical_.end()) {
548         if (childSize.Width() >= twoRadiusPx + arrowWidth) {
549             return true;
550         }
551     }
552     return false;
553 }
554 
InitProps(const RefPtr<BubbleLayoutProperty> & layoutProp,bool showInSubWindow)555 void BubbleLayoutAlgorithm::InitProps(const RefPtr<BubbleLayoutProperty>& layoutProp, bool showInSubWindow)
556 {
557     auto pipeline = PipelineBase::GetCurrentContext();
558     CHECK_NULL_VOID(pipeline);
559     auto popupTheme = pipeline->GetTheme<PopupTheme>();
560     CHECK_NULL_VOID(popupTheme);
561     padding_ = popupTheme->GetPadding();
562     userSetTargetSpace_ = layoutProp->GetTargetSpace().value_or(Dimension(0.0f));
563     borderRadius_ = layoutProp->GetRadius().value_or(popupTheme->GetRadius().GetX());
564     border_.SetBorderRadius(Radius(borderRadius_));
565     targetSpace_ = layoutProp->GetTargetSpace().value_or(popupTheme->GetTargetSpace());
566     placement_ = layoutProp->GetPlacement().value_or(Placement::BOTTOM);
567     isCaretMode_ = layoutProp->GetIsCaretMode().value_or(true);
568     auto height = layoutProp->GetArrowHeight().value_or(DEFAULT_BUBBLE_ARROW_HEIGHT);
569     auto width = layoutProp->GetArrowWidth().value_or(DEFAULT_BUBBLE_ARROW_WIDTH);
570     calculateArrowPoint(height, width);
571     arrowHeight_ = height.ConvertToPx();
572     scaledBubbleSpacing_ = arrowHeight_;
573     realArrowWidth_ = BUBBLE_ARROW_WIDTH.ConvertToPx();
574     realArrowHeight_ = BUBBLE_ARROW_HEIGHT.ConvertToPx();
575     positionOffset_ = layoutProp->GetPositionOffset().value_or(OffsetF());
576     auto constraint = layoutProp->GetLayoutConstraint();
577     enableArrow_ = layoutProp->GetEnableArrow().value_or(true);
578     followTransformOfTarget_ = layoutProp->GetFollowTransformOfTarget().value_or(false);
579     auto wrapperIdealSize =
580         CreateIdealSize(constraint.value(), Axis::FREE, layoutProp->GetMeasureType(MeasureType::MATCH_PARENT), true);
581     wrapperSize_ = wrapperIdealSize;
582     targetSecurity_ = HORIZON_SPACING_WITH_SCREEN.ConvertToPx();
583     auto pipelineContext = PipelineContext::GetMainPipelineContext();
584     CHECK_NULL_VOID(pipelineContext);
585     auto safeAreaManager = pipelineContext->GetSafeAreaManager();
586     CHECK_NULL_VOID(safeAreaManager);
587     top_ = safeAreaManager->GetSafeAreaWithoutProcess().top_.Length();
588     bottom_ = safeAreaManager->GetSafeAreaWithoutProcess().bottom_.Length();
589     marginStart_ = MARGIN_SPACE.ConvertToPx() + DRAW_EDGES_SPACE.ConvertToPx();
590     marginEnd_ = MARGIN_SPACE.ConvertToPx() + DRAW_EDGES_SPACE.ConvertToPx();
591     marginTop_ = top_ + DRAW_EDGES_SPACE.ConvertToPx();
592     marginBottom_ = bottom_ + DRAW_EDGES_SPACE.ConvertToPx();
593     showArrow_ = false;
594 }
595 
GetChildPositionNew(const SizeF & childSize,const RefPtr<BubbleLayoutProperty> & bubbleProp)596 OffsetF BubbleLayoutAlgorithm::GetChildPositionNew(
597     const SizeF& childSize, const RefPtr<BubbleLayoutProperty>& bubbleProp)
598 {
599     static std::map<Placement, std::vector<Placement>> PLACEMENT_STATES = {
600         { Placement::BOTTOM_LEFT,
601             {
602                 Placement::BOTTOM_LEFT,
603                 Placement::TOP_LEFT,
604                 Placement::RIGHT_TOP,
605                 Placement::LEFT_TOP,
606                 Placement::NONE,
607             } },
608         { Placement::BOTTOM,
609             {
610                 Placement::BOTTOM,
611                 Placement::TOP,
612                 Placement::RIGHT,
613                 Placement::LEFT,
614                 Placement::NONE,
615             } },
616         { Placement::BOTTOM_RIGHT,
617             {
618                 Placement::BOTTOM_RIGHT,
619                 Placement::TOP_RIGHT,
620                 Placement::RIGHT_BOTTOM,
621                 Placement::LEFT_BOTTOM,
622                 Placement::NONE,
623             } },
624         { Placement::TOP_LEFT,
625             {
626                 Placement::TOP_LEFT,
627                 Placement::BOTTOM_LEFT,
628                 Placement::RIGHT_TOP,
629                 Placement::LEFT_TOP,
630                 Placement::NONE,
631             } },
632         { Placement::TOP,
633             {
634                 Placement::TOP,
635                 Placement::BOTTOM,
636                 Placement::RIGHT,
637                 Placement::LEFT,
638                 Placement::NONE,
639             } },
640         { Placement::TOP_RIGHT,
641             {
642                 Placement::TOP_RIGHT,
643                 Placement::BOTTOM_RIGHT,
644                 Placement::RIGHT_BOTTOM,
645                 Placement::LEFT_BOTTOM,
646                 Placement::NONE,
647             } },
648         { Placement::LEFT_TOP,
649             {
650                 Placement::LEFT_TOP,
651                 Placement::RIGHT_TOP,
652                 Placement::BOTTOM_LEFT,
653                 Placement::TOP_LEFT,
654                 Placement::NONE,
655             } },
656         { Placement::LEFT,
657             {
658                 Placement::LEFT,
659                 Placement::RIGHT,
660                 Placement::BOTTOM,
661                 Placement::TOP,
662                 Placement::NONE,
663             } },
664         { Placement::LEFT_BOTTOM,
665             {
666                 Placement::LEFT_BOTTOM,
667                 Placement::RIGHT_BOTTOM,
668                 Placement::BOTTOM_RIGHT,
669                 Placement::TOP_RIGHT,
670                 Placement::NONE,
671             } },
672         { Placement::RIGHT_TOP,
673             {
674                 Placement::RIGHT_TOP,
675                 Placement::LEFT_TOP,
676                 Placement::BOTTOM_LEFT,
677                 Placement::TOP_LEFT,
678                 Placement::NONE,
679             } },
680         { Placement::RIGHT,
681             {
682                 Placement::RIGHT,
683                 Placement::LEFT,
684                 Placement::BOTTOM,
685                 Placement::TOP,
686                 Placement::NONE,
687             } },
688         { Placement::RIGHT_BOTTOM,
689             {
690                 Placement::RIGHT_BOTTOM,
691                 Placement::LEFT_BOTTOM,
692                 Placement::BOTTOM_RIGHT,
693                 Placement::TOP_RIGHT,
694                 Placement::NONE,
695             } },
696     };
697     OffsetF bottomPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
698         targetOffset_.GetY() + targetSize_.Height() + targetSpace_.ConvertToPx() + arrowHeight_);
699     OffsetF topPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
700         targetOffset_.GetY() - childSize.Height() - targetSpace_.ConvertToPx() - arrowHeight_);
701     OffsetF defaultPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
702         targetOffset_.GetY() + (targetSize_.Height() - childSize.Height()) / 2.0);
703     OffsetF childPosition;
704     OffsetF ArrowOffset;
705     OffsetF position = defaultPosition;
706     auto positionOffset = positionOffset_;
707     bool didNeedArrow = false;
708     std::vector<Placement> currentPlacementStates = PLACEMENT_STATES.find(Placement::BOTTOM)->second;
709     if (PLACEMENT_STATES.find(placement_) != PLACEMENT_STATES.end()) {
710         currentPlacementStates = PLACEMENT_STATES.find(placement_)->second;
711     }
712     size_t step = ALIGNMENT_STEP_OFFSET;
713     bVertical_ = false;
714     bHorizontal_ = false;
715     for (size_t i = 0, len = currentPlacementStates.size(); i < len;) {
716         placement_ = currentPlacementStates[i];
717         if (placement_ == Placement::NONE) {
718             break;
719         }
720         if (bCaretMode_) { // Caret mode
721             if ((placement_ != Placement::BOTTOM) && (placement_ != Placement::TOP)) {
722                 i++;
723                 continue;
724             }
725         }
726         if (bVertical_) {
727             if (setHorizontal_.find(placement_) != setHorizontal_.end()) {
728                 i++;
729                 continue;
730             }
731         }
732         if (bHorizontal_) {
733             if (setVertical_.find(placement_) != setVertical_.end()) {
734                 i++;
735                 continue;
736             }
737         }
738         if (i >= step) {
739             positionOffset_ = OffsetF(0.0f, 0.0f);
740         }
741         childPosition = GetPositionWithPlacementNew(childSize, topPosition, bottomPosition, ArrowOffset);
742         UpdateChildPosition(childPosition);
743         didNeedArrow = GetIfNeedArrow(bubbleProp, childSize_);
744         position = FitToScreenNew(childPosition, step, i, childSize, didNeedArrow);
745         if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
746             continue;
747         }
748         break;
749     }
750     if (placement_ == Placement::NONE) {
751         bVertical_ = false;
752         bHorizontal_ = false;
753         position = GetAdjustPosition(currentPlacementStates, step, childSize, topPosition, bottomPosition, ArrowOffset);
754         if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
755             showArrow_ = false;
756             position = AdjustPosition(defaultPosition, childSize.Width(), childSize.Height(), targetSecurity_);
757             if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
758                 auto x = std::clamp(
759                     defaultPosition.GetX(), marginStart_, wrapperSize_.Width() - childSize.Width() - marginEnd_);
760                 auto y = marginTop_;
761                 position = OffsetF(x, y);
762             }
763         }
764     }
765     positionOffset_ = positionOffset;
766     arrowPlacement_ = placement_;
767     arrowPosition_ = ArrowOffset;
768     return position;
769 }
770 
GetAdjustPosition(std::vector<Placement> & currentPlacementStates,size_t step,const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)771 OffsetF BubbleLayoutAlgorithm::GetAdjustPosition(std::vector<Placement>& currentPlacementStates, size_t step,
772     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
773 {
774     OffsetF childPosition;
775     OffsetF position;
776     float width = 0.0f;
777     float height = 0.0f;
778     for (size_t i = 0, len = currentPlacementStates.size(); i < len;) {
779         placement_ = currentPlacementStates[i];
780         if (placement_ == Placement::NONE) {
781             break;
782         }
783         if (bCaretMode_) { // Caret mode
784             if ((placement_ != Placement::BOTTOM) && (placement_ != Placement::TOP)) {
785                 i++;
786                 continue;
787             }
788         }
789         if (bVertical_) {
790             if (setHorizontal_.find(placement_) != setHorizontal_.end()) {
791                 i++;
792                 continue;
793             }
794         }
795         if (bHorizontal_) {
796             if (setVertical_.find(placement_) != setVertical_.end()) {
797                 i++;
798                 continue;
799             }
800         }
801         childPosition = GetPositionWithPlacementNew(childSize, topPosition, bottomPosition, arrowPosition);
802         UpdateChildPosition(childPosition);
803         width = childSize.Width();
804         height = childSize.Height();
805         if (showArrow_) {
806             if (setHorizontal_.find(placement_) != setHorizontal_.end()) {
807                 width += BUBBLE_ARROW_HEIGHT.ConvertToPx();
808             }
809             if (setVertical_.find(placement_) != setVertical_.end()) {
810                 height += BUBBLE_ARROW_HEIGHT.ConvertToPx();
811             }
812         }
813         position = AdjustPosition(childPosition, width, height, targetSpace_.ConvertToPx());
814         if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
815             i += step;
816             continue;
817         }
818         break;
819     }
820     return position;
821 }
822 
AdjustPosition(const OffsetF & position,float width,float height,float space)823 OffsetF BubbleLayoutAlgorithm::AdjustPosition(const OffsetF& position, float width, float height, float space)
824 {
825     float xMax = 0.0f;
826     float yMax = 0.0f;
827     float xMin = 1.0f;
828     float yMin = 1.0f;
829     float yTargetOffset = 0.0f;
830     switch (placement_) {
831         case Placement::LEFT_TOP:
832         case Placement::LEFT_BOTTOM:
833         case Placement::LEFT: {
834             xMin = marginStart_;
835             xMax = std::min(targetOffset_.GetX() - width - space, wrapperSize_.Width() - marginEnd_ - width);
836             yMin = marginTop_;
837             yMax = wrapperSize_.Height() - height - marginBottom_;
838             break;
839         }
840         case Placement::RIGHT_TOP:
841         case Placement::RIGHT_BOTTOM:
842         case Placement::RIGHT: {
843             if (showArrow_) {
844                 space = space + BUBBLE_ARROW_HEIGHT.ConvertToPx();
845             }
846             xMin = std::max(targetOffset_.GetX() + targetSize_.Width() + space, marginStart_);
847             xMax = wrapperSize_.Width() - width - marginEnd_;
848             yMin = marginTop_;
849             yMax = wrapperSize_.Height() - height - marginBottom_;
850             break;
851         }
852         case Placement::TOP_LEFT:
853         case Placement::TOP_RIGHT:
854         case Placement::TOP: {
855             xMin = marginStart_;
856             xMax = wrapperSize_.Width() - width - marginEnd_;
857             yMin = marginTop_;
858             yMax = std::min(targetOffset_.GetY() - height - space, wrapperSize_.Height() - marginBottom_ - height);
859             yTargetOffset = targetSecurity_;
860             break;
861         }
862         case Placement::BOTTOM_LEFT:
863         case Placement::BOTTOM_RIGHT:
864         case Placement::BOTTOM: {
865             if (showArrow_) {
866                 space = space + BUBBLE_ARROW_HEIGHT.ConvertToPx();
867             }
868             xMin = marginStart_;
869             xMax = wrapperSize_.Width() - width - marginEnd_;
870             yMin = std::max(targetOffset_.GetY() + targetSize_.Height() + space, marginTop_);
871             yMax = wrapperSize_.Height() - height - marginBottom_;
872             yTargetOffset = -targetSecurity_;
873             break;
874         }
875         case Placement::NONE: {
876             xMin = marginStart_;
877             xMax = wrapperSize_.Width() - width - marginEnd_;
878             yMin = marginTop_;
879             yMax = wrapperSize_.Height() - height - marginBottom_;
880             break;
881         }
882         default:
883             break;
884     }
885     if ((xMax < xMin && !isGreatWrapperWidth_) || yMax < yMin) {
886         return OffsetF(0.0f, 0.0f);
887     } else if (xMax < xMin && isGreatWrapperWidth_) {
888         auto y = std::clamp(position.GetY(), yMin, yMax);
889         return OffsetF(0.0f, y + yTargetOffset);
890     }
891     auto x = std::clamp(position.GetX(), xMin, xMax);
892     auto y = std::clamp(position.GetY(), yMin, yMax);
893     return OffsetF(x, y);
894 }
895 
GetPositionWithPlacementNew(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)896 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementNew(
897     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
898 {
899     OffsetF childPosition;
900     auto func = placementFuncMap_.find(placement_);
901     if (func != placementFuncMap_.end()) {
902         auto placementFunc = func->second;
903         if (placementFunc != nullptr) {
904             childPosition = (this->*placementFunc)(childSize, topPosition, bottomPosition, arrowPosition);
905         }
906     }
907     return childPosition;
908 }
909 
FitToScreenNew(const OffsetF & position,size_t step,size_t & i,const SizeF & childSize,bool didNeedArrow)910 OffsetF BubbleLayoutAlgorithm::FitToScreenNew(
911     const OffsetF& position, size_t step, size_t& i, const SizeF& childSize, bool didNeedArrow)
912 {
913     if (!CheckPosition(position, childSize, step, i)) {
914         return OffsetF(0.0f, 0.0f);
915     }
916     return position;
917 }
918 
AddTargetSpace(const OffsetF & position)919 OffsetF BubbleLayoutAlgorithm::AddTargetSpace(const OffsetF& position)
920 {
921     auto x = position.GetX();
922     auto y = position.GetY();
923     switch (placement_) {
924         case Placement::BOTTOM_LEFT:
925         case Placement::BOTTOM_RIGHT:
926         case Placement::BOTTOM: {
927             y += targetSpace_.ConvertToPx();
928             break;
929         }
930         case Placement::TOP_LEFT:
931         case Placement::TOP_RIGHT:
932         case Placement::TOP: {
933             y -= targetSpace_.ConvertToPx();
934             break;
935         }
936         case Placement::RIGHT_TOP:
937         case Placement::RIGHT_BOTTOM:
938         case Placement::RIGHT: {
939             x += targetSpace_.ConvertToPx();
940             break;
941         }
942         case Placement::LEFT_TOP:
943         case Placement::LEFT_BOTTOM:
944         case Placement::LEFT: {
945             x -= targetSpace_.ConvertToPx();
946             break;
947         }
948         default: {
949             y += targetSpace_.ConvertToPx();
950             break;
951         }
952     }
953     return OffsetF(x, y);
954 }
955 
AddOffset(const OffsetF & position)956 OffsetF BubbleLayoutAlgorithm::AddOffset(const OffsetF& position)
957 {
958     auto x = position.GetX();
959     auto y = position.GetY();
960     x += positionOffset_.GetX();
961     y += positionOffset_.GetY();
962     return OffsetF(x, y);
963 }
964 
UpdateChildPosition(OffsetF & childOffset)965 void BubbleLayoutAlgorithm::UpdateChildPosition(OffsetF& childOffset)
966 {
967     double arrowWidth = BUBBLE_ARROW_WIDTH.ConvertToPx();
968     double twoRadiusPx = borderRadius_.ConvertToPx() * 2.0;
969     float movingDistance = BUBBLE_ARROW_HEIGHT.ConvertToPx();
970     switch (placement_) {
971         case Placement::TOP:
972         case Placement::TOP_LEFT:
973         case Placement::TOP_RIGHT:
974             showArrow_ = GreatOrEqual(childSize_.Width() - twoRadiusPx, arrowWidth);
975             if (!showArrow_ || !enableArrow_) {
976                 childOffset += OffsetF(0.0, movingDistance);
977             }
978             break;
979         case Placement::BOTTOM:
980         case Placement::BOTTOM_LEFT:
981         case Placement::BOTTOM_RIGHT:
982             showArrow_ = GreatOrEqual(childSize_.Width() - twoRadiusPx, arrowWidth);
983             if (!showArrow_ || !enableArrow_) {
984                 childOffset += OffsetF(0.0, -(movingDistance));
985             }
986             break;
987         case Placement::LEFT:
988         case Placement::LEFT_TOP:
989         case Placement::LEFT_BOTTOM:
990             showArrow_ = GreatOrEqual(childSize_.Height() - twoRadiusPx, arrowWidth);
991             if (!showArrow_ || !enableArrow_) {
992                 childOffset += OffsetF(movingDistance, 0.0);
993             }
994             break;
995         case Placement::RIGHT:
996         case Placement::RIGHT_TOP:
997         case Placement::RIGHT_BOTTOM:
998             showArrow_ = GreatOrEqual(childSize_.Height() - twoRadiusPx, arrowWidth);
999             if (!showArrow_ || !enableArrow_) {
1000                 childOffset += OffsetF(-(movingDistance), 0.0);
1001             }
1002             break;
1003         default:
1004             break;
1005     }
1006     if (!enableArrow_) {
1007         showArrow_ = false;
1008     }
1009 }
1010 
UpdateTouchRegion()1011 void BubbleLayoutAlgorithm::UpdateTouchRegion()
1012 {
1013     OffsetF topLeft = childOffset_;
1014     OffsetF bottomRight = OffsetF(childSize_.Width(), childSize_.Height());
1015     switch (arrowPlacement_) {
1016         case Placement::TOP:
1017         case Placement::TOP_LEFT:
1018         case Placement::TOP_RIGHT:
1019             if (showArrow_) {
1020                 bottomRight += OffsetF(0.0, BUBBLE_ARROW_HEIGHT.ConvertToPx());
1021             }
1022             break;
1023         case Placement::BOTTOM:
1024         case Placement::BOTTOM_LEFT:
1025         case Placement::BOTTOM_RIGHT:
1026             if (showArrow_) {
1027                 topLeft += OffsetF(0.0, -BUBBLE_ARROW_HEIGHT.ConvertToPx());
1028                 bottomRight += OffsetF(0.0, BUBBLE_ARROW_HEIGHT.ConvertToPx());
1029             }
1030             break;
1031         case Placement::LEFT:
1032         case Placement::LEFT_TOP:
1033         case Placement::LEFT_BOTTOM:
1034             if (showArrow_) {
1035                 bottomRight += OffsetF(BUBBLE_ARROW_HEIGHT.ConvertToPx(), 0.0);
1036             }
1037             break;
1038         case Placement::RIGHT:
1039         case Placement::RIGHT_TOP:
1040         case Placement::RIGHT_BOTTOM:
1041             if (showArrow_) {
1042                 topLeft += OffsetF(-BUBBLE_ARROW_HEIGHT.ConvertToPx(), 0.0);
1043                 bottomRight += OffsetF(BUBBLE_ARROW_HEIGHT.ConvertToPx(), 0.0);
1044             }
1045             break;
1046         default:
1047             break;
1048     }
1049     touchRegion_ = RectF(topLeft, topLeft + bottomRight);
1050 }
1051 
InitCaretTargetSizeAndPosition()1052 void BubbleLayoutAlgorithm::InitCaretTargetSizeAndPosition()
1053 {
1054     static std::vector<std::string> TEXT_STATES = { V2::TEXTAREA_ETS_TAG, V2::TEXTINPUT_ETS_TAG,
1055         V2::RICH_EDITOR_ETS_TAG, V2::SEARCH_ETS_TAG };
1056     auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
1057     CHECK_NULL_VOID(targetNode);
1058     auto it = std::find(TEXT_STATES.begin(), TEXT_STATES.end(), targetTag_);
1059     bCaretMode_ = false;
1060     CaretMetricsF caretMetrics;
1061     if (it != TEXT_STATES.end()) {
1062         bCaretMode_ = true;
1063         positionOffset_ = OffsetF(0.0f, 0.0f);
1064         if ((placement_ != Placement::BOTTOM) && (placement_ != Placement::TOP)) {
1065             placement_ = Placement::BOTTOM;
1066         }
1067         GetTextCaretMetrics<TextBase>(targetNode, caretMetrics);
1068         targetOffset_ = caretMetrics.offset;
1069         targetSize_.SetHeight(caretMetrics.height);
1070         targetSize_.SetWidth(0.0f);
1071     }
1072 }
1073 
InitTargetSizeAndPosition(bool showInSubWindow)1074 void BubbleLayoutAlgorithm::InitTargetSizeAndPosition(bool showInSubWindow)
1075 {
1076     auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
1077     CHECK_NULL_VOID(targetNode);
1078     if (!targetNode->IsOnMainTree() && !targetNode->IsVisible()) {
1079         return;
1080     }
1081     if (followTransformOfTarget_) {
1082         auto rect = targetNode->GetPaintRectToWindowWithTransform();
1083 
1084         targetSize_ = rect.GetSize();
1085         targetOffset_ = rect.GetOffset();
1086     } else {
1087         auto geometryNode = targetNode->GetGeometryNode();
1088         CHECK_NULL_VOID(geometryNode);
1089         targetSize_ = geometryNode->GetFrameSize();
1090         targetOffset_ = targetNode->GetPaintRectOffset();
1091     }
1092     auto pipelineContext = GetMainPipelineContext();
1093     CHECK_NULL_VOID(pipelineContext);
1094     TAG_LOGD(AceLogTag::ACE_OVERLAY, "popup targetOffset_: %{public}s, targetSize_: %{public}s",
1095         targetOffset_.ToString().c_str(), targetSize_.ToString().c_str());
1096     // Show in SubWindow
1097     if (showInSubWindow) {
1098         auto displayWindowOffset = OffsetF(pipelineContext->GetDisplayWindowRectInfo().GetOffset().GetX(),
1099             pipelineContext->GetDisplayWindowRectInfo().GetOffset().GetY());
1100         targetOffset_ += displayWindowOffset;
1101         auto currentSubwindow = SubwindowManager::GetInstance()->GetCurrentWindow();
1102         if (currentSubwindow) {
1103             auto subwindowRect = currentSubwindow->GetRect();
1104             targetOffset_ -= subwindowRect.GetOffset();
1105         }
1106     }
1107 }
1108 
CheckPositionInPlacementRect(const Rect & rect,const OffsetF & position,const SizeF & childSize)1109 bool BubbleLayoutAlgorithm::CheckPositionInPlacementRect(
1110     const Rect& rect, const OffsetF& position, const SizeF& childSize)
1111 {
1112     auto x = position.GetX();
1113     auto y = position.GetY();
1114     if (x < rect.Left() || (x + childSize.Width()) > rect.Right() || y < rect.Top() ||
1115         (y + childSize.Height()) > rect.Bottom()) {
1116         return false;
1117     }
1118     return true;
1119 }
1120 
CheckPosition(const OffsetF & position,const SizeF & childSize,size_t step,size_t & i)1121 bool BubbleLayoutAlgorithm::CheckPosition(const OffsetF& position, const SizeF& childSize, size_t step, size_t& i)
1122 {
1123     float targetOffsetX = targetOffset_.GetX();
1124     float targetOffsetY = targetOffset_.GetY();
1125     Rect rect;
1126     switch (placement_) {
1127         case Placement::BOTTOM_LEFT:
1128         case Placement::BOTTOM_RIGHT:
1129         case Placement::BOTTOM: {
1130             targetOffsetY += (userSetTargetSpace_.ConvertToPx());
1131             auto y = std::max(targetOffsetY + targetSize_.Height(), marginTop_);
1132             auto height = std::min(wrapperSize_.Height() - marginBottom_ - targetOffsetY - targetSize_.Height(),
1133                 wrapperSize_.Height() - marginBottom_ - marginTop_);
1134             rect.SetRect(marginStart_, y, wrapperSize_.Width() - marginEnd_ - marginStart_, height);
1135             if (childSize.Height() > height) {
1136                 i += step;
1137                 return false;
1138             } else {
1139                 bVertical_ = true;
1140             }
1141             break;
1142         }
1143         case Placement::TOP_LEFT:
1144         case Placement::TOP_RIGHT:
1145         case Placement::TOP: {
1146             targetOffsetY += (-userSetTargetSpace_.ConvertToPx());
1147             auto height = std::min(targetOffsetY - marginTop_, wrapperSize_.Height() - marginTop_ - marginBottom_);
1148             rect.SetRect(marginStart_, marginTop_, wrapperSize_.Width() - marginEnd_ - marginStart_, height);
1149             if (childSize.Height() > height) {
1150                 i += step;
1151                 return false;
1152             } else {
1153                 bVertical_ = true;
1154             }
1155             break;
1156         }
1157         case Placement::RIGHT_TOP:
1158         case Placement::RIGHT_BOTTOM:
1159         case Placement::RIGHT: {
1160             targetOffsetX += (userSetTargetSpace_.ConvertToPx());
1161             auto x = std::max(targetOffsetX + targetSize_.Width(), marginStart_);
1162             auto width = std::min(wrapperSize_.Width() - targetOffsetX - targetSize_.Width() - marginEnd_,
1163                 wrapperSize_.Width() - marginStart_ - marginEnd_);
1164             rect.SetRect(x, marginTop_, width, wrapperSize_.Height() - marginBottom_ - marginTop_);
1165             if (childSize.Width() > width) {
1166                 i += step;
1167                 return false;
1168             } else {
1169                 bHorizontal_ = true;
1170             }
1171             break;
1172         }
1173         case Placement::LEFT_TOP:
1174         case Placement::LEFT_BOTTOM:
1175         case Placement::LEFT: {
1176             targetOffsetX += (-userSetTargetSpace_.ConvertToPx());
1177             auto width = std::min(targetOffsetX - marginStart_, wrapperSize_.Width() - marginEnd_ - marginStart_);
1178             rect.SetRect(marginStart_, marginTop_, width, wrapperSize_.Height() - marginBottom_ - marginTop_);
1179             if (childSize.Width() > width) {
1180                 i += step;
1181                 return false;
1182             } else {
1183                 bHorizontal_ = true;
1184             }
1185             break;
1186         }
1187         default:
1188             return false;
1189     }
1190     i++;
1191     return CheckPositionInPlacementRect(rect, position, childSize);
1192 }
1193 
GetPositionWithPlacementTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1194 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementTop(
1195     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1196 {
1197     float bubbleSpacing = scaledBubbleSpacing_;
1198     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1199     float radius = borderRadius_.ConvertToPx();
1200     arrowPosition = topPosition + OffsetF(radius + arrowHalfWidth, childSize.Height() + bubbleSpacing);
1201     if (bCaretMode_) {
1202         arrowPosition = OffsetF(targetOffset_.GetX(), targetOffset_.GetY() - bubbleSpacing);
1203     }
1204     return topPosition;
1205 }
1206 
GetPositionWithPlacementTopLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1207 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementTopLeft(
1208     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1209 {
1210     OffsetF childPosition;
1211     float marginRight = 0.0f;
1212     float marginBottom = 0.0f;
1213     float bubbleSpacing = scaledBubbleSpacing_;
1214     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1215     float radius = borderRadius_.ConvertToPx();
1216     childPosition = OffsetF(targetOffset_.GetX() - marginRight,
1217         targetOffset_.GetY() - childSize.Height() - bubbleSpacing - marginBottom - targetSpace_.ConvertToPx());
1218     arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, childSize.Height() + bubbleSpacing);
1219     return childPosition;
1220 }
1221 
GetPositionWithPlacementTopRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1222 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementTopRight(
1223     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1224 {
1225     OffsetF childPosition;
1226     float marginBottom = 0.0f;
1227     float marginLeft = 0.0f;
1228     float bubbleSpacing = scaledBubbleSpacing_;
1229     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1230     float radius = borderRadius_.ConvertToPx();
1231     childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
1232         targetOffset_.GetY() - childSize.Height() - targetSpace_.ConvertToPx() - bubbleSpacing - marginBottom);
1233     arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, childSize.Height() + bubbleSpacing);
1234     return childPosition;
1235 }
1236 
GetPositionWithPlacementBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1237 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementBottom(
1238     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1239 {
1240     float bubbleSpacing = scaledBubbleSpacing_;
1241     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1242     float radius = borderRadius_.ConvertToPx();
1243     arrowPosition = bottomPosition + OffsetF(radius + arrowHalfWidth, -bubbleSpacing);
1244     if (bCaretMode_) {
1245         arrowPosition = OffsetF(targetOffset_.GetX(), targetOffset_.GetY() + targetSize_.Height() + bubbleSpacing);
1246     }
1247     return bottomPosition;
1248 }
1249 
GetPositionWithPlacementBottomLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1250 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementBottomLeft(
1251     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1252 {
1253     OffsetF childPosition;
1254     float marginRight = 0.0f;
1255     float marginTop = 0.0f;
1256     float bubbleSpacing = scaledBubbleSpacing_;
1257     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1258     float radius = borderRadius_.ConvertToPx();
1259     childPosition = OffsetF(targetOffset_.GetX() - marginRight,
1260         targetOffset_.GetY() + targetSize_.Height() + targetSpace_.ConvertToPx() + bubbleSpacing + marginTop);
1261     arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, -bubbleSpacing);
1262     return childPosition;
1263 }
1264 
GetPositionWithPlacementBottomRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1265 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementBottomRight(
1266     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1267 {
1268     OffsetF childPosition;
1269     float marginTop = 0.0f;
1270     float marginLeft = 0.0f;
1271     float bubbleSpacing = scaledBubbleSpacing_;
1272     float radius = borderRadius_.ConvertToPx();
1273     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1274     childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
1275         targetOffset_.GetY() + targetSize_.Height() + targetSpace_.ConvertToPx() + bubbleSpacing + marginTop);
1276     arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, -bubbleSpacing);
1277     return childPosition;
1278 }
1279 
GetPositionWithPlacementLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1280 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementLeft(
1281     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1282 {
1283     OffsetF childPosition;
1284     float marginRight = 0.0f;
1285     float bubbleSpacing = scaledBubbleSpacing_;
1286     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1287     float radius = borderRadius_.ConvertToPx();
1288     childPosition =
1289         OffsetF(targetOffset_.GetX() - targetSpace_.ConvertToPx() - bubbleSpacing - childSize.Width() - marginRight,
1290             targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
1291     arrowPosition = childPosition + OffsetF(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
1292     return childPosition;
1293 }
1294 
GetPositionWithPlacementLeftTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1295 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementLeftTop(
1296     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1297 {
1298     OffsetF childPosition;
1299     float marginRight = 0.0f;
1300     float marginBottom = 0.0f;
1301     float bubbleSpacing = scaledBubbleSpacing_;
1302     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1303     float radius = borderRadius_.ConvertToPx();
1304     childPosition =
1305         OffsetF(targetOffset_.GetX() - targetSpace_.ConvertToPx() - bubbleSpacing - childSize.Width() - marginRight,
1306             targetOffset_.GetY() - marginBottom);
1307     arrowPosition = childPosition + OffsetF(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
1308     return childPosition;
1309 }
1310 
GetPositionWithPlacementLeftBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1311 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementLeftBottom(
1312     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1313 {
1314     OffsetF childPosition;
1315     float marginRight = 0.0f;
1316     float marginTop = 0.0f;
1317     float bubbleSpacing = scaledBubbleSpacing_;
1318     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1319     float radius = borderRadius_.ConvertToPx();
1320     childPosition =
1321         OffsetF(targetOffset_.GetX() - targetSpace_.ConvertToPx() - bubbleSpacing - childSize.Width() - marginRight,
1322             targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
1323     arrowPosition = childPosition + OffsetF(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
1324     return childPosition;
1325 }
1326 
GetPositionWithPlacementRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1327 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementRight(
1328     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1329 {
1330     OffsetF childPosition;
1331     float marginLeft = 0.0f;
1332     float bubbleSpacing = scaledBubbleSpacing_;
1333     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1334     float radius = borderRadius_.ConvertToPx();
1335     childPosition =
1336         OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_.ConvertToPx() + bubbleSpacing + marginLeft,
1337             targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
1338     arrowPosition = childPosition + OffsetF(-bubbleSpacing, radius + arrowHalfWidth);
1339     return childPosition;
1340 }
1341 
GetPositionWithPlacementRightTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1342 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementRightTop(
1343     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1344 {
1345     OffsetF childPosition;
1346     float marginBottom = 0.0f;
1347     float marginLeft = 0.0f;
1348     float bubbleSpacing = scaledBubbleSpacing_;
1349     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1350     float radius = borderRadius_.ConvertToPx();
1351     childPosition =
1352         OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_.ConvertToPx() + bubbleSpacing + marginLeft,
1353             targetOffset_.GetY() - marginBottom);
1354     arrowPosition = childPosition + OffsetF(-bubbleSpacing, radius + arrowHalfWidth);
1355     return childPosition;
1356 }
1357 
GetPositionWithPlacementRightBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1358 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementRightBottom(
1359     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1360 {
1361     OffsetF childPosition;
1362     float marginTop = 0.0f;
1363     float marginLeft = 0.0f;
1364     float bubbleSpacing = scaledBubbleSpacing_;
1365     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1366     float radius = borderRadius_.ConvertToPx();
1367     childPosition =
1368         OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_.ConvertToPx() + bubbleSpacing + marginLeft,
1369             targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
1370     arrowPosition = childPosition + OffsetF(-bubbleSpacing, radius + arrowHalfWidth);
1371     return childPosition;
1372 }
1373 
MoveTo(double x,double y)1374 std::string BubbleLayoutAlgorithm::MoveTo(double x, double y)
1375 {
1376     return "M" + std::to_string(x) + " " + std::to_string(y) + " ";
1377 }
1378 
LineTo(double x,double y)1379 std::string BubbleLayoutAlgorithm::LineTo(double x, double y)
1380 {
1381     return "L" + std::to_string(x) + " " + std::to_string(y) + " ";
1382 }
1383 
ArcTo(double rx,double ry,double rotation,int32_t arc_flag,double x,double y)1384 std::string BubbleLayoutAlgorithm::ArcTo(double rx, double ry, double rotation, int32_t arc_flag, double x, double y)
1385 {
1386     int32_t sweep_flag = 1;
1387     return "A" + std::to_string(rx) + " " + std::to_string(ry) + " " + std::to_string(rotation) + " " +
1388            std::to_string(arc_flag) + " " + std::to_string(sweep_flag) + " " + std::to_string(x) + " " +
1389            std::to_string(y) + " ";
1390 }
1391 
UpdateClipOffset(const RefPtr<FrameNode> & frameNode)1392 void BubbleLayoutAlgorithm::UpdateClipOffset(const RefPtr<FrameNode>& frameNode)
1393 {
1394     auto paintProperty = frameNode->GetPaintProperty<BubbleRenderProperty>();
1395     auto childNode = AceType::DynamicCast<FrameNode>(frameNode->GetFirstChild());
1396     if (!bCaretMode_) {
1397         arrowPosition_ =
1398             OffsetF(BUBBLE_ARROW_HEIGHT.ConvertToPx() * DOUBLE, BUBBLE_ARROW_HEIGHT.ConvertToPx() * DOUBLE);
1399         UpdateArrowOffset(paintProperty->GetArrowOffset(), arrowPlacement_);
1400     } else {
1401         arrowPosition_ = arrowPosition_ - childOffset_ + OffsetF(BUBBLE_ARROW_HEIGHT.ConvertToPx(), 0.0f);
1402     }
1403     targetOffset_ = targetOffset_ - childOffset_;
1404     childOffset_ = OffsetF(BUBBLE_ARROW_HEIGHT.ConvertToPx(), BUBBLE_ARROW_HEIGHT.ConvertToPx());
1405     clipFrameNode_ = childNode;
1406     clipPath_.clear();
1407     clipPath_ = ClipBubbleWithPath();
1408 }
1409 
ClipBubbleWithPath()1410 std::string BubbleLayoutAlgorithm::ClipBubbleWithPath()
1411 {
1412     std::string path;
1413     float arrowOffset = 0.0;
1414     if (!bCaretMode_) {
1415         arrowOffset = GetArrowOffset(arrowPlacement_);
1416     }
1417     float radiusPx = borderRadius_.ConvertToPx();
1418     Placement arrowBuildplacement = Placement::NONE;
1419     if (enableArrow_ && showArrow_) {
1420         GetArrowBuildPlacement(arrowBuildplacement);
1421     }
1422     if ((arrowBuildplacement == Placement::TOP_LEFT) || (arrowBuildplacement == Placement::LEFT_TOP)) {
1423         path += MoveTo(childOffset_.GetX(), childOffset_.GetY());
1424     } else {
1425         path += MoveTo(childOffset_.GetX() + radiusPx, childOffset_.GetY());
1426     }
1427     path += BuildTopLinePath(arrowOffset, radiusPx, arrowBuildplacement);
1428     if ((arrowBuildplacement != Placement::TOP_RIGHT) && (arrowBuildplacement != Placement::RIGHT_TOP)) {
1429         path += BuildCornerPath(Placement::TOP_RIGHT, radiusPx);
1430     }
1431     path += BuildRightLinePath(arrowOffset, radiusPx, arrowBuildplacement);
1432     if ((arrowBuildplacement != Placement::RIGHT_BOTTOM) && (arrowBuildplacement != Placement::BOTTOM_RIGHT)) {
1433         path += BuildCornerPath(Placement::BOTTOM_RIGHT, radiusPx);
1434     }
1435     path += BuildBottomLinePath(arrowOffset, radiusPx, arrowBuildplacement);
1436     if ((arrowBuildplacement != Placement::BOTTOM_LEFT) && (arrowBuildplacement != Placement::LEFT_BOTTOM)) {
1437         path += BuildCornerPath(Placement::BOTTOM_LEFT, radiusPx);
1438     }
1439     path += BuildLeftLinePath(arrowOffset, radiusPx, arrowBuildplacement);
1440     if ((arrowBuildplacement != Placement::LEFT_TOP) && (arrowBuildplacement != Placement::TOP_LEFT)) {
1441         path += BuildCornerPath(Placement::TOP_LEFT, radiusPx);
1442     }
1443     return path + "Z";
1444 }
1445 
GetArrowOffset(const Placement & placement)1446 float BubbleLayoutAlgorithm::GetArrowOffset(const Placement& placement)
1447 {
1448     Edge edge;
1449     double arrowOffset;
1450     double edgeValue = 0.0;
1451     double maxMotionRange = 0.0;
1452     double minMotionRange = 0.0;
1453     double targetOffsetXOrY = 0.0;
1454     double childOffsetsetXOrY = 0.0;
1455     double childSizeWidthOrHeight = 0.0;
1456     double targetSizeWidthOrHeight = 0.0;
1457     bool bHorizontal = false;
1458 
1459     InitEdgeSize(edge);
1460     switch (placement) {
1461         case Placement::TOP:
1462         case Placement::TOP_LEFT:
1463         case Placement::TOP_RIGHT:
1464             edgeValue = edge.Top().Value();
1465             bHorizontal = true;
1466             break;
1467         case Placement::BOTTOM:
1468         case Placement::BOTTOM_LEFT:
1469         case Placement::BOTTOM_RIGHT:
1470             edgeValue = edge.Bottom().Value();
1471             bHorizontal = true;
1472             break;
1473         case Placement::LEFT:
1474         case Placement::LEFT_TOP:
1475         case Placement::LEFT_BOTTOM:
1476             edgeValue = edge.Left().Value();
1477             break;
1478         case Placement::RIGHT:
1479         case Placement::RIGHT_TOP:
1480         case Placement::RIGHT_BOTTOM:
1481             edgeValue = edge.Right().Value();
1482             break;
1483         default:
1484             break;
1485     }
1486     if (bHorizontal) {
1487         targetOffsetXOrY = targetOffset_.GetX();
1488         targetSizeWidthOrHeight = targetSize_.Width();
1489         childOffsetsetXOrY = childOffset_.GetX();
1490         childSizeWidthOrHeight = childSize_.Width();
1491     } else {
1492         targetOffsetXOrY = targetOffset_.GetY();
1493         targetSizeWidthOrHeight = targetSize_.Height();
1494         childOffsetsetXOrY = childOffset_.GetY();
1495         childSizeWidthOrHeight = childSize_.Height();
1496     }
1497     maxMotionRange = childSizeWidthOrHeight;
1498     if (arrowOfTargetOffset_ == ArrowOfTargetOffset::START) {
1499         arrowOffset = (targetOffsetXOrY + (arrowOffset_.Value() * targetSizeWidthOrHeight)) - childOffsetsetXOrY +
1500                       BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1501         return arrowOffset;
1502     }
1503     if (arrowOfTargetOffset_ == ArrowOfTargetOffset::CENTER) {
1504         arrowOffset = (targetOffsetXOrY + (arrowOffset_.Value() * targetSizeWidthOrHeight)) - childOffsetsetXOrY;
1505         return arrowOffset;
1506     }
1507     if (arrowOfTargetOffset_ == ArrowOfTargetOffset::END) {
1508         arrowOffset = (targetOffsetXOrY + (arrowOffset_.Value() * targetSizeWidthOrHeight)) - childOffsetsetXOrY -
1509                       BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1510         return arrowOffset;
1511     }
1512     return std::clamp(arrowOffset_.Unit() == DimensionUnit::PERCENT ? arrowOffset_.Value() * maxMotionRange
1513                                                                     : arrowOffset_.ConvertToPx(),
1514         minMotionRange, maxMotionRange);
1515 }
1516 
UpdateArrowOffset(const std::optional<Dimension> & offset,const Placement & placement)1517 void BubbleLayoutAlgorithm::UpdateArrowOffset(const std::optional<Dimension>& offset, const Placement& placement)
1518 {
1519     if (offset.has_value()) {
1520         arrowOffset_ = offset.value();
1521         arrowOfTargetOffset_ = ArrowOfTargetOffset::NONE;
1522         if (arrowOffset_.Unit() == DimensionUnit::PERCENT) {
1523             if (arrowOffset_.Value() == ARROW_OFFSET_START_VALUE) {
1524                 arrowOfTargetOffset_ = ArrowOfTargetOffset::START;
1525             } else if (arrowOffset_.Value() == ARROW_OFFSET_CENTER_VALUE) {
1526                 arrowOfTargetOffset_ = ArrowOfTargetOffset::CENTER;
1527             } else {
1528                 arrowOfTargetOffset_ = ArrowOfTargetOffset::END;
1529             }
1530             arrowOffset_.SetValue(std::clamp(arrowOffset_.Value(), 0.0, 1.0));
1531         }
1532         return;
1533     }
1534     arrowOfTargetOffset_ = ArrowOfTargetOffset::NONE;
1535     switch (placement) {
1536         case Placement::LEFT:
1537         case Placement::RIGHT:
1538         case Placement::TOP:
1539         case Placement::BOTTOM:
1540             arrowOffset_ = BUBBLE_ARROW_HALF_PERCENT_VALUE;
1541             arrowOfTargetOffset_ = ArrowOfTargetOffset::CENTER;
1542             break;
1543         case Placement::TOP_LEFT:
1544         case Placement::BOTTOM_LEFT:
1545         case Placement::LEFT_TOP:
1546         case Placement::RIGHT_TOP:
1547             arrowOffset_ = BUBBLE_ARROW_ZERO_PERCENT_VALUE;
1548             arrowOfTargetOffset_ = ArrowOfTargetOffset::START;
1549             break;
1550         case Placement::TOP_RIGHT:
1551         case Placement::BOTTOM_RIGHT:
1552         case Placement::LEFT_BOTTOM:
1553         case Placement::RIGHT_BOTTOM:
1554             arrowOffset_ = BUBBLE_ARROW_ONE_HUNDRED_PERCENT_VALUE;
1555             arrowOfTargetOffset_ = ArrowOfTargetOffset::END;
1556             break;
1557         default:
1558             break;
1559     }
1560 }
1561 
InitEdgeSize(Edge & edge)1562 void BubbleLayoutAlgorithm::InitEdgeSize(Edge& edge)
1563 {
1564     edge.SetTop(Dimension(std::max(padding_.Left().ConvertToPx(), border_.TopLeftRadius().GetX().ConvertToPx()) +
1565                           std::max(padding_.Right().ConvertToPx(), border_.TopRightRadius().GetX().ConvertToPx())));
1566     edge.SetBottom(
1567         Dimension(std::max(padding_.Left().ConvertToPx(), border_.BottomLeftRadius().GetX().ConvertToPx()) +
1568                   std::max(padding_.Right().ConvertToPx(), border_.BottomRightRadius().GetX().ConvertToPx())));
1569     edge.SetLeft(
1570         Dimension(std::max(padding_.Top().ConvertToPx(), border_.TopRightRadius().GetY().ConvertToPx()) +
1571                   std::max(padding_.Bottom().ConvertToPx(), border_.BottomRightRadius().GetY().ConvertToPx())));
1572     edge.SetRight(
1573         Dimension(std::max(padding_.Top().ConvertToPx(), border_.TopLeftRadius().GetY().ConvertToPx()) +
1574                   std::max(padding_.Bottom().ConvertToPx(), border_.BottomLeftRadius().GetY().ConvertToPx())));
1575 }
1576 
ModifyBorderRadius(float borderRadius,float halfChildHeight)1577 float BubbleLayoutAlgorithm::ModifyBorderRadius(float borderRadius, float halfChildHeight)
1578 {
1579     return GreatOrEqual(borderRadius, halfChildHeight) ? halfChildHeight : borderRadius;
1580 }
1581 
GetArrowBuildPlacement(Placement & arrowBuildplacement)1582 void BubbleLayoutAlgorithm::GetArrowBuildPlacement(Placement& arrowBuildplacement)
1583 {
1584     auto radius = borderRadius_.ConvertToPx();
1585     float maxOffset = 0.0;
1586     switch (arrowPlacement_) {
1587         case Placement::BOTTOM:
1588         case Placement::BOTTOM_LEFT:
1589         case Placement::BOTTOM_RIGHT: // TOP
1590             maxOffset = childOffset_.GetX() + childSize_.Width() - radius -
1591                         BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1592             if ((!bCaretMode_) && (arrowOffset_.Unit() != DimensionUnit::PERCENT)) {
1593                 if ((arrowPosition_.GetX() + arrowOffset_.ConvertToPx()) > maxOffset) {
1594                     arrowBuildplacement = Placement::TOP_RIGHT;
1595                     break;
1596                 }
1597                 if ((arrowOffset_.ConvertToPx()) < radius) {
1598                     arrowBuildplacement = Placement::TOP_LEFT;
1599                     break;
1600                 }
1601                 arrowBuildplacement = Placement::TOP;
1602             } else {
1603                 arrowBuildplacement = Placement::TOP;
1604             }
1605             break;
1606         case Placement::LEFT:
1607         case Placement::LEFT_TOP:
1608         case Placement::LEFT_BOTTOM: // Right
1609             maxOffset = childOffset_.GetY() + childSize_.Height() - radius -
1610                         BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1611             if ((!bCaretMode_) && (arrowOffset_.Unit() != DimensionUnit::PERCENT)) {
1612                 if ((arrowPosition_.GetY() + arrowOffset_.ConvertToPx()) > maxOffset) {
1613                     arrowBuildplacement = Placement::RIGHT_BOTTOM;
1614                     break;
1615                 }
1616                 if ((arrowOffset_.ConvertToPx()) < radius) {
1617                     arrowBuildplacement = Placement::RIGHT_TOP;
1618                     break;
1619                 }
1620                 arrowBuildplacement = Placement::RIGHT;
1621             } else {
1622                 arrowBuildplacement = Placement::RIGHT;
1623             }
1624             break;
1625         case Placement::TOP:
1626         case Placement::TOP_LEFT:
1627         case Placement::TOP_RIGHT: // Bottom
1628             maxOffset = childOffset_.GetX() + childSize_.Width() - radius -
1629                         BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1630             if ((!bCaretMode_) && (arrowOffset_.Unit() != DimensionUnit::PERCENT)) {
1631                 if ((arrowPosition_.GetX() + arrowOffset_.ConvertToPx()) > maxOffset) {
1632                     arrowBuildplacement = Placement::BOTTOM_RIGHT; // replace
1633                     break;
1634                 }
1635                 if ((arrowOffset_.ConvertToPx()) < radius) {
1636                     arrowBuildplacement = Placement::BOTTOM_LEFT; // replace
1637                     break;
1638                 }
1639                 arrowBuildplacement = Placement::BOTTOM; // nomal
1640             } else {
1641                 arrowBuildplacement = Placement::BOTTOM; // nomal
1642             }
1643             break;
1644         case Placement::RIGHT:
1645         case Placement::RIGHT_TOP:
1646         case Placement::RIGHT_BOTTOM: // Left
1647             maxOffset = childOffset_.GetY() + childSize_.Height() - radius -
1648                         BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1649             if ((!bCaretMode_) && (arrowOffset_.Unit() != DimensionUnit::PERCENT)) {
1650                 if ((arrowPosition_.GetY() + arrowOffset_.ConvertToPx()) > maxOffset) {
1651                     arrowBuildplacement = Placement::LEFT_BOTTOM;
1652                     break;
1653                 }
1654                 if ((arrowOffset_.ConvertToPx()) < radius) {
1655                     arrowBuildplacement = Placement::LEFT_TOP;
1656                     break;
1657                 }
1658                 arrowBuildplacement = Placement::LEFT;
1659             } else {
1660                 arrowBuildplacement = Placement::LEFT;
1661             }
1662             break;
1663         default:
1664             break;
1665     }
1666     if (arrowBuildplacement > Placement::BOTTOM) {
1667         arrowPosition_ += OffsetF(-BUBBLE_ARROW_HEIGHT.ConvertToPx(), -BUBBLE_ARROW_HEIGHT.ConvertToPx());
1668     }
1669 }
1670 
SetArrowOffsetsFromClip(const int16_t index,const float offsetX,const float offsetY)1671 void BubbleLayoutAlgorithm::SetArrowOffsetsFromClip(const int16_t index, const float offsetX, const float offsetY)
1672 {
1673     arrowOffsetsFromClip_[index] = { offsetX, offsetY };
1674 }
1675 
BuildTopLinePath(float arrowOffset,float radius,Placement & arrowBuildplacement)1676 std::string BubbleLayoutAlgorithm::BuildTopLinePath(float arrowOffset, float radius, Placement& arrowBuildplacement)
1677 {
1678     std::string path;
1679     float childOffsetY = childOffset_.GetY();
1680     auto leftOffset =
1681         childOffset_.GetX() + radius + BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1682     auto rightOffset = childOffset_.GetX() + childSize_.Width() - radius -
1683                        BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1684     auto arrowTopOffset = std::clamp(
1685         arrowPosition_.GetX() + arrowOffset, static_cast<float>(leftOffset), static_cast<float>(rightOffset));
1686     switch (arrowPlacement_) {
1687         case Placement::BOTTOM:
1688         case Placement::BOTTOM_LEFT:
1689         case Placement::BOTTOM_RIGHT:
1690             if (arrowBuildplacement == Placement::TOP_RIGHT) {
1691                 path += ReplaceArrowTopRight(
1692                     arrowPosition_.GetX() + childSize_.Width() - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF, childOffsetY);
1693             }
1694             if (arrowBuildplacement == Placement::TOP_LEFT) {
1695                 path +=
1696                     ReplaceArrowTopLeft(arrowPosition_.GetX() + BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF, childOffsetY);
1697             }
1698             if (arrowBuildplacement == Placement::TOP) {
1699                 path += LineTo(arrowTopOffset - ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffsetY); // P1
1700                 path += LineTo(arrowTopOffset - ARROW_VERTICAL_P2_OFFSET_X.ConvertToPx(),
1701                     childOffsetY - ARROW_VERTICAL_P2_OFFSET_Y.ConvertToPx()); // P2
1702                 path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
1703                     arrowTopOffset + ARROW_VERTICAL_P4_OFFSET_X.ConvertToPx(),
1704                     childOffsetY - ARROW_VERTICAL_P4_OFFSET_Y.ConvertToPx());                            // P4
1705                 path += LineTo(arrowTopOffset + ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffsetY); // P5
1706                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
1707                     arrowTopOffset - ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffsetY);
1708                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
1709                     arrowTopOffset - ARROW_VERTICAL_P2_OFFSET_X.ConvertToPx(),
1710                     childOffsetY - ARROW_VERTICAL_P2_OFFSET_Y.ConvertToPx());
1711                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
1712                     arrowTopOffset + ARROW_VERTICAL_P4_OFFSET_X.ConvertToPx(),
1713                     childOffsetY - ARROW_VERTICAL_P4_OFFSET_Y.ConvertToPx());
1714                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
1715                     arrowTopOffset + ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffsetY);
1716             }
1717             break;
1718         default:
1719             break;
1720     }
1721     if (arrowBuildplacement != Placement::TOP_RIGHT) {
1722         path += LineTo(childOffset_.GetX() + childSize_.Width() - radius, childOffsetY);
1723     }
1724     return path;
1725 }
1726 
BuildRightLinePath(float arrowOffset,float radius,Placement & arrowBuildplacement)1727 std::string BubbleLayoutAlgorithm::BuildRightLinePath(float arrowOffset, float radius, Placement& arrowBuildplacement)
1728 {
1729     std::string path;
1730     float childOffsetY = childOffset_.GetY();
1731     auto topOffset = childOffset_.GetY() + radius + BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1732     auto bottomOffset = childOffset_.GetY() + childSize_.Height() - radius - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1733     auto arrowRightOffset = std::clamp(
1734         arrowPosition_.GetY() + arrowOffset, static_cast<float>(topOffset), static_cast<float>(bottomOffset));
1735     switch (arrowPlacement_) {
1736         case Placement::LEFT:
1737         case Placement::LEFT_TOP:
1738         case Placement::LEFT_BOTTOM:
1739             if (arrowBuildplacement == Placement::RIGHT_BOTTOM) {
1740                 path += ReplaceArrowRightBottom(arrowPosition_.GetY() + childSize_.Height()
1741                     - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF, childOffset_.GetX() + childSize_.Width());
1742             }
1743             if (arrowBuildplacement == Placement::RIGHT_TOP) {
1744                 path += ReplaceArrowRightTop(arrowPosition_.GetY() + BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF,
1745                     childOffset_.GetX() + childSize_.Width());
1746             }
1747             if (arrowBuildplacement == Placement::RIGHT) {
1748                 path += LineTo(childOffset_.GetX() + childSize_.Width(),
1749                     arrowRightOffset - ARROW_HORIZON_P1_OFFSET_Y.ConvertToPx()); // P1
1750                 path += LineTo(childOffset_.GetX() + childSize_.Width() + ARROW_HORIZON_P2_OFFSET_X.ConvertToPx(),
1751                     arrowRightOffset - ARROW_HORIZON_P2_OFFSET_Y.ConvertToPx()); // P2
1752                 path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
1753                     childOffset_.GetX() + childSize_.Width() + ARROW_HORIZON_P4_OFFSET_X.ConvertToPx(),
1754                     arrowRightOffset + ARROW_HORIZON_P4_OFFSET_Y.ConvertToPx()); // P4
1755                 path += LineTo(childOffset_.GetX() + childSize_.Width(),
1756                     arrowRightOffset + ARROW_HORIZON_P5_OFFSET_Y.ConvertToPx()); // P5
1757                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO, childOffset_.GetX() + childSize_.Width(),
1758                     arrowRightOffset - ARROW_HORIZON_P1_OFFSET_Y.ConvertToPx());
1759                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
1760                     childOffset_.GetX() + childSize_.Width() + ARROW_HORIZON_P2_OFFSET_X.ConvertToPx(),
1761                     arrowRightOffset - ARROW_HORIZON_P2_OFFSET_Y.ConvertToPx());
1762                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
1763                     childOffset_.GetX() + childSize_.Width() + ARROW_HORIZON_P4_OFFSET_X.ConvertToPx(),
1764                     arrowRightOffset + ARROW_HORIZON_P4_OFFSET_Y.ConvertToPx());
1765                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE, childOffset_.GetX() + childSize_.Width(),
1766                     arrowRightOffset + ARROW_HORIZON_P5_OFFSET_Y.ConvertToPx());
1767             }
1768             break;
1769         default:
1770             break;
1771     }
1772     if (arrowBuildplacement != Placement::RIGHT_BOTTOM) {
1773         path += LineTo(childOffset_.GetX() + childSize_.Width(), childOffsetY + childSize_.Height() - radius);
1774     }
1775     return path;
1776 }
1777 
BuildBottomLinePath(float arrowOffset,float radius,Placement & arrowBuildplacement)1778 std::string BubbleLayoutAlgorithm::BuildBottomLinePath(float arrowOffset, float radius, Placement& arrowBuildplacement)
1779 {
1780     std::string path;
1781     float childOffsetY = childOffset_.GetY();
1782     auto leftOffset = childOffset_.GetX() + radius + BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1783     auto rightOffset = childOffset_.GetX() + childSize_.Width() - radius - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1784     auto arrowBottomOffset = std::clamp(
1785         arrowPosition_.GetX() + arrowOffset, static_cast<float>(leftOffset), static_cast<float>(rightOffset));
1786     switch (arrowPlacement_) {
1787         case Placement::TOP:
1788         case Placement::TOP_LEFT:
1789         case Placement::TOP_RIGHT:
1790             if (arrowBuildplacement == Placement::BOTTOM_RIGHT) {
1791                 path += ReplaceArrowBottomRight(arrowPosition_.GetX() + childSize_.Width()
1792                     - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF, childOffsetY + childSize_.Height());
1793             }
1794             if (arrowBuildplacement == Placement::BOTTOM_LEFT) {
1795                 path += ReplaceArrowBottomLeft(arrowPosition_.GetX() + BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF,
1796                     childOffsetY + childSize_.Height());
1797             }
1798             if (arrowBuildplacement == Placement::BOTTOM) {
1799                 path += LineTo(arrowBottomOffset + ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(),
1800                     childOffsetY + childSize_.Height()); // P1
1801                 path += LineTo(arrowBottomOffset + ARROW_VERTICAL_P2_OFFSET_X.ConvertToPx(),
1802                     childOffsetY + childSize_.Height() + ARROW_VERTICAL_P2_OFFSET_Y.ConvertToPx()); // P2
1803                 path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
1804                     arrowBottomOffset - ARROW_VERTICAL_P4_OFFSET_X.ConvertToPx(),
1805                     childOffsetY + childSize_.Height() + ARROW_VERTICAL_P4_OFFSET_Y.ConvertToPx()); // P4
1806                 path += LineTo(arrowBottomOffset - ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx(),
1807                     childOffsetY + childSize_.Height()); // P5
1808                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
1809                     arrowBottomOffset + ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffsetY + childSize_.Height());
1810                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
1811                     arrowBottomOffset + ARROW_VERTICAL_P2_OFFSET_X.ConvertToPx(),
1812                     childOffsetY + childSize_.Height() + ARROW_VERTICAL_P2_OFFSET_Y.ConvertToPx());
1813                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
1814                     arrowBottomOffset - ARROW_VERTICAL_P4_OFFSET_X.ConvertToPx(),
1815                     childOffsetY + childSize_.Height() + ARROW_VERTICAL_P4_OFFSET_Y.ConvertToPx());
1816                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE, arrowBottomOffset
1817                     - ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffsetY + childSize_.Height());
1818             }
1819             break;
1820         default:
1821             break;
1822     }
1823     if (arrowBuildplacement != Placement::BOTTOM_LEFT) {
1824         path += LineTo(childOffset_.GetX() + radius, childOffsetY + childSize_.Height());
1825     }
1826     return path;
1827 }
1828 
BuildLeftLinePath(float arrowOffset,float radius,Placement & arrowBuildplacement)1829 std::string BubbleLayoutAlgorithm::BuildLeftLinePath(float arrowOffset, float radius, Placement& arrowBuildplacement)
1830 {
1831     std::string path;
1832     float childOffsetY = childOffset_.GetY();
1833     auto topOffset =
1834         childOffset_.GetY() + radius + BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1835     auto bottomOffset = childOffset_.GetY() + childSize_.Height() - radius -
1836                         BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
1837     auto arrowLeftOffset = std::clamp(
1838         arrowPosition_.GetY() + arrowOffset, static_cast<float>(topOffset), static_cast<float>(bottomOffset));
1839     switch (arrowPlacement_) {
1840         case Placement::RIGHT:
1841         case Placement::RIGHT_TOP:
1842         case Placement::RIGHT_BOTTOM:
1843             if (arrowBuildplacement == Placement::LEFT_BOTTOM) {
1844                 path += ReplaceArrowLeftBottom(
1845                     arrowPosition_.GetY() + childSize_.Height() - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF,
1846                     childOffset_.GetX());
1847             }
1848             if (arrowBuildplacement == Placement::LEFT_TOP) {
1849                 path += ReplaceArrowLeftTop(
1850                     arrowPosition_.GetY() + BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF, childOffset_.GetX());
1851             }
1852             if (arrowBuildplacement == Placement::LEFT) {
1853                 path += LineTo(childOffset_.GetX(), arrowLeftOffset + ARROW_HORIZON_P1_OFFSET_Y.ConvertToPx()); // P1
1854                 path += LineTo(childOffset_.GetX() - ARROW_HORIZON_P2_OFFSET_X.ConvertToPx(),
1855                     arrowLeftOffset + ARROW_HORIZON_P2_OFFSET_Y.ConvertToPx()); // P2
1856                 path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
1857                     childOffset_.GetX() - ARROW_HORIZON_P4_OFFSET_X.ConvertToPx(),
1858                     arrowLeftOffset - ARROW_HORIZON_P4_OFFSET_Y.ConvertToPx());                                 // P4
1859                 path += LineTo(childOffset_.GetX(), arrowLeftOffset - ARROW_HORIZON_P5_OFFSET_Y.ConvertToPx()); // P5
1860                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
1861                     childOffset_.GetX(), arrowLeftOffset + ARROW_HORIZON_P1_OFFSET_Y.ConvertToPx());
1862                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
1863                     childOffset_.GetX() - ARROW_HORIZON_P2_OFFSET_X.ConvertToPx(),
1864                     arrowLeftOffset + ARROW_HORIZON_P2_OFFSET_Y.ConvertToPx());
1865                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
1866                     childOffset_.GetX() - ARROW_HORIZON_P4_OFFSET_X.ConvertToPx(),
1867                     arrowLeftOffset - ARROW_HORIZON_P4_OFFSET_Y.ConvertToPx());
1868                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
1869                     childOffset_.GetX(), arrowLeftOffset - ARROW_HORIZON_P5_OFFSET_Y.ConvertToPx());
1870             }
1871             break;
1872         default:
1873             break;
1874     }
1875     if (arrowBuildplacement != Placement::LEFT_TOP) {
1876         path += LineTo(childOffset_.GetX(), childOffsetY + radius);
1877     }
1878     return path;
1879 }
1880 
ReplaceArrowTopLeft(const float arrowOffset,const float childOffset)1881 std::string BubbleLayoutAlgorithm::ReplaceArrowTopLeft(const float arrowOffset, const float childOffset)
1882 {
1883     std::string path;
1884     path += LineTo(arrowOffset - ARROW_REPLACE_START_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffset); // P1
1885     path += LineTo(arrowOffset - ARROW_REPLACE_START_VERTICAL_P2_OFFSET_X.ConvertToPx(),
1886         childOffset - ARROW_REPLACE_START_VERTICAL_P2_OFFSET_Y.ConvertToPx()); // P2
1887     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
1888         arrowOffset - ARROW_REPLACE_START_VERTICAL_P4_OFFSET_X.ConvertToPx(),
1889         childOffset - ARROW_REPLACE_START_VERTICAL_P4_OFFSET_Y.ConvertToPx());                         // P4
1890     path += LineTo(arrowOffset + ARROW_REPLACE_START_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffset); // P5
1891     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
1892         arrowOffset - ARROW_REPLACE_START_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffset);
1893     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
1894         arrowOffset - ARROW_REPLACE_START_VERTICAL_P2_OFFSET_X.ConvertToPx(),
1895         childOffset - ARROW_REPLACE_START_VERTICAL_P2_OFFSET_Y.ConvertToPx());
1896     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
1897         arrowOffset - ARROW_REPLACE_START_VERTICAL_P4_OFFSET_X.ConvertToPx(),
1898         childOffset - ARROW_REPLACE_START_VERTICAL_P4_OFFSET_Y.ConvertToPx());
1899     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
1900         arrowOffset + ARROW_REPLACE_START_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffset);
1901     return path;
1902 }
1903 
ReplaceArrowTopRight(const float arrowOffset,const float childOffset)1904 std::string BubbleLayoutAlgorithm::ReplaceArrowTopRight(const float arrowOffset, const float childOffset)
1905 {
1906     std::string path;
1907     path += LineTo((arrowOffset - ARROW_REPLACE_END_VERTICAL_P1_OFFSET_X.ConvertToPx()) / HALF, childOffset); // P1
1908     path += LineTo(arrowOffset - ARROW_REPLACE_END_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffset);          // P1
1909     path += LineTo(arrowOffset + ARROW_REPLACE_END_VERTICAL_P2_OFFSET_X.ConvertToPx(),
1910         childOffset - ARROW_REPLACE_END_VERTICAL_P2_OFFSET_Y.ConvertToPx()); // P2
1911     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
1912         arrowOffset + ARROW_REPLACE_END_VERTICAL_P4_OFFSET_X.ConvertToPx(),
1913         childOffset - ARROW_REPLACE_END_VERTICAL_P4_OFFSET_Y.ConvertToPx());                         // P4
1914     path += LineTo(arrowOffset + ARROW_REPLACE_END_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffset); // P5
1915     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
1916         arrowOffset - ARROW_REPLACE_END_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffset);
1917     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
1918         arrowOffset + ARROW_REPLACE_END_VERTICAL_P2_OFFSET_X.ConvertToPx(),
1919         childOffset - ARROW_REPLACE_END_VERTICAL_P2_OFFSET_Y.ConvertToPx());
1920     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
1921         arrowOffset + ARROW_REPLACE_END_VERTICAL_P4_OFFSET_X.ConvertToPx(),
1922         childOffset - ARROW_REPLACE_END_VERTICAL_P4_OFFSET_Y.ConvertToPx());
1923     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
1924         arrowOffset + ARROW_REPLACE_END_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffset);
1925     return path;
1926 }
1927 
ReplaceArrowRightTop(const float arrowOffset,const float childOffset)1928 std::string BubbleLayoutAlgorithm::ReplaceArrowRightTop(const float arrowOffset, const float childOffset)
1929 {
1930     std::string path;
1931     path += LineTo(childOffset, arrowOffset - ARROW_REPLACE_START_HORIZON_P1_OFFSET_Y.ConvertToPx()); // P1
1932     path += LineTo(childOffset + ARROW_REPLACE_START_HORIZON_P2_OFFSET_X.ConvertToPx(),
1933         arrowOffset - ARROW_REPLACE_START_HORIZON_P2_OFFSET_Y.ConvertToPx()); // P2
1934     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
1935         childOffset + ARROW_REPLACE_START_HORIZON_P4_OFFSET_X.ConvertToPx(),
1936         arrowOffset - ARROW_REPLACE_START_HORIZON_P4_OFFSET_Y.ConvertToPx());                         // P4
1937     path += LineTo(childOffset, arrowOffset + ARROW_REPLACE_START_HORIZON_P5_OFFSET_Y.ConvertToPx()); // P5
1938     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO, childOffset,
1939         arrowOffset - ARROW_REPLACE_START_HORIZON_P1_OFFSET_Y.ConvertToPx());
1940     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
1941         childOffset + ARROW_REPLACE_START_HORIZON_P2_OFFSET_X.ConvertToPx(),
1942         arrowOffset - ARROW_REPLACE_START_HORIZON_P2_OFFSET_Y.ConvertToPx());
1943     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
1944         childOffset + ARROW_REPLACE_START_HORIZON_P4_OFFSET_X.ConvertToPx(),
1945         arrowOffset - ARROW_REPLACE_START_HORIZON_P4_OFFSET_Y.ConvertToPx());
1946     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
1947         childOffset, arrowOffset + ARROW_REPLACE_START_HORIZON_P5_OFFSET_Y.ConvertToPx());
1948     return path;
1949 }
1950 
ReplaceArrowRightBottom(const float arrowOffset,const float childOffset)1951 std::string BubbleLayoutAlgorithm::ReplaceArrowRightBottom(const float arrowOffset, const float childOffset)
1952 {
1953     std::string path;
1954     path += LineTo(childOffset, arrowOffset - ARROW_REPLACE_END_HORIZON_P1_OFFSET_Y.ConvertToPx()); // P1
1955     path += LineTo(childOffset + ARROW_REPLACE_END_HORIZON_P2_OFFSET_X.ConvertToPx(),
1956         arrowOffset + ARROW_REPLACE_END_HORIZON_P2_OFFSET_Y.ConvertToPx()); // P2
1957     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
1958         childOffset + ARROW_REPLACE_END_HORIZON_P4_OFFSET_X.ConvertToPx(),
1959         arrowOffset + ARROW_REPLACE_END_HORIZON_P4_OFFSET_Y.ConvertToPx());                         // P4
1960     path += LineTo(childOffset, arrowOffset + ARROW_REPLACE_END_HORIZON_P5_OFFSET_Y.ConvertToPx()); // P5
1961     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO, childOffset,
1962         arrowOffset - ARROW_REPLACE_END_HORIZON_P1_OFFSET_Y.ConvertToPx());
1963     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
1964         childOffset + ARROW_REPLACE_END_HORIZON_P2_OFFSET_X.ConvertToPx(),
1965         arrowOffset + ARROW_REPLACE_END_HORIZON_P2_OFFSET_Y.ConvertToPx());
1966     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
1967         childOffset + ARROW_REPLACE_END_HORIZON_P4_OFFSET_X.ConvertToPx(),
1968         arrowOffset + ARROW_REPLACE_END_HORIZON_P4_OFFSET_Y.ConvertToPx());
1969     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
1970         childOffset, arrowOffset + ARROW_REPLACE_END_HORIZON_P5_OFFSET_Y.ConvertToPx());
1971     return path;
1972 }
1973 
ReplaceArrowBottomLeft(const float arrowOffset,const float childOffset)1974 std::string BubbleLayoutAlgorithm::ReplaceArrowBottomLeft(const float arrowOffset, const float childOffset)
1975 {
1976     std::string path;
1977     path += LineTo(arrowOffset + ARROW_REPLACE_START_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffset); // P1
1978     path += LineTo(arrowOffset - ARROW_REPLACE_START_VERTICAL_P4_OFFSET_X.ConvertToPx(),
1979         childOffset + ARROW_REPLACE_START_VERTICAL_P4_OFFSET_Y.ConvertToPx()); // P2
1980     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
1981         arrowOffset - ARROW_REPLACE_START_VERTICAL_P2_OFFSET_X.ConvertToPx(),
1982         childOffset + ARROW_REPLACE_START_VERTICAL_P2_OFFSET_Y.ConvertToPx());                         // P4
1983     path += LineTo(arrowOffset - ARROW_REPLACE_START_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffset); // P5
1984     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
1985         arrowOffset + ARROW_REPLACE_START_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffset);
1986     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
1987         arrowOffset - ARROW_REPLACE_START_VERTICAL_P4_OFFSET_X.ConvertToPx(),
1988         childOffset + ARROW_REPLACE_START_VERTICAL_P4_OFFSET_Y.ConvertToPx());
1989     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
1990         arrowOffset - ARROW_REPLACE_START_VERTICAL_P2_OFFSET_X.ConvertToPx(),
1991         childOffset + ARROW_REPLACE_START_VERTICAL_P2_OFFSET_Y.ConvertToPx());
1992     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
1993         arrowOffset - ARROW_REPLACE_START_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffset);
1994     return path;
1995 }
1996 
ReplaceArrowBottomRight(const float arrowOffset,const float childOffset)1997 std::string BubbleLayoutAlgorithm::ReplaceArrowBottomRight(const float arrowOffset, const float childOffset)
1998 {
1999     std::string path;
2000     path += LineTo(arrowOffset + ARROW_REPLACE_END_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffset); // P1
2001     path += LineTo(arrowOffset + ARROW_REPLACE_END_VERTICAL_P4_OFFSET_X.ConvertToPx(),
2002         childOffset + ARROW_REPLACE_END_VERTICAL_P4_OFFSET_Y.ConvertToPx()); // P2
2003     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
2004         arrowOffset + ARROW_REPLACE_END_VERTICAL_P2_OFFSET_X.ConvertToPx(),
2005         childOffset + ARROW_REPLACE_END_VERTICAL_P2_OFFSET_Y.ConvertToPx());                         // P4
2006     path += LineTo(arrowOffset - ARROW_REPLACE_END_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffset); // P5
2007     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
2008         arrowOffset + ARROW_REPLACE_END_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffset);
2009     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
2010         arrowOffset + ARROW_REPLACE_END_VERTICAL_P4_OFFSET_X.ConvertToPx(),
2011         childOffset + ARROW_REPLACE_END_VERTICAL_P4_OFFSET_Y.ConvertToPx());
2012     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
2013         arrowOffset + ARROW_REPLACE_END_VERTICAL_P2_OFFSET_X.ConvertToPx(),
2014         childOffset + ARROW_REPLACE_END_VERTICAL_P2_OFFSET_Y.ConvertToPx());
2015     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
2016         arrowOffset - ARROW_REPLACE_END_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffset);
2017     return path;
2018 }
2019 
ReplaceArrowLeftTop(const float arrowOffset,const float childOffset)2020 std::string BubbleLayoutAlgorithm::ReplaceArrowLeftTop(const float arrowOffset, const float childOffset)
2021 {
2022     std::string path;
2023     path += LineTo(childOffset, arrowOffset + ARROW_REPLACE_START_HORIZON_P1_OFFSET_Y.ConvertToPx()); // P1
2024     path += LineTo(childOffset - ARROW_REPLACE_START_HORIZON_P4_OFFSET_X.ConvertToPx(),
2025         arrowOffset - ARROW_REPLACE_START_HORIZON_P4_OFFSET_Y.ConvertToPx()); // P2
2026     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
2027         childOffset - ARROW_REPLACE_START_HORIZON_P2_OFFSET_X.ConvertToPx(),
2028         arrowOffset - ARROW_REPLACE_START_HORIZON_P2_OFFSET_Y.ConvertToPx());                         // P4
2029     path += LineTo(childOffset, arrowOffset - ARROW_REPLACE_START_HORIZON_P5_OFFSET_Y.ConvertToPx()); // P5
2030     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
2031         childOffset, arrowOffset + ARROW_REPLACE_START_HORIZON_P1_OFFSET_Y.ConvertToPx());
2032     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
2033         childOffset - ARROW_REPLACE_START_HORIZON_P4_OFFSET_X.ConvertToPx(),
2034         arrowOffset - ARROW_REPLACE_START_HORIZON_P4_OFFSET_Y.ConvertToPx());
2035     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
2036         childOffset - ARROW_REPLACE_START_HORIZON_P2_OFFSET_X.ConvertToPx(),
2037         arrowOffset - ARROW_REPLACE_START_HORIZON_P2_OFFSET_Y.ConvertToPx());
2038     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
2039         childOffset, arrowOffset - ARROW_REPLACE_START_HORIZON_P5_OFFSET_Y.ConvertToPx());
2040     return path;
2041 }
2042 
ReplaceArrowLeftBottom(const float arrowOffset,const float childOffset)2043 std::string BubbleLayoutAlgorithm::ReplaceArrowLeftBottom(const float arrowOffset, const float childOffset)
2044 {
2045     std::string path;
2046     path += LineTo(childOffset, arrowOffset + ARROW_REPLACE_END_HORIZON_P1_OFFSET_Y.ConvertToPx()); // P1
2047     path += LineTo(childOffset - ARROW_REPLACE_END_HORIZON_P4_OFFSET_X.ConvertToPx(),
2048         arrowOffset + ARROW_REPLACE_END_HORIZON_P4_OFFSET_Y.ConvertToPx()); // P2
2049     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
2050         childOffset - ARROW_REPLACE_END_HORIZON_P2_OFFSET_X.ConvertToPx(),
2051         arrowOffset + ARROW_REPLACE_END_HORIZON_P2_OFFSET_Y.ConvertToPx());                         // P4
2052     path += LineTo(childOffset, arrowOffset - ARROW_REPLACE_END_HORIZON_P5_OFFSET_Y.ConvertToPx()); // P5
2053     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
2054         childOffset, arrowOffset + ARROW_REPLACE_END_HORIZON_P1_OFFSET_Y.ConvertToPx());
2055     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
2056         childOffset - ARROW_REPLACE_END_HORIZON_P4_OFFSET_X.ConvertToPx(),
2057         arrowOffset + ARROW_REPLACE_END_HORIZON_P4_OFFSET_Y.ConvertToPx());
2058     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
2059         childOffset - ARROW_REPLACE_END_HORIZON_P2_OFFSET_X.ConvertToPx(),
2060         arrowOffset + ARROW_REPLACE_END_HORIZON_P2_OFFSET_Y.ConvertToPx());
2061     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
2062         childOffset, arrowOffset - ARROW_REPLACE_END_HORIZON_P5_OFFSET_Y.ConvertToPx());
2063     return path;
2064 }
2065 
BuildCornerPath(const Placement & placement,float radius)2066 std::string BubbleLayoutAlgorithm::BuildCornerPath(const Placement& placement, float radius)
2067 {
2068     std::string path;
2069     float childOffsetY = childOffset_.GetY();
2070     switch (placement) {
2071         case Placement::TOP_LEFT:
2072             path += ArcTo(radius, radius, 0.0f, 0, childOffset_.GetX() + radius, childOffsetY);
2073             break;
2074         case Placement::TOP_RIGHT:
2075             path += ArcTo(radius, radius, 0.0f, 0, childOffset_.GetX() + childSize_.Width(), childOffsetY + radius);
2076             break;
2077         case Placement::BOTTOM_RIGHT:
2078             path += ArcTo(radius, radius, 0.0f, 0, childOffset_.GetX() + childSize_.Width() - radius,
2079                 childOffsetY + childSize_.Height());
2080             break;
2081         case Placement::BOTTOM_LEFT:
2082             path += ArcTo(radius, radius, 0.0f, 0, childOffset_.GetX(), childOffsetY + childSize_.Height() - radius);
2083             break;
2084         default:
2085             break;
2086     }
2087     return path;
2088 }
2089 
GetChildPosition(const SizeF & childSize,const RefPtr<BubbleLayoutProperty> & layoutProp,bool UseArrowOffset)2090 OffsetF BubbleLayoutAlgorithm::GetChildPosition(
2091     const SizeF& childSize, const RefPtr<BubbleLayoutProperty>& layoutProp, bool UseArrowOffset)
2092 {
2093     OffsetF bottomPosition;
2094     OffsetF topPosition;
2095     OffsetF topArrowPosition;
2096     OffsetF bottomArrowPosition;
2097     OffsetF fitPosition;
2098     OffsetF originOffset;
2099     OffsetF originArrowOffset;
2100     OffsetF childPosition;
2101 
2102     InitArrowTopAndBottomPosition(topArrowPosition, bottomArrowPosition, topPosition, bottomPosition, childSize);
2103     GetPositionWithPlacement(originOffset, originArrowOffset, childSize, placement_);
2104     originOffset = originOffset + positionOffset_;
2105     originArrowOffset += positionOffset_;
2106     arrowPlacement_ = placement_;
2107 
2108     // Fit popup to screen range.
2109     ErrorPositionType errorType = GetErrorPositionType(originOffset, childSize);
2110     if (errorType == ErrorPositionType::NORMAL) {
2111         arrowPosition_ = originArrowOffset;
2112         return originOffset;
2113     }
2114 
2115     if (placement_ == Placement::TOP || placement_ == Placement::TOP_LEFT || placement_ == Placement::TOP_RIGHT) {
2116         fitPosition = topPosition;
2117         arrowPosition_ = topArrowPosition;
2118         arrowPlacement_ = Placement::TOP;
2119     } else {
2120         placement_ = Placement::BOTTOM;
2121         fitPosition = bottomPosition;
2122         arrowPosition_ = bottomArrowPosition;
2123         arrowPlacement_ = Placement::BOTTOM;
2124     }
2125 
2126     childPosition = FitToScreen(fitPosition, childSize);
2127     if (UseArrowOffset) {
2128         arrowPosition_.SetX(
2129             childPosition.GetX() + border_.TopLeftRadius().GetX().ConvertToPx() + BEZIER_WIDTH_HALF.ConvertToPx());
2130     }
2131 
2132     if (GetErrorPositionType(childPosition, childSize) == ErrorPositionType::NORMAL) {
2133         return childPosition;
2134     }
2135 
2136     // Fit popup to opposite position
2137     fitPosition = fitPosition == topPosition ? bottomPosition : topPosition;
2138     arrowPosition_ = arrowPlacement_ == Placement::TOP ? bottomArrowPosition : topArrowPosition;
2139     arrowPlacement_ = arrowPlacement_ == Placement::TOP ? Placement::BOTTOM : Placement::TOP;
2140     placement_ = arrowPlacement_ == Placement::TOP ? Placement::BOTTOM : Placement::TOP;
2141 
2142     childPosition = FitToScreen(fitPosition, childSize);
2143     if (UseArrowOffset) {
2144         arrowPosition_.SetX(
2145             childPosition.GetX() + border_.TopLeftRadius().GetX().ConvertToPx() + BEZIER_WIDTH_HALF.ConvertToPx());
2146     }
2147 
2148     if (GetErrorPositionType(childPosition, childSize) == ErrorPositionType::NORMAL) {
2149         return childPosition;
2150     }
2151 
2152     // If childPosition is error, adjust bubble to origin position.
2153     arrowPlacement_ = placement_;
2154     arrowPosition_ = originArrowOffset;
2155 
2156     return originOffset;
2157 }
2158 
InitArrowTopAndBottomPosition(OffsetF & topArrowPosition,OffsetF & bottomArrowPosition,OffsetF & topPosition,OffsetF & bottomPosition,const SizeF & childSize)2159 void BubbleLayoutAlgorithm::InitArrowTopAndBottomPosition(OffsetF& topArrowPosition, OffsetF& bottomArrowPosition,
2160     OffsetF& topPosition, OffsetF& bottomPosition, const SizeF& childSize)
2161 {
2162     auto arrowCenter = targetOffset_.GetX() + targetSize_.Width() / 2.0;
2163     auto horizonSpacing = static_cast<float>(HORIZON_SPACING_WITH_SCREEN.ConvertToPx());
2164     double arrowWidth = ARROW_WIDTH.ConvertToPx();
2165     float radius = borderRadius_.ConvertToPx();
2166     auto safePosition = horizonSpacing + radius + arrowWidth / 2.0;
2167 
2168     GetPositionWithPlacement(topPosition, topArrowPosition, childSize, Placement::TOP);
2169     GetPositionWithPlacement(bottomPosition, bottomArrowPosition, childSize, Placement::BOTTOM);
2170 
2171     // move the arrow to safe position while arrow too close to window
2172     // In order not to separate the bubble from the arrow
2173     // If ArrowOffset is not set, arrow always point to the middle of the targetNode
2174     if (arrowCenter < safePosition) {
2175         topArrowPosition = topArrowPosition + OffsetF(safePosition - arrowCenter, 0);
2176         bottomArrowPosition = bottomArrowPosition + OffsetF(safePosition - arrowCenter, 0);
2177     }
2178     if (arrowCenter > selfSize_.Width() - safePosition) {
2179         topArrowPosition = topArrowPosition - OffsetF(arrowCenter + safePosition - selfSize_.Width(), 0);
2180         bottomArrowPosition = bottomArrowPosition - OffsetF(arrowCenter + safePosition - selfSize_.Width(), 0);
2181     }
2182 }
2183 
GetPositionWithPlacement(OffsetF & childPosition,OffsetF & arrowPosition,const SizeF & childSize,Placement placement)2184 void BubbleLayoutAlgorithm::GetPositionWithPlacement(
2185     OffsetF& childPosition, OffsetF& arrowPosition, const SizeF& childSize, Placement placement)
2186 {
2187     float bubbleSpacing = scaledBubbleSpacing_;
2188     float marginRight = 0.0f;
2189     float marginBottom = 0.0f;
2190     float marginTop = 0.0f;
2191     float marginLeft = 0.0f;
2192     float arrowHalfWidth = ARROW_WIDTH.ConvertToPx() / 2.0;
2193     float radius = borderRadius_.ConvertToPx();
2194     float targetSpace = targetSpace_.ConvertToPx();
2195     switch (placement) {
2196         case Placement::TOP:
2197             childPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
2198                 targetOffset_.GetY() - childSize.Height() - targetSpace - arrowHeight_);
2199             arrowPosition = childPosition + OffsetF(std::max(padding_.Left().ConvertToPx(),
2200             border_.TopLeftRadius().GetX().ConvertToPx()) +
2201             BEZIER_WIDTH_HALF.ConvertToPx(), childSize.Height() + arrowHeight_);
2202             break;
2203         case Placement::TOP_LEFT:
2204             childPosition = OffsetF(targetOffset_.GetX() - marginRight,
2205                 targetOffset_.GetY() - childSize.Height() - bubbleSpacing - marginBottom - targetSpace - arrowHeight_);
2206             arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, childSize.Height() + bubbleSpacing);
2207             break;
2208         case Placement::TOP_RIGHT:
2209             childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
2210                 targetOffset_.GetY() - childSize.Height() - targetSpace - bubbleSpacing - marginBottom - arrowHeight_);
2211             arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, childSize.Height() + bubbleSpacing);
2212             break;
2213         case Placement::BOTTOM:
2214             childPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
2215                 targetOffset_.GetY() + targetSize_.Height() + targetSpace + arrowHeight_);
2216             arrowPosition = childPosition + OffsetF(std::max(padding_.Left().ConvertToPx(),
2217             border_.BottomLeftRadius().GetX().ConvertToPx()) +
2218             BEZIER_WIDTH_HALF.ConvertToPx(), -arrowHeight_);
2219             break;
2220         case Placement::BOTTOM_LEFT:
2221             childPosition = OffsetF(targetOffset_.GetX() - marginRight,
2222                 targetOffset_.GetY() + targetSize_.Height() + targetSpace + bubbleSpacing + marginTop + arrowHeight_);
2223             arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, -bubbleSpacing);
2224             break;
2225         case Placement::BOTTOM_RIGHT:
2226             childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
2227                 targetOffset_.GetY() + targetSize_.Height() + targetSpace + bubbleSpacing + marginTop + arrowHeight_);
2228             arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, -bubbleSpacing);
2229             break;
2230         case Placement::LEFT:
2231             childPosition = OffsetF(
2232                 targetOffset_.GetX() - targetSpace - bubbleSpacing - childSize.Width() - marginRight - arrowHeight_,
2233                 targetOffset_.GetY() + targetSize_.Height() / HALF - childSize.Height() / HALF);
2234             arrowPosition = childPosition + OffsetF(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
2235             break;
2236         case Placement::LEFT_TOP:
2237             childPosition = OffsetF(
2238                 targetOffset_.GetX() - targetSpace - bubbleSpacing - childSize.Width() - marginRight - arrowHeight_,
2239                 targetOffset_.GetY() - marginBottom);
2240             arrowPosition = childPosition + OffsetF(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
2241             break;
2242         case Placement::LEFT_BOTTOM:
2243             childPosition = OffsetF(
2244                 targetOffset_.GetX() - targetSpace - bubbleSpacing - childSize.Width() - marginRight - arrowHeight_,
2245                 targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
2246             arrowPosition = childPosition + OffsetF(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
2247             break;
2248         case Placement::RIGHT:
2249             childPosition = OffsetF(
2250                 targetOffset_.GetX() + targetSize_.Width() + targetSpace + bubbleSpacing + marginLeft + arrowHeight_,
2251                 targetOffset_.GetY() + targetSize_.Height() / HALF - childSize.Height() / HALF);
2252             arrowPosition = childPosition + OffsetF(-bubbleSpacing, radius + arrowHalfWidth);
2253             break;
2254         case Placement::RIGHT_TOP:
2255             childPosition = OffsetF(
2256                 targetOffset_.GetX() + targetSize_.Width() + targetSpace + bubbleSpacing + marginLeft + arrowHeight_,
2257                 targetOffset_.GetY() - marginBottom);
2258             arrowPosition = childPosition + OffsetF(-bubbleSpacing, radius + arrowHalfWidth);
2259             break;
2260         case Placement::RIGHT_BOTTOM:
2261             childPosition = OffsetF(
2262                 targetOffset_.GetX() + targetSize_.Width() + targetSpace + bubbleSpacing + marginLeft + arrowHeight_,
2263                 targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
2264             arrowPosition = childPosition + OffsetF(-bubbleSpacing, radius + arrowHalfWidth);
2265             break;
2266         default:
2267             break;
2268     }
2269 }
2270 
GetErrorPositionType(const OffsetF & childOffset,const SizeF & childSize)2271 BubbleLayoutAlgorithm::ErrorPositionType BubbleLayoutAlgorithm::GetErrorPositionType(
2272     const OffsetF& childOffset, const SizeF& childSize)
2273 {
2274     auto horizonSpacing = static_cast<float>(HORIZON_SPACING_WITH_SCREEN.ConvertToPx());
2275     RectF validRegion =
2276         RectF(OffsetF(horizonSpacing, top_), OffsetF(selfSize_.Width() - horizonSpacing, selfSize_.Height() - bottom_));
2277     PointF childPoint(childOffset.GetX(), childOffset.GetY());
2278     if (!validRegion.IsInRegion(childPoint)) {
2279         return ErrorPositionType::TOP_LEFT_ERROR;
2280     }
2281     if (!validRegion.IsInRegion(
2282             PointF(childOffset.GetX() + childSize.Width(), childOffset.GetY() + childSize.Height()))) {
2283         return ErrorPositionType::BOTTOM_RIGHT_ERROR;
2284     }
2285     return ErrorPositionType::NORMAL;
2286 }
2287 
FitToScreen(const OffsetF & fitPosition,const SizeF & childSize)2288 OffsetF BubbleLayoutAlgorithm::FitToScreen(const OffsetF& fitPosition, const SizeF& childSize)
2289 {
2290     auto validation = GetErrorPositionType(fitPosition, childSize);
2291     if (validation == ErrorPositionType::NORMAL) {
2292         return fitPosition;
2293     }
2294     OffsetF childPosition = fitPosition;
2295     auto horizonSpacing = static_cast<float>(HORIZON_SPACING_WITH_SCREEN.ConvertToPx());
2296     if (validation == ErrorPositionType::TOP_LEFT_ERROR) {
2297         childPosition.SetX(horizonSpacing);
2298     } else {
2299         childPosition.SetX(selfSize_.Width() - childSize.Width() - horizonSpacing);
2300     }
2301     return childPosition;
2302 }
2303 
UpdateMarginByWidth()2304 void BubbleLayoutAlgorithm::UpdateMarginByWidth()
2305 {
2306     isGreatWrapperWidth_ = GreatOrEqual(childSize_.Width(), wrapperSize_.Width() - MARGIN_SPACE.ConvertToPx());
2307     marginStart_ = isGreatWrapperWidth_ ? 0.0f : marginStart_;
2308     marginEnd_ = isGreatWrapperWidth_ ? 0.0f : marginEnd_;
2309 }
2310 
2311 } // namespace OHOS::Ace::NG
2312