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