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