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