• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/bubble/bubble_layout_algorithm.h"
17 
18 #include <algorithm>
19 
20 #include "base/geometry/dimension.h"
21 #include "base/geometry/ng/offset_t.h"
22 #include "base/geometry/ng/point_t.h"
23 #include "base/geometry/ng/size_t.h"
24 #include "base/memory/ace_type.h"
25 #include "base/subwindow/subwindow_manager.h"
26 #include "base/utils/device_config.h"
27 #include "base/utils/system_properties.h"
28 #include "base/utils/utils.h"
29 #include "core/common/ace_engine.h"
30 #include "core/common/container.h"
31 #include "core/components/common/layout/grid_system_manager.h"
32 #include "core/components/common/properties/placement.h"
33 #include "core/components/popup/popup_theme.h"
34 #include "core/components_ng/pattern/bubble/bubble_pattern.h"
35 #include "core/components_ng/pattern/menu/menu_paint_property.h"
36 #include "core/components_ng/pattern/overlay/overlay_manager.h"
37 #include "core/components_ng/pattern/text/text_pattern.h"
38 #include "core/pipeline/pipeline_base.h"
39 #include "core/components_ng/pattern/scroll/scroll_layout_property.h"
40 #include "core/components_ng/pattern/overlay/dialog_manager.h"
41 
42 namespace OHOS::Ace::NG {
43 namespace {
44 constexpr double HALF = 2.0;
45 constexpr double DOUBLE = 2.0;
46 constexpr Dimension ARROW_RADIUS = 2.0_vp;
47 constexpr Dimension MARGIN_SPACE = 6.0_vp;
48 constexpr Dimension TIPS_MARGIN_SPACE = 8.0_vp;
49 constexpr Dimension DRAW_EDGES_SPACE = 1.0_vp;
50 constexpr double BUBBLE_ARROW_HALF = 2.0;
51 constexpr size_t ALIGNMENT_STEP_OFFSET = 1;
52 constexpr Dimension KEYBOARD_SPACE = 8.0_vp;
53 
54 // help value to calculate p2 p4 position
55 constexpr Dimension DEFAULT_BUBBLE_ARROW_WIDTH = 16.0_vp;
56 constexpr Dimension DEFAULT_BUBBLE_ARROW_HEIGHT = 8.0_vp;
57 Dimension DEFAULT_P2_HEIGHT = 7.32_vp;
58 Dimension DEFAULT_P2_WIDTH = 1.5_vp;
59 Dimension DEFAULT_P4_END_Y = 6.0_vp;
60 Dimension DEFAULT_P2_END_X = 12.8_vp;
61 Dimension DEFAULT_P2_END_Y = 7.6_vp;
62 
63 Dimension BUBBLE_ARROW_WIDTH = 16.0_vp;
64 Dimension BUBBLE_ARROW_HEIGHT = 8.0_vp;
65 std::optional<float> BUBBLE_ARROW_WIDTH_F = std::nullopt;
66 std::optional<float> BUBBLE_ARROW_HEIGHT_F = std::nullopt;
67 constexpr double ARROW_OFFSET_START_VALUE = 0.0;
68 constexpr double ARROW_OFFSET_CENTER_VALUE = 0.5;
69 constexpr Dimension HORIZON_SPACING_WITH_SCREEN = 8.0_vp;
70 constexpr Dimension BEZIER_WIDTH_HALF = 8.0_vp;
71 
72 Dimension POPUP_MIN_HEIGHT = 40.0_vp;
73 Dimension POPUP_MIN_WIDTH = 40.0_vp;
74 
75 constexpr Dimension DEFAULT_ARROW_VERTICAL_P1_OFFSET_X = 8.0_vp;
76 Dimension ARROW_VERTICAL_P1_OFFSET_X = 8.0_vp;
77 Dimension ARROW_VERTICAL_P2_OFFSET_X = 1.5_vp;
78 Dimension ARROW_VERTICAL_P2_OFFSET_Y = 7.32_vp;
79 Dimension ARROW_VERTICAL_P4_OFFSET_X = 1.5_vp;
80 Dimension ARROW_VERTICAL_P4_OFFSET_Y = 7.32_vp;
81 constexpr Dimension DEFAULT_ARROW_VERTICAL_P5_OFFSET_X = 8.0_vp;
82 Dimension ARROW_VERTICAL_P5_OFFSET_X = 8.0_vp;
83 
84 constexpr Dimension DEFAULT_ARROW_HORIZON_P1_OFFSET_Y = 8.0_vp;
85 Dimension ARROW_HORIZON_P1_OFFSET_Y = 8.0_vp;
86 Dimension ARROW_HORIZON_P2_OFFSET_Y = 1.5_vp;
87 Dimension ARROW_HORIZON_P2_OFFSET_X = 7.32_vp;
88 Dimension ARROW_HORIZON_P4_OFFSET_Y = 1.5_vp;
89 Dimension ARROW_HORIZON_P4_OFFSET_X = 7.32_vp;
90 constexpr Dimension DEFAULT_ARROW_HORIZON_P5_OFFSET_Y = 8.0_vp;
91 Dimension ARROW_HORIZON_P5_OFFSET_Y = 8.0_vp;
92 
93 Dimension ARROW_REPLACE_START_VERTICAL_P1_OFFSET_X = 8.0_vp;
94 Dimension ARROW_REPLACE_START_VERTICAL_P2_OFFSET_X = 8.0_vp;
95 Dimension ARROW_REPLACE_START_VERTICAL_P2_OFFSET_Y = 6.0_vp;
96 Dimension ARROW_REPLACE_START_VERTICAL_P4_OFFSET_X = 4.8_vp;
97 Dimension ARROW_REPLACE_START_VERTICAL_P4_OFFSET_Y = 7.6_vp;
98 Dimension ARROW_REPLACE_START_VERTICAL_P5_OFFSET_X = 8.0_vp;
99 
100 Dimension ARROW_REPLACE_END_VERTICAL_P1_OFFSET_X = 8.0_vp;
101 Dimension ARROW_REPLACE_END_VERTICAL_P2_OFFSET_X = 4.8_vp;
102 Dimension ARROW_REPLACE_END_VERTICAL_P2_OFFSET_Y = 7.6_vp;
103 Dimension ARROW_REPLACE_END_VERTICAL_P4_OFFSET_X = 8.0_vp;
104 Dimension ARROW_REPLACE_END_VERTICAL_P4_OFFSET_Y = 6.0_vp;
105 Dimension ARROW_REPLACE_END_VERTICAL_P5_OFFSET_X = 8.0_vp;
106 
107 Dimension ARROW_REPLACE_START_HORIZON_P1_OFFSET_Y = 8.0_vp;
108 Dimension ARROW_REPLACE_START_HORIZON_P2_OFFSET_X = 6.0_vp;
109 Dimension ARROW_REPLACE_START_HORIZON_P2_OFFSET_Y = 8.0_vp;
110 Dimension ARROW_REPLACE_START_HORIZON_P4_OFFSET_X = 7.6_vp;
111 Dimension ARROW_REPLACE_START_HORIZON_P4_OFFSET_Y = 4.8_vp;
112 Dimension ARROW_REPLACE_START_HORIZON_P5_OFFSET_Y = 8.0_vp;
113 
114 Dimension ARROW_REPLACE_END_HORIZON_P1_OFFSET_Y = 8.0_vp;
115 Dimension ARROW_REPLACE_END_HORIZON_P2_OFFSET_X = 7.6_vp;
116 Dimension ARROW_REPLACE_END_HORIZON_P2_OFFSET_Y = 4.8_vp;
117 Dimension ARROW_REPLACE_END_HORIZON_P4_OFFSET_X = 6.0_vp;
118 Dimension ARROW_REPLACE_END_HORIZON_P4_OFFSET_Y = 8.0_vp;
119 Dimension ARROW_REPLACE_END_HORIZON_P5_OFFSET_Y = 8.0_vp;
120 
121 constexpr Dimension BUBBLE_ARROW_ZERO_PERCENT_VALUE = 0.0_pct;
122 constexpr Dimension BUBBLE_ARROW_HALF_PERCENT_VALUE = 0.5_pct;
123 constexpr Dimension BUBBLE_ARROW_ONE_HUNDRED_PERCENT_VALUE = 1.0_pct;
124 constexpr Dimension OUT_RANGE_SPACE = 40.0_vp;
125 
126 constexpr int16_t ARROW_OFFSETS_INDEX_ZERO = 0;
127 constexpr int16_t ARROW_OFFSETS_INDEX_ONE = 1;
128 constexpr int16_t ARROW_OFFSETS_INDEX_TWO = 2;
129 constexpr int16_t ARROW_OFFSETS_INDEX_THREE = 3;
130 
131 constexpr Dimension MOUSE_WIDTH = 16.0_vp;
132 constexpr Dimension MOUSE_HEIGHT = 24.0_vp;
133 constexpr Dimension TIPS_MOUSE_SPACE = 8.0_vp;
134 constexpr Dimension MAX_TIP_WIDTH = 480.0_vp;
135 
136 const std::vector<Placement> FOLLOW_CURSOR_TIPS = { Placement::BOTTOM_LEFT, Placement::TOP_LEFT,
137     Placement::BOTTOM_RIGHT, Placement::TOP_RIGHT, Placement::BOTTOM, Placement::TOP, Placement::RIGHT_TOP,
138     Placement::LEFT_TOP, Placement::NONE };
139 
GetPopupTheme(LayoutWrapper * layoutWrapper)140 static RefPtr<PopupTheme> GetPopupTheme(LayoutWrapper* layoutWrapper)
141 {
142     RefPtr<PipelineContext> pipeline;
143     auto hostNode = layoutWrapper->GetHostNode();
144     CHECK_NULL_RETURN(hostNode, nullptr);
145     pipeline = hostNode->GetContext();
146     CHECK_NULL_RETURN(pipeline, nullptr);
147     auto popupTheme = pipeline->GetTheme<PopupTheme>();
148     CHECK_NULL_RETURN(popupTheme, nullptr);
149     return popupTheme;
150 }
151 
ConvertToPxByLayoutWrapper(const Dimension & dimension,LayoutWrapper * layoutWrapper)152 double ConvertToPxByLayoutWrapper(const Dimension& dimension, LayoutWrapper* layoutWrapper)
153 {
154     CHECK_NULL_RETURN(layoutWrapper, dimension.ConvertToPx());
155     auto hostNode = layoutWrapper->GetHostNode();
156     CHECK_NULL_RETURN(hostNode, dimension.ConvertToPx());
157     auto pipelineContext = DialogManager::GetMainPipelineContext(hostNode);
158     CHECK_NULL_RETURN(pipelineContext, dimension.ConvertToPx());
159     return pipelineContext->NormalizeToPx(dimension);
160 }
161 
GetEndP2P4(const Dimension & radius)162 void GetEndP2P4(const Dimension& radius)
163 {
164     auto h1 = BUBBLE_ARROW_HEIGHT.ConvertToPx() - radius.ConvertToPx();
165     auto w1 = BUBBLE_ARROW_WIDTH.ConvertToPx() - radius.ConvertToPx();
166     CHECK_EQUAL_VOID(w1, 0);
167     auto theta = std::atan(h1 / w1);
168     auto side = w1 /std::cos(theta);
169     auto alpha = std::asin(radius.ConvertToPx() / side);
170     auto beta = theta + alpha;
171     DEFAULT_P4_END_Y = Dimension(h1);
172     auto side1 = side * std::cos(alpha);
173     DEFAULT_P2_END_X = Dimension(side1 * std::cos(beta));
174     DEFAULT_P2_END_Y = Dimension(side1 * std::sin(beta));
175 }
176 
GetP2(const Dimension & radius)177 void GetP2(const Dimension& radius)
178 {
179     auto h1 = BUBBLE_ARROW_HEIGHT.ConvertToPx() - radius.ConvertToPx();
180     auto w1 = BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
181     CHECK_EQUAL_VOID(w1, 0);
182     auto theta = std::atan(h1 / w1);
183     auto side = w1 /std::cos(theta);
184     auto alpha = std::asin(radius.ConvertToPx() / side);
185     auto side1 = radius.ConvertToPx() / std::tan(alpha);
186     auto beta = alpha + theta;
187     DEFAULT_P2_HEIGHT = Dimension(side1 * std::sin(beta));
188     DEFAULT_P2_WIDTH = Dimension(w1 - side1 * std::cos(beta));
189 }
190 
calculateArrowPoint(Dimension height,Dimension width,LayoutWrapper * layoutWrapper)191 void calculateArrowPoint(Dimension height, Dimension width, LayoutWrapper* layoutWrapper)
192 {
193     // When the popup or tips show in subwindow, layout algorithm's pipeline will run at main window then subwindow.
194     // When user has set arrow width and unit is lpx, should convert to px by using main window's pipeline.
195     auto lastWidth = ConvertToPxByLayoutWrapper(DEFAULT_BUBBLE_ARROW_WIDTH, layoutWrapper);
196     auto rateX = NearZero(lastWidth) ? 1.0f : ConvertToPxByLayoutWrapper(width, layoutWrapper) / lastWidth;
197     BUBBLE_ARROW_WIDTH = width;
198     BUBBLE_ARROW_HEIGHT = height;
199     BUBBLE_ARROW_WIDTH_F = ConvertToPxByLayoutWrapper(width, layoutWrapper);
200     BUBBLE_ARROW_HEIGHT_F = ConvertToPxByLayoutWrapper(height, layoutWrapper);
201 
202     GetEndP2P4(ARROW_RADIUS);
203     GetP2(ARROW_RADIUS);
204 
205     ARROW_VERTICAL_P1_OFFSET_X = DEFAULT_ARROW_VERTICAL_P1_OFFSET_X * rateX;
206     ARROW_VERTICAL_P2_OFFSET_Y = DEFAULT_P2_HEIGHT;
207     ARROW_VERTICAL_P2_OFFSET_X = DEFAULT_P2_WIDTH;
208     ARROW_VERTICAL_P4_OFFSET_Y = DEFAULT_P2_HEIGHT;
209     ARROW_VERTICAL_P4_OFFSET_X = DEFAULT_P2_WIDTH;
210     ARROW_VERTICAL_P5_OFFSET_X = DEFAULT_ARROW_VERTICAL_P5_OFFSET_X * rateX;
211 
212     ARROW_HORIZON_P1_OFFSET_Y = DEFAULT_ARROW_HORIZON_P1_OFFSET_Y * rateX;
213     ARROW_HORIZON_P2_OFFSET_X = DEFAULT_P2_HEIGHT;
214     ARROW_HORIZON_P2_OFFSET_Y = DEFAULT_P2_WIDTH;
215     ARROW_HORIZON_P4_OFFSET_X = DEFAULT_P2_HEIGHT;
216     ARROW_HORIZON_P4_OFFSET_Y = DEFAULT_P2_WIDTH;
217     ARROW_HORIZON_P5_OFFSET_Y = DEFAULT_ARROW_HORIZON_P5_OFFSET_Y * rateX;
218 
219     auto p1x = BUBBLE_ARROW_WIDTH / HALF;
220     auto p2x = Dimension(DEFAULT_P2_END_X.ConvertToPx() - p1x.ConvertToPx());
221     auto p2y = DEFAULT_P2_END_Y;
222     auto p4y = DEFAULT_P4_END_Y;
223 
224     ARROW_REPLACE_START_VERTICAL_P1_OFFSET_X = p1x;
225     ARROW_REPLACE_START_VERTICAL_P2_OFFSET_X = p1x;
226     ARROW_REPLACE_START_VERTICAL_P2_OFFSET_Y = p4y;
227     ARROW_REPLACE_START_VERTICAL_P4_OFFSET_X = p2x;
228     ARROW_REPLACE_START_VERTICAL_P4_OFFSET_Y = p2y;
229     ARROW_REPLACE_START_VERTICAL_P5_OFFSET_X = p1x;
230 
231     ARROW_REPLACE_END_VERTICAL_P1_OFFSET_X = p1x;
232     ARROW_REPLACE_END_VERTICAL_P2_OFFSET_X = p2x;
233     ARROW_REPLACE_END_VERTICAL_P2_OFFSET_Y = p2y;
234     ARROW_REPLACE_END_VERTICAL_P4_OFFSET_X = p1x;
235     ARROW_REPLACE_END_VERTICAL_P4_OFFSET_Y = p4y;
236     ARROW_REPLACE_END_VERTICAL_P5_OFFSET_X = p1x;
237 
238     ARROW_REPLACE_START_HORIZON_P1_OFFSET_Y = p1x;
239     ARROW_REPLACE_START_HORIZON_P2_OFFSET_X = p4y;
240     ARROW_REPLACE_START_HORIZON_P2_OFFSET_Y = p1x;
241     ARROW_REPLACE_START_HORIZON_P4_OFFSET_X = p2y;
242     ARROW_REPLACE_START_HORIZON_P4_OFFSET_Y = p2x;
243     ARROW_REPLACE_START_HORIZON_P5_OFFSET_Y = p1x;
244 
245     ARROW_REPLACE_END_HORIZON_P1_OFFSET_Y = p1x;
246     ARROW_REPLACE_END_HORIZON_P2_OFFSET_X = p2y;
247     ARROW_REPLACE_END_HORIZON_P2_OFFSET_Y = p2x;
248     ARROW_REPLACE_END_HORIZON_P4_OFFSET_X = p4y;
249     ARROW_REPLACE_END_HORIZON_P4_OFFSET_Y = p1x;
250     ARROW_REPLACE_END_HORIZON_P5_OFFSET_Y = p1x;
251 }
252 
ResetTipsMaxLines(const RefPtr<LayoutWrapper> & childWrapper,bool isTips)253 void ResetTipsMaxLines(const RefPtr<LayoutWrapper>& childWrapper, bool isTips)
254 {
255     if (!isTips) {
256         return;
257     }
258     auto children = childWrapper->GetAllChildrenWithBuild();
259     CHECK_EQUAL_VOID(children.empty(), true);
260     auto text = children.front();
261     CHECK_NULL_VOID(text);
262     if (text->GetHostTag() != V2::TEXT_ETS_TAG) {
263         return;
264     }
265     auto layoutProps = AceType::DynamicCast<TextLayoutProperty>(text->GetLayoutProperty());
266     CHECK_NULL_VOID(layoutProps);
267     layoutProps->ResetMaxLines();
268 }
269 
SetArrowSize(float & width,float & height)270 void SetArrowSize(float& width, float& height)
271 {
272     width = BUBBLE_ARROW_WIDTH.ConvertToPx();
273     height = BUBBLE_ARROW_HEIGHT.ConvertToPx();
274 }
275 } // namespace
276 
BubbleLayoutAlgorithm(int32_t id,const std::string & tag,const std::optional<OffsetF> & targetOffset,const std::optional<SizeF> & targetSize,const std::optional<Offset> & mouseOffset)277 BubbleLayoutAlgorithm::BubbleLayoutAlgorithm(int32_t id, const std::string& tag,
278     const std::optional<OffsetF>& targetOffset, const std::optional<SizeF>& targetSize,
279     const std::optional<Offset>& mouseOffset)
280     : targetNodeId_(id), targetTag_(tag)
281 {
282     if (targetOffset.has_value()) {
283         targetOffset_ = targetOffset.value();
284     }
285     if (targetSize.has_value()) {
286         targetSize_ = targetSize.value();
287     }
288     if (mouseOffset.has_value()) {
289         targetOffset_ = OffsetF(mouseOffset->GetX(), mouseOffset->GetY());
290         targetSize_ = SizeF(MOUSE_WIDTH.ConvertToPx(), MOUSE_HEIGHT.ConvertToPx());
291     }
292     placementFuncMap_[Placement::TOP] = &BubbleLayoutAlgorithm::GetPositionWithPlacementTop;
293     placementFuncMap_[Placement::TOP_LEFT] = &BubbleLayoutAlgorithm::GetPositionWithPlacementTopLeft;
294     placementFuncMap_[Placement::TOP_RIGHT] = &BubbleLayoutAlgorithm::GetPositionWithPlacementTopRight;
295     placementFuncMap_[Placement::BOTTOM] = &BubbleLayoutAlgorithm::GetPositionWithPlacementBottom;
296     placementFuncMap_[Placement::BOTTOM_LEFT] = &BubbleLayoutAlgorithm::GetPositionWithPlacementBottomLeft;
297     placementFuncMap_[Placement::BOTTOM_RIGHT] = &BubbleLayoutAlgorithm::GetPositionWithPlacementBottomRight;
298     placementFuncMap_[Placement::LEFT] = &BubbleLayoutAlgorithm::GetPositionWithPlacementLeft;
299     placementFuncMap_[Placement::LEFT_TOP] = &BubbleLayoutAlgorithm::GetPositionWithPlacementLeftTop;
300     placementFuncMap_[Placement::LEFT_BOTTOM] = &BubbleLayoutAlgorithm::GetPositionWithPlacementLeftBottom;
301     placementFuncMap_[Placement::RIGHT] = &BubbleLayoutAlgorithm::GetPositionWithPlacementRight;
302     placementFuncMap_[Placement::RIGHT_TOP] = &BubbleLayoutAlgorithm::GetPositionWithPlacementRightTop;
303     placementFuncMap_[Placement::RIGHT_BOTTOM] = &BubbleLayoutAlgorithm::GetPositionWithPlacementRightBottom;
304 
305     setHorizontal_ = { Placement::LEFT, Placement::LEFT_BOTTOM, Placement::LEFT_TOP, Placement::RIGHT,
306         Placement::RIGHT_BOTTOM, Placement::RIGHT_TOP };
307     setVertical_ = { Placement::TOP, Placement::TOP_LEFT, Placement::TOP_RIGHT, Placement::BOTTOM,
308         Placement::BOTTOM_LEFT, Placement::BOTTOM_RIGHT };
309 }
310 
UpdateBubbleMaxSize(LayoutWrapper * layoutWrapper,bool showInSubWindow)311 void BubbleLayoutAlgorithm::UpdateBubbleMaxSize(LayoutWrapper* layoutWrapper, bool showInSubWindow)
312 {
313     CHECK_EQUAL_VOID(isTips_, true);
314     CHECK_NULL_VOID(layoutWrapper);
315     auto bubbleNode = layoutWrapper->GetHostNode();
316     CHECK_NULL_VOID(bubbleNode);
317     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
318     if (children.empty()) {
319         return;
320     }
321     auto child = children.front();
322     CHECK_NULL_VOID(child);
323     auto childProp = child->GetLayoutProperty();
324     CHECK_NULL_VOID(childProp);
325     auto maxSize = GetPopupMaxWidthAndHeight(showInSubWindow, bubbleNode);
326     float popupMaxWidth = maxSize.Width();
327     float popupMaxHeight = maxSize.Height();
328     if (useCustom_) {
329         childProp->UpdateCalcMaxSize(CalcSize(std::nullopt, NG::CalcLength(Dimension(popupMaxHeight))));
330     } else if (GreatNotEqual(popupMaxWidth, 0.0f) && GreatNotEqual(popupMaxHeight, 0.0f)) {
331         childProp->UpdateCalcMaxSize(
332             CalcSize(NG::CalcLength(Dimension(popupMaxWidth)), NG::CalcLength(Dimension(popupMaxHeight))));
333     }
334 }
335 
FitAvailableRect(LayoutWrapper * layoutWrapper,bool showInSubWindow)336 void BubbleLayoutAlgorithm::FitAvailableRect(LayoutWrapper* layoutWrapper, bool showInSubWindow)
337 {
338     auto bubbleNode = layoutWrapper->GetHostNode();
339     CHECK_NULL_VOID(bubbleNode);
340     auto pipelineContext = bubbleNode->GetContextRefPtr();
341     CHECK_NULL_VOID(pipelineContext);
342     auto containerId = pipelineContext->GetInstanceId();
343     auto container = AceEngine::Get().GetContainer(containerId);
344     CHECK_NULL_VOID(container);
345     if (container->IsSubContainer()) {
346         auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(containerId);
347         container = AceEngine::Get().GetContainer(parentContainerId);
348         CHECK_NULL_VOID(container);
349     }
350     CHECK_EQUAL_VOID(expandDisplay_, false);
351     Rect availableRect;
352     // In superFoldDisplayDevice, the rect is the full screen's available rect when the displayId is 0.
353     if (SystemProperties::IsSuperFoldDisplayDevice() && container->GetDisplayId() == 0 && !showInSubWindow) {
354         availableRect = container->GetFoldExpandAvailableRect();
355     } else {
356         availableRect = OverlayManager::GetDisplayAvailableRect(bubbleNode,
357             static_cast<int32_t>(SubwindowType::TYPE_POPUP));
358     }
359     TAG_LOGI(AceLogTag::ACE_OVERLAY, "popup currentId: %{public}d, displayAvailableRect: %{public}s",
360         containerId, availableRect.ToString().c_str());
361     if (showInSubWindow) {
362         CHECK_EQUAL_VOID(container->IsSceneBoardWindow(), true);
363         marginTop_ = std::max(marginTop_,
364             static_cast<float>(availableRect.Top() + (isTips_ ? TIPS_MARGIN_SPACE.ConvertToPx() : .0f)));
365         marginBottom_ = std::max(marginBottom_, static_cast<float>(wrapperSize_.Height()
366             - availableRect.Top() - availableRect.Height() + (isTips_ ? TIPS_MARGIN_SPACE.ConvertToPx() : .0f)));
367     } else {
368         auto displayWindowRect = pipelineContext->GetDisplayWindowRectInfo();
369         auto wrapperOffset = layoutWrapper->GetParentGlobalOffsetWithSafeArea();
370         auto realWrapperRect = Rect(wrapperOffset.GetX() + displayWindowRect.Left(),
371                                     wrapperOffset.GetY() + displayWindowRect.Top(),
372                                     wrapperSize_.Width(), wrapperSize_.Height());
373         auto commonRect = availableRect.IntersectRect(realWrapperRect);
374         CHECK_EQUAL_VOID(commonRect.IsValid(), false);
375         wrapperSize_.SetHeight(commonRect.Top() + commonRect.Height() - realWrapperRect.Top());
376         wrapperSize_.SetWidth(commonRect.Left() + commonRect.Width() - realWrapperRect.Left());
377         marginTop_ = std::max(marginTop_, static_cast<float>(commonRect.Top() -
378             realWrapperRect.Top()));
379         marginStart_ = std::max(marginStart_, static_cast<float>(commonRect.Left() -
380             realWrapperRect.Left() + marginStart_));
381     }
382 }
383 
FitMouseOffset(LayoutWrapper * layoutWrapper)384 void BubbleLayoutAlgorithm::FitMouseOffset(LayoutWrapper* layoutWrapper)
385 {
386     CHECK_EQUAL_VOID(followCursor_, false);
387     CHECK_EQUAL_VOID(expandDisplay_, true);
388     CHECK_NULL_VOID(layoutWrapper);
389     auto host = layoutWrapper->GetHostNode();
390     CHECK_NULL_VOID(host);
391     RefPtr<PipelineContext> pipelineContext = host->GetContextRefPtr();
392     CHECK_NULL_VOID(pipelineContext);
393     auto containerId = pipelineContext->GetInstanceId();
394     auto container = AceEngine::Get().GetContainer(containerId);
395     CHECK_NULL_VOID(container);
396     RefPtr<Container> parentContainer;
397     if (container->IsSubContainer()) {
398         auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(containerId);
399         parentContainer = AceEngine::Get().GetContainer(parentContainerId);
400         CHECK_NULL_VOID(parentContainer);
401         pipelineContext = AceType::DynamicCast<PipelineContext>(parentContainer->GetPipelineContext());
402         CHECK_NULL_VOID(pipelineContext);
403     }
404     CHECK_NULL_VOID(parentContainer);
405     auto displayWindowRect = pipelineContext->GetDisplayWindowRectInfo();
406 
407     float scaleX = 1.0f;
408     float scaleY = 1.0f;
409     if (container->IsSubContainer()) {
410         auto rect = parentContainer->GetGlobalScaledRect();
411         if (!NearZero(displayWindowRect.Width())) {
412             scaleX = rect.Width() / static_cast<float>(displayWindowRect.Width());
413         }
414         if (!NearZero(displayWindowRect.Height())) {
415             scaleY = rect.Height() / static_cast<float>(displayWindowRect.Height());
416         }
417     }
418     if (!NearZero(scaleX) && !NearZero(scaleY)) {
419         targetOffset_ -= OffsetF(displayWindowRect.GetOffset().GetX(), displayWindowRect.GetOffset().GetY());
420         targetOffset_.SetX(targetOffset_.GetX() / scaleX);
421         targetOffset_.SetY(targetOffset_.GetY() / scaleY);
422     }
423 }
424 
Measure(LayoutWrapper * layoutWrapper)425 void BubbleLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
426 {
427     CHECK_NULL_VOID(layoutWrapper);
428     auto bubbleProp = DynamicCast<BubbleLayoutProperty>(layoutWrapper->GetLayoutProperty());
429     CHECK_NULL_VOID(bubbleProp);
430     auto bubbleLayoutProperty = AceType::DynamicCast<BubbleLayoutProperty>(layoutWrapper->GetLayoutProperty());
431     CHECK_NULL_VOID(bubbleLayoutProperty);
432     bool showInSubWindow = bubbleLayoutProperty->GetShowInSubWindowValue(false);
433     useCustom_ = bubbleLayoutProperty->GetUseCustom().value_or(false);
434     isTips_ = bubbleLayoutProperty->GetIsTips().value_or(false);
435     UpdateBubbleMaxSize(layoutWrapper, showInSubWindow);
436     InitProps(bubbleProp, showInSubWindow, layoutWrapper);
437     auto bubbleNode = layoutWrapper->GetHostNode();
438     CHECK_NULL_VOID(bubbleNode);
439     FitAvailableRect(layoutWrapper, showInSubWindow);
440     FitMouseOffset(layoutWrapper);
441     const auto& layoutConstraint = bubbleLayoutProperty->GetLayoutConstraint();
442     if (!layoutConstraint) {
443         LOGE("fail to measure bubble due to layoutConstraint is nullptr");
444         return;
445     }
446     // bubble size fit screen.
447     layoutWrapper->GetGeometryNode()->SetFrameSize(layoutConstraint->maxSize);
448     layoutWrapper->GetGeometryNode()->SetContentSize(layoutConstraint->maxSize);
449     // update child layout constraint
450     LayoutConstraintF childLayoutConstraint = bubbleLayoutProperty->CreateChildConstraint();
451     if (avoidKeyboard_) {
452         childLayoutConstraint.maxSize.SetHeight(wrapperSize_.Height() - marginTop_ - KEYBOARD_SPACE.ConvertToPx());
453         childLayoutConstraint.maxSize.SetWidth(wrapperSize_.Width());
454     }
455     float minHeight = minHeight_.ConvertToPx();
456     if (GreatNotEqual(static_cast<double>(minHeight), 0.0)) {
457         childLayoutConstraint.minSize.SetHeight(minHeight);
458     }
459     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
460     if (children.empty()) {
461         return;
462     }
463     auto child = children.front();
464     CHECK_NULL_VOID(child);
465     measureChildSizeBefore_ = child->GetGeometryNode()->GetFrameSize();
466     if (isHalfFoldHover_) {
467         SizeF size = SizeF(childLayoutConstraint.maxSize.Width(),
468             static_cast<float>(std::floor(wrapperRect_.Height())));
469         childLayoutConstraint.UpdateMaxSizeWithCheck(size);
470     }
471     // childSize_ and childOffset_ is used in Layout.
472     auto childProp = child->GetLayoutProperty();
473     CHECK_NULL_VOID(childProp);
474     childProp->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE);
475     ResetTipsMaxLines(child, isTips_);
476     child->Measure(childLayoutConstraint);
477     measureChildSizeAfter_ = child->GetGeometryNode()->GetFrameSize();
478     if (isTips_) {
479         followCursor_ ? MeasureTipsRegion(child, childLayoutConstraint)
480                       : MeasureTipsFollowTarget(child, childLayoutConstraint);
481     }
482     if (!NearEqual(measureChildSizeBefore_, measureChildSizeAfter_)) {
483         auto childShowWidth = child->GetGeometryNode()->GetFrameSize().Width() + BUBBLE_ARROW_HEIGHT.ConvertToPx() * 2;
484         auto childShowHeight =
485             child->GetGeometryNode()->GetFrameSize().Height() + BUBBLE_ARROW_HEIGHT.ConvertToPx() * 2;
486         child->GetGeometryNode()->SetFrameSize(SizeF { childShowWidth, childShowHeight });
487         measureChildSizeLast_ = child->GetGeometryNode()->GetFrameSize();
488     } else {
489         measureChildSizeLast_ = child->GetGeometryNode()->GetFrameSize();
490     }
491     if (useCustom_ && !showInSubWindow) {
492         auto context = bubbleNode->GetContext();
493         CHECK_NULL_VOID(context);
494         float rootH = context->GetRootHeight();
495         float rootW = context->GetRootWidth();
496         auto childHeight = child->GetGeometryNode()->GetMarginFrameSize().Height();
497         auto childWidth = child->GetGeometryNode()->GetMarginFrameSize().Width();
498         auto scaledBubbleSpacing = scaledBubbleSpacing_ * 2;
499         auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
500         CHECK_NULL_VOID(targetNode);
501         auto geometryNode = targetNode->GetGeometryNode();
502         CHECK_NULL_VOID(geometryNode);
503         auto targetSize = geometryNode->GetFrameSize();
504         auto targetOffset = targetNode->GetPaintRectOffset();
505         auto constrainHeight = layoutWrapper->GetGeometryNode()->GetFrameSize().Height();
506         auto constrainWidth = layoutWrapper->GetGeometryNode()->GetFrameSize().Width();
507         float maxWidth = constrainWidth - targetSecurity_ * DOUBLE;
508         auto placement = bubbleLayoutProperty->GetPlacement().value_or(Placement::BOTTOM);
509         std::unordered_set<Placement> setHorizontal = { Placement::LEFT, Placement::LEFT_BOTTOM, Placement::LEFT_TOP,
510             Placement::RIGHT, Placement::RIGHT_BOTTOM, Placement::RIGHT_TOP };
511         std::unordered_set<Placement> setVertical = { Placement::TOP, Placement::TOP_LEFT, Placement::TOP_RIGHT,
512             Placement::BOTTOM, Placement::BOTTOM_LEFT, Placement::BOTTOM_RIGHT };
513         if (setHorizontal.find(placement) != setHorizontal.end()) {
514             if (childWidth + targetOffset.GetX() + targetSize.Width() + scaledBubbleSpacing <= rootW &&
515                 targetOffset.GetX() - childWidth - scaledBubbleSpacing >= 0) {
516                 return;
517             }
518             constrainWidth = rootW - scaledBubbleSpacing;
519         }
520         if (Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
521             if (setVertical.find(placement) != setVertical.end()) {
522                 if (childHeight + targetOffset.GetY() + targetSize.Height() + scaledBubbleSpacing <= rootH &&
523                     targetOffset.GetY() - childHeight - scaledBubbleSpacing >= 0) {
524                     return;
525                 }
526                 constrainHeight = std::max(rootH - targetOffset.GetY() - targetSize.Height() - scaledBubbleSpacing,
527                     targetOffset.GetY() - scaledBubbleSpacing);
528             }
529         }
530         constrainWidth = std::min(constrainWidth, maxWidth);
531         SizeF size = SizeF(constrainWidth, constrainHeight);
532         childLayoutConstraint.UpdateMaxSizeWithCheck(size);
533         child->Measure(childLayoutConstraint);
534     }
535 }
536 
GetMaxWith(uint32_t maxColumns)537 Dimension GetMaxWith(uint32_t maxColumns)
538 {
539     auto gridColumnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::BUBBLE_TYPE);
540     auto parent = gridColumnInfo->GetParent();
541     if (parent) {
542         parent->BuildColumnWidth();
543     }
544     auto maxWidth = Dimension(gridColumnInfo->GetMaxWidth());
545     if (maxColumns > 0) {
546         maxWidth = Dimension(gridColumnInfo->GetWidth(maxColumns));
547     }
548     return maxWidth;
549 }
550 
GetPopupMaxWidthAndHeight(bool showInSubWindow,const RefPtr<FrameNode> & frameNode)551 SizeF BubbleLayoutAlgorithm::GetPopupMaxWidthAndHeight(bool showInSubWindow, const RefPtr<FrameNode>& frameNode)
552 {
553     auto pipelineContext = PipelineContext::GetMainPipelineContext();
554     CHECK_NULL_RETURN(pipelineContext, SizeF());
555     auto windowGlobalRect = pipelineContext->GetDisplayWindowRectInfo();
556 
557     CHECK_NULL_RETURN(frameNode, SizeF());
558     auto geometryNode = frameNode->GetGeometryNode();
559     CHECK_NULL_RETURN(geometryNode, SizeF());
560     auto width = geometryNode->GetMarginFrameSize().Width();
561 
562     auto safeAreaInsets = OverlayManager::GetSafeAreaInsets(frameNode);
563     // system safeArea(AvoidAreaType.TYPE_SYSTEM) only include status bar, the bottom is 0
564     auto bottom = 0.0;
565     auto top = safeAreaInsets.top_.Length();
566     auto maxHeight = windowGlobalRect.Height();
567     if (showInSubWindow) {
568         maxHeight = SystemProperties::GetDeviceHeight();
569     }
570     auto popupMaxWidth = GetMaxWith(maxColumns_).Value();
571     if (useCustom_) {
572         popupMaxWidth = width;
573     }
574     auto popupMaxHeight = maxHeight - OUT_RANGE_SPACE.ConvertToPx() - OUT_RANGE_SPACE.ConvertToPx() - bottom - top;
575     return SizeF(popupMaxWidth, popupMaxHeight);
576 }
577 
SetBubbleRadius()578 void BubbleLayoutAlgorithm::SetBubbleRadius()
579 {
580     auto littleSide = childSize_.Height() > childSize_.Width() ? childSize_.Width() : childSize_.Height();
581     auto littleSideHalf = littleSide / HALF;
582     if (borderRadius_.Unit() == DimensionUnit::PERCENT) {
583         auto value = borderRadius_.Value() * littleSideHalf;
584         borderRadius_.SetValue(value);
585         borderRadius_.SetUnit(DimensionUnit::PX);
586         border_.SetBorderRadius(Radius(borderRadius_));
587     }
588     auto borderRadius = ModifyBorderRadius(border_.BottomLeftRadius().GetY().ConvertToPx(), childSize_.Height() / 2);
589     auto borderRadius2 = ModifyBorderRadius(border_.BottomLeftRadius().GetY().ConvertToPx(), childSize_.Width() / 2);
590     float radiusPx = borderRadius < borderRadius2 ? borderRadius : borderRadius2;
591     borderRadius_.SetValue(radiusPx);
592     borderRadius_.SetUnit(DimensionUnit::PX);
593     border_.SetBorderRadius(Radius(borderRadius_));
594 }
595 
BubbleAvoidanceRule(RefPtr<LayoutWrapper> child,RefPtr<BubbleLayoutProperty> bubbleProp,RefPtr<FrameNode> bubbleNode,bool showInSubWindow,LayoutWrapper * layoutWrapper)596 void BubbleLayoutAlgorithm::BubbleAvoidanceRule(RefPtr<LayoutWrapper> child, RefPtr<BubbleLayoutProperty> bubbleProp,
597     RefPtr<FrameNode> bubbleNode, bool showInSubWindow, LayoutWrapper* layoutWrapper)
598 {
599     enableArrow_ = followCursor_ ? false : bubbleProp->GetEnableArrow().value_or(false);
600     auto bubblePattern = bubbleNode->GetPattern<BubblePattern>();
601     CHECK_NULL_VOID(bubblePattern);
602     auto bubblePaintProperty = bubbleNode->GetPaintProperty<BubbleRenderProperty>();
603     CHECK_NULL_VOID(bubblePaintProperty);
604     bool UseArrowOffset = bubblePaintProperty->GetArrowOffset().has_value();
605     if (!bubblePattern->IsExiting()) {
606         InitTargetSizeAndPosition(showInSubWindow, layoutWrapper);
607         if (isCaretMode_) {
608             InitCaretTargetSizeAndPosition();
609         }
610         // subtract the global offset of the overlay node,
611         // because the final node position is set relative to the overlay node.
612         auto overlayGlobalOffset = bubbleNode->GetOffsetRelativeToWindow();
613         targetOffset_ -= overlayGlobalOffset;
614     }
615     childSize_ = child->GetGeometryNode()->GetMarginFrameSize(); // bubble's size
616     auto childShowWidth = childSize_.Width() - BUBBLE_ARROW_HEIGHT.ConvertToPx() * 2;
617     auto childShowHeight = childSize_.Height() - BUBBLE_ARROW_HEIGHT.ConvertToPx() * 2;
618     childSize_ = SizeF(childShowWidth, childShowHeight);
619     SetBubbleRadius();
620     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
621         childOffset_ = GetChildPosition(childSize_, bubbleProp, UseArrowOffset); // bubble's offset
622         placement_ = arrowPlacement_;
623         UpdateChildPosition(childOffset_);
624         if (arrowPlacement_ == Placement::TOP) {
625             if (bCaretMode_) {
626                 arrowPosition_ = OffsetF(targetOffset_.GetX(), targetOffset_.GetY() - scaledBubbleSpacing_);
627             }
628         }
629         if (arrowPlacement_ == Placement::BOTTOM) {
630             if (bCaretMode_) {
631                 arrowPosition_ =
632                     OffsetF(targetOffset_.GetX(), targetOffset_.GetY() + targetSize_.Height() + scaledBubbleSpacing_);
633             }
634         }
635     } else {
636         UpdateMarginByWidth();
637         avoidTarget_ = bubblePattern->GetAvoidTarget();
638         hasPlacement_ = bubblePattern->HasPlacement();
639         hasWidth_ = bubblePattern->HasWidth();
640         childOffset_ = GetChildPositionNew(childSize_, bubbleProp, child); // bubble's offset
641         childOffset_ = AddOffset(childOffset_);
642         dumpInfo_.finalPlacement = PlacementUtils::ConvertPlacementToString(placement_);
643     }
644 }
645 
Layout(LayoutWrapper * layoutWrapper)646 void BubbleLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
647 {
648     CHECK_NULL_VOID(layoutWrapper);
649     auto bubbleProp = DynamicCast<BubbleLayoutProperty>(layoutWrapper->GetLayoutProperty());
650     CHECK_NULL_VOID(bubbleProp);
651     auto frameNode = layoutWrapper->GetHostNode();
652     CHECK_NULL_VOID(frameNode);
653     auto bubblePattern = frameNode->GetPattern<BubblePattern>();
654     CHECK_NULL_VOID(bubblePattern);
655     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
656     if (children.empty()) {
657         return;
658     }
659     selfSize_ = layoutWrapper->GetGeometryNode()->GetFrameSize(); // window's size
660     auto childWrapper = children.front();
661     CHECK_NULL_VOID(childWrapper);
662     bool showInSubWindow = bubbleProp->GetShowInSubWindowValue(false);
663     auto layoutChildSize = childWrapper->GetGeometryNode()->GetMarginFrameSize();
664     auto childMaxSize = GetPopupMaxWidthAndHeight(showInSubWindow, childWrapper->GetHostNode());
665     if (NearEqual(childMaxSize, layoutChildSize) || !NearEqual(measureChildSizeLast_, layoutChildSize)) {
666         auto childShowWidth =
667             childWrapper->GetGeometryNode()->GetFrameSize().Width() + BUBBLE_ARROW_HEIGHT.ConvertToPx() * 2;
668         auto childShowHeight =
669             childWrapper->GetGeometryNode()->GetFrameSize().Height() + BUBBLE_ARROW_HEIGHT.ConvertToPx() * 2;
670         childWrapper->GetGeometryNode()->SetFrameSize(SizeF { childShowWidth, childShowHeight });
671     }
672     auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
673     if (!targetNode) {
674         TAG_LOGD(AceLogTag::ACE_OVERLAY, "Popup can not get target node, stop layout");
675         return;
676     }
677     if (bubblePattern->IsExiting()) {
678         return;
679     }
680     BubbleAvoidanceRule(childWrapper, bubbleProp, frameNode, showInSubWindow, layoutWrapper);
681     UpdateTouchRegion();
682     auto childShowOffset = OffsetF(childOffset_.GetX() - BUBBLE_ARROW_HEIGHT.ConvertToPx(),
683         childOffset_.GetY() - BUBBLE_ARROW_HEIGHT.ConvertToPx());
684     childWrapper->GetGeometryNode()->SetMarginFrameOffset(childShowOffset);
685     childWrapper->Layout();
686     auto childLayoutWrapper = layoutWrapper->GetOrCreateChildByIndex(0);
687     CHECK_NULL_VOID(childLayoutWrapper);
688     const auto& columnChild = childLayoutWrapper->GetAllChildrenWithBuild();
689     if (columnChild.size() > 1 && !useCustom_) {
690         auto buttonRow = columnChild.back();
691         buttonRowSize_ = buttonRow->GetGeometryNode()->GetMarginFrameSize();
692         buttonRowOffset_ = buttonRow->GetGeometryNode()->GetMarginFrameOffset() + childOffset_;
693     }
694     targetOffsetForPaint_ = targetOffset_;
695     childOffsetForPaint_ = childOffset_;
696     arrowPositionForPaint_ = arrowPosition_;
697     auto isBlock = bubbleProp->GetBlockEventValue(true);
698     dumpInfo_.mask = isBlock;
699     UpdateHostWindowRect();
700     if (!isTips_) {
701         SetHotAreas(showInSubWindow, isBlock, frameNode, bubblePattern->GetContainerId());
702     }
703     UpdateClipOffset(frameNode);
704 }
705 
UpdateHostWindowRect()706 void BubbleLayoutAlgorithm::UpdateHostWindowRect()
707 {
708     hostWindowRect_ = SubwindowManager::GetInstance()->GetParentWindowRect();
709     auto currentId = Container::CurrentId();
710     auto container = Container::Current();
711     CHECK_NULL_VOID(container);
712     if (container->IsSubContainer()) {
713         auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(currentId);
714         container = AceEngine::Get().GetContainer(parentContainerId);
715         CHECK_NULL_VOID(container);
716     }
717     if (container->IsUIExtensionWindow()) {
718         auto subwindow = SubwindowManager::GetInstance()->GetSubwindowByType(currentId, SubwindowType::TYPE_POPUP);
719         CHECK_NULL_VOID(subwindow);
720         hostWindowRect_ = subwindow->GetUIExtensionHostWindowRect();
721     }
722 }
723 
SetHotAreas(bool showInSubWindow,bool isBlock,RefPtr<FrameNode> frameNode,int32_t containerId)724 void BubbleLayoutAlgorithm::SetHotAreas(bool showInSubWindow, bool isBlock,
725     RefPtr<FrameNode> frameNode, int32_t containerId)
726 {
727     if (showInSubWindow) {
728         std::vector<Rect> rects;
729         if (!isBlock) {
730             auto rect = Rect(childOffsetForPaint_.GetX(), childOffsetForPaint_.GetY(),
731                 childSize_.Width(), childSize_.Height());
732             rects.emplace_back(rect);
733         } else {
734             auto rect = Rect(childOffsetForPaint_.GetX(), childOffsetForPaint_.GetY(),
735                 childSize_.Width(), childSize_.Height());
736             rects.emplace_back(hostWindowRect_);
737             rects.emplace_back(rect);
738         }
739         auto context = frameNode->GetContextRefPtr();
740         CHECK_NULL_VOID(context);
741         auto taskExecutor = context->GetTaskExecutor();
742         CHECK_NULL_VOID(taskExecutor);
743         taskExecutor->PostTask(
744             [rects, containerId, frameNodeWK = WeakClaim(RawPtr(frameNode))]() {
745                 auto frameNode = frameNodeWK.Upgrade();
746                 CHECK_NULL_VOID(frameNode);
747                 auto subWindowMgr = SubwindowManager::GetInstance();
748                 subWindowMgr->SetHotAreas(rects, SubwindowType::TYPE_POPUP, frameNode->GetId(), containerId);
749             },
750             TaskExecutor::TaskType::UI, "ArkUIPopupSetHotAreas");
751     }
752 }
753 
IsUIExtensionWindow()754 bool BubbleLayoutAlgorithm::IsUIExtensionWindow()
755 {
756     auto currentId = Container::CurrentId();
757     auto container = Container::Current();
758     CHECK_NULL_RETURN(container, false);
759     if (container->IsSubContainer()) {
760         currentId = SubwindowManager::GetInstance()->GetParentContainerId(currentId);
761         container = AceEngine::Get().GetContainer(currentId);
762         CHECK_NULL_RETURN(container, false);
763     }
764     if (container->IsUIExtensionWindow()) {
765         return true;
766     }
767     return false;
768 }
769 
GetIfNeedArrow(const RefPtr<BubbleLayoutProperty> & bubbleProp,const SizeF & childSize)770 bool BubbleLayoutAlgorithm::GetIfNeedArrow(const RefPtr<BubbleLayoutProperty>& bubbleProp, const SizeF& childSize)
771 {
772     auto enableArrow = bubbleProp->GetEnableArrow().value_or(true);
773     if (!enableArrow) {
774         return false;
775     }
776     double arrowWidth = BUBBLE_ARROW_WIDTH.ConvertToPx();
777     double twoRadiusPx = borderRadius_.ConvertToPx() * 2.0;
778     if (setHorizontal_.find(placement_) != setHorizontal_.end()) {
779         if (childSize.Height() >= twoRadiusPx + arrowWidth) {
780             return true;
781         }
782     }
783     if (setVertical_.find(placement_) != setVertical_.end()) {
784         if (childSize.Width() >= twoRadiusPx + arrowWidth) {
785             return true;
786         }
787     }
788     return false;
789 }
790 
UpdateDumpInfo()791 void BubbleLayoutAlgorithm::UpdateDumpInfo()
792 {
793     dumpInfo_.targetSpace = targetSpace_;
794     dumpInfo_.originPlacement = PlacementUtils::ConvertPlacementToString(placement_);
795     dumpInfo_.userOffset = positionOffset_;
796     dumpInfo_.enableArrow = enableArrow_;
797     dumpInfo_.top = top_;
798     dumpInfo_.bottom = bottom_;
799     dumpInfo_.targetNode = targetTag_;
800     dumpInfo_.targetID = targetNodeId_;
801 }
802 
InitProps(const RefPtr<BubbleLayoutProperty> & layoutProp,bool showInSubWindow,LayoutWrapper * layoutWrapper)803 void BubbleLayoutAlgorithm::InitProps(const RefPtr<BubbleLayoutProperty>& layoutProp, bool showInSubWindow,
804     LayoutWrapper* layoutWrapper)
805 {
806     CHECK_NULL_VOID(layoutWrapper);
807     auto popupTheme = GetPopupTheme(layoutWrapper);
808     CHECK_NULL_VOID(popupTheme);
809     padding_ = isTips_ ? popupTheme->GetTipsPadding() : popupTheme->GetPadding();
810     doubleBorderEnable_ = popupTheme->GetPopupDoubleBorderEnable();
811     CHECK_NULL_VOID(layoutProp);
812     userSetTargetSpace_ = layoutProp->GetTargetSpace().value_or(Dimension(0.0f));
813     borderRadius_ = layoutProp->GetRadius().value_or(popupTheme->GetRadius().GetX());
814     border_.SetBorderRadius(Radius(borderRadius_));
815     targetSpace_ = layoutProp->GetTargetSpace().value_or(popupTheme->GetTargetSpace());
816     placement_ = layoutProp->GetPlacement().value_or(Placement::BOTTOM);
817     isCaretMode_ = layoutProp->GetIsCaretMode().value_or(true);
818     auto height = layoutProp->GetArrowHeight().value_or(DEFAULT_BUBBLE_ARROW_HEIGHT);
819     auto width = layoutProp->GetArrowWidth().value_or(DEFAULT_BUBBLE_ARROW_WIDTH);
820     calculateArrowPoint(height, width, layoutWrapper);
821     followCursor_ = isTips_ && layoutProp->GetShowAtAnchorValue(TipsAnchorType::TARGET) == TipsAnchorType::CURSOR;
822     arrowHeight_ = height.ConvertToPx();
823     scaledBubbleSpacing_ = arrowHeight_;
824     SetArrowSize(realArrowWidth_, realArrowHeight_);
825     positionOffset_ = layoutProp->GetPositionOffset().value_or(OffsetF());
826     auto constraint = layoutProp->GetLayoutConstraint();
827     enableArrow_ = followCursor_ ? false : layoutProp->GetEnableArrow().value_or(true);
828     followTransformOfTarget_ = layoutProp->GetFollowTransformOfTarget().value_or(false);
829     auto wrapperIdealSize =
830         CreateIdealSize(constraint.value(), Axis::FREE, layoutProp->GetMeasureType(MeasureType::MATCH_PARENT), true);
831     wrapperSize_ = wrapperIdealSize;
832     targetSecurity_ = HORIZON_SPACING_WITH_SCREEN.ConvertToPx();
833     auto pipelineContext = PipelineContext::GetMainPipelineContext();
834     CHECK_NULL_VOID(pipelineContext);
835     auto safeAreaInsets = OverlayManager::GetSafeAreaInsets(layoutWrapper->GetHostNode());
836     top_ = safeAreaInsets.top_.Length();
837     bottom_ = safeAreaInsets.bottom_.Length();
838     UpdateDumpInfo();
839     marginStart_ = (isTips_ ? TIPS_MARGIN_SPACE : MARGIN_SPACE + DRAW_EDGES_SPACE).ConvertToPx();
840     marginEnd_ = (isTips_ ? TIPS_MARGIN_SPACE : MARGIN_SPACE + DRAW_EDGES_SPACE).ConvertToPx();
841     marginTop_ = top_ + (isTips_ ? TIPS_MARGIN_SPACE : DRAW_EDGES_SPACE).ConvertToPx();
842     marginBottom_ = bottom_ + (isTips_ ? TIPS_MARGIN_SPACE : DRAW_EDGES_SPACE).ConvertToPx();
843     HandleKeyboard(layoutWrapper, showInSubWindow);
844     showArrow_ = false;
845     minHeight_ = popupTheme->GetMinHeight();
846     maxColumns_ = popupTheme->GetMaxColumns();
847     expandDisplay_ = DialogManager::GetInstance().IsPcOrFreeMultiWindow(layoutWrapper->GetHostNode());
848     InitWrapperRect(layoutWrapper, layoutProp);
849     if (!useCustom_) {
850         UpdateScrollHeight(layoutWrapper, showInSubWindow);
851     }
852 }
853 
HandleKeyboard(LayoutWrapper * layoutWrapper,bool showInSubWindow)854 void BubbleLayoutAlgorithm::HandleKeyboard(LayoutWrapper* layoutWrapper, bool showInSubWindow)
855 {
856     auto bubbleNode = layoutWrapper->GetHostNode();
857     CHECK_NULL_VOID(bubbleNode);
858     auto bubblePattern = bubbleNode->GetPattern<BubblePattern>();
859     CHECK_NULL_VOID(bubblePattern);
860     avoidKeyboard_ = bubblePattern->GetAvoidKeyboard();
861     dumpInfo_.avoidKeyboard = avoidKeyboard_;
862     if (!avoidKeyboard_) {
863         return;
864     }
865     if (IsUIExtensionWindow()) {
866         HandleUIExtensionKeyboard(layoutWrapper, showInSubWindow);
867         return;
868     }
869     auto pipelineContext = PipelineContext::GetMainPipelineContext();
870     CHECK_NULL_VOID(pipelineContext);
871     auto safeAreaManager = pipelineContext->GetSafeAreaManager();
872     CHECK_NULL_VOID(safeAreaManager);
873     auto keyboardHeight = safeAreaManager->GetKeyboardInset().Length();
874     auto container = Container::Current();
875     CHECK_NULL_VOID(container);
876     if (GreatNotEqual(keyboardHeight, 0)) {
877         auto tipsMarginKeyBoard = followCursor_ ? KEYBOARD_SPACE.ConvertToPx() : .0f;
878         auto wrapperHeight =  container->IsSceneBoardEnabled() ? wrapperSize_.Height() - keyboardHeight :
879             wrapperSize_.Height() - keyboardHeight - marginBottom_ + tipsMarginKeyBoard;
880         wrapperSize_.SetHeight(wrapperHeight);
881         marginBottom_ = KEYBOARD_SPACE.ConvertToPx();
882     } else if (showInSubWindow) {
883         auto currentContext = bubbleNode->GetContextRefPtr();
884         CHECK_NULL_VOID(currentContext);
885         auto currentSafeAreaManager = currentContext->GetSafeAreaManager();
886         CHECK_NULL_VOID(currentSafeAreaManager);
887         auto currentKeyboardHeight = currentSafeAreaManager->GetKeyboardInset().Length();
888         if (GreatNotEqual(currentKeyboardHeight, 0)) {
889             auto wrapperHeight =  container->IsSceneBoardEnabled() ? wrapperSize_.Height() - currentKeyboardHeight :
890                 wrapperSize_.Height() - currentKeyboardHeight - marginBottom_;
891             wrapperSize_.SetHeight(wrapperHeight);
892             marginBottom_ = KEYBOARD_SPACE.ConvertToPx();
893         }
894     }
895 }
896 
HandleUIExtensionKeyboard(LayoutWrapper * layoutWrapper,bool showInSubWindow)897 void BubbleLayoutAlgorithm::HandleUIExtensionKeyboard(LayoutWrapper* layoutWrapper, bool showInSubWindow)
898 {
899     auto pipelineContext = PipelineContext::GetMainPipelineContext();
900     CHECK_NULL_VOID(pipelineContext);
901     auto safeAreaManager = pipelineContext->GetSafeAreaManager();
902     CHECK_NULL_VOID(safeAreaManager);
903     auto keyboardInset = safeAreaManager->GetKeyboardInset();
904     auto keyboardHeight = keyboardInset.Length();
905     auto wrapperRect = pipelineContext->GetDisplayWindowRectInfo();
906     if (showInSubWindow) {
907         if (GreatNotEqual(keyboardHeight, 0)) {
908             keyboardHeight = wrapperSize_.Height() - wrapperRect.Top() - wrapperRect.Height() + keyboardHeight;
909             wrapperSize_.SetHeight(wrapperSize_.Height() - keyboardHeight);
910             marginBottom_ = KEYBOARD_SPACE.ConvertToPx();
911         } else {
912             auto bubbleNode = layoutWrapper->GetHostNode();
913             CHECK_NULL_VOID(bubbleNode);
914             auto currentContext = bubbleNode->GetContextRefPtr();
915             CHECK_NULL_VOID(currentContext);
916             auto currentSafeAreaManager = currentContext->GetSafeAreaManager();
917             CHECK_NULL_VOID(currentSafeAreaManager);
918             auto currentKeyboardHeight = currentSafeAreaManager->GetKeyboardInset().Length();
919             if (GreatNotEqual(currentKeyboardHeight, 0)) {
920                 marginBottom_ = KEYBOARD_SPACE.ConvertToPx();
921                 wrapperSize_.SetHeight(wrapperSize_.Height() - currentKeyboardHeight);
922             }
923         }
924     } else {
925         auto topInset = safeAreaManager->GetSafeAreaWithoutProcess().top_;
926         auto bottomInset = safeAreaManager->GetSafeAreaWithoutProcess().bottom_;
927         marginTop_ = DRAW_EDGES_SPACE.ConvertToPx();
928         if (topInset.Length() > 0 && LessNotEqual(wrapperRect.Top(), topInset.end)) {
929             marginTop_ = topInset.end - wrapperRect.Top() + marginTop_;
930         }
931         marginBottom_ = DRAW_EDGES_SPACE.ConvertToPx();
932         if (keyboardHeight > 0) {
933             marginBottom_ = KEYBOARD_SPACE.ConvertToPx();
934             wrapperSize_.SetHeight(wrapperSize_.Height() - keyboardHeight);
935         } else if (bottomInset.Length() > 0 && GreatNotEqual(wrapperRect.Top() + wrapperRect.Height(),
936             bottomInset.start)) {
937             marginBottom_ = wrapperRect.Top() + wrapperRect.Height() - bottomInset.start + marginBottom_;
938         }
939     }
940 }
941 
InitWrapperRect(LayoutWrapper * layoutWrapper,const RefPtr<BubbleLayoutProperty> & layoutProp)942 void BubbleLayoutAlgorithm::InitWrapperRect(
943     LayoutWrapper* layoutWrapper, const RefPtr<BubbleLayoutProperty>& layoutProp)
944 {
945     auto bubbleNode = layoutWrapper->GetHostNode();
946     CHECK_NULL_VOID(bubbleNode);
947     CHECK_NULL_VOID(layoutProp);
948     auto enableHoverMode = layoutProp->GetEnableHoverMode();
949     auto context = bubbleNode->GetContext();
950     CHECK_NULL_VOID(context);
951 
952     isHalfFoldHover_ = OverlayManager::IsNeedAvoidFoldCrease(bubbleNode, true, expandDisplay_, enableHoverMode);
953     auto container = Container::Current();
954     CHECK_NULL_VOID(container);
955     auto displayInfo = container->GetDisplayInfo();
956     if (displayInfo) {
957         auto foldCreaseRects = displayInfo->GetCurrentFoldCreaseRegion();
958         if (!foldCreaseRects.empty()) {
959             auto foldCrease = foldCreaseRects.front();
960             foldCreaseTop_ = foldCrease.Top();
961             foldCreaseBottom_ = foldCrease.Bottom();
962         }
963     } else {
964         TAG_LOGW(AceLogTag::ACE_OVERLAY, "DisplayInfo is null");
965     }
966     auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
967     CHECK_NULL_VOID(targetNode);
968     auto targetOffset = targetNode->GetPaintRectOffset();
969     float getY = 0;
970     getY = targetOffset.GetY();
971     auto bubblePattern = bubbleNode->GetPattern<BubblePattern>();
972     CHECK_NULL_VOID(bubblePattern);
973     dumpInfo_.enableHoverMode = enableHoverMode.value_or(false);
974     if (isHalfFoldHover_) {
975         auto creaseHeightOffset = context->GetDisplayAvailableRect().Top();
976         auto foldCreaseTop = foldCreaseTop_ - creaseHeightOffset;
977         auto foldCreaseBottom = foldCreaseBottom_ - creaseHeightOffset;
978         if (LessNotEqual(getY, foldCreaseTop)) {
979             wrapperRect_.SetRect(marginStart_, marginTop_,
980                 wrapperSize_.Width() - marginEnd_ - marginStart_, foldCreaseTop - marginTop_);
981         } else if (GreatNotEqual(getY, foldCreaseBottom)) {
982             wrapperRect_.SetRect(marginStart_, foldCreaseBottom,
983                 wrapperSize_.Width() - marginEnd_ - marginStart_,
984                     wrapperSize_.Height() - foldCreaseBottom - marginBottom_);
985         } else {
986             isHalfFoldHover_ = false;
987         }
988     }
989 }
990 
UpdateScrollHeight(LayoutWrapper * layoutWrapper,bool showInSubWindow)991 void BubbleLayoutAlgorithm::UpdateScrollHeight(LayoutWrapper* layoutWrapper, bool showInSubWindow)
992 {
993     auto bubbleNode = layoutWrapper->GetHostNode();
994     CHECK_NULL_VOID(bubbleNode);
995     auto layoutProp = bubbleNode->GetLayoutProperty<BubbleLayoutProperty>();
996     CHECK_NULL_VOID(layoutProp);
997     auto enableHoverMode = layoutProp->GetEnableHoverMode();
998     if (!enableHoverMode.value_or(false)) {
999         return;
1000     }
1001 
1002     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
1003     if (children.empty()) {
1004         return;
1005     }
1006     auto childWrapper = children.front();
1007     CHECK_NULL_VOID(childWrapper);
1008     auto childMaxSize = GetPopupMaxWidthAndHeight(showInSubWindow, childWrapper->GetHostNode());
1009 
1010     auto columnNode = AceType::DynamicCast<FrameNode>(bubbleNode->GetLastChild());
1011     CHECK_NULL_VOID(columnNode);
1012     auto lastColumnNode = AceType::DynamicCast<FrameNode>(columnNode->GetLastChild());
1013     CHECK_NULL_VOID(lastColumnNode);
1014     auto buttonRowNode = AceType::DynamicCast<FrameNode>(lastColumnNode->GetLastChild());
1015     CHECK_NULL_VOID(buttonRowNode);
1016 
1017     if (buttonRowNode->GetChildren().empty()) {
1018         return;
1019     }
1020     const auto& lastChildren = lastColumnNode->GetChildren();
1021     buttonRowSize_ = buttonRowNode->GetGeometryNode()->GetFrameSize();
1022 
1023     for (const auto& uinode : lastChildren) {
1024         if (uinode->GetTag() == V2::SCROLL_ETS_TAG) {
1025             auto scrollNode = AceType::DynamicCast<FrameNode>(uinode);
1026             CHECK_NULL_VOID(scrollNode);
1027 
1028             auto scrollProps = scrollNode->GetLayoutProperty<ScrollLayoutProperty>();
1029             CHECK_NULL_VOID(scrollProps);
1030             if (isHalfFoldHover_) {
1031                 scrollProps->UpdateCalcMaxSize(CalcSize(
1032                     std::nullopt,
1033                     CalcLength(Dimension(wrapperRect_.Height() - buttonRowSize_.Height()))));
1034             } else {
1035                 scrollProps->UpdateCalcMaxSize(CalcSize(
1036                     std::nullopt,
1037                     CalcLength(Dimension(childMaxSize.Height() - buttonRowSize_.Height()))));
1038             }
1039             scrollNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1040         }
1041     }
1042 }
1043 
GetChildPositionNew(const SizeF & childSize,const RefPtr<BubbleLayoutProperty> & bubbleProp,const RefPtr<LayoutWrapper> child)1044 OffsetF BubbleLayoutAlgorithm::GetChildPositionNew(
1045     const SizeF& childSize, const RefPtr<BubbleLayoutProperty>& bubbleProp, const RefPtr<LayoutWrapper> child)
1046 {
1047     static std::map<Placement, std::vector<Placement>> PLACEMENT_STATES = {
1048         { Placement::BOTTOM_LEFT,
1049             {
1050                 Placement::BOTTOM_LEFT,
1051                 Placement::TOP_LEFT,
1052                 Placement::RIGHT_TOP,
1053                 Placement::LEFT_TOP,
1054                 Placement::NONE,
1055             } },
1056         { Placement::BOTTOM,
1057             {
1058                 Placement::BOTTOM,
1059                 Placement::TOP,
1060                 Placement::RIGHT,
1061                 Placement::LEFT,
1062                 Placement::NONE,
1063             } },
1064         { Placement::BOTTOM_RIGHT,
1065             {
1066                 Placement::BOTTOM_RIGHT,
1067                 Placement::TOP_RIGHT,
1068                 Placement::RIGHT_BOTTOM,
1069                 Placement::LEFT_BOTTOM,
1070                 Placement::NONE,
1071             } },
1072         { Placement::TOP_LEFT,
1073             {
1074                 Placement::TOP_LEFT,
1075                 Placement::BOTTOM_LEFT,
1076                 Placement::RIGHT_TOP,
1077                 Placement::LEFT_TOP,
1078                 Placement::NONE,
1079             } },
1080         { Placement::TOP,
1081             {
1082                 Placement::TOP,
1083                 Placement::BOTTOM,
1084                 Placement::RIGHT,
1085                 Placement::LEFT,
1086                 Placement::NONE,
1087             } },
1088         { Placement::TOP_RIGHT,
1089             {
1090                 Placement::TOP_RIGHT,
1091                 Placement::BOTTOM_RIGHT,
1092                 Placement::RIGHT_BOTTOM,
1093                 Placement::LEFT_BOTTOM,
1094                 Placement::NONE,
1095             } },
1096         { Placement::LEFT_TOP,
1097             {
1098                 Placement::LEFT_TOP,
1099                 Placement::RIGHT_TOP,
1100                 Placement::BOTTOM_LEFT,
1101                 Placement::TOP_LEFT,
1102                 Placement::NONE,
1103             } },
1104         { Placement::LEFT,
1105             {
1106                 Placement::LEFT,
1107                 Placement::RIGHT,
1108                 Placement::BOTTOM,
1109                 Placement::TOP,
1110                 Placement::NONE,
1111             } },
1112         { Placement::LEFT_BOTTOM,
1113             {
1114                 Placement::LEFT_BOTTOM,
1115                 Placement::RIGHT_BOTTOM,
1116                 Placement::BOTTOM_RIGHT,
1117                 Placement::TOP_RIGHT,
1118                 Placement::NONE,
1119             } },
1120         { Placement::RIGHT_TOP,
1121             {
1122                 Placement::RIGHT_TOP,
1123                 Placement::LEFT_TOP,
1124                 Placement::BOTTOM_LEFT,
1125                 Placement::TOP_LEFT,
1126                 Placement::NONE,
1127             } },
1128         { Placement::RIGHT,
1129             {
1130                 Placement::RIGHT,
1131                 Placement::LEFT,
1132                 Placement::BOTTOM,
1133                 Placement::TOP,
1134                 Placement::NONE,
1135             } },
1136         { Placement::RIGHT_BOTTOM,
1137             {
1138                 Placement::RIGHT_BOTTOM,
1139                 Placement::LEFT_BOTTOM,
1140                 Placement::BOTTOM_RIGHT,
1141                 Placement::TOP_RIGHT,
1142                 Placement::NONE,
1143             } },
1144     };
1145     OffsetF bottomPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
1146         targetOffset_.GetY() + targetSize_.Height() + targetSpace_.ConvertToPx() + arrowHeight_);
1147     OffsetF topPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
1148         targetOffset_.GetY() - childSize.Height() - targetSpace_.ConvertToPx() - arrowHeight_);
1149     OffsetF defaultPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
1150         targetOffset_.GetY() + (targetSize_.Height() - childSize.Height()) / 2.0);
1151     OffsetF childPosition;
1152     OffsetF ArrowOffset;
1153     OffsetF position = defaultPosition;
1154     auto positionOffset = positionOffset_;
1155     auto originPlacement = placement_;
1156     bool didNeedArrow = false;
1157     std::vector<Placement> currentPlacementStates = PLACEMENT_STATES.find(Placement::BOTTOM)->second;
1158     if (PLACEMENT_STATES.find(placement_) != PLACEMENT_STATES.end()) {
1159         currentPlacementStates = PLACEMENT_STATES.find(placement_)->second;
1160     }
1161     if (followCursor_) {
1162         currentPlacementStates = { tipsPlacement_ };
1163         currentPlacementStates.insert(
1164             currentPlacementStates.end(), FOLLOW_CURSOR_TIPS.begin(), FOLLOW_CURSOR_TIPS.end());
1165     }
1166     size_t step = ALIGNMENT_STEP_OFFSET;
1167     bVertical_ = false;
1168     bHorizontal_ = false;
1169     for (size_t i = 0, len = currentPlacementStates.size(); i < len;) {
1170         placement_ = currentPlacementStates[i];
1171         if (placement_ == Placement::NONE) {
1172             break;
1173         }
1174         if (bCaretMode_) { // Caret mode
1175             if ((placement_ != Placement::BOTTOM) && (placement_ != Placement::TOP)) {
1176                 i++;
1177                 continue;
1178             }
1179         }
1180         if (bVertical_) {
1181             if (setHorizontal_.find(placement_) != setHorizontal_.end()) {
1182                 i++;
1183                 continue;
1184             }
1185         }
1186         if (bHorizontal_) {
1187             if (setVertical_.find(placement_) != setVertical_.end()) {
1188                 i++;
1189                 continue;
1190             }
1191         }
1192         if (i >= step) {
1193             positionOffset_ = OffsetF(0.0f, 0.0f);
1194         }
1195         childPosition = GetPositionWithPlacementNew(childSize, topPosition, bottomPosition, ArrowOffset);
1196         UpdateChildPosition(childPosition);
1197         didNeedArrow = GetIfNeedArrow(bubbleProp, childSize_);
1198         checkArrowPosition_ = ArrowOffset;
1199         position = FitToScreenNew(childPosition, step, i, childSize, didNeedArrow);
1200         if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
1201             continue;
1202         }
1203         break;
1204     }
1205     if (placement_ == Placement::NONE) {
1206         bVertical_ = false;
1207         bHorizontal_ = false;
1208         position = GetAdjustPosition(currentPlacementStates, step, childSize, topPosition, bottomPosition, ArrowOffset);
1209         if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
1210             position = AvoidOrCoverParent(childSize, bubbleProp, child, originPlacement, ArrowOffset);
1211         }
1212     }
1213     positionOffset_ = positionOffset;
1214     arrowPlacement_ = placement_;
1215     arrowPosition_ = ArrowOffset;
1216     return position;
1217 }
1218 
UpdateTextNodeMaxLines(const RefPtr<LayoutWrapper> & childWrapper,const LayoutConstraintF & layoutConstraint)1219 void BubbleLayoutAlgorithm::UpdateTextNodeMaxLines(
1220     const RefPtr<LayoutWrapper>& childWrapper, const LayoutConstraintF& layoutConstraint)
1221 {
1222     auto children = childWrapper->GetAllChildrenWithBuild();
1223     CHECK_EQUAL_VOID(children.empty(), true);
1224     auto text = children.front();
1225     CHECK_NULL_VOID(text);
1226     if (text->GetHostTag() != V2::TEXT_ETS_TAG) {
1227         return;
1228     }
1229     auto layoutProps = DynamicCast<TextLayoutProperty>(text->GetLayoutProperty());
1230     CHECK_NULL_VOID(layoutProps);
1231     auto constraint = layoutConstraint;
1232     auto maxSize = layoutConstraint.maxSize;
1233     float tipsMarginSpace = static_cast<float>(TIPS_MARGIN_SPACE.ConvertToPx());
1234     constraint.maxSize.SetWidth(maxSize.Width() - tipsMarginSpace * DOUBLE);
1235     constraint.maxSize.SetHeight(maxSize.Height() - tipsMarginSpace * DOUBLE);
1236     text->Measure(constraint);
1237     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(text->GetLayoutAlgorithm());
1238     CHECK_NULL_VOID(layoutAlgorithmWrapper);
1239     auto textLayoutAlgorithm = DynamicCast<TextLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1240     CHECK_NULL_VOID(textLayoutAlgorithm);
1241     auto paragraph = textLayoutAlgorithm->GetSingleParagraph();
1242     CHECK_NULL_VOID(paragraph);
1243     auto paragHeight = paragraph->GetHeight();
1244     auto paragLineCount = paragraph->GetLineCount();
1245     int32_t textLineHeight = 0;
1246     if (paragLineCount > 0) {
1247         textLineHeight = static_cast<int32_t>(paragHeight / paragLineCount);
1248     }
1249     if (textLineHeight > 0) {
1250         layoutProps->UpdateMaxLines(
1251             static_cast<int32_t>(std::floor(layoutConstraint.maxSize.Height() / textLineHeight)) - 1);
1252     }
1253 }
1254 
MeasureTipsRegion(const RefPtr<LayoutWrapper> & childWrapper,const LayoutConstraintF & childContraint)1255 void BubbleLayoutAlgorithm::MeasureTipsRegion(
1256     const RefPtr<LayoutWrapper>& childWrapper, const LayoutConstraintF& childContraint)
1257 {
1258     CHECK_EQUAL_VOID(followCursor_, false);
1259     CHECK_NULL_VOID(childWrapper);
1260     const SizeF& childSize = measureChildSizeAfter_;
1261     float defaultXOffset = GreatNotEqual(targetOffset_.GetX(), marginStart_)
1262                                ? (wrapperSize_.Width() - marginEnd_ - childSize.Width())
1263                                : marginStart_;
1264     OffsetF bottomPosition =
1265         OffsetF(defaultXOffset, targetOffset_.GetY() + targetSize_.Height() + targetSpace_.ConvertToPx());
1266     OffsetF topPosition =
1267         OffsetF(defaultXOffset, targetOffset_.GetY() - childSize.Height() - targetSpace_.ConvertToPx());
1268     OffsetF arrowOffset;
1269     OffsetF childPosition;
1270     OffsetF position;
1271     Placement origin = placement_;
1272     for (size_t i = 0; i < FOLLOW_CURSOR_TIPS.size();) {
1273         placement_ = FOLLOW_CURSOR_TIPS[i];
1274         if (placement_ == Placement::NONE) {
1275             break;
1276         }
1277         childPosition = GetPositionWithPlacementNew(childSize, topPosition, bottomPosition, arrowOffset);
1278         UpdateChildPosition(childPosition);
1279         position = FitToScreenNew(childPosition, ALIGNMENT_STEP_OFFSET, i, childSize, false);
1280         if (!NearEqual(position, OffsetF(0.0f, 0.0f))) {
1281             break;
1282         }
1283     }
1284     if (placement_ == Placement::NONE) {
1285         SizeF newSize;
1286         placement_ = CalculateTipsDirections(newSize);
1287         LayoutConstraintF columnContraint = childContraint;
1288         columnContraint.maxSize = newSize;
1289         auto childProp = childWrapper->GetLayoutProperty();
1290         if (childProp) {
1291             childProp->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE);
1292         }
1293         UpdateTextNodeMaxLines(childWrapper, columnContraint);
1294         childWrapper->Measure(columnContraint);
1295         measureChildSizeAfter_ = childWrapper->GetGeometryNode()->GetFrameSize();
1296         resetTipsSize_ = true;
1297     }
1298     tipsPlacement_ = placement_;
1299     placement_ = origin;
1300 }
1301 
MeasureTipsFollowTarget(const RefPtr<LayoutWrapper> & childWrapper,const LayoutConstraintF & childConstraint)1302 void BubbleLayoutAlgorithm::MeasureTipsFollowTarget(
1303     const RefPtr<LayoutWrapper>& childWrapper, const LayoutConstraintF& childConstraint)
1304 {
1305     CHECK_NULL_VOID(childWrapper);
1306     const double maxTipsWidth = MAX_TIP_WIDTH.ConvertToPx();
1307     float height = isHalfFoldHover_ ? wrapperRect_.Height() : (wrapperSize_.Height() - marginTop_ - marginBottom_);
1308     float width = std::min(wrapperSize_.Width() - marginEnd_ - marginStart_, static_cast<float>(maxTipsWidth));
1309     SizeF newSize(width, height);
1310     LayoutConstraintF columnConstraint = childConstraint;
1311     columnConstraint.maxSize = newSize;
1312     auto childProp = childWrapper->GetLayoutProperty();
1313     if (childProp) {
1314         childProp->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE);
1315     }
1316     UpdateTextNodeMaxLines(childWrapper, columnConstraint);
1317     childWrapper->Measure(columnConstraint);
1318     CHECK_NULL_VOID(childWrapper->GetGeometryNode());
1319     measureChildSizeAfter_ = childWrapper->GetGeometryNode()->GetFrameSize();
1320 }
1321 
CalculateTipsDirections(SizeF & newSize)1322 Placement BubbleLayoutAlgorithm::CalculateTipsDirections(SizeF& newSize)
1323 {
1324     Placement placement = Placement::NONE;
1325     const double tipsMouseSpace = TIPS_MOUSE_SPACE.ConvertToPx();
1326     const double maxTipsWidth = MAX_TIP_WIDTH.ConvertToPx();
1327     float hHeight = isHalfFoldHover_ ? wrapperRect_.Height() : (wrapperSize_.Height() - marginTop_ - marginBottom_);
1328     float vWidth = std::min(wrapperSize_.Width() - marginEnd_ - marginStart_, static_cast<float>(maxTipsWidth));
1329     float leftWidth = std::min(targetOffset_.GetX() - tipsMouseSpace - marginStart_, maxTipsWidth);
1330     float leftArea = leftWidth * hHeight;
1331 
1332     float rightWidth = std::min(
1333         wrapperSize_.Width() - marginEnd_ - targetOffset_.GetX() - targetSize_.Width() - tipsMouseSpace, maxTipsWidth);
1334     float rightArea = rightWidth * hHeight;
1335 
1336     float topHeight = targetOffset_.GetY() - tipsMouseSpace - (isHalfFoldHover_ ? wrapperRect_.Top() : marginTop_);
1337     float topArea = vWidth * topHeight;
1338 
1339     float bottomHeight =
1340         wrapperSize_.Height() - marginBottom_ - targetOffset_.GetY() - targetSize_.Height() - tipsMouseSpace;
1341     if (isHalfFoldHover_) {
1342         bottomHeight = wrapperRect_.Bottom() - targetOffset_.GetY() - targetSize_.Height() - tipsMouseSpace;
1343     }
1344     float bottomArea = vWidth * bottomHeight;
1345     double maxArea = std::max({ bottomArea, topArea, rightArea, leftArea });
1346     if (maxArea == bottomArea) {
1347         placement = Placement::BOTTOM_LEFT;
1348         newSize.SetWidth(static_cast<float>(vWidth));
1349         newSize.SetHeight(static_cast<float>(bottomHeight));
1350     } else if (maxArea == topArea) {
1351         placement = Placement::TOP_LEFT;
1352         newSize.SetWidth(static_cast<float>(vWidth));
1353         newSize.SetHeight(static_cast<float>(topHeight));
1354     } else if (maxArea == rightArea) {
1355         placement = Placement::RIGHT_TOP;
1356         newSize.SetWidth(static_cast<float>(rightWidth));
1357         newSize.SetHeight(static_cast<float>(hHeight));
1358     } else if (maxArea == leftArea) {
1359         placement = Placement::LEFT_TOP;
1360         newSize.SetWidth(static_cast<float>(leftWidth));
1361         newSize.SetHeight(static_cast<float>(hHeight));
1362     }
1363     return placement;
1364 }
1365 
AdjustPositionNew(const OffsetF & position,float width,float height)1366 OffsetF BubbleLayoutAlgorithm::AdjustPositionNew(const OffsetF& position, float width, float height)
1367 {
1368     OffsetF result = position;
1369     OffsetF positionEnd = position + OffsetF(width, height);
1370     if (GreatNotEqual(positionEnd.GetX(), wrapperSize_.Width() - marginEnd_)) {
1371         result.SetX(wrapperSize_.Width() - marginEnd_ - width);
1372     }
1373     if (GreatNotEqual(positionEnd.GetY(), wrapperSize_.Height() - marginBottom_)) {
1374         result.SetY(wrapperSize_.Height()- marginBottom_ - height);
1375     }
1376     if (LessNotEqual(position.GetX(), marginStart_)) {
1377         result.SetX(marginStart_);
1378     }
1379     if (LessNotEqual(position.GetY(), marginTop_)) {
1380         result.SetY(marginTop_);
1381     }
1382     return result;
1383 }
1384 
AdjustAvoidPosition(const OffsetF & position,float width,float height,OffsetF & arrowPosition)1385 OffsetF BubbleLayoutAlgorithm::AdjustAvoidPosition(
1386     const OffsetF& position, float width, float height, OffsetF& arrowPosition) // adjust position to avoid boundary
1387 {
1388     OffsetF result = position;
1389     OffsetF positionEnd = position + OffsetF(width, height);
1390     if (GreatNotEqual(positionEnd.GetX(), wrapperSize_.Width() - marginEnd_)) {
1391         result.SetX(wrapperSize_.Width() - marginEnd_ - width);
1392         arrowPosition = OffsetF(
1393             arrowPosition.GetX() + wrapperSize_.Width() - marginEnd_ - width - position.GetX(), arrowPosition.GetY());
1394     }
1395     if (GreatNotEqual(positionEnd.GetY(), wrapperSize_.Height() - marginBottom_)) {
1396         result.SetY(wrapperSize_.Height() - marginBottom_ - height);
1397         arrowPosition = OffsetF(arrowPosition.GetX(),
1398             arrowPosition.GetY() + wrapperSize_.Height() - marginBottom_ - height - position.GetY());
1399     }
1400     if (LessNotEqual(position.GetX(), marginStart_)) {
1401         result.SetX(marginStart_);
1402         arrowPosition = OffsetF(arrowPosition.GetX() + marginStart_ - position.GetX(), arrowPosition.GetY());
1403     }
1404     if (LessNotEqual(position.GetY(), marginTop_)) {
1405         result.SetY(marginTop_);
1406         arrowPosition = OffsetF(arrowPosition.GetX(), arrowPosition.GetY() + marginTop_ - position.GetY());
1407     }
1408     return result;
1409 }
1410 
GetAdjustPosition(std::vector<Placement> & currentPlacementStates,size_t step,const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)1411 OffsetF BubbleLayoutAlgorithm::GetAdjustPosition(std::vector<Placement>& currentPlacementStates, size_t step,
1412     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
1413 {
1414     OffsetF childPosition;
1415     OffsetF position;
1416     float width = 0.0f;
1417     float height = 0.0f;
1418     size_t len = currentPlacementStates.size();
1419     for (size_t i = 0; i < len;) {
1420         placement_ = currentPlacementStates[i];
1421         if (placement_ == Placement::NONE) {
1422             break;
1423         }
1424         if (bCaretMode_) { // Caret mode
1425             if ((placement_ != Placement::BOTTOM) && (placement_ != Placement::TOP)) {
1426                 i++;
1427                 continue;
1428             }
1429         }
1430         if (bVertical_) {
1431             if (setHorizontal_.find(placement_) != setHorizontal_.end()) {
1432                 i++;
1433                 continue;
1434             }
1435         }
1436         if (bHorizontal_) {
1437             if (setVertical_.find(placement_) != setVertical_.end()) {
1438                 i++;
1439                 continue;
1440             }
1441         }
1442         childPosition = GetPositionWithPlacementNew(childSize, topPosition, bottomPosition, arrowPosition);
1443         UpdateChildPosition(childPosition);
1444         width = childSize.Width();
1445         height = childSize.Height();
1446         if (showArrow_) {
1447             if (setHorizontal_.find(placement_) != setHorizontal_.end()) {
1448                 width += BUBBLE_ARROW_HEIGHT.ConvertToPx();
1449             }
1450             if (setVertical_.find(placement_) != setVertical_.end()) {
1451                 height += BUBBLE_ARROW_HEIGHT.ConvertToPx();
1452             }
1453         }
1454         position = AdjustPosition(childPosition, width, height, targetSpace_.ConvertToPx());
1455         if ((!NearEqual(position, OffsetF(0.0f, 0.0f))) || (len - i <= step)) {
1456             break;
1457         }
1458         i += step;
1459     }
1460     return position;
1461 }
1462 
AdjustPosition(const OffsetF & position,float width,float height,float space)1463 OffsetF BubbleLayoutAlgorithm::AdjustPosition(const OffsetF& position, float width, float height, float space)
1464 {
1465     float xMax = 0.0f;
1466     float yMax = 0.0f;
1467     float xMin = 1.0f;
1468     float yMin = 1.0f;
1469     float yTargetOffset = 0.0f;
1470     switch (placement_) {
1471         case Placement::LEFT_TOP:
1472         case Placement::LEFT_BOTTOM:
1473         case Placement::LEFT: {
1474             xMin = marginStart_;
1475             xMax = std::min(targetOffset_.GetX() - width - space, wrapperSize_.Width() - marginEnd_ - width);
1476             yMin = marginTop_;
1477             yMax = wrapperSize_.Height() - height - marginBottom_;
1478             if (isHalfFoldHover_) {
1479                 yMin = wrapperRect_.Top();
1480                 yMax = wrapperRect_.Bottom() - height;
1481             }
1482             break;
1483         }
1484         case Placement::RIGHT_TOP:
1485         case Placement::RIGHT_BOTTOM:
1486         case Placement::RIGHT: {
1487             xMin = std::max(targetOffset_.GetX() + targetSize_.Width() + space, marginStart_);
1488             xMax = wrapperSize_.Width() - width - marginEnd_;
1489             yMin = marginTop_;
1490             yMax = wrapperSize_.Height() - height - marginBottom_;
1491             if (isHalfFoldHover_) {
1492                 yMin = wrapperRect_.Top();
1493                 yMax = wrapperRect_.Bottom() - height;
1494             }
1495             break;
1496         }
1497         case Placement::TOP_LEFT:
1498         case Placement::TOP_RIGHT:
1499         case Placement::TOP: {
1500             xMin = marginStart_;
1501             xMax = wrapperSize_.Width() - width - marginEnd_;
1502             yMin = marginTop_;
1503             yMax = std::min(targetOffset_.GetY() - height - space, wrapperSize_.Height() - marginBottom_ - height);
1504             if (isHalfFoldHover_) {
1505                 yMin = wrapperRect_.Top();
1506                 yMax = std::min(targetOffset_.GetY() - height - space,
1507                     static_cast<float>(wrapperRect_.Bottom()) - height);
1508             }
1509             yTargetOffset = targetSecurity_;
1510             break;
1511         }
1512         case Placement::BOTTOM_LEFT:
1513         case Placement::BOTTOM_RIGHT:
1514         case Placement::BOTTOM: {
1515             xMin = marginStart_;
1516             xMax = wrapperSize_.Width() - width - marginEnd_;
1517             yMin = std::max(targetOffset_.GetY() + targetSize_.Height() + space, marginTop_);
1518             yMax = wrapperSize_.Height() - height - marginBottom_;
1519             if (isHalfFoldHover_) {
1520                 yMin = std::max(targetOffset_.GetY() + targetSize_.Height() + space,
1521                     static_cast<float>(wrapperRect_.Top()));
1522                 yMax = wrapperRect_.Bottom() - height;
1523             }
1524             yTargetOffset = -targetSecurity_;
1525             break;
1526         }
1527         case Placement::NONE: {
1528             xMin = marginStart_;
1529             xMax = wrapperSize_.Width() - width - marginEnd_;
1530             yMin = marginTop_;
1531             yMax = wrapperSize_.Height() - height - marginBottom_;
1532             if (isHalfFoldHover_) {
1533                 yMin = wrapperRect_.Top();
1534                 yMax = wrapperRect_.Bottom() - height;
1535             }
1536             break;
1537         }
1538         default:
1539             break;
1540     }
1541     if ((LessNotEqual(xMax, xMin) && !isGreatWrapperWidth_) || LessNotEqual(yMax, yMin)) {
1542         needRemoveArrow_ = CheckIfNeedRemoveArrow(xMin, xMax, yMin, yMax);
1543         if (!needRemoveArrow_) {
1544             return OffsetF(0.0f, 0.0f);
1545         }
1546         TAG_LOGD(AceLogTag::ACE_OVERLAY, "Popup need remove arrow");
1547     } else if (LessNotEqual(xMax, xMin) && isGreatWrapperWidth_) {
1548         auto y = std::clamp(position.GetY(), yMin, yMax);
1549         return OffsetF(0.0f, y + yTargetOffset);
1550     }
1551     auto result = GetBubblePosition(position, xMin, xMax, yMin, yMax);
1552     CheckArrowPosition(result, width, height);
1553     return result;
1554 }
1555 
BottomAndTopPosition(OffsetF & bottomPosition,OffsetF & topPosition,const SizeF & childSize)1556 void BubbleLayoutAlgorithm::BottomAndTopPosition(OffsetF& bottomPosition, OffsetF& topPosition, const SizeF& childSize)
1557 {
1558     bottomPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / HALF,
1559         targetOffset_.GetY() + targetSize_.Height() + targetSpace_.ConvertToPx() + arrowHeight_);
1560     topPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / HALF,
1561         targetOffset_.GetY() - childSize.Height() - targetSpace_.ConvertToPx() - arrowHeight_);
1562 }
1563 
CoverParent(const SizeF & childSize,Placement originPlacement)1564 OffsetF BubbleLayoutAlgorithm::CoverParent(const SizeF& childSize, Placement originPlacement)
1565 {
1566     // default popup position
1567     OffsetF bottomPosition;
1568     OffsetF topPosition;
1569     BottomAndTopPosition(bottomPosition, topPosition, childSize);
1570     OffsetF defaultPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / HALF,
1571         targetOffset_.GetY() + (targetSize_.Height() - childSize.Height()) / HALF);
1572     showArrow_ = false;
1573     OffsetF position;
1574     if (avoidKeyboard_ && !isHalfFoldHover_) {
1575         placement_ = originPlacement;
1576         OffsetF arrowOffset;
1577         auto originPosition = GetPositionWithPlacementNew(childSize, topPosition, bottomPosition, arrowOffset);
1578         position = AdjustPositionNew(originPosition, childSize.Width(), childSize.Height());
1579     } else {
1580         position = AdjustPosition(defaultPosition, childSize.Width(), childSize.Height(), targetSecurity_);
1581     }
1582     if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
1583         auto x =
1584             std::clamp(defaultPosition.GetX(), marginStart_, wrapperSize_.Width() - childSize.Width() - marginEnd_);
1585         auto y = marginTop_;
1586         position = OffsetF(x, y);
1587     }
1588     return position;
1589 }
1590 
AvoidOrCoverParent(const SizeF & childSize,const RefPtr<BubbleLayoutProperty> & bubbleProp,const RefPtr<LayoutWrapper> child,Placement originPlacement,OffsetF & arrowOffset)1591 OffsetF BubbleLayoutAlgorithm::AvoidOrCoverParent(const SizeF& childSize,
1592     const RefPtr<BubbleLayoutProperty>& bubbleProp, const RefPtr<LayoutWrapper> child, Placement originPlacement,
1593     OffsetF& arrowOffset)
1594 {
1595     // popup avoid parent or cover parent
1596     OffsetF position;
1597     bool canPlaceAround = canPlacement_.bottom || canPlacement_.top || canPlacement_.left || canPlacement_.right;
1598     bool canPlaceTopOrBottom = canPlacement_.bottom || canPlacement_.top;
1599     if (avoidTarget_.value_or(AvoidanceMode::COVER_TARGET) == AvoidanceMode::AVOID_AROUND_TARGET &&
1600         ((!hasWidth_ && canPlaceAround) || (hasWidth_ && canPlaceTopOrBottom))) {
1601         showArrow_ = true;
1602         OffsetF tempPosition;
1603         OffsetF tempArrowPosition;
1604         SizeF tempSize;
1605         placement_ = originPlacement;
1606         if ((hasPlacement_ && AvoidToTargetPlacement(childSize, tempArrowPosition, tempPosition, tempSize, true) &&
1607                 !hasWidth_) ||
1608             (hasPlacement_ && hasWidth_ &&
1609                 AvoidToTargetPlacement(childSize, tempArrowPosition, tempPosition, tempSize, false))) {
1610             position = tempPosition;
1611             arrowOffset = tempArrowPosition;
1612         } else if (hasWidth_) {
1613             tempPosition = AvoidToTopOrBottomByWidth(childSize, tempArrowPosition, tempSize);
1614             position = AdjustAvoidPosition(tempPosition, tempSize.Width(), tempSize.Height(), tempArrowPosition);
1615             arrowOffset = tempArrowPosition;
1616         } else {
1617             position = AdjustAvoidPosition(maxAreaInfo_.position, maxAreaInfo_.size.Width(), maxAreaInfo_.size.Height(),
1618                 maxAreaInfo_.arrowPosition);
1619             arrowOffset = maxAreaInfo_.arrowPosition;
1620             placement_ = maxAreaInfo_.placement;
1621             tempSize = maxAreaInfo_.size;
1622         }
1623         CHECK_NULL_RETURN(bubbleProp, position);
1624         LayoutConstraintF childLayoutConstraint = bubbleProp->CreateChildConstraint();
1625         childLayoutConstraint.UpdateMaxSizeWithCheck(tempSize);
1626         auto childProp = child->GetLayoutProperty();
1627         CHECK_NULL_RETURN(childProp, position);
1628         childProp->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE);
1629         CHECK_NULL_RETURN(child, position);
1630         child->Measure(childLayoutConstraint);
1631         auto childShowWidth = tempSize.Width() + BUBBLE_ARROW_HEIGHT.ConvertToPx() * DOUBLE;
1632         auto childShowHeight = tempSize.Height() + BUBBLE_ARROW_HEIGHT.ConvertToPx() * DOUBLE;
1633         child->GetGeometryNode()->SetFrameSize(SizeF { childShowWidth, childShowHeight });
1634         childSize_ = tempSize;
1635     } else {
1636         position = CoverParent(childSize, originPlacement);
1637     }
1638     return position;
1639 }
1640 
GetBottomRect(const Dimension & targetSpace)1641 Rect BubbleLayoutAlgorithm::GetBottomRect(const Dimension& targetSpace)
1642 {
1643     Rect rect;
1644     float targetOffsetY = targetOffset_.GetY();
1645     targetOffsetY += (targetSpace.ConvertToPx());
1646     auto y = std::max(targetOffsetY + targetSize_.Height(), marginTop_);
1647     auto height = std::min(wrapperSize_.Height() - marginBottom_ - targetOffsetY - targetSize_.Height(),
1648         wrapperSize_.Height() - marginBottom_ - marginTop_);
1649     rect.SetRect(marginStart_, y, wrapperSize_.Width() - marginEnd_ - marginStart_, height);
1650     if (isHalfFoldHover_) {
1651         y = std::max(targetOffsetY + targetSize_.Height(), static_cast<float>(wrapperRect_.Top()));
1652         height = std::min(static_cast<float>(wrapperRect_.Bottom()) - targetOffsetY - targetSize_.Height(),
1653             wrapperSize_.Height() - marginBottom_ - marginTop_);
1654         rect.SetRect(marginStart_, y, wrapperSize_.Width() - marginEnd_ - marginStart_, height);
1655     }
1656     return rect;
1657 }
1658 
GetTopRect(const Dimension & targetSpace)1659 Rect BubbleLayoutAlgorithm::GetTopRect(const Dimension& targetSpace)
1660 {
1661     Rect rect;
1662     float targetOffsetY = targetOffset_.GetY();
1663     targetOffsetY += (-targetSpace.ConvertToPx());
1664     auto height = std::min(targetOffsetY - marginTop_, wrapperSize_.Height() - marginTop_ - marginBottom_);
1665     rect.SetRect(marginStart_, marginTop_, wrapperSize_.Width() - marginEnd_ - marginStart_, height);
1666     if (isHalfFoldHover_) {
1667         height = std::min(
1668             targetOffsetY - static_cast<float>(wrapperRect_.Top()), wrapperSize_.Height() - marginTop_ - marginBottom_);
1669         rect.SetRect(marginStart_, wrapperRect_.Top(), wrapperSize_.Width() - marginEnd_ - marginStart_, height);
1670     }
1671     return rect;
1672 }
1673 
GetRightRect(const Dimension & targetSpace)1674 Rect BubbleLayoutAlgorithm::GetRightRect(const Dimension& targetSpace)
1675 {
1676     Rect rect;
1677     float targetOffsetX = targetOffset_.GetX();
1678     targetOffsetX += (targetSpace.ConvertToPx());
1679     auto x = std::max(targetOffsetX + targetSize_.Width(), marginStart_);
1680     auto width = std::min(wrapperSize_.Width() - targetOffsetX - targetSize_.Width() - marginEnd_,
1681         wrapperSize_.Width() - marginStart_ - marginEnd_);
1682     rect.SetRect(x, marginTop_, width, wrapperSize_.Height() - marginBottom_ - marginTop_);
1683     if (isHalfFoldHover_) {
1684         rect.SetRect(x, wrapperRect_.Top(), width, wrapperRect_.Height());
1685     }
1686     return rect;
1687 }
1688 
GetLeftRect(const Dimension & targetSpace)1689 Rect BubbleLayoutAlgorithm::GetLeftRect(const Dimension& targetSpace)
1690 {
1691     Rect rect;
1692     float targetOffsetX = targetOffset_.GetX();
1693     targetOffsetX += (-targetSpace.ConvertToPx());
1694     auto width = std::min(targetOffsetX - marginStart_, wrapperSize_.Width() - marginEnd_ - marginStart_);
1695     rect.SetRect(marginStart_, marginTop_, width, wrapperSize_.Height() - marginBottom_ - marginTop_);
1696     if (isHalfFoldHover_) {
1697         rect.SetRect(marginStart_, wrapperRect_.Top(), width, wrapperRect_.Height());
1698     }
1699     return rect;
1700 }
1701 
AvoidToTopOrBottomByWidth(const SizeF & childSize,OffsetF & arrowPosition,SizeF & resultSize)1702 OffsetF BubbleLayoutAlgorithm::AvoidToTopOrBottomByWidth(
1703     const SizeF& childSize, OffsetF& arrowPosition, SizeF& resultSize)
1704 {
1705     // popup avoid parent to top or bottom
1706     OffsetF bottomPosition;
1707     OffsetF topPosition;
1708     BottomAndTopPosition(bottomPosition, topPosition, childSize);
1709     OffsetF resultPosition;
1710     auto topHeight = std::min<float>(
1711         targetOffset_.GetY() - targetSpace_.ConvertToPx() - marginTop_ - BUBBLE_ARROW_HEIGHT.ConvertToPx(),
1712         wrapperSize_.Height() - marginTop_ - marginBottom_);
1713     auto bottomHeight = std::min<float>(wrapperSize_.Height() - marginBottom_ - targetOffset_.GetY() -
1714         targetSpace_.ConvertToPx() - targetSize_.Height() - BUBBLE_ARROW_HEIGHT.ConvertToPx(),
1715         wrapperSize_.Height() - marginBottom_ - marginTop_);
1716     if (GreatNotEqual(bottomHeight, topHeight)) {
1717         placement_ = Placement::BOTTOM;
1718         resultPosition = GetPositionWithPlacementBottom(childSize, topPosition, bottomPosition, arrowPosition);
1719         Rect rect = GetBottomRect(targetSpace_);
1720         auto maxHeight = std::min<float>(rect.Height() - BUBBLE_ARROW_HEIGHT.ConvertToPx(), childSize.Height());
1721         auto maxWidth = std::min<float>(rect.Width(), childSize.Width());
1722         resultSize = SizeF(maxWidth, maxHeight);
1723     } else {
1724         placement_ = Placement::TOP;
1725         Rect rect = GetTopRect(targetSpace_);
1726         auto maxHeight = std::min<float>(rect.Height() - BUBBLE_ARROW_HEIGHT.ConvertToPx(), childSize.Height());
1727         auto maxWidth = std::min<float>(rect.Width(), childSize.Width());
1728         resultSize = SizeF(maxWidth, maxHeight);
1729         resultPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - maxWidth) / HALF,
1730             targetOffset_.GetY() - maxHeight - targetSpace_.ConvertToPx() - BUBBLE_ARROW_HEIGHT.ConvertToPx());
1731     }
1732     return resultPosition;
1733 }
1734 
AvoidToTargetPlacement(const SizeF & childSize,OffsetF & arrowPosition,OffsetF & resultPosition,SizeF & resultSize,bool canCompress)1735 bool BubbleLayoutAlgorithm::AvoidToTargetPlacement(
1736     const SizeF& childSize, OffsetF& arrowPosition, OffsetF& resultPosition, SizeF& resultSize, bool canCompress)
1737 {
1738     // popup avoid to target placement
1739     switch (placement_) {
1740         case Placement::BOTTOM_LEFT:
1741         case Placement::BOTTOM_RIGHT:
1742         case Placement::BOTTOM: {
1743             if (!canPlacement_.bottom) {
1744                 return false;
1745             }
1746             return AvoidToTargetBottom(childSize, arrowPosition, resultPosition, resultSize, canCompress);
1747             break;
1748         }
1749         case Placement::TOP_LEFT:
1750         case Placement::TOP_RIGHT:
1751         case Placement::TOP: {
1752             if (!canPlacement_.top) {
1753                 return false;
1754             }
1755             return AvoidToTargetTop(childSize, arrowPosition, resultPosition, resultSize, canCompress);
1756             break;
1757         }
1758         case Placement::RIGHT_TOP:
1759         case Placement::RIGHT_BOTTOM:
1760         case Placement::RIGHT: {
1761             if (!canPlacement_.right) {
1762                 return false;
1763             }
1764             return AvoidToTargetRight(childSize, arrowPosition, resultPosition, resultSize, canCompress);
1765             break;
1766         }
1767         case Placement::LEFT_TOP:
1768         case Placement::LEFT_BOTTOM:
1769         case Placement::LEFT: {
1770             if (!canPlacement_.left) {
1771                 return false;
1772             }
1773             return AvoidToTargetLeft(childSize, arrowPosition, resultPosition, resultSize, canCompress);
1774             break;
1775         }
1776         default:
1777             return false;
1778             break;
1779     }
1780     return true;
1781 }
1782 
AvoidToTargetBottom(const SizeF & childSize,OffsetF & arrowPosition,OffsetF & resultPosition,SizeF & resultSize,bool canCompress)1783 bool BubbleLayoutAlgorithm::AvoidToTargetBottom(
1784     const SizeF& childSize, OffsetF& arrowPosition, OffsetF& resultPosition, SizeF& resultSize, bool canCompress)
1785 {
1786     OffsetF bottomPosition;
1787     OffsetF topPosition;
1788     BottomAndTopPosition(bottomPosition, topPosition, childSize);
1789     OffsetF beforePosition = GetPositionWithPlacementNew(childSize, topPosition, bottomPosition, arrowPosition);
1790     resultPosition = beforePosition;
1791     float maxHeight = 0.0f;
1792     float maxWidth = 0.0f;
1793     Rect rect = GetBottomRect(targetSpace_);
1794     maxHeight = std::min<float>(rect.Height() - BUBBLE_ARROW_HEIGHT.ConvertToPx(), childSize.Height());
1795     if (placement_ == Placement::BOTTOM_LEFT) {
1796         resultPosition = beforePosition;
1797         if (canCompress == false && GreatNotEqual(childSize.Width(), rect.Right() - resultPosition.GetX())) {
1798             return false;
1799         }
1800         maxWidth = std::min<float>(rect.Right() - resultPosition.GetX(), childSize.Width());
1801     } else if (placement_ == Placement::BOTTOM_RIGHT) {
1802         resultPosition = OffsetF(std::max<float>(rect.Left(), beforePosition.GetX()), beforePosition.GetY());
1803         if (canCompress == false &&
1804             GreatNotEqual(childSize.Width(), targetOffset_.GetX() + targetSize_.Width() - resultPosition.GetX())) {
1805             return false;
1806         }
1807         maxWidth =
1808             std::min<float>(targetOffset_.GetX() + targetSize_.Width() - resultPosition.GetX(), childSize.Width());
1809     } else if (placement_ == Placement::BOTTOM) {
1810         if (canCompress == false && LessNotEqual(beforePosition.GetX(), rect.Left())) {
1811             return false;
1812         }
1813         resultPosition = OffsetF(std::max<float>(rect.Left(), beforePosition.GetX()), beforePosition.GetY());
1814         maxWidth = std::min<float>(rect.Right() - resultPosition.GetX(), childSize.Width());
1815     }
1816     maxHeight = std::max(0.0f, maxHeight);
1817     maxWidth = std::max(0.0f, maxWidth);
1818     resultSize = SizeF { maxWidth, maxHeight };
1819     return true;
1820 }
1821 
AvoidToTargetTop(const SizeF & childSize,OffsetF & arrowPosition,OffsetF & resultPosition,SizeF & resultSize,bool canCompress)1822 bool BubbleLayoutAlgorithm::AvoidToTargetTop(
1823     const SizeF& childSize, OffsetF& arrowPosition, OffsetF& resultPosition, SizeF& resultSize, bool canCompress)
1824 {
1825     OffsetF bottomPosition;
1826     OffsetF topPosition;
1827     BottomAndTopPosition(bottomPosition, topPosition, childSize);
1828     float maxWidth = 0.0f;
1829     float bubbleSpacing = scaledBubbleSpacing_;
1830     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1831     float radius = borderRadius_.ConvertToPx();
1832     Rect rect = GetTopRect(targetSpace_);
1833     float maxHeight = std::min<float>(rect.Height() - BUBBLE_ARROW_HEIGHT.ConvertToPx(), childSize.Height());
1834     if (placement_ == Placement::TOP_LEFT) {
1835         OffsetF newTopPosition = OffsetF(
1836             targetOffset_.GetX(), targetOffset_.GetY() - maxHeight - targetSpace_.ConvertToPx() - bubbleSpacing);
1837         resultPosition = OffsetF(
1838             std::max<float>(rect.Left(), newTopPosition.GetX()), std::max<float>(rect.Top(), newTopPosition.GetY()));
1839         if (canCompress == false && GreatNotEqual(childSize.Width(), rect.Right() - resultPosition.GetX())) {
1840             return false;
1841         }
1842         maxWidth = std::min<float>(rect.Right() - resultPosition.GetX(), childSize.Width());
1843         arrowPosition = resultPosition + OffsetF(radius + arrowHalfWidth, maxHeight + bubbleSpacing);
1844         maxHeight = std::max(0.0f, maxHeight);
1845         maxWidth = std::max(0.0f, maxWidth);
1846         resultSize = SizeF { maxWidth, maxHeight };
1847     } else if (placement_ == Placement::TOP_RIGHT) {
1848         if (canCompress == false &&
1849             GreatNotEqual(childSize.Width(), targetOffset_.GetX() + targetSize_.Width() - rect.Left())) {
1850             return false;
1851         }
1852         maxWidth = std::min<float>(targetOffset_.GetX() + targetSize_.Width() - rect.Left(), childSize.Width());
1853         OffsetF newTopPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - maxWidth,
1854             targetOffset_.GetY() - maxHeight - targetSpace_.ConvertToPx() - bubbleSpacing);
1855         resultPosition = OffsetF(
1856             std::max<float>(rect.Left(), newTopPosition.GetX()), std::max<float>(rect.Top(), newTopPosition.GetY()));
1857         arrowPosition = resultPosition + OffsetF(radius + arrowHalfWidth, maxHeight + bubbleSpacing);
1858         maxHeight = std::max(0.0f, maxHeight);
1859         maxWidth = std::max(0.0f, maxWidth);
1860         resultSize = SizeF { maxWidth, maxHeight };
1861     } else if (placement_ == Placement::TOP) {
1862         return AvoidToTargetTopMid(childSize, arrowPosition, resultPosition, resultSize, canCompress);
1863     }
1864     return true;
1865 }
1866 
AvoidToTargetTopMid(const SizeF & childSize,OffsetF & arrowPosition,OffsetF & resultPosition,SizeF & resultSize,bool canCompress)1867 bool BubbleLayoutAlgorithm::AvoidToTargetTopMid(
1868     const SizeF& childSize, OffsetF& arrowPosition, OffsetF& resultPosition, SizeF& resultSize, bool canCompress)
1869 {
1870     OffsetF bottomPosition;
1871     OffsetF topPosition;
1872     BottomAndTopPosition(bottomPosition, topPosition, childSize);
1873     float maxWidth = 0.0f;
1874     float bubbleSpacing = scaledBubbleSpacing_;
1875     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1876     float radius = borderRadius_.ConvertToPx();
1877     Rect rect = GetTopRect(targetSpace_);
1878     float maxHeight = std::min<float>(rect.Height() - BUBBLE_ARROW_HEIGHT.ConvertToPx(), childSize.Height());
1879     OffsetF newTopPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / HALF,
1880         targetOffset_.GetY() - maxHeight - targetSpace_.ConvertToPx() - bubbleSpacing);
1881     if (canCompress == false && LessNotEqual(newTopPosition.GetX(), rect.Left())) {
1882         return false;
1883     }
1884     resultPosition = OffsetF(
1885         std::max<float>(rect.Left(), newTopPosition.GetX()), std::max<float>(rect.Top(), newTopPosition.GetY()));
1886     maxWidth = std::min<float>(rect.Right() - resultPosition.GetX(), childSize.Width());
1887 
1888     if (bCaretMode_) {
1889         arrowPosition = OffsetF(targetOffset_.GetX(), targetOffset_.GetY() - bubbleSpacing);
1890     } else {
1891         arrowPosition = resultPosition + OffsetF(radius + arrowHalfWidth, maxHeight + bubbleSpacing);
1892     }
1893     maxHeight = std::max(0.0f, maxHeight);
1894     maxWidth = std::max(0.0f, maxWidth);
1895     resultSize = SizeF { maxWidth, maxHeight };
1896     return true;
1897 }
1898 
AvoidToTargetRight(const SizeF & childSize,OffsetF & arrowPosition,OffsetF & resultPosition,SizeF & resultSize,bool canCompress)1899 bool BubbleLayoutAlgorithm::AvoidToTargetRight(
1900     const SizeF& childSize, OffsetF& arrowPosition, OffsetF& resultPosition, SizeF& resultSize, bool canCompress)
1901 {
1902     OffsetF bottomPosition;
1903     OffsetF topPosition;
1904     BottomAndTopPosition(bottomPosition, topPosition, childSize);
1905     OffsetF beforePosition = GetPositionWithPlacementNew(childSize, topPosition, bottomPosition, arrowPosition);
1906     resultPosition = beforePosition;
1907     float maxHeight = 0.0f;
1908     float maxWidth = 0.0f;
1909     Rect rect = GetRightRect(targetSpace_);
1910     if (canCompress == false && GreatNotEqual(childSize.Width(), rect.Width() - BUBBLE_ARROW_HEIGHT.ConvertToPx())) {
1911         return false;
1912     }
1913     maxWidth = std::min<float>(rect.Width() - BUBBLE_ARROW_HEIGHT.ConvertToPx(), childSize.Width());
1914     if (placement_ == Placement::RIGHT_TOP) {
1915         resultPosition = beforePosition;
1916         maxHeight = std::min<float>(rect.Bottom() - resultPosition.GetY(), childSize.Height());
1917     } else if (placement_ == Placement::RIGHT_BOTTOM) {
1918         resultPosition = OffsetF(beforePosition.GetX(), std::max<float>(rect.Top(), beforePosition.GetY()));
1919         maxHeight =
1920             std::min<float>(targetOffset_.GetY() + targetSize_.Height() - resultPosition.GetY(), childSize.Height());
1921     } else if (placement_ == Placement::RIGHT) {
1922         resultPosition = OffsetF(beforePosition.GetX(), std::max<float>(rect.Top(), beforePosition.GetY()));
1923         maxHeight = std::min<float>(rect.Bottom() - resultPosition.GetY(), childSize.Height());
1924     }
1925     maxHeight = std::max(0.0f, maxHeight);
1926     maxWidth = std::max(0.0f, maxWidth);
1927     resultSize = SizeF { maxWidth, maxHeight };
1928     return true;
1929 }
1930 
AvoidToTargetLeft(const SizeF & childSize,OffsetF & arrowPosition,OffsetF & resultPosition,SizeF & resultSize,bool canCompress)1931 bool BubbleLayoutAlgorithm::AvoidToTargetLeft(
1932     const SizeF& childSize, OffsetF& arrowPosition, OffsetF& resultPosition, SizeF& resultSize, bool canCompress)
1933 {
1934     OffsetF bottomPosition;
1935     OffsetF topPosition;
1936     BottomAndTopPosition(bottomPosition, topPosition, childSize);
1937     OffsetF beforePosition = GetPositionWithPlacementNew(childSize, topPosition, bottomPosition, arrowPosition);
1938     resultPosition = beforePosition;
1939     float maxHeight = 0.0f;
1940     float maxWidth = 0.0f;
1941     float bubbleSpacing = scaledBubbleSpacing_;
1942     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
1943     float radius = borderRadius_.ConvertToPx();
1944     Rect rect = GetLeftRect(targetSpace_);
1945     if (canCompress == false && GreatNotEqual(childSize.Width(), rect.Width() - BUBBLE_ARROW_HEIGHT.ConvertToPx())) {
1946         return false;
1947     }
1948     maxWidth = std::min<float>(rect.Width() - BUBBLE_ARROW_HEIGHT.ConvertToPx(), childSize.Width());
1949     if (placement_ == Placement::LEFT_TOP) {
1950         OffsetF newLeftPosition =
1951             OffsetF(targetOffset_.GetX() - targetSpace_.ConvertToPx() - bubbleSpacing - maxWidth, targetOffset_.GetY());
1952         resultPosition = OffsetF(
1953             std::max<float>(rect.Left(), newLeftPosition.GetX()), std::max<float>(rect.Top(), newLeftPosition.GetY()));
1954         maxHeight = std::min<float>(rect.Bottom() - resultPosition.GetY(), childSize.Height());
1955     } else if (placement_ == Placement::LEFT_BOTTOM) {
1956         maxHeight = std::min<float>(targetOffset_.GetY() + targetSize_.Height() - rect.Top(), childSize.Height());
1957         OffsetF newLeftPosition = OffsetF(targetOffset_.GetX() - targetSpace_.ConvertToPx() - bubbleSpacing - maxWidth,
1958             targetOffset_.GetY() + targetSize_.Height() - maxHeight);
1959         resultPosition = OffsetF(
1960             std::max<float>(rect.Left(), newLeftPosition.GetX()), std::max<float>(rect.Top(), newLeftPosition.GetY()));
1961     } else if (placement_ == Placement::LEFT) {
1962         OffsetF newLeftPosition = OffsetF(targetOffset_.GetX() - targetSpace_.ConvertToPx() - bubbleSpacing - maxWidth,
1963             targetOffset_.GetY() + targetSize_.Height() / HALF - childSize.Height() / HALF);
1964         resultPosition = OffsetF(
1965             std::max<float>(rect.Left(), newLeftPosition.GetX()), std::max<float>(rect.Top(), newLeftPosition.GetY()));
1966         maxHeight = std::min<float>(rect.Bottom() - resultPosition.GetY(), childSize.Height());
1967     }
1968     arrowPosition = resultPosition + OffsetF(maxWidth + bubbleSpacing, radius + arrowHalfWidth);
1969     maxHeight = std::max(0.0f, maxHeight);
1970     maxWidth = std::max(0.0f, maxWidth);
1971     resultSize = SizeF { maxWidth, maxHeight };
1972     return true;
1973 }
1974 
CheckIfNeedRemoveArrow(float & xMin,float & xMax,float & yMin,float & yMax)1975 bool BubbleLayoutAlgorithm::CheckIfNeedRemoveArrow(float& xMin, float& xMax, float& yMin, float& yMax)
1976 {
1977     if (!showArrow_ || !avoidKeyboard_) {
1978         return false;
1979     }
1980     bool isHorizontal = false;
1981     if (setHorizontal_.find(placement_) != setHorizontal_.end()) {
1982         isHorizontal = true;
1983     }
1984     if ((isHorizontal && LessNotEqual(yMax, yMin)) || (!isHorizontal && LessNotEqual(xMax, xMin))) {
1985         return false;
1986     }
1987     if (isHorizontal && GreatOrEqual(xMax + BUBBLE_ARROW_HEIGHT.ConvertToPx(), xMin)) {
1988         xMax += BUBBLE_ARROW_HEIGHT.ConvertToPx();
1989         showArrow_ = false;
1990         return true;
1991     }
1992     if (!isHorizontal && GreatOrEqual(yMax + BUBBLE_ARROW_HEIGHT.ConvertToPx(), yMin)) {
1993         yMax += BUBBLE_ARROW_HEIGHT.ConvertToPx();
1994         showArrow_ = false;
1995         return true;
1996     }
1997     return false;
1998 }
1999 
GetSimplePlacement(Placement & placement)2000 Placement GetSimplePlacement(Placement& placement)
2001 {
2002     switch (placement) {
2003         case Placement::LEFT_TOP:
2004         case Placement::LEFT_BOTTOM:
2005         case Placement::LEFT: {
2006             return Placement::LEFT;
2007         }
2008         case Placement::RIGHT_TOP:
2009         case Placement::RIGHT_BOTTOM:
2010         case Placement::RIGHT: {
2011             return Placement::RIGHT;
2012         }
2013         case Placement::TOP_LEFT:
2014         case Placement::TOP_RIGHT:
2015         case Placement::TOP: {
2016             return Placement::TOP;
2017         }
2018         case Placement::BOTTOM_LEFT:
2019         case Placement::BOTTOM_RIGHT:
2020         case Placement::BOTTOM: {
2021             return Placement::BOTTOM;
2022         }
2023         default:
2024             return Placement::NONE;
2025     }
2026 }
2027 
GetBubblePosition(const OffsetF & position,float xMin,float xMax,float yMin,float yMax)2028 OffsetF BubbleLayoutAlgorithm::GetBubblePosition(const OffsetF& position, float xMin,
2029     float xMax, float yMin, float yMax)
2030 {
2031     auto positionX = position.GetX();
2032     auto positionY = position.GetY();
2033     if (needRemoveArrow_) {
2034         if (GetSimplePlacement(placement_) == Placement::TOP) {
2035             positionY += BUBBLE_ARROW_HEIGHT.ConvertToPx();
2036         } else if (GetSimplePlacement(placement_) == Placement::BOTTOM) {
2037             positionY -= BUBBLE_ARROW_HEIGHT.ConvertToPx();
2038         } else if (GetSimplePlacement(placement_) == Placement::LEFT) {
2039             positionX += BUBBLE_ARROW_HEIGHT.ConvertToPx();
2040         } else if (GetSimplePlacement(placement_) == Placement::RIGHT) {
2041             positionX -= BUBBLE_ARROW_HEIGHT.ConvertToPx();
2042         }
2043     } else if (showArrow_) {
2044         UpdateContentPositionRange(xMin, xMax, yMin, yMax);
2045     }
2046     auto x = std::clamp(positionX, xMin, xMax);
2047     auto y = std::clamp(positionY, yMin, yMax);
2048     if (!showArrow_ || !avoidKeyboard_) {
2049         return OffsetF(x, y);
2050     }
2051     bool isHorizontal = false;
2052     if (setHorizontal_.find(placement_) != setHorizontal_.end()) {
2053         isHorizontal = true;
2054     }
2055     if (isHorizontal) {
2056         if (GreatNotEqual(positionX, xMax)) {
2057             showArrow_ = false;
2058             x += BUBBLE_ARROW_HEIGHT.ConvertToPx();
2059         } else if (LessNotEqual(positionX, xMin)) {
2060             showArrow_ = false;
2061         }
2062     } else {
2063         if (GreatNotEqual(positionY, yMax)) {
2064             showArrow_ = false;
2065             y += BUBBLE_ARROW_HEIGHT.ConvertToPx();
2066         } else if (LessNotEqual(position.GetY(), yMin)) {
2067             showArrow_ = false;
2068         }
2069     }
2070     return OffsetF(x, y);
2071 }
2072 
UpdateContentPositionRange(float & xMin,float & xMax,float & yMin,float & yMax)2073 void BubbleLayoutAlgorithm::UpdateContentPositionRange(float& xMin, float& xMax, float& yMin, float& yMax)
2074 {
2075     if (GetSimplePlacement(placement_) == Placement::BOTTOM) {
2076         yMin += BUBBLE_ARROW_HEIGHT.ConvertToPx();
2077         yMax += BUBBLE_ARROW_HEIGHT.ConvertToPx();
2078     } else if (GetSimplePlacement(placement_) == Placement::RIGHT) {
2079         xMin += BUBBLE_ARROW_HEIGHT.ConvertToPx();
2080         xMax += BUBBLE_ARROW_HEIGHT.ConvertToPx();
2081     }
2082 }
2083 
CheckArrowPosition(OffsetF & position,float width,float height)2084 void BubbleLayoutAlgorithm::CheckArrowPosition(OffsetF& position, float width, float height)
2085 {
2086     if (!showArrow_ || !avoidKeyboard_) {
2087         return;
2088     }
2089     float xMax = 0.0f;
2090     float yMax = 0.0f;
2091     float xMin = 1.0f;
2092     float yMin = 1.0f;
2093     float cornerDistance = borderRadius_.ConvertToPx() + BUBBLE_ARROW_WIDTH.ConvertToPx() / DOUBLE;
2094     auto simplePlacement = GetSimplePlacement(placement_);
2095     if (simplePlacement == Placement::LEFT) {
2096         yMin = position.GetY() + cornerDistance;
2097         yMax = position.GetY() + height - cornerDistance;
2098         if (GreatNotEqual(yMin, targetOffset_.GetY() + targetSize_.Height()) ||
2099             LessNotEqual(yMax, targetOffset_.GetY())) {
2100             showArrow_ = false;
2101             position.SetX(position.GetX() + BUBBLE_ARROW_HEIGHT.ConvertToPx());
2102         }
2103     } else if (simplePlacement == Placement::RIGHT) {
2104         yMin = position.GetY() + cornerDistance;
2105         yMax = position.GetY() + height - cornerDistance;
2106         if (GreatNotEqual(yMin, targetOffset_.GetY() + targetSize_.Height()) ||
2107             LessNotEqual(yMax, targetOffset_.GetY())) {
2108             showArrow_ = false;
2109             position.SetX(position.GetX() - BUBBLE_ARROW_HEIGHT.ConvertToPx());
2110         }
2111     } else if (simplePlacement == Placement::TOP) {
2112         xMin = position.GetX() + cornerDistance;
2113         xMax = position.GetX() + width - cornerDistance;
2114         if (GreatNotEqual(xMin, targetOffset_.GetX() + targetSize_.Width()) ||
2115             LessNotEqual(xMax, targetOffset_.GetX())) {
2116             showArrow_ = false;
2117             position.SetY(position.GetY() + BUBBLE_ARROW_HEIGHT.ConvertToPx());
2118         }
2119     } else if (simplePlacement == Placement::BOTTOM) {
2120         xMin = position.GetX() + cornerDistance;
2121         xMax = position.GetX() + width - cornerDistance;
2122         if (GreatNotEqual(xMin, targetOffset_.GetX() + targetSize_.Width()) ||
2123             LessNotEqual(xMax, targetOffset_.GetX())) {
2124             showArrow_ = false;
2125             position.SetY(position.GetY() - BUBBLE_ARROW_HEIGHT.ConvertToPx());
2126         }
2127     }
2128 }
2129 
GetPositionWithPlacementNew(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)2130 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementNew(
2131     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
2132 {
2133     OffsetF childPosition;
2134     OffsetF tmpTopPosition = topPosition;
2135     OffsetF tmpBottomPosition = bottomPosition;
2136     if (followCursor_ && (placement_ == Placement::BOTTOM || placement_ == Placement::TOP)) {
2137         float defaultXOffset = GreatNotEqual(targetOffset_.GetX(), marginStart_)
2138                                    ? (wrapperSize_.Width() - marginEnd_ - childSize.Width())
2139                                    : marginStart_;
2140         tmpBottomPosition = OffsetF(defaultXOffset,
2141             targetOffset_.GetY() + targetSize_.Height() + targetSpace_.ConvertToPx() + TIPS_MOUSE_SPACE.ConvertToPx());
2142         tmpTopPosition = OffsetF(defaultXOffset,
2143             targetOffset_.GetY() - childSize.Height() - targetSpace_.ConvertToPx() - TIPS_MOUSE_SPACE.ConvertToPx());
2144     }
2145     auto func = placementFuncMap_.find(placement_);
2146     if (func != placementFuncMap_.end()) {
2147         auto placementFunc = func->second;
2148         if (placementFunc != nullptr) {
2149             childPosition = (this->*placementFunc)(childSize, tmpTopPosition, tmpBottomPosition, arrowPosition);
2150         }
2151     }
2152     return childPosition;
2153 }
2154 
FitToScreenNew(const OffsetF & position,size_t step,size_t & i,const SizeF & childSize,bool didNeedArrow)2155 OffsetF BubbleLayoutAlgorithm::FitToScreenNew(const OffsetF& position, size_t step, size_t& i,
2156     const SizeF& childSize, bool didNeedArrow)
2157 {
2158     OffsetF arrowPosition = checkArrowPosition_;
2159     if (!CheckPosition(position, childSize, step, i, arrowPosition)) {
2160         return OffsetF(0.0f, 0.0f);
2161     }
2162     return position;
2163 }
2164 
AddTargetSpace(const OffsetF & position)2165 OffsetF BubbleLayoutAlgorithm::AddTargetSpace(const OffsetF& position)
2166 {
2167     auto x = position.GetX();
2168     auto y = position.GetY();
2169     switch (placement_) {
2170         case Placement::BOTTOM_LEFT:
2171         case Placement::BOTTOM_RIGHT:
2172         case Placement::BOTTOM: {
2173             y += targetSpace_.ConvertToPx();
2174             break;
2175         }
2176         case Placement::TOP_LEFT:
2177         case Placement::TOP_RIGHT:
2178         case Placement::TOP: {
2179             y -= targetSpace_.ConvertToPx();
2180             break;
2181         }
2182         case Placement::RIGHT_TOP:
2183         case Placement::RIGHT_BOTTOM:
2184         case Placement::RIGHT: {
2185             x += targetSpace_.ConvertToPx();
2186             break;
2187         }
2188         case Placement::LEFT_TOP:
2189         case Placement::LEFT_BOTTOM:
2190         case Placement::LEFT: {
2191             x -= targetSpace_.ConvertToPx();
2192             break;
2193         }
2194         default: {
2195             y += targetSpace_.ConvertToPx();
2196             break;
2197         }
2198     }
2199     return OffsetF(x, y);
2200 }
2201 
AddOffset(const OffsetF & position)2202 OffsetF BubbleLayoutAlgorithm::AddOffset(const OffsetF& position)
2203 {
2204     auto x = position.GetX();
2205     auto y = position.GetY();
2206     x += positionOffset_.GetX();
2207     y += positionOffset_.GetY();
2208     if (doubleBorderEnable_) {
2209         x = std::round(x);
2210         y = std::round(y);
2211     }
2212     return OffsetF(x, y);
2213 }
2214 
UpdateChildPosition(OffsetF & childOffset)2215 void BubbleLayoutAlgorithm::UpdateChildPosition(OffsetF& childOffset)
2216 {
2217     double arrowWidth = BUBBLE_ARROW_WIDTH_F.value_or(BUBBLE_ARROW_WIDTH.ConvertToPx());
2218     double twoRadiusPx = borderRadius_.ConvertToPx() * 2.0;
2219     float movingDistance = BUBBLE_ARROW_HEIGHT_F.value_or(BUBBLE_ARROW_HEIGHT.ConvertToPx());
2220     switch (placement_) {
2221         case Placement::TOP:
2222         case Placement::TOP_LEFT:
2223         case Placement::TOP_RIGHT:
2224             showArrow_ = GreatOrEqual(childSize_.Width() - twoRadiusPx, arrowWidth);
2225             if (!showArrow_ || !enableArrow_) {
2226                 childOffset += OffsetF(0.0, movingDistance);
2227             }
2228             break;
2229         case Placement::BOTTOM:
2230         case Placement::BOTTOM_LEFT:
2231         case Placement::BOTTOM_RIGHT:
2232             showArrow_ = GreatOrEqual(childSize_.Width() - twoRadiusPx, arrowWidth);
2233             if (!showArrow_ || !enableArrow_) {
2234                 childOffset += OffsetF(0.0, -(movingDistance));
2235             }
2236             break;
2237         case Placement::LEFT:
2238         case Placement::LEFT_TOP:
2239         case Placement::LEFT_BOTTOM:
2240             showArrow_ = GreatOrEqual(childSize_.Height() - twoRadiusPx, arrowWidth);
2241             if (!showArrow_ || !enableArrow_) {
2242                 childOffset += OffsetF(movingDistance, 0.0);
2243             }
2244             break;
2245         case Placement::RIGHT:
2246         case Placement::RIGHT_TOP:
2247         case Placement::RIGHT_BOTTOM:
2248             showArrow_ = GreatOrEqual(childSize_.Height() - twoRadiusPx, arrowWidth);
2249             if (!showArrow_ || !enableArrow_) {
2250                 childOffset += OffsetF(-(movingDistance), 0.0);
2251             }
2252             break;
2253         default:
2254             break;
2255     }
2256     if (!enableArrow_) {
2257         showArrow_ = false;
2258     }
2259 }
2260 
UpdateTouchRegion()2261 void BubbleLayoutAlgorithm::UpdateTouchRegion()
2262 {
2263     OffsetF topLeft = childOffset_;
2264     OffsetF bottomRight = OffsetF(childSize_.Width(), childSize_.Height());
2265     switch (arrowPlacement_) {
2266         case Placement::TOP:
2267         case Placement::TOP_LEFT:
2268         case Placement::TOP_RIGHT:
2269             if (showArrow_) {
2270                 bottomRight += OffsetF(0.0, BUBBLE_ARROW_HEIGHT.ConvertToPx());
2271             }
2272             break;
2273         case Placement::BOTTOM:
2274         case Placement::BOTTOM_LEFT:
2275         case Placement::BOTTOM_RIGHT:
2276             if (showArrow_) {
2277                 topLeft += OffsetF(0.0, -BUBBLE_ARROW_HEIGHT.ConvertToPx());
2278                 bottomRight += OffsetF(0.0, BUBBLE_ARROW_HEIGHT.ConvertToPx());
2279             }
2280             break;
2281         case Placement::LEFT:
2282         case Placement::LEFT_TOP:
2283         case Placement::LEFT_BOTTOM:
2284             if (showArrow_) {
2285                 bottomRight += OffsetF(BUBBLE_ARROW_HEIGHT.ConvertToPx(), 0.0);
2286             }
2287             break;
2288         case Placement::RIGHT:
2289         case Placement::RIGHT_TOP:
2290         case Placement::RIGHT_BOTTOM:
2291             if (showArrow_) {
2292                 topLeft += OffsetF(-BUBBLE_ARROW_HEIGHT.ConvertToPx(), 0.0);
2293                 bottomRight += OffsetF(BUBBLE_ARROW_HEIGHT.ConvertToPx(), 0.0);
2294             }
2295             break;
2296         default:
2297             break;
2298     }
2299     touchRegion_ = RectF(topLeft, topLeft + bottomRight);
2300     dumpInfo_.touchRegion = touchRegion_;
2301 }
2302 
InitCaretTargetSizeAndPosition()2303 void BubbleLayoutAlgorithm::InitCaretTargetSizeAndPosition()
2304 {
2305     static std::vector<std::string> TEXT_STATES = { V2::TEXTAREA_ETS_TAG, V2::TEXTINPUT_ETS_TAG,
2306         V2::RICH_EDITOR_ETS_TAG, V2::SEARCH_ETS_TAG };
2307     auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
2308     CHECK_NULL_VOID(targetNode);
2309     auto it = std::find(TEXT_STATES.begin(), TEXT_STATES.end(), targetTag_);
2310     bCaretMode_ = false;
2311     CaretMetricsF caretMetrics;
2312     if (it != TEXT_STATES.end()) {
2313         bCaretMode_ = true;
2314         positionOffset_ = OffsetF(0.0f, 0.0f);
2315         if ((placement_ != Placement::BOTTOM) && (placement_ != Placement::TOP)) {
2316             placement_ = Placement::BOTTOM;
2317         }
2318         GetTextCaretMetrics<TextBase>(targetNode, caretMetrics);
2319         targetOffset_ = caretMetrics.offset;
2320         targetSize_.SetHeight(caretMetrics.height);
2321         targetSize_.SetWidth(0.0f);
2322     }
2323 }
2324 
InitTargetSizeAndPosition(bool showInSubWindow,LayoutWrapper * layoutWrapper)2325 void BubbleLayoutAlgorithm::InitTargetSizeAndPosition(bool showInSubWindow, LayoutWrapper* layoutWrapper)
2326 {
2327     if (followCursor_) {
2328         return;
2329     }
2330     auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
2331     CHECK_NULL_VOID(targetNode);
2332     if (!targetNode->IsOnMainTree() && !targetNode->IsVisible()) {
2333         return;
2334     }
2335     if (followTransformOfTarget_) {
2336         auto rect = targetNode->GetPaintRectToWindowWithTransform();
2337         targetSize_ = rect.GetSize();
2338         targetOffset_ = rect.GetOffset();
2339     } else {
2340         auto geometryNode = targetNode->GetGeometryNode();
2341         CHECK_NULL_VOID(geometryNode);
2342         targetSize_ = geometryNode->GetFrameSize();
2343         targetOffset_ = targetNode->GetPaintRectOffset(false, false, true);
2344     }
2345 
2346     auto expandDisplay = SubwindowManager::GetInstance()->GetIsExpandDisplay();
2347     RefPtr<PipelineContext> pipelineContext;
2348     if (expandDisplay) {
2349         pipelineContext = targetNode->GetContextRefPtr();
2350     } else {
2351         auto host = layoutWrapper->GetHostNode();
2352         CHECK_NULL_VOID(host);
2353         pipelineContext = DialogManager::GetMainPipelineContext(host);
2354     }
2355     CHECK_NULL_VOID(pipelineContext);
2356 
2357     TAG_LOGI(AceLogTag::ACE_OVERLAY, "popup targetOffset_: %{public}s, targetSize_: %{public}s, "
2358         "followTransformOfTarget_: %{public}d",
2359         targetOffset_.ToString().c_str(), targetSize_.ToString().c_str(), followTransformOfTarget_);
2360     // Show in SubWindow
2361     if (showInSubWindow) {
2362         auto displayWindowOffset = OffsetF(pipelineContext->GetDisplayWindowRectInfo().GetOffset().GetX(),
2363             pipelineContext->GetDisplayWindowRectInfo().GetOffset().GetY());
2364         targetOffset_ += displayWindowOffset;
2365         auto currentSubwindow = SubwindowManager::GetInstance()->GetSubwindowByType(
2366             pipelineContext->GetInstanceId(), SubwindowType::TYPE_POPUP);
2367         if (currentSubwindow) {
2368             auto subwindowRect = currentSubwindow->GetRect();
2369             targetOffset_ -= subwindowRect.GetOffset();
2370         }
2371     }
2372     dumpInfo_.targetOffset = targetOffset_;
2373     dumpInfo_.targetSize = targetSize_;
2374 }
2375 
CheckPositionInPlacementRect(const Rect & rect,const OffsetF & position,const SizeF & childSize)2376 bool BubbleLayoutAlgorithm::CheckPositionInPlacementRect(
2377     const Rect& rect, const OffsetF& position, const SizeF& childSize)
2378 {
2379     auto x = position.GetX();
2380     auto y = position.GetY();
2381     if (LessNotEqual(x, rect.Left()) || GreatNotEqual(x + childSize.Width(), rect.Right()) ||
2382         LessNotEqual(y, rect.Top()) || GreatNotEqual(y + childSize.Height(), rect.Bottom())) {
2383         return false;
2384     }
2385     return true;
2386 }
2387 
RecordMaxSpace(const float maxAreaSpace,const OffsetF & position,const float maxWidth,const float maxHeight,const OffsetF & arrowPosition)2388 void BubbleLayoutAlgorithm::RecordMaxSpace(const float maxAreaSpace, const OffsetF& position, const float maxWidth,
2389     const float maxHeight, const OffsetF& arrowPosition)
2390 {
2391     if (GreatNotEqual(maxAreaSpace, maxAreaSpace_)) {
2392         maxAreaInfo_ = PopupMaxAreaInfo { placement_, position, SizeF { maxWidth, maxHeight }, arrowPosition };
2393         maxAreaSpace_ = maxAreaSpace;
2394     }
2395     return;
2396 }
2397 
CheckPositionBottom(const OffsetF & position,const SizeF & childSize,size_t step,size_t & i,const OffsetF & arrowPosition)2398 bool BubbleLayoutAlgorithm::CheckPositionBottom(
2399     const OffsetF& position, const SizeF& childSize, size_t step, size_t& i, const OffsetF& arrowPosition)
2400 {
2401     Rect rect = GetBottomRect(userSetTargetSpace_);
2402     Rect avoidParentRect = GetBottomRect(targetSpace_);
2403     auto maxHeight = std::min<float>(avoidParentRect.Height() - BUBBLE_ARROW_HEIGHT.ConvertToPx(), childSize.Height());
2404     auto maxWidth = std::min<float>(avoidParentRect.Width(), childSize.Width());
2405     maxHeight = std::max(0.0f, maxHeight);
2406     maxWidth = std::max(0.0f, maxWidth);
2407     auto maxAreaSpace = maxHeight * maxWidth;
2408     auto minHeight = BUBBLE_ARROW_HEIGHT.ConvertToPx() + POPUP_MIN_HEIGHT.ConvertToPx();
2409     if (GreatNotEqual(avoidParentRect.Height(), minHeight)) {
2410         canPlacement_.bottom = true;
2411         RecordMaxSpace(maxAreaSpace, position, maxWidth, maxHeight, arrowPosition);
2412     }
2413     if (GreatNotEqual(childSize.Height(), rect.Height())) {
2414         i += step;
2415         return false;
2416     } else {
2417         bVertical_ = true;
2418     }
2419     return true;
2420 }
2421 
CheckPositionTop(const OffsetF & position,const SizeF & childSize,size_t step,size_t & i,const OffsetF & arrowPosition)2422 bool BubbleLayoutAlgorithm::CheckPositionTop(
2423     const OffsetF& position, const SizeF& childSize, size_t step, size_t& i, const OffsetF& arrowPosition)
2424 {
2425     Rect rect = GetTopRect(userSetTargetSpace_);
2426     Rect avoidParentRect = GetTopRect(targetSpace_);
2427     auto maxHeight = std::min<float>(avoidParentRect.Height() - BUBBLE_ARROW_HEIGHT.ConvertToPx(), childSize.Height());
2428     auto maxWidth = std::min<float>(avoidParentRect.Width(), childSize.Width());
2429     maxHeight = std::max(0.0f, maxHeight);
2430     maxWidth = std::max(0.0f, maxWidth);
2431     auto maxAreaSpace = maxHeight * maxWidth;
2432     auto minHeight = BUBBLE_ARROW_HEIGHT.ConvertToPx() + POPUP_MIN_HEIGHT.ConvertToPx();
2433     if (GreatNotEqual(avoidParentRect.Height(), minHeight)) {
2434         canPlacement_.top = true;
2435         RecordMaxSpace(maxAreaSpace, position, maxWidth, maxHeight, arrowPosition);
2436     }
2437     if (GreatNotEqual(childSize.Height(), rect.Height())) {
2438         i += step;
2439         return false;
2440     } else {
2441         bVertical_ = true;
2442     }
2443     return true;
2444 }
2445 
CheckPositionRight(const OffsetF & position,const SizeF & childSize,size_t step,size_t & i,const OffsetF & arrowPosition)2446 bool BubbleLayoutAlgorithm::CheckPositionRight(
2447     const OffsetF& position, const SizeF& childSize, size_t step, size_t& i, const OffsetF& arrowPosition)
2448 {
2449     Rect rect = GetRightRect(userSetTargetSpace_);
2450     Rect avoidParentRect = GetRightRect(targetSpace_);
2451     auto maxHeight = std::min<float>(avoidParentRect.Height(), childSize.Height());
2452     auto maxWidth = std::min<float>(avoidParentRect.Width() - BUBBLE_ARROW_HEIGHT.ConvertToPx(), childSize.Width());
2453     maxHeight = std::max(0.0f, maxHeight);
2454     maxWidth = std::max(0.0f, maxWidth);
2455     auto maxAreaSpace = maxHeight * maxWidth;
2456     auto minWidth = BUBBLE_ARROW_HEIGHT.ConvertToPx() + POPUP_MIN_WIDTH.ConvertToPx();
2457     if (GreatNotEqual(avoidParentRect.Width(), minWidth)) {
2458         canPlacement_.right = true;
2459         RecordMaxSpace(maxAreaSpace, position, maxWidth, maxHeight, arrowPosition);
2460     }
2461     if (GreatNotEqual(childSize.Width(), rect.Width())) {
2462         i += step;
2463         return false;
2464     } else {
2465         bHorizontal_ = true;
2466     }
2467     return true;
2468 }
2469 
CheckPositionLeft(const OffsetF & position,const SizeF & childSize,size_t step,size_t & i,const OffsetF & arrowPosition)2470 bool BubbleLayoutAlgorithm::CheckPositionLeft(
2471     const OffsetF& position, const SizeF& childSize, size_t step, size_t& i, const OffsetF& arrowPosition)
2472 {
2473     Rect rect = GetLeftRect(userSetTargetSpace_);
2474     Rect avoidParentRect = GetLeftRect(targetSpace_);
2475     auto maxHeight = std::min<float>(avoidParentRect.Height(), childSize.Height());
2476     auto maxWidth = std::min<float>(avoidParentRect.Width() - BUBBLE_ARROW_HEIGHT.ConvertToPx(), childSize.Width());
2477     maxHeight = std::max(0.0f, maxHeight);
2478     maxWidth = std::max(0.0f, maxWidth);
2479     auto maxAreaSpace = maxHeight * maxWidth;
2480     auto minWidth = BUBBLE_ARROW_HEIGHT.ConvertToPx() + POPUP_MIN_WIDTH.ConvertToPx();
2481     if (GreatNotEqual(avoidParentRect.Width(), minWidth)) {
2482         canPlacement_.left = true;
2483         RecordMaxSpace(maxAreaSpace, position, maxWidth, maxHeight, arrowPosition);
2484     }
2485     if (GreatNotEqual(childSize.Width(), rect.Width())) {
2486         i += step;
2487         return false;
2488     } else {
2489         bHorizontal_ = true;
2490     }
2491     return true;
2492 }
2493 
CheckPosition(const OffsetF & position,const SizeF & childSize,size_t step,size_t & i,const OffsetF & arrowPosition)2494 bool BubbleLayoutAlgorithm::CheckPosition(
2495     const OffsetF& position, const SizeF& childSize, size_t step, size_t& i, const OffsetF& arrowPosition)
2496 {
2497     // check popup can place at targetPlacement or not, and record max area space
2498     Rect rect;
2499     switch (placement_) {
2500         case Placement::BOTTOM_LEFT:
2501         case Placement::BOTTOM_RIGHT:
2502         case Placement::BOTTOM: {
2503             rect = GetBottomRect(userSetTargetSpace_);
2504             if (!CheckPositionBottom(position, childSize, step, i, arrowPosition)) {
2505                 return false;
2506             }
2507             break;
2508         }
2509         case Placement::TOP_LEFT:
2510         case Placement::TOP_RIGHT:
2511         case Placement::TOP: {
2512             rect = GetTopRect(userSetTargetSpace_);
2513             if (!CheckPositionTop(position, childSize, step, i, arrowPosition)) {
2514                 return false;
2515             }
2516             break;
2517         }
2518         case Placement::RIGHT_TOP:
2519         case Placement::RIGHT_BOTTOM:
2520         case Placement::RIGHT: {
2521             rect = GetRightRect(userSetTargetSpace_);
2522             if (!CheckPositionRight(position, childSize, step, i, arrowPosition)) {
2523                 return false;
2524             }
2525             break;
2526         }
2527         case Placement::LEFT_TOP:
2528         case Placement::LEFT_BOTTOM:
2529         case Placement::LEFT: {
2530             rect = GetLeftRect(userSetTargetSpace_);
2531             if (!CheckPositionLeft(position, childSize, step, i, arrowPosition)) {
2532                 return false;
2533             }
2534             break;
2535         }
2536         default:
2537             return false;
2538     }
2539     i++;
2540     return CheckPositionInPlacementRect(rect, position, childSize);
2541 }
2542 
GetPositionWithPlacementTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)2543 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementTop(
2544     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
2545 {
2546     float bubbleSpacing = scaledBubbleSpacing_;
2547     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
2548     float radius = borderRadius_.ConvertToPx();
2549     arrowPosition = topPosition + OffsetF(radius + arrowHalfWidth, childSize.Height() + bubbleSpacing);
2550     if (bCaretMode_) {
2551         arrowPosition = OffsetF(targetOffset_.GetX(), targetOffset_.GetY() - bubbleSpacing);
2552     }
2553     return topPosition;
2554 }
2555 
GetPositionWithPlacementTopLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)2556 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementTopLeft(
2557     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
2558 {
2559     OffsetF childPosition;
2560     float marginRight = 0.0f;
2561     float marginBottom = 0.0f;
2562     float bubbleSpacing = scaledBubbleSpacing_;
2563     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
2564     float radius = borderRadius_.ConvertToPx();
2565     childPosition = OffsetF(targetOffset_.GetX() - marginRight,
2566         targetOffset_.GetY() - childSize.Height() - bubbleSpacing - marginBottom - targetSpace_.ConvertToPx());
2567     arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, childSize.Height() + bubbleSpacing);
2568     return childPosition;
2569 }
2570 
GetPositionWithPlacementTopRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)2571 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementTopRight(
2572     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
2573 {
2574     OffsetF childPosition;
2575     float marginBottom = 0.0f;
2576     float marginLeft = 0.0f;
2577     float bubbleSpacing = scaledBubbleSpacing_;
2578     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
2579     float radius = borderRadius_.ConvertToPx();
2580     childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
2581         targetOffset_.GetY() - childSize.Height() - targetSpace_.ConvertToPx() - bubbleSpacing - marginBottom);
2582     arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, childSize.Height() + bubbleSpacing);
2583     return childPosition;
2584 }
2585 
GetPositionWithPlacementBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)2586 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementBottom(
2587     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
2588 {
2589     float bubbleSpacing = scaledBubbleSpacing_;
2590     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
2591     float radius = borderRadius_.ConvertToPx();
2592     arrowPosition = bottomPosition + OffsetF(radius + arrowHalfWidth, -bubbleSpacing);
2593     if (bCaretMode_) {
2594         arrowPosition = OffsetF(targetOffset_.GetX(), targetOffset_.GetY() + targetSize_.Height() + bubbleSpacing);
2595     }
2596     return bottomPosition;
2597 }
2598 
GetPositionWithPlacementBottomLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)2599 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementBottomLeft(
2600     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
2601 {
2602     OffsetF childPosition;
2603     float marginRight = 0.0f;
2604     float marginTop = 0.0f;
2605     float bubbleSpacing = scaledBubbleSpacing_;
2606     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
2607     float radius = borderRadius_.ConvertToPx();
2608     childPosition = OffsetF(targetOffset_.GetX() - marginRight,
2609         targetOffset_.GetY() + targetSize_.Height() + targetSpace_.ConvertToPx() + bubbleSpacing + marginTop);
2610     arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, -bubbleSpacing);
2611     return childPosition;
2612 }
2613 
GetPositionWithPlacementBottomRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)2614 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementBottomRight(
2615     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
2616 {
2617     OffsetF childPosition;
2618     float marginTop = 0.0f;
2619     float marginLeft = 0.0f;
2620     float bubbleSpacing = scaledBubbleSpacing_;
2621     float radius = borderRadius_.ConvertToPx();
2622     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
2623     childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
2624         targetOffset_.GetY() + targetSize_.Height() + targetSpace_.ConvertToPx() + bubbleSpacing + marginTop);
2625     arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, -bubbleSpacing);
2626     return childPosition;
2627 }
2628 
GetPositionWithPlacementLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)2629 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementLeft(
2630     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
2631 {
2632     OffsetF childPosition;
2633     float marginRight = 0.0f;
2634     float bubbleSpacing = scaledBubbleSpacing_;
2635     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
2636     float radius = borderRadius_.ConvertToPx();
2637     childPosition =
2638         OffsetF(targetOffset_.GetX() - targetSpace_.ConvertToPx() - bubbleSpacing - childSize.Width() - marginRight,
2639             targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
2640     arrowPosition = childPosition + OffsetF(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
2641     return childPosition;
2642 }
2643 
GetPositionWithPlacementLeftTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)2644 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementLeftTop(
2645     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
2646 {
2647     OffsetF childPosition;
2648     float marginRight = 0.0f;
2649     float marginBottom = 0.0f;
2650     float bubbleSpacing = scaledBubbleSpacing_;
2651     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
2652     float radius = borderRadius_.ConvertToPx();
2653     if (resetTipsSize_) {
2654         float offsetY =
2655             (isHalfFoldHover_ ? wrapperRect_.Bottom() : (wrapperSize_.Height() - marginBottom_)) - childSize.Height();
2656         if (GreatNotEqual(offsetY, targetOffset_.GetY())) {
2657             offsetY = targetOffset_.GetY();
2658         }
2659         childPosition =
2660             OffsetF(targetOffset_.GetX() - targetSpace_.ConvertToPx() - bubbleSpacing - childSize.Width() - marginRight,
2661                 offsetY);
2662     } else {
2663         childPosition =
2664             OffsetF(targetOffset_.GetX() - targetSpace_.ConvertToPx() - bubbleSpacing - childSize.Width() - marginRight,
2665                 targetOffset_.GetY() - marginBottom);
2666     }
2667     arrowPosition = childPosition + OffsetF(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
2668     return childPosition;
2669 }
2670 
GetPositionWithPlacementLeftBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)2671 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementLeftBottom(
2672     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
2673 {
2674     OffsetF childPosition;
2675     float marginRight = 0.0f;
2676     float marginTop = 0.0f;
2677     float bubbleSpacing = scaledBubbleSpacing_;
2678     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
2679     float radius = borderRadius_.ConvertToPx();
2680     childPosition =
2681         OffsetF(targetOffset_.GetX() - targetSpace_.ConvertToPx() - bubbleSpacing - childSize.Width() - marginRight,
2682             targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
2683     arrowPosition = childPosition + OffsetF(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
2684     return childPosition;
2685 }
2686 
GetPositionWithPlacementRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)2687 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementRight(
2688     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
2689 {
2690     OffsetF childPosition;
2691     float marginLeft = 0.0f;
2692     float bubbleSpacing = scaledBubbleSpacing_;
2693     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
2694     float radius = borderRadius_.ConvertToPx();
2695     childPosition =
2696         OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_.ConvertToPx() + bubbleSpacing + marginLeft,
2697             targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
2698     arrowPosition = childPosition + OffsetF(-bubbleSpacing, radius + arrowHalfWidth);
2699     return childPosition;
2700 }
2701 
GetPositionWithPlacementRightTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)2702 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementRightTop(
2703     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
2704 {
2705     OffsetF childPosition;
2706     float marginBottom = 0.0f;
2707     float marginLeft = 0.0f;
2708     float bubbleSpacing = scaledBubbleSpacing_;
2709     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
2710     float radius = borderRadius_.ConvertToPx();
2711     if (resetTipsSize_) {
2712         float offsetY =
2713             (isHalfFoldHover_ ? wrapperRect_.Bottom() : (wrapperSize_.Height() - marginBottom_)) - childSize.Height();
2714         if (GreatNotEqual(offsetY, targetOffset_.GetY())) {
2715             offsetY = targetOffset_.GetY();
2716         }
2717         childPosition = OffsetF(
2718             targetOffset_.GetX() + targetSize_.Width() + targetSpace_.ConvertToPx() + bubbleSpacing + marginLeft,
2719             offsetY);
2720     } else {
2721         childPosition = OffsetF(
2722             targetOffset_.GetX() + targetSize_.Width() + targetSpace_.ConvertToPx() + bubbleSpacing + marginLeft,
2723             targetOffset_.GetY() - marginBottom);
2724     }
2725     arrowPosition = childPosition + OffsetF(-bubbleSpacing, radius + arrowHalfWidth);
2726     return childPosition;
2727 }
2728 
GetPositionWithPlacementRightBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition,OffsetF & arrowPosition)2729 OffsetF BubbleLayoutAlgorithm::GetPositionWithPlacementRightBottom(
2730     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition, OffsetF& arrowPosition)
2731 {
2732     OffsetF childPosition;
2733     float marginTop = 0.0f;
2734     float marginLeft = 0.0f;
2735     float bubbleSpacing = scaledBubbleSpacing_;
2736     float arrowHalfWidth = BUBBLE_ARROW_WIDTH.ConvertToPx() / BUBBLE_ARROW_HALF;
2737     float radius = borderRadius_.ConvertToPx();
2738     childPosition =
2739         OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_.ConvertToPx() + bubbleSpacing + marginLeft,
2740             targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
2741     arrowPosition = childPosition + OffsetF(-bubbleSpacing, radius + arrowHalfWidth);
2742     return childPosition;
2743 }
2744 
MoveTo(double x,double y)2745 std::string BubbleLayoutAlgorithm::MoveTo(double x, double y)
2746 {
2747     return "M" + std::to_string(x) + " " + std::to_string(y) + " ";
2748 }
2749 
LineTo(double x,double y)2750 std::string BubbleLayoutAlgorithm::LineTo(double x, double y)
2751 {
2752     return "L" + std::to_string(x) + " " + std::to_string(y) + " ";
2753 }
2754 
ArcTo(double rx,double ry,double rotation,int32_t arc_flag,double x,double y)2755 std::string BubbleLayoutAlgorithm::ArcTo(double rx, double ry, double rotation, int32_t arc_flag, double x, double y)
2756 {
2757     int32_t sweep_flag = 1;
2758     return "A" + std::to_string(rx) + " " + std::to_string(ry) + " " + std::to_string(rotation) + " " +
2759            std::to_string(arc_flag) + " " + std::to_string(sweep_flag) + " " + std::to_string(x) + " " +
2760            std::to_string(y) + " ";
2761 }
2762 
UpdateClipOffset(const RefPtr<FrameNode> & frameNode)2763 void BubbleLayoutAlgorithm::UpdateClipOffset(const RefPtr<FrameNode>& frameNode)
2764 {
2765     auto paintProperty = frameNode->GetPaintProperty<BubbleRenderProperty>();
2766     auto childNode = AceType::DynamicCast<FrameNode>(frameNode->GetFirstChild());
2767     if (!bCaretMode_) {
2768         arrowPosition_ =
2769             OffsetF(BUBBLE_ARROW_HEIGHT.ConvertToPx() * DOUBLE, BUBBLE_ARROW_HEIGHT.ConvertToPx() * DOUBLE);
2770         UpdateArrowOffset(paintProperty->GetArrowOffset(), arrowPlacement_);
2771     } else {
2772         arrowPosition_ = arrowPosition_ - childOffset_ + OffsetF(BUBBLE_ARROW_HEIGHT.ConvertToPx(), 0.0f);
2773     }
2774     targetOffset_ = targetOffset_ - childOffset_;
2775     childOffset_ = OffsetF(BUBBLE_ARROW_HEIGHT.ConvertToPx(), BUBBLE_ARROW_HEIGHT.ConvertToPx());
2776     clipFrameNode_ = childNode;
2777     clipPath_.clear();
2778     clipPath_ = ClipBubbleWithPath();
2779 }
2780 
ClipBubbleWithPath()2781 std::string BubbleLayoutAlgorithm::ClipBubbleWithPath()
2782 {
2783     std::string path;
2784     float arrowOffset = 0.0;
2785     if (!bCaretMode_) {
2786         arrowOffset = GetArrowOffset(arrowPlacement_);
2787     }
2788     float radiusPx = borderRadius_.ConvertToPx();
2789     Placement arrowBuildplacement = Placement::NONE;
2790     if (enableArrow_ && showArrow_) {
2791         GetArrowBuildPlacement(arrowBuildplacement);
2792         arrowBuildPlacement_ = arrowBuildplacement;
2793     }
2794     if (doubleBorderEnable_) {
2795         arrowOffset = std::round(arrowOffset);
2796     }
2797     if ((arrowBuildplacement == Placement::TOP_LEFT) || (arrowBuildplacement == Placement::LEFT_TOP)) {
2798         path += MoveTo(childOffset_.GetX(), childOffset_.GetY());
2799     } else {
2800         path += MoveTo(childOffset_.GetX() + radiusPx, childOffset_.GetY());
2801     }
2802     path += BuildTopLinePath(arrowOffset, radiusPx, arrowBuildplacement);
2803     if ((arrowBuildplacement != Placement::TOP_RIGHT) && (arrowBuildplacement != Placement::RIGHT_TOP)) {
2804         path += BuildCornerPath(Placement::TOP_RIGHT, radiusPx);
2805     }
2806     path += BuildRightLinePath(arrowOffset, radiusPx, arrowBuildplacement);
2807     if ((arrowBuildplacement != Placement::RIGHT_BOTTOM) && (arrowBuildplacement != Placement::BOTTOM_RIGHT)) {
2808         path += BuildCornerPath(Placement::BOTTOM_RIGHT, radiusPx);
2809     }
2810     path += BuildBottomLinePath(arrowOffset, radiusPx, arrowBuildplacement);
2811     if ((arrowBuildplacement != Placement::BOTTOM_LEFT) && (arrowBuildplacement != Placement::LEFT_BOTTOM)) {
2812         path += BuildCornerPath(Placement::BOTTOM_LEFT, radiusPx);
2813     }
2814     path += BuildLeftLinePath(arrowOffset, radiusPx, arrowBuildplacement);
2815     if ((arrowBuildplacement != Placement::LEFT_TOP) && (arrowBuildplacement != Placement::TOP_LEFT)) {
2816         path += BuildCornerPath(Placement::TOP_LEFT, radiusPx);
2817     }
2818     return path + "Z";
2819 }
2820 
GetArrowOffset(const Placement & placement)2821 float BubbleLayoutAlgorithm::GetArrowOffset(const Placement& placement)
2822 {
2823     Edge edge;
2824     double arrowOffset;
2825     double maxMotionRange = 0.0;
2826     double minMotionRange = 0.0;
2827     double targetOffsetXOrY = 0.0;
2828     double childOffsetsetXOrY = 0.0;
2829     double childSizeWidthOrHeight = 0.0;
2830     double targetSizeWidthOrHeight = 0.0;
2831     bool bHorizontal = false;
2832 
2833     InitEdgeSize(edge);
2834     switch (placement) {
2835         case Placement::TOP:
2836         case Placement::TOP_LEFT:
2837         case Placement::TOP_RIGHT:
2838             bHorizontal = true;
2839             break;
2840         case Placement::BOTTOM:
2841         case Placement::BOTTOM_LEFT:
2842         case Placement::BOTTOM_RIGHT:
2843             bHorizontal = true;
2844             break;
2845         case Placement::LEFT:
2846         case Placement::LEFT_TOP:
2847         case Placement::LEFT_BOTTOM:
2848             break;
2849         case Placement::RIGHT:
2850         case Placement::RIGHT_TOP:
2851         case Placement::RIGHT_BOTTOM:
2852             break;
2853         default:
2854             break;
2855     }
2856     if (bHorizontal) {
2857         targetOffsetXOrY = targetOffset_.GetX();
2858         targetSizeWidthOrHeight = targetSize_.Width();
2859         childOffsetsetXOrY = childOffset_.GetX();
2860         childSizeWidthOrHeight = childSize_.Width();
2861     } else {
2862         targetOffsetXOrY = targetOffset_.GetY();
2863         targetSizeWidthOrHeight = targetSize_.Height();
2864         childOffsetsetXOrY = childOffset_.GetY();
2865         childSizeWidthOrHeight = childSize_.Height();
2866     }
2867     maxMotionRange = childSizeWidthOrHeight;
2868     if (arrowOfTargetOffset_ == ArrowOfTargetOffset::START) {
2869         arrowOffset = (targetOffsetXOrY + (arrowOffset_.Value() * targetSizeWidthOrHeight)) - childOffsetsetXOrY +
2870                       BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
2871         return arrowOffset;
2872     }
2873     if (arrowOfTargetOffset_ == ArrowOfTargetOffset::CENTER) {
2874         arrowOffset = (targetOffsetXOrY + (arrowOffset_.Value() * targetSizeWidthOrHeight)) - childOffsetsetXOrY;
2875         return arrowOffset;
2876     }
2877     if (arrowOfTargetOffset_ == ArrowOfTargetOffset::END) {
2878         arrowOffset = (targetOffsetXOrY + (arrowOffset_.Value() * targetSizeWidthOrHeight)) - childOffsetsetXOrY -
2879                       BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
2880         return arrowOffset;
2881     }
2882     return std::clamp(arrowOffset_.Unit() == DimensionUnit::PERCENT ? arrowOffset_.Value() * maxMotionRange
2883                                                                     : arrowOffset_.ConvertToPx(),
2884         minMotionRange, maxMotionRange);
2885 }
2886 
UpdateArrowOffset(const std::optional<Dimension> & offset,const Placement & placement)2887 void BubbleLayoutAlgorithm::UpdateArrowOffset(const std::optional<Dimension>& offset, const Placement& placement)
2888 {
2889     if (offset.has_value()) {
2890         arrowOffset_ = offset.value();
2891         arrowOfTargetOffset_ = ArrowOfTargetOffset::NONE;
2892         if (arrowOffset_.Unit() == DimensionUnit::PERCENT) {
2893             if (arrowOffset_.Value() == ARROW_OFFSET_START_VALUE) {
2894                 arrowOfTargetOffset_ = ArrowOfTargetOffset::START;
2895             } else if (arrowOffset_.Value() == ARROW_OFFSET_CENTER_VALUE) {
2896                 arrowOfTargetOffset_ = ArrowOfTargetOffset::CENTER;
2897             } else {
2898                 arrowOfTargetOffset_ = ArrowOfTargetOffset::END;
2899             }
2900             arrowOffset_.SetValue(std::clamp(arrowOffset_.Value(), 0.0, 1.0));
2901         }
2902         return;
2903     }
2904     arrowOfTargetOffset_ = ArrowOfTargetOffset::NONE;
2905     switch (placement) {
2906         case Placement::LEFT:
2907         case Placement::RIGHT:
2908         case Placement::TOP:
2909         case Placement::BOTTOM:
2910             arrowOffset_ = BUBBLE_ARROW_HALF_PERCENT_VALUE;
2911             arrowOfTargetOffset_ = ArrowOfTargetOffset::CENTER;
2912             break;
2913         case Placement::TOP_LEFT:
2914         case Placement::BOTTOM_LEFT:
2915         case Placement::LEFT_TOP:
2916         case Placement::RIGHT_TOP:
2917             arrowOffset_ = BUBBLE_ARROW_ZERO_PERCENT_VALUE;
2918             arrowOfTargetOffset_ = ArrowOfTargetOffset::START;
2919             break;
2920         case Placement::TOP_RIGHT:
2921         case Placement::BOTTOM_RIGHT:
2922         case Placement::LEFT_BOTTOM:
2923         case Placement::RIGHT_BOTTOM:
2924             arrowOffset_ = BUBBLE_ARROW_ONE_HUNDRED_PERCENT_VALUE;
2925             arrowOfTargetOffset_ = ArrowOfTargetOffset::END;
2926             break;
2927         default:
2928             break;
2929     }
2930 }
2931 
InitEdgeSize(Edge & edge)2932 void BubbleLayoutAlgorithm::InitEdgeSize(Edge& edge)
2933 {
2934     edge.SetTop(Dimension(std::max(padding_.Left().ConvertToPx(), border_.TopLeftRadius().GetX().ConvertToPx()) +
2935                           std::max(padding_.Right().ConvertToPx(), border_.TopRightRadius().GetX().ConvertToPx())));
2936     edge.SetBottom(
2937         Dimension(std::max(padding_.Left().ConvertToPx(), border_.BottomLeftRadius().GetX().ConvertToPx()) +
2938                   std::max(padding_.Right().ConvertToPx(), border_.BottomRightRadius().GetX().ConvertToPx())));
2939     edge.SetLeft(
2940         Dimension(std::max(padding_.Top().ConvertToPx(), border_.TopRightRadius().GetY().ConvertToPx()) +
2941                   std::max(padding_.Bottom().ConvertToPx(), border_.BottomRightRadius().GetY().ConvertToPx())));
2942     edge.SetRight(
2943         Dimension(std::max(padding_.Top().ConvertToPx(), border_.TopLeftRadius().GetY().ConvertToPx()) +
2944                   std::max(padding_.Bottom().ConvertToPx(), border_.BottomLeftRadius().GetY().ConvertToPx())));
2945 }
2946 
ModifyBorderRadius(float borderRadius,float halfChildHeight)2947 float BubbleLayoutAlgorithm::ModifyBorderRadius(float borderRadius, float halfChildHeight)
2948 {
2949     return GreatOrEqual(borderRadius, halfChildHeight) ? halfChildHeight : borderRadius;
2950 }
2951 
GetArrowBuildPlacement(Placement & arrowBuildplacement)2952 void BubbleLayoutAlgorithm::GetArrowBuildPlacement(Placement& arrowBuildplacement)
2953 {
2954     auto radius = borderRadius_.ConvertToPx();
2955     float maxOffset = 0.0;
2956     switch (arrowPlacement_) {
2957         case Placement::BOTTOM:
2958         case Placement::BOTTOM_LEFT:
2959         case Placement::BOTTOM_RIGHT: // TOP
2960             maxOffset = childOffset_.GetX() + childSize_.Width() - radius -
2961                         BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
2962             if ((!bCaretMode_) && (arrowOffset_.Unit() != DimensionUnit::PERCENT)) {
2963                 if ((arrowPosition_.GetX() + arrowOffset_.ConvertToPx()) > maxOffset) {
2964                     arrowBuildplacement = Placement::TOP_RIGHT;
2965                     break;
2966                 }
2967                 if ((arrowOffset_.ConvertToPx()) < radius) {
2968                     arrowBuildplacement = Placement::TOP_LEFT;
2969                     break;
2970                 }
2971                 arrowBuildplacement = Placement::TOP;
2972             } else {
2973                 arrowBuildplacement = Placement::TOP;
2974             }
2975             break;
2976         case Placement::LEFT:
2977         case Placement::LEFT_TOP:
2978         case Placement::LEFT_BOTTOM: // Right
2979             maxOffset = childOffset_.GetY() + childSize_.Height() - radius -
2980                         BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
2981             if ((!bCaretMode_) && (arrowOffset_.Unit() != DimensionUnit::PERCENT)) {
2982                 if ((arrowPosition_.GetY() + arrowOffset_.ConvertToPx()) > maxOffset) {
2983                     arrowBuildplacement = Placement::RIGHT_BOTTOM;
2984                     break;
2985                 }
2986                 if ((arrowOffset_.ConvertToPx()) < radius) {
2987                     arrowBuildplacement = Placement::RIGHT_TOP;
2988                     break;
2989                 }
2990                 arrowBuildplacement = Placement::RIGHT;
2991             } else {
2992                 arrowBuildplacement = Placement::RIGHT;
2993             }
2994             break;
2995         case Placement::TOP:
2996         case Placement::TOP_LEFT:
2997         case Placement::TOP_RIGHT: // Bottom
2998             maxOffset = childOffset_.GetX() + childSize_.Width() - radius -
2999                         BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
3000             if ((!bCaretMode_) && (arrowOffset_.Unit() != DimensionUnit::PERCENT)) {
3001                 if ((arrowPosition_.GetX() + arrowOffset_.ConvertToPx()) > maxOffset) {
3002                     arrowBuildplacement = Placement::BOTTOM_RIGHT; // replace
3003                     break;
3004                 }
3005                 if ((arrowOffset_.ConvertToPx()) < radius) {
3006                     arrowBuildplacement = Placement::BOTTOM_LEFT; // replace
3007                     break;
3008                 }
3009                 arrowBuildplacement = Placement::BOTTOM; // nomal
3010             } else {
3011                 arrowBuildplacement = Placement::BOTTOM; // nomal
3012             }
3013             break;
3014         case Placement::RIGHT:
3015         case Placement::RIGHT_TOP:
3016         case Placement::RIGHT_BOTTOM: // Left
3017             maxOffset = childOffset_.GetY() + childSize_.Height() - radius -
3018                         BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
3019             if ((!bCaretMode_) && (arrowOffset_.Unit() != DimensionUnit::PERCENT)) {
3020                 if ((arrowPosition_.GetY() + arrowOffset_.ConvertToPx()) > maxOffset) {
3021                     arrowBuildplacement = Placement::LEFT_BOTTOM;
3022                     break;
3023                 }
3024                 if ((arrowOffset_.ConvertToPx()) < radius) {
3025                     arrowBuildplacement = Placement::LEFT_TOP;
3026                     break;
3027                 }
3028                 arrowBuildplacement = Placement::LEFT;
3029             } else {
3030                 arrowBuildplacement = Placement::LEFT;
3031             }
3032             break;
3033         default:
3034             break;
3035     }
3036     if (arrowBuildplacement > Placement::BOTTOM) {
3037         arrowPosition_ += OffsetF(-BUBBLE_ARROW_HEIGHT.ConvertToPx(), -BUBBLE_ARROW_HEIGHT.ConvertToPx());
3038     }
3039 }
3040 
SetArrowOffsetsFromClip(const int16_t index,const float offsetX,const float offsetY)3041 void BubbleLayoutAlgorithm::SetArrowOffsetsFromClip(const int16_t index, const float offsetX, const float offsetY)
3042 {
3043     arrowOffsetsFromClip_[index] = { offsetX, offsetY };
3044 }
3045 
BuildTopLinePath(float arrowOffset,float radius,Placement & arrowBuildplacement)3046 std::string BubbleLayoutAlgorithm::BuildTopLinePath(float arrowOffset, float radius, Placement& arrowBuildplacement)
3047 {
3048     std::string path;
3049     float childOffsetY = childOffset_.GetY();
3050     auto leftOffset =
3051         childOffset_.GetX() + radius + BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
3052     auto rightOffset = childOffset_.GetX() + childSize_.Width() - radius -
3053                        BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
3054     auto arrowTopOffset = std::clamp(
3055         arrowPosition_.GetX() + arrowOffset, static_cast<float>(leftOffset), static_cast<float>(rightOffset));
3056     switch (arrowPlacement_) {
3057         case Placement::BOTTOM:
3058         case Placement::BOTTOM_LEFT:
3059         case Placement::BOTTOM_RIGHT:
3060             if (arrowBuildplacement == Placement::TOP_RIGHT) {
3061                 path += ReplaceArrowTopRight(
3062                     arrowPosition_.GetX() + childSize_.Width() - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF, childOffsetY);
3063             }
3064             if (arrowBuildplacement == Placement::TOP_LEFT) {
3065                 path +=
3066                     ReplaceArrowTopLeft(arrowPosition_.GetX() + BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF, childOffsetY);
3067             }
3068             if (arrowBuildplacement == Placement::TOP) {
3069                 path += LineTo(arrowTopOffset - ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffsetY); // P1
3070                 path += LineTo(arrowTopOffset - ARROW_VERTICAL_P2_OFFSET_X.ConvertToPx(),
3071                     childOffsetY - ARROW_VERTICAL_P2_OFFSET_Y.ConvertToPx()); // P2
3072                 path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
3073                     arrowTopOffset + ARROW_VERTICAL_P4_OFFSET_X.ConvertToPx(),
3074                     childOffsetY - ARROW_VERTICAL_P4_OFFSET_Y.ConvertToPx());                            // P4
3075                 path += LineTo(arrowTopOffset + ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffsetY); // P5
3076                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
3077                     arrowTopOffset - ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffsetY);
3078                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
3079                     arrowTopOffset - ARROW_VERTICAL_P2_OFFSET_X.ConvertToPx(),
3080                     childOffsetY - ARROW_VERTICAL_P2_OFFSET_Y.ConvertToPx());
3081                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
3082                     arrowTopOffset + ARROW_VERTICAL_P4_OFFSET_X.ConvertToPx(),
3083                     childOffsetY - ARROW_VERTICAL_P4_OFFSET_Y.ConvertToPx());
3084                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
3085                     arrowTopOffset + ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffsetY);
3086             }
3087             break;
3088         default:
3089             break;
3090     }
3091     if (arrowBuildplacement != Placement::TOP_RIGHT) {
3092         path += LineTo(childOffset_.GetX() + childSize_.Width() - radius, childOffsetY);
3093     }
3094     return path;
3095 }
3096 
BuildRightLinePath(float arrowOffset,float radius,Placement & arrowBuildplacement)3097 std::string BubbleLayoutAlgorithm::BuildRightLinePath(float arrowOffset, float radius, Placement& arrowBuildplacement)
3098 {
3099     std::string path;
3100     float childOffsetY = childOffset_.GetY();
3101     auto topOffset = childOffset_.GetY() + radius + BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
3102     auto bottomOffset = childOffset_.GetY() + childSize_.Height() - radius - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
3103     auto arrowRightOffset = std::clamp(
3104         arrowPosition_.GetY() + arrowOffset, static_cast<float>(topOffset), static_cast<float>(bottomOffset));
3105     switch (arrowPlacement_) {
3106         case Placement::LEFT:
3107         case Placement::LEFT_TOP:
3108         case Placement::LEFT_BOTTOM:
3109             if (arrowBuildplacement == Placement::RIGHT_BOTTOM) {
3110                 path += ReplaceArrowRightBottom(arrowPosition_.GetY() + childSize_.Height()
3111                     - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF, childOffset_.GetX() + childSize_.Width());
3112             }
3113             if (arrowBuildplacement == Placement::RIGHT_TOP) {
3114                 path += ReplaceArrowRightTop(arrowPosition_.GetY() + BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF,
3115                     childOffset_.GetX() + childSize_.Width());
3116             }
3117             if (arrowBuildplacement == Placement::RIGHT) {
3118                 path += LineTo(childOffset_.GetX() + childSize_.Width(),
3119                     arrowRightOffset - ARROW_HORIZON_P1_OFFSET_Y.ConvertToPx()); // P1
3120                 path += LineTo(childOffset_.GetX() + childSize_.Width() + ARROW_HORIZON_P2_OFFSET_X.ConvertToPx(),
3121                     arrowRightOffset - ARROW_HORIZON_P2_OFFSET_Y.ConvertToPx()); // P2
3122                 path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
3123                     childOffset_.GetX() + childSize_.Width() + ARROW_HORIZON_P4_OFFSET_X.ConvertToPx(),
3124                     arrowRightOffset + ARROW_HORIZON_P4_OFFSET_Y.ConvertToPx()); // P4
3125                 path += LineTo(childOffset_.GetX() + childSize_.Width(),
3126                     arrowRightOffset + ARROW_HORIZON_P5_OFFSET_Y.ConvertToPx()); // P5
3127                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO, childOffset_.GetX() + childSize_.Width(),
3128                     arrowRightOffset - ARROW_HORIZON_P1_OFFSET_Y.ConvertToPx());
3129                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
3130                     childOffset_.GetX() + childSize_.Width() + ARROW_HORIZON_P2_OFFSET_X.ConvertToPx(),
3131                     arrowRightOffset - ARROW_HORIZON_P2_OFFSET_Y.ConvertToPx());
3132                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
3133                     childOffset_.GetX() + childSize_.Width() + ARROW_HORIZON_P4_OFFSET_X.ConvertToPx(),
3134                     arrowRightOffset + ARROW_HORIZON_P4_OFFSET_Y.ConvertToPx());
3135                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE, childOffset_.GetX() + childSize_.Width(),
3136                     arrowRightOffset + ARROW_HORIZON_P5_OFFSET_Y.ConvertToPx());
3137             }
3138             break;
3139         default:
3140             break;
3141     }
3142     if (arrowBuildplacement != Placement::RIGHT_BOTTOM) {
3143         path += LineTo(childOffset_.GetX() + childSize_.Width(), childOffsetY + childSize_.Height() - radius);
3144     }
3145     return path;
3146 }
3147 
BuildBottomLinePath(float arrowOffset,float radius,Placement & arrowBuildplacement)3148 std::string BubbleLayoutAlgorithm::BuildBottomLinePath(float arrowOffset, float radius, Placement& arrowBuildplacement)
3149 {
3150     std::string path;
3151     float childOffsetY = childOffset_.GetY();
3152     auto leftOffset = childOffset_.GetX() + radius + BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
3153     auto rightOffset = childOffset_.GetX() + childSize_.Width() - radius - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
3154     auto arrowBottomOffset = std::clamp(
3155         arrowPosition_.GetX() + arrowOffset, static_cast<float>(leftOffset), static_cast<float>(rightOffset));
3156     switch (arrowPlacement_) {
3157         case Placement::TOP:
3158         case Placement::TOP_LEFT:
3159         case Placement::TOP_RIGHT:
3160             if (arrowBuildplacement == Placement::BOTTOM_RIGHT) {
3161                 path += ReplaceArrowBottomRight(arrowPosition_.GetX() + childSize_.Width()
3162                     - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF, childOffsetY + childSize_.Height());
3163             }
3164             if (arrowBuildplacement == Placement::BOTTOM_LEFT) {
3165                 path += ReplaceArrowBottomLeft(arrowPosition_.GetX() + BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF,
3166                     childOffsetY + childSize_.Height());
3167             }
3168             if (arrowBuildplacement == Placement::BOTTOM) {
3169                 path += LineTo(arrowBottomOffset + ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(),
3170                     childOffsetY + childSize_.Height()); // P1
3171                 path += LineTo(arrowBottomOffset + ARROW_VERTICAL_P2_OFFSET_X.ConvertToPx(),
3172                     childOffsetY + childSize_.Height() + ARROW_VERTICAL_P2_OFFSET_Y.ConvertToPx()); // P2
3173                 path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
3174                     arrowBottomOffset - ARROW_VERTICAL_P4_OFFSET_X.ConvertToPx(),
3175                     childOffsetY + childSize_.Height() + ARROW_VERTICAL_P4_OFFSET_Y.ConvertToPx()); // P4
3176                 path += LineTo(arrowBottomOffset - ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx(),
3177                     childOffsetY + childSize_.Height()); // P5
3178                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
3179                     arrowBottomOffset + ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffsetY + childSize_.Height());
3180                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
3181                     arrowBottomOffset + ARROW_VERTICAL_P2_OFFSET_X.ConvertToPx(),
3182                     childOffsetY + childSize_.Height() + ARROW_VERTICAL_P2_OFFSET_Y.ConvertToPx());
3183                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
3184                     arrowBottomOffset - ARROW_VERTICAL_P4_OFFSET_X.ConvertToPx(),
3185                     childOffsetY + childSize_.Height() + ARROW_VERTICAL_P4_OFFSET_Y.ConvertToPx());
3186                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE, arrowBottomOffset
3187                     - ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffsetY + childSize_.Height());
3188             }
3189             break;
3190         default:
3191             break;
3192     }
3193     if (arrowBuildplacement != Placement::BOTTOM_LEFT) {
3194         path += LineTo(childOffset_.GetX() + radius, childOffsetY + childSize_.Height());
3195     }
3196     return path;
3197 }
3198 
BuildLeftLinePath(float arrowOffset,float radius,Placement & arrowBuildplacement)3199 std::string BubbleLayoutAlgorithm::BuildLeftLinePath(float arrowOffset, float radius, Placement& arrowBuildplacement)
3200 {
3201     std::string path;
3202     float childOffsetY = childOffset_.GetY();
3203     auto topOffset =
3204         childOffset_.GetY() + radius + BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
3205     auto bottomOffset = childOffset_.GetY() + childSize_.Height() - radius -
3206                         BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
3207     auto arrowLeftOffset = std::clamp(
3208         arrowPosition_.GetY() + arrowOffset, static_cast<float>(topOffset), static_cast<float>(bottomOffset));
3209     switch (arrowPlacement_) {
3210         case Placement::RIGHT:
3211         case Placement::RIGHT_TOP:
3212         case Placement::RIGHT_BOTTOM:
3213             if (arrowBuildplacement == Placement::LEFT_BOTTOM) {
3214                 path += ReplaceArrowLeftBottom(
3215                     arrowPosition_.GetY() + childSize_.Height() - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF,
3216                     childOffset_.GetX());
3217             }
3218             if (arrowBuildplacement == Placement::LEFT_TOP) {
3219                 path += ReplaceArrowLeftTop(
3220                     arrowPosition_.GetY() + BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF, childOffset_.GetX());
3221             }
3222             if (arrowBuildplacement == Placement::LEFT) {
3223                 path += LineTo(childOffset_.GetX(), arrowLeftOffset + ARROW_HORIZON_P1_OFFSET_Y.ConvertToPx()); // P1
3224                 path += LineTo(childOffset_.GetX() - ARROW_HORIZON_P2_OFFSET_X.ConvertToPx(),
3225                     arrowLeftOffset + ARROW_HORIZON_P2_OFFSET_Y.ConvertToPx()); // P2
3226                 path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
3227                     childOffset_.GetX() - ARROW_HORIZON_P4_OFFSET_X.ConvertToPx(),
3228                     arrowLeftOffset - ARROW_HORIZON_P4_OFFSET_Y.ConvertToPx());                                 // P4
3229                 path += LineTo(childOffset_.GetX(), arrowLeftOffset - ARROW_HORIZON_P5_OFFSET_Y.ConvertToPx()); // P5
3230                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
3231                     childOffset_.GetX(), arrowLeftOffset + ARROW_HORIZON_P1_OFFSET_Y.ConvertToPx());
3232                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
3233                     childOffset_.GetX() - ARROW_HORIZON_P2_OFFSET_X.ConvertToPx(),
3234                     arrowLeftOffset + ARROW_HORIZON_P2_OFFSET_Y.ConvertToPx());
3235                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
3236                     childOffset_.GetX() - ARROW_HORIZON_P4_OFFSET_X.ConvertToPx(),
3237                     arrowLeftOffset - ARROW_HORIZON_P4_OFFSET_Y.ConvertToPx());
3238                 SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
3239                     childOffset_.GetX(), arrowLeftOffset - ARROW_HORIZON_P5_OFFSET_Y.ConvertToPx());
3240             }
3241             break;
3242         default:
3243             break;
3244     }
3245     if (arrowBuildplacement != Placement::LEFT_TOP) {
3246         path += LineTo(childOffset_.GetX(), childOffsetY + radius);
3247     }
3248     return path;
3249 }
3250 
ReplaceArrowTopLeft(const float arrowOffset,const float childOffset)3251 std::string BubbleLayoutAlgorithm::ReplaceArrowTopLeft(const float arrowOffset, const float childOffset)
3252 {
3253     std::string path;
3254     path += LineTo(arrowOffset - ARROW_REPLACE_START_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffset); // P1
3255     path += LineTo(arrowOffset - ARROW_REPLACE_START_VERTICAL_P2_OFFSET_X.ConvertToPx(),
3256         childOffset - ARROW_REPLACE_START_VERTICAL_P2_OFFSET_Y.ConvertToPx()); // P2
3257     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
3258         arrowOffset - ARROW_REPLACE_START_VERTICAL_P4_OFFSET_X.ConvertToPx(),
3259         childOffset - ARROW_REPLACE_START_VERTICAL_P4_OFFSET_Y.ConvertToPx());                         // P4
3260     path += LineTo(arrowOffset + ARROW_REPLACE_START_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffset); // P5
3261     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
3262         arrowOffset - ARROW_REPLACE_START_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffset);
3263     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
3264         arrowOffset - ARROW_REPLACE_START_VERTICAL_P2_OFFSET_X.ConvertToPx(),
3265         childOffset - ARROW_REPLACE_START_VERTICAL_P2_OFFSET_Y.ConvertToPx());
3266     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
3267         arrowOffset - ARROW_REPLACE_START_VERTICAL_P4_OFFSET_X.ConvertToPx(),
3268         childOffset - ARROW_REPLACE_START_VERTICAL_P4_OFFSET_Y.ConvertToPx());
3269     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
3270         arrowOffset + ARROW_REPLACE_START_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffset);
3271     return path;
3272 }
3273 
ReplaceArrowTopRight(const float arrowOffset,const float childOffset)3274 std::string BubbleLayoutAlgorithm::ReplaceArrowTopRight(const float arrowOffset, const float childOffset)
3275 {
3276     std::string path;
3277     path += LineTo((arrowOffset - ARROW_REPLACE_END_VERTICAL_P1_OFFSET_X.ConvertToPx()) / HALF, childOffset); // P1
3278     path += LineTo(arrowOffset - ARROW_REPLACE_END_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffset);          // P1
3279     path += LineTo(arrowOffset + ARROW_REPLACE_END_VERTICAL_P2_OFFSET_X.ConvertToPx(),
3280         childOffset - ARROW_REPLACE_END_VERTICAL_P2_OFFSET_Y.ConvertToPx()); // P2
3281     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
3282         arrowOffset + ARROW_REPLACE_END_VERTICAL_P4_OFFSET_X.ConvertToPx(),
3283         childOffset - ARROW_REPLACE_END_VERTICAL_P4_OFFSET_Y.ConvertToPx());                         // P4
3284     path += LineTo(arrowOffset + ARROW_REPLACE_END_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffset); // P5
3285     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
3286         arrowOffset - ARROW_REPLACE_END_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffset);
3287     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
3288         arrowOffset + ARROW_REPLACE_END_VERTICAL_P2_OFFSET_X.ConvertToPx(),
3289         childOffset - ARROW_REPLACE_END_VERTICAL_P2_OFFSET_Y.ConvertToPx());
3290     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
3291         arrowOffset + ARROW_REPLACE_END_VERTICAL_P4_OFFSET_X.ConvertToPx(),
3292         childOffset - ARROW_REPLACE_END_VERTICAL_P4_OFFSET_Y.ConvertToPx());
3293     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
3294         arrowOffset + ARROW_REPLACE_END_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffset);
3295     return path;
3296 }
3297 
ReplaceArrowRightTop(const float arrowOffset,const float childOffset)3298 std::string BubbleLayoutAlgorithm::ReplaceArrowRightTop(const float arrowOffset, const float childOffset)
3299 {
3300     std::string path;
3301     path += LineTo(childOffset, arrowOffset - ARROW_REPLACE_START_HORIZON_P1_OFFSET_Y.ConvertToPx()); // P1
3302     path += LineTo(childOffset + ARROW_REPLACE_START_HORIZON_P2_OFFSET_X.ConvertToPx(),
3303         arrowOffset - ARROW_REPLACE_START_HORIZON_P2_OFFSET_Y.ConvertToPx()); // P2
3304     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
3305         childOffset + ARROW_REPLACE_START_HORIZON_P4_OFFSET_X.ConvertToPx(),
3306         arrowOffset - ARROW_REPLACE_START_HORIZON_P4_OFFSET_Y.ConvertToPx());                         // P4
3307     path += LineTo(childOffset, arrowOffset + ARROW_REPLACE_START_HORIZON_P5_OFFSET_Y.ConvertToPx()); // P5
3308     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO, childOffset,
3309         arrowOffset - ARROW_REPLACE_START_HORIZON_P1_OFFSET_Y.ConvertToPx());
3310     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
3311         childOffset + ARROW_REPLACE_START_HORIZON_P2_OFFSET_X.ConvertToPx(),
3312         arrowOffset - ARROW_REPLACE_START_HORIZON_P2_OFFSET_Y.ConvertToPx());
3313     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
3314         childOffset + ARROW_REPLACE_START_HORIZON_P4_OFFSET_X.ConvertToPx(),
3315         arrowOffset - ARROW_REPLACE_START_HORIZON_P4_OFFSET_Y.ConvertToPx());
3316     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
3317         childOffset, arrowOffset + ARROW_REPLACE_START_HORIZON_P5_OFFSET_Y.ConvertToPx());
3318     return path;
3319 }
3320 
ReplaceArrowRightBottom(const float arrowOffset,const float childOffset)3321 std::string BubbleLayoutAlgorithm::ReplaceArrowRightBottom(const float arrowOffset, const float childOffset)
3322 {
3323     std::string path;
3324     path += LineTo(childOffset, arrowOffset - ARROW_REPLACE_END_HORIZON_P1_OFFSET_Y.ConvertToPx()); // P1
3325     path += LineTo(childOffset + ARROW_REPLACE_END_HORIZON_P2_OFFSET_X.ConvertToPx(),
3326         arrowOffset + ARROW_REPLACE_END_HORIZON_P2_OFFSET_Y.ConvertToPx()); // P2
3327     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
3328         childOffset + ARROW_REPLACE_END_HORIZON_P4_OFFSET_X.ConvertToPx(),
3329         arrowOffset + ARROW_REPLACE_END_HORIZON_P4_OFFSET_Y.ConvertToPx());                         // P4
3330     path += LineTo(childOffset, arrowOffset + ARROW_REPLACE_END_HORIZON_P5_OFFSET_Y.ConvertToPx()); // P5
3331     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO, childOffset,
3332         arrowOffset - ARROW_REPLACE_END_HORIZON_P1_OFFSET_Y.ConvertToPx());
3333     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
3334         childOffset + ARROW_REPLACE_END_HORIZON_P2_OFFSET_X.ConvertToPx(),
3335         arrowOffset + ARROW_REPLACE_END_HORIZON_P2_OFFSET_Y.ConvertToPx());
3336     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
3337         childOffset + ARROW_REPLACE_END_HORIZON_P4_OFFSET_X.ConvertToPx(),
3338         arrowOffset + ARROW_REPLACE_END_HORIZON_P4_OFFSET_Y.ConvertToPx());
3339     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
3340         childOffset, arrowOffset + ARROW_REPLACE_END_HORIZON_P5_OFFSET_Y.ConvertToPx());
3341     return path;
3342 }
3343 
ReplaceArrowBottomLeft(const float arrowOffset,const float childOffset)3344 std::string BubbleLayoutAlgorithm::ReplaceArrowBottomLeft(const float arrowOffset, const float childOffset)
3345 {
3346     std::string path;
3347     path += LineTo(arrowOffset + ARROW_REPLACE_START_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffset); // P1
3348     path += LineTo(arrowOffset - ARROW_REPLACE_START_VERTICAL_P4_OFFSET_X.ConvertToPx(),
3349         childOffset + ARROW_REPLACE_START_VERTICAL_P4_OFFSET_Y.ConvertToPx()); // P2
3350     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
3351         arrowOffset - ARROW_REPLACE_START_VERTICAL_P2_OFFSET_X.ConvertToPx(),
3352         childOffset + ARROW_REPLACE_START_VERTICAL_P2_OFFSET_Y.ConvertToPx());                         // P4
3353     path += LineTo(arrowOffset - ARROW_REPLACE_START_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffset); // P5
3354     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
3355         arrowOffset + ARROW_REPLACE_START_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffset);
3356     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
3357         arrowOffset - ARROW_REPLACE_START_VERTICAL_P4_OFFSET_X.ConvertToPx(),
3358         childOffset + ARROW_REPLACE_START_VERTICAL_P4_OFFSET_Y.ConvertToPx());
3359     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
3360         arrowOffset - ARROW_REPLACE_START_VERTICAL_P2_OFFSET_X.ConvertToPx(),
3361         childOffset + ARROW_REPLACE_START_VERTICAL_P2_OFFSET_Y.ConvertToPx());
3362     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
3363         arrowOffset - ARROW_REPLACE_START_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffset);
3364     return path;
3365 }
3366 
ReplaceArrowBottomRight(const float arrowOffset,const float childOffset)3367 std::string BubbleLayoutAlgorithm::ReplaceArrowBottomRight(const float arrowOffset, const float childOffset)
3368 {
3369     std::string path;
3370     path += LineTo(arrowOffset + ARROW_REPLACE_END_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffset); // P1
3371     path += LineTo(arrowOffset + ARROW_REPLACE_END_VERTICAL_P4_OFFSET_X.ConvertToPx(),
3372         childOffset + ARROW_REPLACE_END_VERTICAL_P4_OFFSET_Y.ConvertToPx()); // P2
3373     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
3374         arrowOffset + ARROW_REPLACE_END_VERTICAL_P2_OFFSET_X.ConvertToPx(),
3375         childOffset + ARROW_REPLACE_END_VERTICAL_P2_OFFSET_Y.ConvertToPx());                         // P4
3376     path += LineTo(arrowOffset - ARROW_REPLACE_END_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffset); // P5
3377     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
3378         arrowOffset + ARROW_REPLACE_END_VERTICAL_P1_OFFSET_X.ConvertToPx(), childOffset);
3379     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
3380         arrowOffset + ARROW_REPLACE_END_VERTICAL_P4_OFFSET_X.ConvertToPx(),
3381         childOffset + ARROW_REPLACE_END_VERTICAL_P4_OFFSET_Y.ConvertToPx());
3382     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
3383         arrowOffset + ARROW_REPLACE_END_VERTICAL_P2_OFFSET_X.ConvertToPx(),
3384         childOffset + ARROW_REPLACE_END_VERTICAL_P2_OFFSET_Y.ConvertToPx());
3385     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
3386         arrowOffset - ARROW_REPLACE_END_VERTICAL_P5_OFFSET_X.ConvertToPx(), childOffset);
3387     return path;
3388 }
3389 
ReplaceArrowLeftTop(const float arrowOffset,const float childOffset)3390 std::string BubbleLayoutAlgorithm::ReplaceArrowLeftTop(const float arrowOffset, const float childOffset)
3391 {
3392     std::string path;
3393     path += LineTo(childOffset, arrowOffset + ARROW_REPLACE_START_HORIZON_P1_OFFSET_Y.ConvertToPx()); // P1
3394     path += LineTo(childOffset - ARROW_REPLACE_START_HORIZON_P4_OFFSET_X.ConvertToPx(),
3395         arrowOffset - ARROW_REPLACE_START_HORIZON_P4_OFFSET_Y.ConvertToPx()); // P2
3396     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
3397         childOffset - ARROW_REPLACE_START_HORIZON_P2_OFFSET_X.ConvertToPx(),
3398         arrowOffset - ARROW_REPLACE_START_HORIZON_P2_OFFSET_Y.ConvertToPx());                         // P4
3399     path += LineTo(childOffset, arrowOffset - ARROW_REPLACE_START_HORIZON_P5_OFFSET_Y.ConvertToPx()); // P5
3400     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
3401         childOffset, arrowOffset + ARROW_REPLACE_START_HORIZON_P1_OFFSET_Y.ConvertToPx());
3402     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
3403         childOffset - ARROW_REPLACE_START_HORIZON_P4_OFFSET_X.ConvertToPx(),
3404         arrowOffset - ARROW_REPLACE_START_HORIZON_P4_OFFSET_Y.ConvertToPx());
3405     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
3406         childOffset - ARROW_REPLACE_START_HORIZON_P2_OFFSET_X.ConvertToPx(),
3407         arrowOffset - ARROW_REPLACE_START_HORIZON_P2_OFFSET_Y.ConvertToPx());
3408     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
3409         childOffset, arrowOffset - ARROW_REPLACE_START_HORIZON_P5_OFFSET_Y.ConvertToPx());
3410     return path;
3411 }
3412 
ReplaceArrowLeftBottom(const float arrowOffset,const float childOffset)3413 std::string BubbleLayoutAlgorithm::ReplaceArrowLeftBottom(const float arrowOffset, const float childOffset)
3414 {
3415     std::string path;
3416     path += LineTo(childOffset, arrowOffset + ARROW_REPLACE_END_HORIZON_P1_OFFSET_Y.ConvertToPx()); // P1
3417     path += LineTo(childOffset - ARROW_REPLACE_END_HORIZON_P4_OFFSET_X.ConvertToPx(),
3418         arrowOffset + ARROW_REPLACE_END_HORIZON_P4_OFFSET_Y.ConvertToPx()); // P2
3419     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
3420         childOffset - ARROW_REPLACE_END_HORIZON_P2_OFFSET_X.ConvertToPx(),
3421         arrowOffset + ARROW_REPLACE_END_HORIZON_P2_OFFSET_Y.ConvertToPx());                         // P4
3422     path += LineTo(childOffset, arrowOffset - ARROW_REPLACE_END_HORIZON_P5_OFFSET_Y.ConvertToPx()); // P5
3423     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ZERO,
3424         childOffset, arrowOffset + ARROW_REPLACE_END_HORIZON_P1_OFFSET_Y.ConvertToPx());
3425     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_ONE,
3426         childOffset - ARROW_REPLACE_END_HORIZON_P4_OFFSET_X.ConvertToPx(),
3427         arrowOffset + ARROW_REPLACE_END_HORIZON_P4_OFFSET_Y.ConvertToPx());
3428     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_TWO,
3429         childOffset - ARROW_REPLACE_END_HORIZON_P2_OFFSET_X.ConvertToPx(),
3430         arrowOffset + ARROW_REPLACE_END_HORIZON_P2_OFFSET_Y.ConvertToPx());
3431     SetArrowOffsetsFromClip(ARROW_OFFSETS_INDEX_THREE,
3432         childOffset, arrowOffset - ARROW_REPLACE_END_HORIZON_P5_OFFSET_Y.ConvertToPx());
3433     return path;
3434 }
3435 
BuildCornerPath(const Placement & placement,float radius)3436 std::string BubbleLayoutAlgorithm::BuildCornerPath(const Placement& placement, float radius)
3437 {
3438     std::string path;
3439     float childOffsetY = childOffset_.GetY();
3440     switch (placement) {
3441         case Placement::TOP_LEFT:
3442             path += ArcTo(radius, radius, 0.0f, 0, childOffset_.GetX() + radius, childOffsetY);
3443             break;
3444         case Placement::TOP_RIGHT:
3445             path += ArcTo(radius, radius, 0.0f, 0, childOffset_.GetX() + childSize_.Width(), childOffsetY + radius);
3446             break;
3447         case Placement::BOTTOM_RIGHT:
3448             path += ArcTo(radius, radius, 0.0f, 0, childOffset_.GetX() + childSize_.Width() - radius,
3449                 childOffsetY + childSize_.Height());
3450             break;
3451         case Placement::BOTTOM_LEFT:
3452             path += ArcTo(radius, radius, 0.0f, 0, childOffset_.GetX(), childOffsetY + childSize_.Height() - radius);
3453             break;
3454         default:
3455             break;
3456     }
3457     return path;
3458 }
3459 
GetChildPosition(const SizeF & childSize,const RefPtr<BubbleLayoutProperty> & layoutProp,bool UseArrowOffset)3460 OffsetF BubbleLayoutAlgorithm::GetChildPosition(
3461     const SizeF& childSize, const RefPtr<BubbleLayoutProperty>& layoutProp, bool UseArrowOffset)
3462 {
3463     OffsetF bottomPosition;
3464     OffsetF topPosition;
3465     OffsetF topArrowPosition;
3466     OffsetF bottomArrowPosition;
3467     OffsetF fitPosition;
3468     OffsetF originOffset;
3469     OffsetF originArrowOffset;
3470     OffsetF childPosition;
3471 
3472     InitArrowTopAndBottomPosition(topArrowPosition, bottomArrowPosition, topPosition, bottomPosition, childSize);
3473     GetPositionWithPlacement(originOffset, originArrowOffset, childSize, placement_);
3474     originOffset = originOffset + positionOffset_;
3475     originArrowOffset += positionOffset_;
3476     arrowPlacement_ = placement_;
3477 
3478     // Fit popup to screen range.
3479     ErrorPositionType errorType = GetErrorPositionType(originOffset, childSize);
3480     if (errorType == ErrorPositionType::NORMAL) {
3481         arrowPosition_ = originArrowOffset;
3482         return originOffset;
3483     }
3484 
3485     if (placement_ == Placement::TOP || placement_ == Placement::TOP_LEFT || placement_ == Placement::TOP_RIGHT) {
3486         fitPosition = topPosition;
3487         arrowPosition_ = topArrowPosition;
3488         arrowPlacement_ = Placement::TOP;
3489     } else {
3490         placement_ = Placement::BOTTOM;
3491         fitPosition = bottomPosition;
3492         arrowPosition_ = bottomArrowPosition;
3493         arrowPlacement_ = Placement::BOTTOM;
3494     }
3495 
3496     childPosition = FitToScreen(fitPosition, childSize);
3497     if (UseArrowOffset) {
3498         arrowPosition_.SetX(
3499             childPosition.GetX() + border_.TopLeftRadius().GetX().ConvertToPx() + BEZIER_WIDTH_HALF.ConvertToPx());
3500     }
3501 
3502     if (GetErrorPositionType(childPosition, childSize) == ErrorPositionType::NORMAL) {
3503         return childPosition;
3504     }
3505 
3506     // Fit popup to opposite position
3507     fitPosition = fitPosition == topPosition ? bottomPosition : topPosition;
3508     arrowPosition_ = arrowPlacement_ == Placement::TOP ? bottomArrowPosition : topArrowPosition;
3509     arrowPlacement_ = arrowPlacement_ == Placement::TOP ? Placement::BOTTOM : Placement::TOP;
3510     placement_ = arrowPlacement_ == Placement::TOP ? Placement::BOTTOM : Placement::TOP;
3511 
3512     childPosition = FitToScreen(fitPosition, childSize);
3513     if (UseArrowOffset) {
3514         arrowPosition_.SetX(
3515             childPosition.GetX() + border_.TopLeftRadius().GetX().ConvertToPx() + BEZIER_WIDTH_HALF.ConvertToPx());
3516     }
3517 
3518     if (GetErrorPositionType(childPosition, childSize) == ErrorPositionType::NORMAL) {
3519         return childPosition;
3520     }
3521 
3522     // If childPosition is error, adjust bubble to origin position.
3523     arrowPlacement_ = placement_;
3524     arrowPosition_ = originArrowOffset;
3525 
3526     return originOffset;
3527 }
3528 
InitArrowTopAndBottomPosition(OffsetF & topArrowPosition,OffsetF & bottomArrowPosition,OffsetF & topPosition,OffsetF & bottomPosition,const SizeF & childSize)3529 void BubbleLayoutAlgorithm::InitArrowTopAndBottomPosition(OffsetF& topArrowPosition, OffsetF& bottomArrowPosition,
3530     OffsetF& topPosition, OffsetF& bottomPosition, const SizeF& childSize)
3531 {
3532     auto arrowCenter = targetOffset_.GetX() + targetSize_.Width() / 2.0;
3533     auto horizonSpacing = static_cast<float>(HORIZON_SPACING_WITH_SCREEN.ConvertToPx());
3534     double arrowWidth = ARROW_WIDTH.ConvertToPx();
3535     float radius = borderRadius_.ConvertToPx();
3536     auto safePosition = horizonSpacing + radius + arrowWidth / 2.0;
3537 
3538     GetPositionWithPlacement(topPosition, topArrowPosition, childSize, Placement::TOP);
3539     GetPositionWithPlacement(bottomPosition, bottomArrowPosition, childSize, Placement::BOTTOM);
3540 
3541     // move the arrow to safe position while arrow too close to window
3542     // In order not to separate the bubble from the arrow
3543     // If ArrowOffset is not set, arrow always point to the middle of the targetNode
3544     if (arrowCenter < safePosition) {
3545         topArrowPosition = topArrowPosition + OffsetF(safePosition - arrowCenter, 0);
3546         bottomArrowPosition = bottomArrowPosition + OffsetF(safePosition - arrowCenter, 0);
3547     }
3548     if (arrowCenter > selfSize_.Width() - safePosition) {
3549         topArrowPosition = topArrowPosition - OffsetF(arrowCenter + safePosition - selfSize_.Width(), 0);
3550         bottomArrowPosition = bottomArrowPosition - OffsetF(arrowCenter + safePosition - selfSize_.Width(), 0);
3551     }
3552 }
3553 
GetPositionWithPlacement(OffsetF & childPosition,OffsetF & arrowPosition,const SizeF & childSize,Placement placement)3554 void BubbleLayoutAlgorithm::GetPositionWithPlacement(
3555     OffsetF& childPosition, OffsetF& arrowPosition, const SizeF& childSize, Placement placement)
3556 {
3557     float bubbleSpacing = scaledBubbleSpacing_;
3558     float marginRight = 0.0f;
3559     float marginBottom = 0.0f;
3560     float marginTop = 0.0f;
3561     float marginLeft = 0.0f;
3562     float arrowHalfWidth = ARROW_WIDTH.ConvertToPx() / 2.0;
3563     float radius = borderRadius_.ConvertToPx();
3564     float targetSpace = targetSpace_.ConvertToPx();
3565     switch (placement) {
3566         case Placement::TOP:
3567             childPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
3568                 targetOffset_.GetY() - childSize.Height() - targetSpace - arrowHeight_);
3569             arrowPosition = childPosition + OffsetF(std::max(padding_.Left().ConvertToPx(),
3570             border_.TopLeftRadius().GetX().ConvertToPx()) +
3571             BEZIER_WIDTH_HALF.ConvertToPx(), childSize.Height() + arrowHeight_);
3572             break;
3573         case Placement::TOP_LEFT:
3574             childPosition = OffsetF(targetOffset_.GetX() - marginRight,
3575                 targetOffset_.GetY() - childSize.Height() - bubbleSpacing - marginBottom - targetSpace - arrowHeight_);
3576             arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, childSize.Height() + bubbleSpacing);
3577             break;
3578         case Placement::TOP_RIGHT:
3579             childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
3580                 targetOffset_.GetY() - childSize.Height() - targetSpace - bubbleSpacing - marginBottom - arrowHeight_);
3581             arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, childSize.Height() + bubbleSpacing);
3582             break;
3583         case Placement::BOTTOM:
3584             childPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
3585                 targetOffset_.GetY() + targetSize_.Height() + targetSpace + arrowHeight_);
3586             arrowPosition = childPosition + OffsetF(std::max(padding_.Left().ConvertToPx(),
3587             border_.BottomLeftRadius().GetX().ConvertToPx()) +
3588             BEZIER_WIDTH_HALF.ConvertToPx(), -arrowHeight_);
3589             break;
3590         case Placement::BOTTOM_LEFT:
3591             childPosition = OffsetF(targetOffset_.GetX() - marginRight,
3592                 targetOffset_.GetY() + targetSize_.Height() + targetSpace + bubbleSpacing + marginTop + arrowHeight_);
3593             arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, -bubbleSpacing);
3594             break;
3595         case Placement::BOTTOM_RIGHT:
3596             childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
3597                 targetOffset_.GetY() + targetSize_.Height() + targetSpace + bubbleSpacing + marginTop + arrowHeight_);
3598             arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, -bubbleSpacing);
3599             break;
3600         case Placement::LEFT:
3601             childPosition = OffsetF(
3602                 targetOffset_.GetX() - targetSpace - bubbleSpacing - childSize.Width() - marginRight - arrowHeight_,
3603                 targetOffset_.GetY() + targetSize_.Height() / HALF - childSize.Height() / HALF);
3604             arrowPosition = childPosition + OffsetF(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
3605             break;
3606         case Placement::LEFT_TOP:
3607             childPosition = OffsetF(
3608                 targetOffset_.GetX() - targetSpace - bubbleSpacing - childSize.Width() - marginRight - arrowHeight_,
3609                 targetOffset_.GetY() - marginBottom);
3610             arrowPosition = childPosition + OffsetF(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
3611             break;
3612         case Placement::LEFT_BOTTOM:
3613             childPosition = OffsetF(
3614                 targetOffset_.GetX() - targetSpace - bubbleSpacing - childSize.Width() - marginRight - arrowHeight_,
3615                 targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
3616             arrowPosition = childPosition + OffsetF(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
3617             break;
3618         case Placement::RIGHT:
3619             childPosition = OffsetF(
3620                 targetOffset_.GetX() + targetSize_.Width() + targetSpace + bubbleSpacing + marginLeft + arrowHeight_,
3621                 targetOffset_.GetY() + targetSize_.Height() / HALF - childSize.Height() / HALF);
3622             arrowPosition = childPosition + OffsetF(-bubbleSpacing, radius + arrowHalfWidth);
3623             break;
3624         case Placement::RIGHT_TOP:
3625             childPosition = OffsetF(
3626                 targetOffset_.GetX() + targetSize_.Width() + targetSpace + bubbleSpacing + marginLeft + arrowHeight_,
3627                 targetOffset_.GetY() - marginBottom);
3628             arrowPosition = childPosition + OffsetF(-bubbleSpacing, radius + arrowHalfWidth);
3629             break;
3630         case Placement::RIGHT_BOTTOM:
3631             childPosition = OffsetF(
3632                 targetOffset_.GetX() + targetSize_.Width() + targetSpace + bubbleSpacing + marginLeft + arrowHeight_,
3633                 targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
3634             arrowPosition = childPosition + OffsetF(-bubbleSpacing, radius + arrowHalfWidth);
3635             break;
3636         default:
3637             break;
3638     }
3639 }
3640 
GetErrorPositionType(const OffsetF & childOffset,const SizeF & childSize)3641 BubbleLayoutAlgorithm::ErrorPositionType BubbleLayoutAlgorithm::GetErrorPositionType(
3642     const OffsetF& childOffset, const SizeF& childSize)
3643 {
3644     auto horizonSpacing = static_cast<float>(HORIZON_SPACING_WITH_SCREEN.ConvertToPx());
3645     RectF validRegion =
3646         RectF(OffsetF(horizonSpacing, top_), OffsetF(selfSize_.Width() - horizonSpacing, selfSize_.Height() - bottom_));
3647     PointF childPoint(childOffset.GetX(), childOffset.GetY());
3648     if (!validRegion.IsInRegion(childPoint)) {
3649         return ErrorPositionType::TOP_LEFT_ERROR;
3650     }
3651     if (!validRegion.IsInRegion(
3652             PointF(childOffset.GetX() + childSize.Width(), childOffset.GetY() + childSize.Height()))) {
3653         return ErrorPositionType::BOTTOM_RIGHT_ERROR;
3654     }
3655     return ErrorPositionType::NORMAL;
3656 }
3657 
FitToScreen(const OffsetF & fitPosition,const SizeF & childSize)3658 OffsetF BubbleLayoutAlgorithm::FitToScreen(const OffsetF& fitPosition, const SizeF& childSize)
3659 {
3660     auto validation = GetErrorPositionType(fitPosition, childSize);
3661     if (validation == ErrorPositionType::NORMAL) {
3662         return fitPosition;
3663     }
3664     OffsetF childPosition = fitPosition;
3665     auto horizonSpacing = static_cast<float>(HORIZON_SPACING_WITH_SCREEN.ConvertToPx());
3666     if (validation == ErrorPositionType::TOP_LEFT_ERROR) {
3667         childPosition.SetX(horizonSpacing);
3668     } else {
3669         childPosition.SetX(selfSize_.Width() - childSize.Width() - horizonSpacing);
3670     }
3671     return childPosition;
3672 }
3673 
UpdateMarginByWidth()3674 void BubbleLayoutAlgorithm::UpdateMarginByWidth()
3675 {
3676     isGreatWrapperWidth_ = GreatOrEqual(childSize_.Width(), wrapperSize_.Width() - marginStart_ - marginEnd_);
3677     marginStart_ = isGreatWrapperWidth_ ? 0.0f : marginStart_;
3678     marginEnd_ = isGreatWrapperWidth_ ? 0.0f : marginEnd_;
3679 }
3680 
3681 } // namespace OHOS::Ace::NG
3682