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/properties/placement.h"
32 #include "core/components/popup/popup_theme.h"
33 #include "core/components_ng/base/frame_node.h"
34 #include "core/components_ng/pattern/bubble/bubble_layout_property.h"
35 #include "core/components_ng/pattern/bubble/bubble_pattern.h"
36 #include "core/pipeline/pipeline_base.h"
37 #include "core/pipeline_ng/pipeline_context.h"
38
39 namespace OHOS::Ace::NG {
40 namespace {
41 constexpr Dimension ARROW_WIDTH = 32.0_vp;
42 constexpr Dimension ARROW_HEIGHT = 8.0_vp;
43 constexpr Dimension HORIZON_SPACING_WITH_SCREEN = 6.0_vp;
44 constexpr Dimension BEZIER_WIDTH_HALF = 16.0_vp;
45
46 // get main window's pipeline
GetMainPipelineContext()47 RefPtr<PipelineContext> GetMainPipelineContext()
48 {
49 auto containerId = Container::CurrentId();
50 RefPtr<PipelineContext> context;
51 if (containerId >= MIN_SUBCONTAINER_ID) {
52 auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(containerId);
53 auto parentContainer = AceEngine::Get().GetContainer(parentContainerId);
54 CHECK_NULL_RETURN(parentContainer, nullptr);
55 context = AceType::DynamicCast<PipelineContext>(parentContainer->GetPipelineContext());
56 } else {
57 context = PipelineContext::GetCurrentContext();
58 }
59 return context;
60 }
61 } // namespace
62
Measure(LayoutWrapper * layoutWrapper)63 void BubbleLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
64 {
65 CHECK_NULL_VOID(layoutWrapper);
66 auto bubbleProp = DynamicCast<BubbleLayoutProperty>(layoutWrapper->GetLayoutProperty());
67 CHECK_NULL_VOID(bubbleProp);
68 InitProps(bubbleProp);
69 auto bubbleLayoutProperty = AceType::DynamicCast<BubbleLayoutProperty>(layoutWrapper->GetLayoutProperty());
70 CHECK_NULL_VOID(bubbleLayoutProperty);
71
72 const auto& layoutConstraint = bubbleLayoutProperty->GetLayoutConstraint();
73 if (!layoutConstraint) {
74 LOGE("fail to measure bubble due to layoutConstraint is nullptr");
75 return;
76 }
77 bool useCustom = bubbleLayoutProperty->GetUseCustom().value_or(false);
78 // bubble size fit screen.
79 layoutWrapper->GetGeometryNode()->SetFrameSize(layoutConstraint->maxSize);
80 layoutWrapper->GetGeometryNode()->SetContentSize(layoutConstraint->maxSize);
81
82 // update child layout constraint
83 LayoutConstraintF childLayoutConstraint = bubbleLayoutProperty->CreateChildConstraint();
84 const auto& children = layoutWrapper->GetAllChildrenWithBuild();
85 if (children.empty()) {
86 return;
87 }
88 auto child = children.front();
89 // childSize_ and childOffset_ is used in Layout.
90 child->Measure(childLayoutConstraint);
91 bool showInSubWindow = bubbleLayoutProperty->GetShowInSubWindowValue(false);
92 if (useCustom && !showInSubWindow) {
93 auto context = PipelineBase::GetCurrentContext();
94 CHECK_NULL_VOID(context);
95 float rootH = context->GetRootHeight();
96 float rootW = context->GetRootWidth();
97 auto childHeight = child->GetGeometryNode()->GetMarginFrameSize().Height();
98 auto childWidth = child->GetGeometryNode()->GetMarginFrameSize().Width();
99 auto scaledBubbleSpacing = scaledBubbleSpacing_ * 2;
100 auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
101 CHECK_NULL_VOID(targetNode);
102 auto geometryNode = targetNode->GetGeometryNode();
103 CHECK_NULL_VOID(geometryNode);
104 auto targetSize = geometryNode->GetFrameSize();
105 auto targetOffset = targetNode->GetPaintRectOffset();
106 auto constrainHeight = layoutWrapper->GetGeometryNode()->GetFrameSize().Height();
107 auto constrainWidth = layoutWrapper->GetGeometryNode()->GetFrameSize().Width();
108 auto placement = bubbleLayoutProperty->GetPlacement().value_or(Placement::BOTTOM);
109 std::unordered_set<Placement> setHorizontal = { Placement::LEFT, Placement::LEFT_BOTTOM, Placement::LEFT_TOP,
110 Placement::RIGHT, Placement::RIGHT_BOTTOM, Placement::RIGHT_TOP };
111 std::unordered_set<Placement> setVertical = { Placement::TOP, Placement::TOP_LEFT, Placement::TOP_RIGHT,
112 Placement::BOTTOM, Placement::BOTTOM_LEFT, Placement::BOTTOM_RIGHT };
113 if (setHorizontal.find(placement) != setHorizontal.end()) {
114 if (childWidth + targetOffset.GetX() + targetSize.Width() + scaledBubbleSpacing <= rootW &&
115 targetOffset.GetX() - childWidth - scaledBubbleSpacing >= 0) {
116 return;
117 }
118 constrainWidth = rootW - scaledBubbleSpacing;
119 }
120 if (setVertical.find(placement) != setVertical.end()) {
121 if (childHeight + targetOffset.GetY() + targetSize.Height() + scaledBubbleSpacing <= rootH &&
122 targetOffset.GetY() - childHeight - scaledBubbleSpacing >= 0) {
123 return;
124 }
125 constrainHeight = std::max(rootH - targetOffset.GetY() - targetSize.Height() - scaledBubbleSpacing,
126 targetOffset.GetY() - scaledBubbleSpacing);
127 }
128 SizeF size = SizeF(constrainWidth, constrainHeight);
129 childLayoutConstraint.UpdateMaxSizeWithCheck(size);
130 child->Measure(childLayoutConstraint);
131 }
132 }
133
Layout(LayoutWrapper * layoutWrapper)134 void BubbleLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
135 {
136 CHECK_NULL_VOID(layoutWrapper);
137 const auto& children = layoutWrapper->GetAllChildrenWithBuild();
138 if (children.empty()) {
139 return;
140 }
141 auto child = children.front();
142 child->Layout();
143 }
144
145 /*
146 Because of bubble's position depends on targetNode,
147 The position of the bubble needs to be calculated after the targetNode layout is complete.
148 */
GetChildOffsetAfterLayout(const RefPtr<LayoutWrapper> & layoutWrapper)149 OffsetT<Dimension> BubbleLayoutAlgorithm::GetChildOffsetAfterLayout(const RefPtr<LayoutWrapper>& layoutWrapper)
150 {
151 CHECK_NULL_RETURN(layoutWrapper, OffsetT<Dimension> {});
152 auto bubbleProp = DynamicCast<BubbleLayoutProperty>(layoutWrapper->GetLayoutProperty());
153 CHECK_NULL_RETURN(bubbleProp, OffsetT<Dimension> {});
154 auto frameNode = layoutWrapper->GetHostNode();
155 CHECK_NULL_RETURN(frameNode, OffsetT<Dimension> {});
156 auto bubblePattern = frameNode->GetPattern<BubblePattern>();
157 CHECK_NULL_RETURN(bubblePattern, OffsetT<Dimension> {});
158 auto bubblePaintProperty = frameNode->GetPaintProperty<BubbleRenderProperty>();
159 CHECK_NULL_RETURN(bubblePaintProperty, OffsetT<Dimension> {});
160 bool UseArrowOffset = bubblePaintProperty->GetArrowOffset().has_value();
161 auto ShowInSubWindow = bubbleProp->GetShowInSubWindow().value_or(false);
162 if (!bubblePattern->IsExiting()) {
163 InitTargetSizeAndPosition(ShowInSubWindow);
164 // subtract the global offset of the overlay node,
165 // because the final node position is set relative to the overlay node.
166 auto overlayGlobalOffset = frameNode->GetPaintRectOffset();
167 targetOffset_ -= overlayGlobalOffset;
168 }
169 const auto& children = layoutWrapper->GetAllChildrenWithBuild();
170 auto childWrapper = children.front();
171 if (children.empty()) {
172 return OffsetT<Dimension> {};
173 }
174 selfSize_ = layoutWrapper->GetGeometryNode()->GetFrameSize(); // window's size
175 childSize_ = childWrapper->GetGeometryNode()->GetMarginFrameSize(); // bubble's size
176 childOffset_ = GetChildPosition(childSize_, bubbleProp, UseArrowOffset); // bubble's offset
177 UpdateChildPosition(bubbleProp);
178 UpdateTouchRegion();
179
180 // If bubble displayed in subwindow, set the hotarea of subwindow.
181 if (bubbleProp->GetShowInSubWindowValue(false) && (!bubblePattern->IsSkipHotArea())) {
182 std::vector<Rect> rects;
183 if (!bubbleProp->GetBlockEventValue(true)) {
184 auto rect = Rect(childOffset_.GetX(), childOffset_.GetY(), childSize_.Width(), childSize_.Height());
185 rects.emplace_back(rect);
186 } else {
187 auto parentWindowRect = SubwindowManager::GetInstance()->GetParentWindowRect();
188 auto rect = Rect(childOffset_.GetX(), childOffset_.GetY(), childSize_.Width(), childSize_.Height());
189 rects.emplace_back(parentWindowRect);
190 rects.emplace_back(rect);
191 }
192 SubwindowManager::GetInstance()->SetHotAreas(rects, frameNode->GetId(), bubblePattern->GetContainerId());
193 }
194 bubblePattern->SetSkipHotArea(false);
195
196 Dimension childOffsetX(childOffset_.GetX());
197 Dimension childOffsetY(childOffset_.GetY());
198 OffsetT<Dimension> childRenderOffset(childOffsetX, childOffsetY);
199 return childRenderOffset;
200 }
201
InitProps(const RefPtr<BubbleLayoutProperty> & layoutProp)202 void BubbleLayoutAlgorithm::InitProps(const RefPtr<BubbleLayoutProperty>& layoutProp)
203 {
204 auto pipeline = PipelineBase::GetCurrentContext();
205 CHECK_NULL_VOID(pipeline);
206 auto popupTheme = pipeline->GetTheme<PopupTheme>();
207 CHECK_NULL_VOID(popupTheme);
208 padding_ = popupTheme->GetPadding();
209 borderRadius_ = popupTheme->GetRadius().GetX();
210 border_.SetBorderRadius(popupTheme->GetRadius());
211 targetSpace_ = layoutProp->GetTargetSpace().value_or(popupTheme->GetTargetSpace());
212 placement_ = layoutProp->GetPlacement().value_or(Placement::BOTTOM);
213 scaledBubbleSpacing_ = static_cast<float>(popupTheme->GetBubbleSpacing().ConvertToPx());
214 arrowHeight_ = static_cast<float>(popupTheme->GetArrowHeight().ConvertToPx());
215 positionOffset_ = layoutProp->GetPositionOffset().value_or(OffsetF());
216 }
217
GetChildPosition(const SizeF & childSize,const RefPtr<BubbleLayoutProperty> & layoutProp,bool UseArrowOffset)218 OffsetF BubbleLayoutAlgorithm::GetChildPosition(
219 const SizeF& childSize, const RefPtr<BubbleLayoutProperty>& layoutProp, bool UseArrowOffset)
220 {
221 OffsetF bottomPosition;
222 OffsetF topPosition;
223 OffsetF topArrowPosition;
224 OffsetF bottomArrowPosition;
225 OffsetF fitPosition;
226 OffsetF originOffset;
227 OffsetF originArrowOffset;
228 OffsetF childPosition;
229
230 InitArrowTopAndBottomPosition(topArrowPosition, bottomArrowPosition, topPosition, bottomPosition, childSize);
231 GetPositionWithPlacement(originOffset, originArrowOffset, childSize, placement_);
232 originOffset = originOffset + positionOffset_;
233 originArrowOffset += positionOffset_;
234 arrowPlacement_ = placement_;
235
236 // Fit popup to screen range.
237 ErrorPositionType errorType = GetErrorPositionType(originOffset, childSize);
238 if (errorType == ErrorPositionType::NORMAL) {
239 arrowPosition_ = originArrowOffset;
240 return originOffset;
241 }
242
243 if (placement_ == Placement::TOP || placement_ == Placement::TOP_LEFT || placement_ == Placement::TOP_RIGHT) {
244 fitPosition = topPosition;
245 arrowPosition_ = topArrowPosition;
246 arrowPlacement_ = Placement::TOP;
247 } else {
248 fitPosition = bottomPosition;
249 arrowPosition_ = bottomArrowPosition;
250 arrowPlacement_ = Placement::BOTTOM;
251 }
252
253 childPosition = FitToScreen(fitPosition, childSize);
254 if (UseArrowOffset) {
255 arrowPosition_.SetX(
256 childPosition.GetX() + border_.TopLeftRadius().GetX().ConvertToPx() + BEZIER_WIDTH_HALF.ConvertToPx());
257 }
258
259 if (GetErrorPositionType(childPosition, childSize) == ErrorPositionType::NORMAL) {
260 return childPosition;
261 }
262
263 // Fit popup to opposite position
264 fitPosition = fitPosition == topPosition ? bottomPosition : topPosition;
265 arrowPosition_ = arrowPosition_ == topArrowPosition ? bottomArrowPosition : topArrowPosition;
266 arrowPlacement_ = arrowPlacement_ == Placement::TOP ? Placement::BOTTOM : Placement::TOP;
267
268 childPosition = FitToScreen(fitPosition, childSize);
269 if (UseArrowOffset) {
270 arrowPosition_.SetX(
271 childPosition.GetX() + border_.TopLeftRadius().GetX().ConvertToPx() + BEZIER_WIDTH_HALF.ConvertToPx());
272 arrowPosition_.SetY(childPosition.GetY() - arrowHeight_);
273 }
274
275 if (GetErrorPositionType(childPosition, childSize) == ErrorPositionType::NORMAL) {
276 return childPosition;
277 }
278
279 // If childPosition is error, adjust bubble to origin position.
280 arrowPlacement_ = placement_;
281 arrowPosition_ = originArrowOffset;
282
283 return originOffset;
284 }
285
InitArrowTopAndBottomPosition(OffsetF & topArrowPosition,OffsetF & bottomArrowPosition,OffsetF & topPosition,OffsetF & bottomPosition,const SizeF & childSize)286 void BubbleLayoutAlgorithm::InitArrowTopAndBottomPosition(OffsetF& topArrowPosition, OffsetF& bottomArrowPosition,
287 OffsetF& topPosition, OffsetF& bottomPosition, const SizeF& childSize)
288 {
289 auto arrowCenter = targetOffset_.GetX() + targetSize_.Width() / 2.0;
290 auto horizonSpacing = static_cast<float>(HORIZON_SPACING_WITH_SCREEN.ConvertToPx());
291 double arrowWidth = ARROW_WIDTH.ConvertToPx();
292 float radius = borderRadius_.ConvertToPx();
293 auto safePosition = horizonSpacing + radius + arrowWidth / 2.0;
294
295 GetPositionWithPlacement(topPosition, topArrowPosition, childSize, Placement::TOP);
296 GetPositionWithPlacement(bottomPosition, bottomArrowPosition, childSize, Placement::BOTTOM);
297
298 // move the arrow to safe position while arrow too close to window
299 // In order not to separate the bubble from the arrow
300 // If ArrowOffset is not set, arrow always point to the middle of the targetNode
301 if (arrowCenter < safePosition) {
302 topArrowPosition = topArrowPosition + OffsetF(safePosition - arrowCenter, 0);
303 bottomArrowPosition = bottomArrowPosition + OffsetF(safePosition - arrowCenter, 0);
304 }
305 if (arrowCenter > selfSize_.Width() - safePosition) {
306 topArrowPosition = topArrowPosition - OffsetF(arrowCenter + safePosition - selfSize_.Width(), 0);
307 bottomArrowPosition = bottomArrowPosition - OffsetF(arrowCenter + safePosition - selfSize_.Width(), 0);
308 }
309 }
310
GetPositionWithPlacement(OffsetF & childPosition,OffsetF & arrowPosition,const SizeF & childSize,Placement placement)311 void BubbleLayoutAlgorithm::GetPositionWithPlacement(
312 OffsetF& childPosition, OffsetF& arrowPosition, const SizeF& childSize, Placement placement)
313 {
314 float bubbleSpacing = scaledBubbleSpacing_;
315 float marginRight = 0.0f;
316 float marginBottom = 0.0f;
317 float marginTop = 0.0f;
318 float marginLeft = 0.0f;
319 float arrowHalfWidth = ARROW_WIDTH.ConvertToPx() / 2.0;
320 float radius = borderRadius_.ConvertToPx();
321 float targetSpace = targetSpace_.ConvertToPx();
322 switch (placement) {
323 case Placement::TOP:
324 childPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
325 targetOffset_.GetY() - childSize.Height() - targetSpace - arrowHeight_);
326 arrowPosition = childPosition + OffsetF(std::max(padding_.Left().ConvertToPx(),
327 border_.TopLeftRadius().GetX().ConvertToPx()) + BEZIER_WIDTH_HALF.ConvertToPx(),
328 childSize.Height() + arrowHeight_);
329 break;
330 case Placement::TOP_LEFT:
331 childPosition = OffsetF(targetOffset_.GetX() - marginRight,
332 targetOffset_.GetY() - childSize.Height() - bubbleSpacing - marginBottom - targetSpace);
333 arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, childSize.Height() + bubbleSpacing);
334 break;
335 case Placement::TOP_RIGHT:
336 childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
337 targetOffset_.GetY() - childSize.Height() - targetSpace - bubbleSpacing - marginBottom);
338 arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, childSize.Height() + bubbleSpacing);
339 break;
340 case Placement::BOTTOM:
341 childPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
342 targetOffset_.GetY() + targetSize_.Height() + targetSpace + arrowHeight_);
343 arrowPosition = childPosition + OffsetF(std::max(padding_.Left().ConvertToPx(),
344 border_.BottomLeftRadius().GetX().ConvertToPx()) + BEZIER_WIDTH_HALF.ConvertToPx(), -arrowHeight_);
345 break;
346 case Placement::BOTTOM_LEFT:
347 childPosition = OffsetF(targetOffset_.GetX() - marginRight,
348 targetOffset_.GetY() + targetSize_.Height() + targetSpace + bubbleSpacing + marginTop);
349 arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, -bubbleSpacing);
350 break;
351 case Placement::BOTTOM_RIGHT:
352 childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
353 targetOffset_.GetY() + targetSize_.Height() + targetSpace + bubbleSpacing + marginTop);
354 arrowPosition = childPosition + OffsetF(radius + arrowHalfWidth, -bubbleSpacing);
355 break;
356 case Placement::LEFT:
357 childPosition =
358 OffsetF(targetOffset_.GetX() - targetSpace - bubbleSpacing - childSize.Width() - marginRight,
359 targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
360 arrowPosition = childPosition + OffsetF(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
361 break;
362 case Placement::LEFT_TOP:
363 childPosition =
364 OffsetF(targetOffset_.GetX() - targetSpace - bubbleSpacing - childSize.Width() - marginRight,
365 targetOffset_.GetY() - marginBottom);
366 arrowPosition = childPosition + OffsetF(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
367 break;
368 case Placement::LEFT_BOTTOM:
369 childPosition =
370 OffsetF(targetOffset_.GetX() - targetSpace - bubbleSpacing - childSize.Width() - marginRight,
371 targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
372 arrowPosition = childPosition + OffsetF(childSize_.Width() + bubbleSpacing, radius + arrowHalfWidth);
373 break;
374 case Placement::RIGHT:
375 childPosition =
376 OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace + bubbleSpacing + marginLeft,
377 targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
378 arrowPosition = childPosition + OffsetF(-bubbleSpacing, radius + arrowHalfWidth);
379 break;
380 case Placement::RIGHT_TOP:
381 childPosition =
382 OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace + bubbleSpacing + marginLeft,
383 targetOffset_.GetY() - marginBottom);
384 arrowPosition = childPosition + OffsetF(-bubbleSpacing, radius + arrowHalfWidth);
385 break;
386 case Placement::RIGHT_BOTTOM:
387 childPosition =
388 OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace + bubbleSpacing + marginLeft,
389 targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
390 arrowPosition = childPosition + OffsetF(-bubbleSpacing, radius + arrowHalfWidth);
391 break;
392 default:
393 break;
394 }
395 }
396
GetErrorPositionType(const OffsetF & childOffset,const SizeF & childSize)397 BubbleLayoutAlgorithm::ErrorPositionType BubbleLayoutAlgorithm::GetErrorPositionType(
398 const OffsetF& childOffset, const SizeF& childSize)
399 {
400 auto horizonSpacing = static_cast<float>(HORIZON_SPACING_WITH_SCREEN.ConvertToPx());
401 RectF validRegion =
402 RectF(OffsetF(horizonSpacing, 0.0), OffsetF(selfSize_.Width() - horizonSpacing, selfSize_.Height()));
403 PointF childPoint(childOffset.GetX(), childOffset.GetY());
404 if (!validRegion.IsInRegion(childPoint)) {
405 return ErrorPositionType::TOP_LEFT_ERROR;
406 }
407 if (!validRegion.IsInRegion(
408 PointF(childOffset.GetX() + childSize.Width(), childOffset.GetY() + childSize.Height()))) {
409 return ErrorPositionType::BOTTOM_RIGHT_ERROR;
410 }
411 return ErrorPositionType::NORMAL;
412 }
413
FitToScreen(const OffsetF & fitPosition,const SizeF & childSize)414 OffsetF BubbleLayoutAlgorithm::FitToScreen(const OffsetF& fitPosition, const SizeF& childSize)
415 {
416 auto validation = GetErrorPositionType(fitPosition, childSize);
417 if (validation == ErrorPositionType::NORMAL) {
418 return fitPosition;
419 }
420 OffsetF childPosition = fitPosition;
421 auto horizonSpacing = static_cast<float>(HORIZON_SPACING_WITH_SCREEN.ConvertToPx());
422 if (validation == ErrorPositionType::TOP_LEFT_ERROR) {
423 childPosition.SetX(horizonSpacing);
424 } else {
425 childPosition.SetX(selfSize_.Width() - childSize.Width() - horizonSpacing);
426 }
427 return childPosition;
428 }
429
UpdateChildPosition(const RefPtr<BubbleLayoutProperty> & layoutProp)430 void BubbleLayoutAlgorithm::UpdateChildPosition(const RefPtr<BubbleLayoutProperty>& layoutProp)
431 {
432 auto enableArrow = layoutProp->GetEnableArrow().value_or(true);
433 double arrowWidth = ARROW_WIDTH.ConvertToPx();
434 double twoRadiusPx = borderRadius_.ConvertToPx() * 2.0;
435 switch (arrowPlacement_) {
436 case Placement::TOP:
437 case Placement::TOP_LEFT:
438 case Placement::TOP_RIGHT:
439 showArrow_ = GreatOrEqual(childSize_.Width() - twoRadiusPx, arrowWidth);
440 if (!showArrow_ || !enableArrow) {
441 childOffset_ += OffsetF(0.0, ARROW_HEIGHT.ConvertToPx());
442 }
443 break;
444 case Placement::BOTTOM:
445 case Placement::BOTTOM_LEFT:
446 case Placement::BOTTOM_RIGHT:
447 showArrow_ = GreatOrEqual(childSize_.Width() - twoRadiusPx, arrowWidth);
448 if (!showArrow_ || !enableArrow) {
449 childOffset_ += OffsetF(0.0, -ARROW_HEIGHT.ConvertToPx());
450 }
451 break;
452 case Placement::LEFT:
453 case Placement::LEFT_TOP:
454 case Placement::LEFT_BOTTOM:
455 showArrow_ = GreatOrEqual(childSize_.Height() - twoRadiusPx, arrowWidth);
456 if (!showArrow_ || !enableArrow) {
457 childOffset_ += OffsetF(ARROW_HEIGHT.ConvertToPx(), 0.0);
458 }
459 break;
460 case Placement::RIGHT:
461 case Placement::RIGHT_TOP:
462 case Placement::RIGHT_BOTTOM:
463 showArrow_ = GreatOrEqual(childSize_.Height() - twoRadiusPx, arrowWidth);
464 if (!showArrow_ || !enableArrow) {
465 childOffset_ += OffsetF(-ARROW_HEIGHT.ConvertToPx(), 0.0);
466 }
467 break;
468 default:
469 break;
470 }
471 }
472
UpdateTouchRegion()473 void BubbleLayoutAlgorithm::UpdateTouchRegion()
474 {
475 OffsetF topLeft;
476 OffsetF bottomRight;
477 switch (arrowPlacement_) {
478 case Placement::TOP:
479 case Placement::TOP_LEFT:
480 case Placement::TOP_RIGHT:
481 topLeft = childOffset_;
482 bottomRight = OffsetF(childSize_.Width(), targetSpace_.ConvertToPx() + childSize_.Height());
483 if (showArrow_) {
484 bottomRight += OffsetF(0.0, ARROW_HEIGHT.ConvertToPx());
485 }
486 break;
487 case Placement::BOTTOM:
488 case Placement::BOTTOM_LEFT:
489 case Placement::BOTTOM_RIGHT:
490 topLeft = childOffset_ + OffsetF(0.0, -targetSpace_.ConvertToPx());
491 bottomRight = OffsetF(childSize_.Width(), targetSpace_.ConvertToPx() + childSize_.Height());
492 if (showArrow_) {
493 topLeft += OffsetF(0.0, -ARROW_HEIGHT.ConvertToPx());
494 bottomRight += OffsetF(0.0, ARROW_HEIGHT.ConvertToPx());
495 }
496 break;
497 case Placement::LEFT:
498 case Placement::LEFT_TOP:
499 case Placement::LEFT_BOTTOM:
500 topLeft = childOffset_;
501 bottomRight = OffsetF(targetSpace_.ConvertToPx() + childSize_.Width(), childSize_.Height());
502 if (showArrow_) {
503 bottomRight += OffsetF(ARROW_HEIGHT.ConvertToPx(), 0.0);
504 }
505 break;
506 case Placement::RIGHT:
507 case Placement::RIGHT_TOP:
508 case Placement::RIGHT_BOTTOM:
509 topLeft = childOffset_ + OffsetF(-targetSpace_.ConvertToPx(), 0.0);
510 bottomRight = OffsetF(targetSpace_.ConvertToPx() + childSize_.Width(), childSize_.Height());
511 if (showArrow_) {
512 topLeft += OffsetF(-ARROW_HEIGHT.ConvertToPx(), 0.0);
513 bottomRight += OffsetF(ARROW_HEIGHT.ConvertToPx(), 0.0);
514 }
515 break;
516 default:
517 break;
518 }
519 touchRegion_ = RectF(topLeft, topLeft + bottomRight);
520 }
521
InitTargetSizeAndPosition(bool showInSubWindow)522 void BubbleLayoutAlgorithm::InitTargetSizeAndPosition(bool showInSubWindow)
523 {
524 auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
525 CHECK_NULL_VOID(targetNode);
526 if (!targetNode->IsOnMainTree() || !targetNode->IsVisible()) {
527 return;
528 }
529 auto geometryNode = targetNode->GetGeometryNode();
530 CHECK_NULL_VOID(geometryNode);
531 targetSize_ = geometryNode->GetFrameSize();
532 auto pipelineContext = GetMainPipelineContext();
533 CHECK_NULL_VOID(pipelineContext);
534 targetOffset_ = targetNode->GetPaintRectOffset();
535 // Show in SubWindow
536 if (showInSubWindow) {
537 auto displayWindowOffset = OffsetF(pipelineContext->GetDisplayWindowRectInfo().GetOffset().GetX(),
538 pipelineContext->GetDisplayWindowRectInfo().GetOffset().GetY());
539 targetOffset_ += displayWindowOffset;
540 auto currentSubwindow = SubwindowManager::GetInstance()->GetCurrentWindow();
541 if (currentSubwindow) {
542 auto subwindowRect = currentSubwindow->GetRect();
543 targetOffset_ -= subwindowRect.GetOffset();
544 }
545 }
546 }
547 } // namespace OHOS::Ace::NG
548