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