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