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