• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 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/layout/box_layout_algorithm.h"
17 #include "core/components_ng/layout/layout_wrapper.h"
18 #include "core/components_ng/pattern/overlay/sheet_presentation_layout_algorithm.h"
19 #include "core/components_ng/pattern/overlay/sheet_presentation_pattern.h"
20 #include "core/components_ng/pattern/sheet/sheet_mask_pattern.h"
21 #include "core/components_ng/pattern/overlay/sheet_presentation_property.h"
22 #include "core/components_ng/pattern/overlay/sheet_wrapper_layout_algorithm.h"
23 #include "core/components_ng/pattern/overlay/sheet_wrapper_pattern.h"
24 #include "core/pipeline_ng/pipeline_context.h"
25 
26 namespace OHOS::Ace::NG {
27 namespace {
28 constexpr int32_t DOUBLE_SIZE = 2;
29 constexpr Dimension WINDOW_EDGE_SPACE = 6.0_vp;
30 constexpr Dimension ARROW_VERTICAL_P1_OFFSET_X = 8.0_vp;
31 constexpr Dimension ARROW_VERTICAL_P5_OFFSET_X = 8.0_vp;
32 std::map<Placement, std::vector<Placement>> PLACEMENT_STATES = {
33     { Placement::BOTTOM_LEFT,
34         {
35             Placement::BOTTOM_LEFT,
36             Placement::TOP_LEFT,
37             Placement::RIGHT_TOP,
38             Placement::LEFT_TOP,
39             Placement::NONE,
40         } },
41     { Placement::BOTTOM,
42         {
43             Placement::BOTTOM,
44             Placement::TOP,
45             Placement::RIGHT,
46             Placement::LEFT,
47             Placement::NONE,
48         } },
49     { Placement::BOTTOM_RIGHT,
50         {
51             Placement::BOTTOM_RIGHT,
52             Placement::TOP_RIGHT,
53             Placement::RIGHT_BOTTOM,
54             Placement::LEFT_BOTTOM,
55             Placement::NONE,
56         } },
57     { Placement::TOP_LEFT,
58         {
59             Placement::TOP_LEFT,
60             Placement::BOTTOM_LEFT,
61             Placement::RIGHT_TOP,
62             Placement::LEFT_TOP,
63             Placement::NONE,
64         } },
65     { Placement::TOP,
66         {
67             Placement::TOP,
68             Placement::BOTTOM,
69             Placement::RIGHT,
70             Placement::LEFT,
71             Placement::NONE,
72         } },
73     { Placement::TOP_RIGHT,
74         {
75             Placement::TOP_RIGHT,
76             Placement::BOTTOM_RIGHT,
77             Placement::RIGHT_BOTTOM,
78             Placement::LEFT_BOTTOM,
79             Placement::NONE,
80         } },
81     { Placement::LEFT_TOP,
82         {
83             Placement::LEFT_TOP,
84             Placement::RIGHT_TOP,
85             Placement::BOTTOM_LEFT,
86             Placement::TOP_LEFT,
87             Placement::NONE,
88         } },
89     { Placement::LEFT,
90         {
91             Placement::LEFT,
92             Placement::RIGHT,
93             Placement::BOTTOM,
94             Placement::TOP,
95             Placement::NONE,
96         } },
97     { Placement::LEFT_BOTTOM,
98         {
99             Placement::LEFT_BOTTOM,
100             Placement::RIGHT_BOTTOM,
101             Placement::BOTTOM_RIGHT,
102             Placement::TOP_RIGHT,
103             Placement::NONE,
104         } },
105     { Placement::RIGHT_TOP,
106         {
107             Placement::RIGHT_TOP,
108             Placement::LEFT_TOP,
109             Placement::BOTTOM_LEFT,
110             Placement::TOP_LEFT,
111             Placement::NONE,
112         } },
113     { Placement::RIGHT,
114         {
115             Placement::RIGHT,
116             Placement::LEFT,
117             Placement::BOTTOM,
118             Placement::TOP,
119             Placement::NONE,
120         } },
121     { Placement::RIGHT_BOTTOM,
122         {
123             Placement::RIGHT_BOTTOM,
124             Placement::LEFT_BOTTOM,
125             Placement::BOTTOM_RIGHT,
126             Placement::TOP_RIGHT,
127             Placement::NONE,
128         } },
129 };
130 }   // namespace
131 
Measure(LayoutWrapper * layoutWrapper)132 void SheetWrapperLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
133 {
134     CHECK_NULL_VOID(layoutWrapper);
135     InitParameter(layoutWrapper);
136     BoxLayoutAlgorithm::PerformMeasureSelf(layoutWrapper);
137     auto host = layoutWrapper->GetHostNode();
138     CHECK_NULL_VOID(host);
139     auto sheetWrapperPattern = host->GetPattern<SheetWrapperPattern>();
140     CHECK_NULL_VOID(sheetWrapperPattern);
141     auto child = sheetWrapperPattern->GetSheetPageNode();
142     CHECK_NULL_VOID(child);
143     auto sheetLayoutProperty = child->GetLayoutProperty();
144     CHECK_NULL_VOID(sheetLayoutProperty);
145     sheetLayoutProperty->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE);
146     auto layoutProp = layoutWrapper->GetLayoutProperty();
147     CHECK_NULL_VOID(layoutProp);
148     auto childConstraint = layoutProp->CreateChildConstraint();
149     child->Measure(childConstraint);
150     GetSheetPageSize(layoutWrapper);
151     MeasureSheetMask(layoutWrapper);
152 }
153 
MeasureSheetMask(LayoutWrapper * layoutWrapper)154 void SheetWrapperLayoutAlgorithm::MeasureSheetMask(LayoutWrapper* layoutWrapper)
155 {
156     auto host = layoutWrapper->GetHostNode();
157     CHECK_NULL_VOID(host);
158     auto sheetWrapperPattern = host->GetPattern<SheetWrapperPattern>();
159     CHECK_NULL_VOID(sheetWrapperPattern);
160 
161     if (!sheetWrapperPattern->ShowInUEC()) {
162         return;
163     }
164     auto maskNode = sheetWrapperPattern->GetSheetMaskNode();
165     CHECK_NULL_VOID(maskNode);
166     auto index = host->GetChildIndexById(maskNode->GetId());
167     auto maskWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
168     CHECK_NULL_VOID(maskWrapper);
169     auto rect = sheetWrapperPattern->GetMainWindowRect();
170     auto layoutProp = layoutWrapper->GetLayoutProperty();
171     CHECK_NULL_VOID(layoutProp);
172     auto constraint = layoutProp->CreateChildConstraint();
173     constraint.selfIdealSize = OptionalSizeF(rect.Width(), rect.Height());
174     maskWrapper->Measure(constraint);
175 }
176 
InitParameter(LayoutWrapper * layoutWrapper)177 void SheetWrapperLayoutAlgorithm::InitParameter(LayoutWrapper* layoutWrapper)
178 {
179     auto host = layoutWrapper->GetHostNode();
180     CHECK_NULL_VOID(host);
181     auto pipeline = host->GetContext();
182     CHECK_NULL_VOID(pipeline);
183     auto sheetTheme = pipeline->GetTheme<SheetTheme>();
184     CHECK_NULL_VOID(sheetTheme);
185 
186     auto sheetWrapperPattern = host->GetPattern<SheetWrapperPattern>();
187     CHECK_NULL_VOID(sheetWrapperPattern);
188     auto sheetPage = sheetWrapperPattern->GetSheetPageNode();
189     CHECK_NULL_VOID(sheetPage);
190     auto sheetPattern = DynamicCast<SheetPresentationPattern>(sheetPage->GetPattern());
191     CHECK_NULL_VOID(sheetPattern);
192     sheetRadius_ = BorderRadiusProperty(sheetTheme->GetSheetRadius());
193     sheetPattern->CalculateSheetRadius(sheetRadius_);
194     auto layoutProperty = sheetPage->GetLayoutProperty<SheetPresentationProperty>();
195     CHECK_NULL_VOID(layoutProperty);
196     auto sheetStyle = layoutProperty->GetSheetStyleValue();
197     placement_ = sheetStyle.placement.value_or(Placement::BOTTOM);
198     sheetPopupInfo_.finalPlacement = placement_;
199     sheetPopupInfo_.placementOnTarget = sheetStyle.placementOnTarget.value_or(true);
200     windowGlobalRect_ = pipeline->GetDisplayWindowRectInfo();
201     windowEdgeWidth_ = WINDOW_EDGE_SPACE.ConvertToPx();
202     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)) {
203         // global rect need to reduce the top and bottom safe area
204         windowGlobalRect_ = pipeline->GetCurrentWindowRect();
205         auto safeArea = pipeline->GetSafeArea();
206         RectF floatContainerModal;
207         RectF titleRect;
208         // get window rect title height
209         pipeline->GetContainerModalButtonsRect(floatContainerModal, titleRect);
210         auto offsetY = safeArea.top_.Length() + titleRect.Height();
211         auto height = windowGlobalRect_.Height() - offsetY - safeArea.bottom_.Length();
212         // windowRect neet to set as origin point, because sheet offset is relative to window rect
213         windowGlobalRect_ = Rect(0.f, offsetY, windowGlobalRect_.Width(), height);
214     }
215 }
216 
GetSheetPageSize(LayoutWrapper * layoutWrapper)217 void SheetWrapperLayoutAlgorithm::GetSheetPageSize(LayoutWrapper* layoutWrapper)
218 {
219     CHECK_NULL_VOID(layoutWrapper);
220     auto host = layoutWrapper->GetHostNode();
221     CHECK_NULL_VOID(host);
222     auto sheetWrapperPattern = host->GetPattern<SheetWrapperPattern>();
223     CHECK_NULL_VOID(sheetWrapperPattern);
224     auto sheetPage = sheetWrapperPattern->GetSheetPageNode();
225     CHECK_NULL_VOID(sheetPage);
226     auto sheetGeometryNode = sheetPage->GetGeometryNode();
227     CHECK_NULL_VOID(sheetGeometryNode);
228     sheetWidth_ = sheetGeometryNode->GetFrameSize().Width();
229     sheetHeight_ = sheetGeometryNode->GetFrameSize().Height();
230     DecreaseArrowHeightWhenArrowIsShown(sheetPage);
231     // when sheetWidth > global rect - 2 * windowEdgeSpace, windowEdgeSpace is set to the half of left space
232     if (GreatNotEqual(sheetWidth_, windowGlobalRect_.Width() - DOUBLE_SIZE * WINDOW_EDGE_SPACE.ConvertToPx())) {
233         windowEdgeWidth_ = (windowGlobalRect_.Width() - sheetWidth_) / DOUBLE_SIZE;
234     }
235 }
236 
DecreaseArrowHeightWhenArrowIsShown(const RefPtr<FrameNode> & sheetNode)237 void SheetWrapperLayoutAlgorithm::DecreaseArrowHeightWhenArrowIsShown(const RefPtr<FrameNode>& sheetNode)
238 {
239     if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)) {
240         return;
241     }
242 
243     auto sheetPattern = sheetNode->GetPattern<SheetPresentationPattern>();
244     CHECK_NULL_VOID(sheetPattern);
245     auto prePopupInfo = sheetPattern->GetSheetPopupInfo();
246     if (!prePopupInfo.showArrow) {
247         return;
248     }
249 
250     switch (prePopupInfo.finalPlacement) {
251         case Placement::BOTTOM_LEFT:
252             [[fallthrough]];
253         case Placement::BOTTOM_RIGHT:
254             [[fallthrough]];
255         case Placement::BOTTOM:
256             [[fallthrough]];
257         case Placement::TOP_LEFT:
258             [[fallthrough]];
259         case Placement::TOP_RIGHT:
260             [[fallthrough]];
261         case Placement::TOP: {
262             sheetHeight_ -= SHEET_ARROW_HEIGHT.ConvertToPx();
263             break;
264         }
265         case Placement::RIGHT_TOP:
266             [[fallthrough]];
267         case Placement::RIGHT_BOTTOM:
268             [[fallthrough]];
269         case Placement::RIGHT:
270             [[fallthrough]];
271         case Placement::LEFT_TOP:
272             [[fallthrough]];
273         case Placement::LEFT_BOTTOM:
274             [[fallthrough]];
275         case Placement::LEFT: {
276             sheetWidth_ -= SHEET_ARROW_HEIGHT.ConvertToPx();
277             break;
278         }
279         default:
280             break;
281     }
282 }
283 
GetPopupStyleSheetOffset(LayoutWrapper * layoutWrapper)284 OffsetF SheetWrapperLayoutAlgorithm::GetPopupStyleSheetOffset(LayoutWrapper* layoutWrapper)
285 {
286     auto host = layoutWrapper->GetHostNode();
287     CHECK_NULL_RETURN(host, OffsetF());
288     auto sheetWrapperPattern = host->GetPattern<SheetWrapperPattern>();
289     CHECK_NULL_RETURN(sheetWrapperPattern, OffsetF());
290     auto targetNode = sheetWrapperPattern->GetTargetNode();
291     CHECK_NULL_RETURN(targetNode, OffsetF());
292     auto geometryNode = targetNode->GetGeometryNode();
293     CHECK_NULL_RETURN(geometryNode, OffsetF());
294     auto targetSize = geometryNode->GetFrameSize();
295     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)) {
296         targetSize = targetNode->GetPaintRectWithTransform().GetSize();
297     }
298     auto targetOffset = targetNode->GetPaintRectOffset();
299     return GetOffsetInAvoidanceRule(layoutWrapper, targetSize, targetOffset);
300 }
301 
GetOffsetInAvoidanceRule(LayoutWrapper * layoutWrapper,const SizeF & targetSize,const OffsetF & targetOffset)302 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetInAvoidanceRule(
303     LayoutWrapper* layoutWrapper, const SizeF& targetSize, const OffsetF& targetOffset)
304 {
305     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)) {
306         sheetPopupInfo_.finalPlacement = AvoidanceRuleOfPlacement(layoutWrapper, targetSize, targetOffset);
307     } else {
308         // before api 16, only placement bottom is used
309         sheetPopupInfo_.finalPlacement = AvoidanceRuleBottom(Placement::BOTTOM, targetSize, targetOffset);
310     }
311     TAG_LOGI(AceLogTag::ACE_SHEET, "finalPlacement %{public}s",
312         PlacementUtils::ConvertPlacementToString(sheetPopupInfo_.finalPlacement).c_str());
313     if (getOffsetFunc_.find(sheetPopupInfo_.finalPlacement) == getOffsetFunc_.end()) {
314         TAG_LOGW(AceLogTag::ACE_SHEET, "It is an invalid Placement for current PopSheet.");
315         return OffsetF(0.f, 0.f);
316     }
317     auto offsetFunc = getOffsetFunc_[sheetPopupInfo_.finalPlacement];
318     CHECK_NULL_RETURN(offsetFunc, OffsetF());
319     /*
320      * steps 1. get sheet offset and restrict in window global rect
321      * steps 2. check whether need to show arrow
322      * steps 3. get arrow offset and check whether it is overlap sheet radius
323      */
324     return (this->*offsetFunc)(targetSize, targetOffset);
325 }
326 
AvoidanceRuleBottom(const Placement & currentPlacement,const SizeF & targetSize,const OffsetF & targetOffset)327 Placement SheetWrapperLayoutAlgorithm::AvoidanceRuleBottom(
328     const Placement& currentPlacement, const SizeF& targetSize, const OffsetF& targetOffset)
329 {
330     static std::map<Placement, std::vector<Placement>> PLACEMENT_STATES_BOTTOM = {
331         { Placement::BOTTOM,
332             {
333                 Placement::BOTTOM,
334                 Placement::BOTTOM_RIGHT,
335                 Placement::BOTTOM_LEFT,
336             } },
337     };
338     Placement targetPlacement = currentPlacement;
339     TAG_LOGD(AceLogTag::ACE_SHEET, "Init PopupSheet placement: %{public}s",
340         PlacementUtils::ConvertPlacementToString(targetPlacement).c_str());
341     // Step1: Determine the Placement in direction Bottom
342     auto& placementVec = PLACEMENT_STATES_BOTTOM[targetPlacement];
343     for (auto placement : placementVec) {
344         auto& placementFunc = placementCheckFunc_[placement];
345         if (placementFunc == nullptr) {
346             continue;
347         }
348         if ((this->*placementFunc)(targetSize, targetOffset)) {
349             targetPlacement = placement;
350             break;
351         }
352     }
353     TAG_LOGD(AceLogTag::ACE_SHEET, "After placementCheck, placement: %{public}s",
354         PlacementUtils::ConvertPlacementToString(targetPlacement).c_str());
355     return targetPlacement;
356 }
357 
AvoidanceRuleOfPlacement(LayoutWrapper * layoutWrapper,const SizeF & targetSize,const OffsetF & targetOffset)358 Placement SheetWrapperLayoutAlgorithm::AvoidanceRuleOfPlacement(
359     LayoutWrapper* layoutWrapper, const SizeF& targetSize, const OffsetF& targetOffset)
360 {
361     auto finalPlacement = placement_;
362     // step1: confirm the direction to place popup
363     TAG_LOGD(AceLogTag::ACE_SHEET, "Init PopupSheet placement: %{public}s",
364         PlacementUtils::ConvertPlacementToString(finalPlacement).c_str());
365     auto& placementStateVec = PLACEMENT_STATES[finalPlacement];
366     for (auto placement : placementStateVec) {
367         finalPlacement = placement;
368         auto& directionFunc = directionCheckFunc_[placement];
369         if (directionFunc == nullptr) {
370             continue;
371         }
372         if ((this->*directionFunc)(targetSize, targetOffset)) {
373             break;
374         }
375     }
376     TAG_LOGD(AceLogTag::ACE_SHEET, "After directionCheck, placement: %{public}s",
377         PlacementUtils::ConvertPlacementToString(finalPlacement).c_str());
378     // step2: check whether placement direction is NONE;
379     if (finalPlacement == Placement::NONE) {
380         sheetPopupInfo_.placementRechecked = true;
381         if (sheetPopupInfo_.placementOnTarget) {
382             finalPlacement = placement_;
383         } else {
384             SizeF bestSize = SizeF(0.f, 0.f);
385             finalPlacement = RecheckBestPlacementWithInsufficientSpace(targetSize, targetOffset, bestSize);
386             sheetHeight_ = bestSize.Height();
387         }
388     } else {
389         sheetPopupInfo_.placementRechecked = false;
390     }
391     return finalPlacement;
392 }
393 
CheckDirectionBottom(const SizeF & targetSize,const OffsetF & targetOffset)394 bool SheetWrapperLayoutAlgorithm::CheckDirectionBottom(const SizeF& targetSize, const OffsetF& targetOffset)
395 {
396     float sheetPageAvoidHeight = (SHEET_TARGET_SPACE + WINDOW_EDGE_SPACE).ConvertToPx();
397     /* if sheetWidth < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show,
398      * so that arrowHeight is no need to avoid.
399      */
400     if (GreatOrEqual(sheetWidth_, sheetRadius_.radiusTopLeft->ConvertToPx() +
401         sheetRadius_.radiusTopRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
402         sheetPageAvoidHeight += SHEET_ARROW_HEIGHT.ConvertToPx();
403     }
404     return GreatOrEqual(windowGlobalRect_.Width(), sheetWidth_ + DOUBLE_SIZE * windowEdgeWidth_) &&
405         GreatOrEqual(windowGlobalRect_.Height() + windowGlobalRect_.Top() - targetOffset.GetY() - targetSize.Height(),
406             sheetHeight_ + sheetPageAvoidHeight);
407 }
408 
CheckDirectionTop(const SizeF & targetSize,const OffsetF & targetOffset)409 bool SheetWrapperLayoutAlgorithm::CheckDirectionTop(const SizeF& targetSize, const OffsetF& targetOffset)
410 {
411     float sheetPageAvoidHeight = (SHEET_TARGET_SPACE + WINDOW_EDGE_SPACE).ConvertToPx();
412     /* if sheetWidth < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show,
413      * so that arrowHeight is no need to avoid.
414      */
415     if (GreatOrEqual(sheetWidth_, sheetRadius_.radiusBottomLeft->ConvertToPx() +
416         sheetRadius_.radiusBottomRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
417         sheetPageAvoidHeight += SHEET_ARROW_HEIGHT.ConvertToPx();
418     }
419     return GreatOrEqual(windowGlobalRect_.Width(), sheetWidth_ + DOUBLE_SIZE * windowEdgeWidth_) &&
420         GreatOrEqual(targetOffset.GetY() - windowGlobalRect_.Top(),
421             sheetHeight_ + sheetPageAvoidHeight);
422 }
423 
CheckDirectionRight(const SizeF & targetSize,const OffsetF & targetOffset)424 bool SheetWrapperLayoutAlgorithm::CheckDirectionRight(const SizeF& targetSize, const OffsetF& targetOffset)
425 {
426     float sheetPageAvoidWidth = (SHEET_TARGET_SPACE + WINDOW_EDGE_SPACE).ConvertToPx();
427     /* if sheetHeight < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show,
428      * so that arrowHeight is no need to avoid.
429      */
430     if (GreatOrEqual(sheetHeight_, sheetRadius_.radiusTopLeft->ConvertToPx() +
431         sheetRadius_.radiusBottomLeft->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
432         sheetPageAvoidWidth += SHEET_ARROW_HEIGHT.ConvertToPx();
433     }
434     return GreatOrEqual(windowGlobalRect_.Width() - targetOffset.GetX() - targetSize.Width(),
435         sheetWidth_ + sheetPageAvoidWidth) &&
436            GreatOrEqual(windowGlobalRect_.Height(), sheetHeight_ + DOUBLE_SIZE * WINDOW_EDGE_SPACE.ConvertToPx());
437 }
438 
CheckDirectionLeft(const SizeF & targetSize,const OffsetF & targetOffset)439 bool SheetWrapperLayoutAlgorithm::CheckDirectionLeft(const SizeF& targetSize, const OffsetF& targetOffset)
440 {
441     float sheetPageAvoidWidth = (SHEET_TARGET_SPACE + WINDOW_EDGE_SPACE).ConvertToPx();
442     /* if sheetHeight < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show,
443      * so that arrowHeight is no need to avoid.
444      */
445     if (GreatOrEqual(sheetHeight_, sheetRadius_.radiusTopRight->ConvertToPx() +
446         sheetRadius_.radiusBottomRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
447         sheetPageAvoidWidth += SHEET_ARROW_HEIGHT.ConvertToPx();
448     }
449     return GreatOrEqual(targetOffset.GetX(), sheetWidth_ + sheetPageAvoidWidth) &&
450         GreatOrEqual(windowGlobalRect_.Height(),
451             sheetHeight_ + DOUBLE_SIZE * WINDOW_EDGE_SPACE.ConvertToPx());
452 }
453 
RecheckBestPlacementWithInsufficientSpace(const SizeF & targetSize,const OffsetF & targetOffset,SizeF & bestSize)454 Placement SheetWrapperLayoutAlgorithm::RecheckBestPlacementWithInsufficientSpace(
455     const SizeF& targetSize, const OffsetF& targetOffset, SizeF& bestSize)
456 {
457     auto expectedPlacement = Placement::NONE;
458     auto& recheckPlacementVec = PLACEMENT_STATES[placement_];
459     for (auto placement : recheckPlacementVec) {
460         auto curLeftSpace = GetLeftSpaceWithPlacement(placement, targetSize, targetOffset);
461         /* best placement need to meet the following conditions:
462          * 1.space width >= sheetWidth
463          * 2.space height is the biggest in every placement
464          */
465         if (curLeftSpace.Width() >= sheetWidth_ && curLeftSpace.Height() < sheetHeight_) {
466             if (curLeftSpace.Height() > bestSize.Height()) {
467                 bestSize = curLeftSpace;
468                 expectedPlacement = placement;
469             }
470         }
471     }
472     TAG_LOGI(AceLogTag::ACE_SHEET, "get best size in insufficient space: %{public}s", bestSize.ToString().c_str());
473     return expectedPlacement;
474 }
475 
CheckPlacementBottom(const SizeF & targetSize,const OffsetF & targetOffset)476 bool SheetWrapperLayoutAlgorithm::CheckPlacementBottom(const SizeF& targetSize, const OffsetF& targetOffset)
477 {
478     return GreatOrEqual(
479         windowGlobalRect_.Width() - WINDOW_EDGE_SPACE.ConvertToPx(),
480         targetOffset.GetX() + targetSize.Width() / DOUBLE_SIZE + sheetWidth_ / DOUBLE_SIZE) &&
481         LessOrEqual(
482             WINDOW_EDGE_SPACE.ConvertToPx(),
483             targetOffset.GetX() + targetSize.Width() / DOUBLE_SIZE - sheetWidth_ / DOUBLE_SIZE);
484 }
485 
CheckPlacementBottomLeft(const SizeF & targetSize,const OffsetF & targetOffset)486 bool SheetWrapperLayoutAlgorithm::CheckPlacementBottomLeft(const SizeF& targetSize, const OffsetF& targetOffset)
487 {
488     return LessOrEqual(WINDOW_EDGE_SPACE.ConvertToPx(), targetOffset.GetX()) &&
489            GreatOrEqual(windowGlobalRect_.Width() - WINDOW_EDGE_SPACE.ConvertToPx(), targetOffset.GetX() + sheetWidth_);
490 }
491 
CheckPlacementBottomRight(const SizeF & targetSize,const OffsetF & targetOffset)492 bool SheetWrapperLayoutAlgorithm::CheckPlacementBottomRight(const SizeF& targetSize, const OffsetF& targetOffset)
493 {
494     return LessOrEqual(WINDOW_EDGE_SPACE.ConvertToPx(), targetOffset.GetX() + targetSize.Width() - sheetWidth_) &&
495            GreatOrEqual(
496                windowGlobalRect_.Width() - WINDOW_EDGE_SPACE.ConvertToPx(), targetOffset.GetX() + targetSize.Width());
497 }
498 
GetLeftSpaceWithPlacement(const Placement & placement,const SizeF & targetSize,const OffsetF & targetOffset)499 SizeF SheetWrapperLayoutAlgorithm::GetLeftSpaceWithPlacement(
500     const Placement& placement, const SizeF& targetSize, const OffsetF& targetOffset)
501 {
502     float width = 0.f;
503     float height = 0.f;
504     float sheetPageAvoidHeight = (SHEET_TARGET_SPACE + WINDOW_EDGE_SPACE).ConvertToPx();
505     float sheetPageAvoidWidth = (SHEET_TARGET_SPACE + WINDOW_EDGE_SPACE).ConvertToPx();
506     switch (placement) {
507         case Placement::BOTTOM_LEFT:
508             [[fallthrough]];
509         case Placement::BOTTOM_RIGHT:
510             [[fallthrough]];
511         case Placement::BOTTOM: {
512             if (GreatOrEqual(sheetWidth_, sheetRadius_.radiusTopLeft->ConvertToPx() +
513                 sheetRadius_.radiusTopRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
514                 sheetPageAvoidHeight += SHEET_ARROW_HEIGHT.ConvertToPx();
515             }
516             width = windowGlobalRect_.Width() - DOUBLE_SIZE * windowEdgeWidth_;
517             height = windowGlobalRect_.Height() + windowGlobalRect_.Top() -
518                 targetOffset.GetY() - targetSize.Height() - sheetPageAvoidHeight;
519             break;
520         }
521         case Placement::TOP_LEFT:
522             [[fallthrough]];
523         case Placement::TOP_RIGHT:
524             [[fallthrough]];
525         case Placement::TOP: {
526             if (GreatOrEqual(sheetWidth_, sheetRadius_.radiusBottomLeft->ConvertToPx() +
527                 sheetRadius_.radiusBottomRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
528                 sheetPageAvoidHeight += SHEET_ARROW_HEIGHT.ConvertToPx();
529             }
530             width = windowGlobalRect_.Width() - DOUBLE_SIZE * windowEdgeWidth_;
531             height = targetOffset.GetY() - windowGlobalRect_.Top() - sheetPageAvoidHeight;
532             break;
533         }
534         case Placement::RIGHT_TOP:
535             [[fallthrough]];
536         case Placement::RIGHT_BOTTOM:
537             [[fallthrough]];
538         case Placement::RIGHT: {
539             if (GreatOrEqual(sheetHeight_, sheetRadius_.radiusTopLeft->ConvertToPx() +
540                 sheetRadius_.radiusBottomLeft->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
541                 sheetPageAvoidWidth += SHEET_ARROW_HEIGHT.ConvertToPx();
542             }
543             width = windowGlobalRect_.Width() - targetOffset.GetX() - targetSize.Width() - sheetPageAvoidWidth;
544             height = windowGlobalRect_.Height() - DOUBLE_SIZE * WINDOW_EDGE_SPACE.ConvertToPx();
545             break;
546         }
547         case Placement::LEFT_TOP:
548             [[fallthrough]];
549         case Placement::LEFT_BOTTOM:
550             [[fallthrough]];
551         case Placement::LEFT: {
552             if (GreatOrEqual(sheetHeight_, sheetRadius_.radiusTopRight->ConvertToPx() +
553                 sheetRadius_.radiusBottomRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
554                 sheetPageAvoidWidth += SHEET_ARROW_HEIGHT.ConvertToPx();
555             }
556             width = targetOffset.GetX() - sheetPageAvoidWidth;
557             height = windowGlobalRect_.Height() - DOUBLE_SIZE * WINDOW_EDGE_SPACE.ConvertToPx();
558             break;
559         }
560         default:
561             break;
562     }
563     return SizeF(width, height);
564 }
565 
GetOffsetWithBottom(const SizeF & targetSize,const OffsetF & targetOffset)566 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetWithBottom(
567     const SizeF& targetSize, const OffsetF& targetOffset)
568 {
569     if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)) {
570         sheetPopupInfo_.arrowOffsetX = sheetWidth_ / DOUBLE_SIZE;
571         return OffsetF(targetOffset.GetX() + (targetSize.Width() - sheetWidth_) / DOUBLE_SIZE,
572             targetOffset.GetY() + targetSize.Height() + SHEET_TARGET_SPACE.ConvertToPx());
573     }
574 
575     float finalOffsetX = targetOffset.GetX() + (targetSize.Width() - sheetWidth_) / DOUBLE_SIZE;
576     float finalOffsetY = targetOffset.GetY() + targetSize.Height() + SHEET_TARGET_SPACE.ConvertToPx();
577     RestrictOffsetInSpaceBottom(finalOffsetX, finalOffsetY);
578     if (!sheetPopupInfo_.showArrow) {
579         return OffsetF(finalOffsetX, finalOffsetY);
580     }
581     // if sheetWidth < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show
582     if (LessNotEqual(sheetWidth_, sheetRadius_.radiusTopLeft->ConvertToPx() +
583         sheetRadius_.radiusTopRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
584         sheetPopupInfo_.showArrow = false;
585         return OffsetF(finalOffsetX, finalOffsetY);
586     }
587     SetArrowOffsetInBottomOrTop(targetSize, targetOffset, finalOffsetX);
588     CheckIsArrowOverlapSheetRadius();
589     return OffsetF(finalOffsetX, finalOffsetY);
590 }
591 
GetOffsetWithBottomLeft(const SizeF & targetSize,const OffsetF & targetOffset)592 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetWithBottomLeft(
593     const SizeF& targetSize, const OffsetF& targetOffset)
594 {
595     if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)) {
596         sheetPopupInfo_.arrowOffsetX = targetSize.Width() / DOUBLE_SIZE;
597         auto sheetOffset =
598             OffsetF(targetOffset.GetX(), targetOffset.GetY() + targetSize.Height() + SHEET_TARGET_SPACE.ConvertToPx());
599 
600         // if the arrow overlaps the sheet left corner, move sheet to the 6vp from the left edge
601         if (LessNotEqual(sheetPopupInfo_.arrowOffsetX - ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(),
602             sheetRadius_.radiusTopLeft->ConvertToPx())) {
603             sheetOffset.SetX(WINDOW_EDGE_SPACE.ConvertToPx());
604             sheetPopupInfo_.arrowOffsetX = targetOffset.GetX() + targetSize.Width() / DOUBLE_SIZE - sheetOffset.GetX();
605             TAG_LOGD(AceLogTag::ACE_SHEET, "Adjust sheet to the left boundary of the screen");
606         }
607 
608         // if the arrow still overlaps the sheet left corner, the arrow will become a right angle.
609         if (LessNotEqual(sheetPopupInfo_.arrowOffsetX - ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(),
610             sheetRadius_.radiusTopLeft->ConvertToPx())) {
611             TAG_LOGD(AceLogTag::ACE_SHEET, "Need to switch the arrow into the right-angle arrow");
612         }
613         return sheetOffset;
614     }
615 
616     float finalOffsetX = targetOffset.GetX();
617     float finalOffsetY = targetOffset.GetY() + targetSize.Height() + SHEET_TARGET_SPACE.ConvertToPx();
618     RestrictOffsetInSpaceBottom(finalOffsetX, finalOffsetY);
619     if (!sheetPopupInfo_.showArrow) {
620         return OffsetF(finalOffsetX, finalOffsetY);
621     }
622     // if sheetWidth < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show
623     if (LessNotEqual(sheetWidth_, sheetRadius_.radiusTopLeft->ConvertToPx() +
624         sheetRadius_.radiusTopRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
625         sheetPopupInfo_.showArrow = false;
626         return OffsetF(finalOffsetX, finalOffsetY);
627     }
628     SetArrowOffsetInBottomOrTop(targetSize, targetOffset, finalOffsetX);
629     CheckIsArrowOverlapSheetRadius();
630     return OffsetF(finalOffsetX, finalOffsetY);
631 }
632 
GetOffsetWithBottomRight(const SizeF & targetSize,const OffsetF & targetOffset)633 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetWithBottomRight(
634     const SizeF& targetSize, const OffsetF& targetOffset)
635 {
636     if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)) {
637         sheetPopupInfo_.arrowOffsetX = sheetWidth_ - targetSize.Width() / DOUBLE_SIZE;
638         auto sheetOffset = OffsetF(targetOffset.GetX() + targetSize.Width() - sheetWidth_,
639             targetOffset.GetY() + targetSize.Height() + SHEET_TARGET_SPACE.ConvertToPx());
640 
641         // if the arrow overlaps the sheet right corner, move sheet to the 6vp from the right edge
642         if (GreatNotEqual(sheetPopupInfo_.arrowOffsetX + sheetRadius_.radiusTopRight->ConvertToPx() +
643             ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx(), sheetWidth_)) {
644             sheetOffset.SetX(windowGlobalRect_.Width() - WINDOW_EDGE_SPACE.ConvertToPx() - sheetWidth_);
645             sheetPopupInfo_.arrowOffsetX = targetOffset.GetX() + targetSize.Width() / DOUBLE_SIZE - sheetOffset.GetX();
646             TAG_LOGD(AceLogTag::ACE_SHEET, "Adjust sheet to the right boundary of the screen");
647         }
648 
649         // if the arrow still overlaps the sheet right corner, the arrow will become a right angle.
650         if (GreatNotEqual(sheetPopupInfo_.arrowOffsetX + sheetRadius_.radiusTopRight->ConvertToPx() +
651             ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx(), sheetWidth_)) {
652             TAG_LOGD(AceLogTag::ACE_SHEET, "Need to switch the arrow into the right angle arrow");
653         }
654         return sheetOffset;
655     }
656 
657     float finalOffsetX = targetOffset.GetX() + targetSize.Width() - sheetWidth_;
658     float finalOffsetY = targetOffset.GetY() + targetSize.Height() + SHEET_TARGET_SPACE.ConvertToPx();
659     RestrictOffsetInSpaceBottom(finalOffsetX, finalOffsetY);
660     if (!sheetPopupInfo_.showArrow) {
661         return OffsetF(finalOffsetX, finalOffsetY);
662     }
663     // if sheetWidth < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show
664     if (LessNotEqual(sheetWidth_, sheetRadius_.radiusTopLeft->ConvertToPx() +
665         sheetRadius_.radiusTopRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
666         sheetPopupInfo_.showArrow = false;
667         return OffsetF(finalOffsetX, finalOffsetY);
668     }
669     SetArrowOffsetInBottomOrTop(targetSize, targetOffset, finalOffsetX);
670     CheckIsArrowOverlapSheetRadius();
671     return OffsetF(finalOffsetX, finalOffsetY);
672 }
673 
GetOffsetWithTop(const SizeF & targetSize,const OffsetF & targetOffset)674 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetWithTop(const SizeF& targetSize, const OffsetF& targetOffset)
675 {
676     float finalOffsetX = targetOffset.GetX() + (targetSize.Width() - sheetWidth_) / DOUBLE_SIZE;
677     float finalOffsetY = targetOffset.GetY() -
678         SHEET_TARGET_SPACE.ConvertToPx() - sheetHeight_ - SHEET_ARROW_HEIGHT.ConvertToPx();
679     RestrictOffsetInSpaceTop(finalOffsetX, finalOffsetY);
680     if (!sheetPopupInfo_.showArrow) {
681         return OffsetF(finalOffsetX, finalOffsetY);
682     }
683     // if sheetWidth < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show
684     if (LessNotEqual(sheetWidth_, sheetRadius_.radiusBottomLeft->ConvertToPx() +
685         sheetRadius_.radiusBottomRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
686         sheetPopupInfo_.showArrow = false;
687         return OffsetF(finalOffsetX, finalOffsetY);
688     }
689     SetArrowOffsetInBottomOrTop(targetSize, targetOffset, finalOffsetX);
690     CheckIsArrowOverlapSheetRadius();
691     return OffsetF(finalOffsetX, finalOffsetY);
692 }
693 
GetOffsetWithTopLeft(const SizeF & targetSize,const OffsetF & targetOffset)694 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetWithTopLeft(const SizeF& targetSize, const OffsetF& targetOffset)
695 {
696     float finalOffsetX = targetOffset.GetX();
697     float finalOffsetY = targetOffset.GetY() -
698         SHEET_TARGET_SPACE.ConvertToPx() - sheetHeight_ - SHEET_ARROW_HEIGHT.ConvertToPx();
699     RestrictOffsetInSpaceTop(finalOffsetX, finalOffsetY);
700     if (!sheetPopupInfo_.showArrow) {
701         return OffsetF(finalOffsetX, finalOffsetY);
702     }
703     // if sheetWidth < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show
704     if (LessNotEqual(sheetWidth_, sheetRadius_.radiusBottomLeft->ConvertToPx() +
705         sheetRadius_.radiusBottomRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
706         sheetPopupInfo_.showArrow = false;
707         return OffsetF(finalOffsetX, finalOffsetY);
708     }
709     SetArrowOffsetInBottomOrTop(targetSize, targetOffset, finalOffsetX);
710     CheckIsArrowOverlapSheetRadius();
711     return OffsetF(finalOffsetX, finalOffsetY);
712 }
713 
GetOffsetWithTopRight(const SizeF & targetSize,const OffsetF & targetOffset)714 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetWithTopRight(
715     const SizeF& targetSize, const OffsetF& targetOffset)
716 {
717     float finalOffsetX = targetOffset.GetX() + targetSize.Width() - sheetWidth_;
718     float finalOffsetY = targetOffset.GetY() -
719         SHEET_TARGET_SPACE.ConvertToPx() - sheetHeight_ - SHEET_ARROW_HEIGHT.ConvertToPx();
720     RestrictOffsetInSpaceTop(finalOffsetX, finalOffsetY);
721     if (!sheetPopupInfo_.showArrow) {
722         return OffsetF(finalOffsetX, finalOffsetY);
723     }
724     // if sheetWidth < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show
725     if (LessNotEqual(sheetWidth_, sheetRadius_.radiusBottomLeft->ConvertToPx() +
726         sheetRadius_.radiusBottomRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
727         sheetPopupInfo_.showArrow = false;
728         return OffsetF(finalOffsetX, finalOffsetY);
729     }
730     SetArrowOffsetInBottomOrTop(targetSize, targetOffset, finalOffsetX);
731     CheckIsArrowOverlapSheetRadius();
732     return OffsetF(finalOffsetX, finalOffsetY);
733 }
734 
GetOffsetWithLeft(const SizeF & targetSize,const OffsetF & targetOffset)735 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetWithLeft(const SizeF& targetSize, const OffsetF& targetOffset)
736 {
737     float finalOffsetX = targetOffset.GetX() -
738         sheetWidth_ - SHEET_TARGET_SPACE.ConvertToPx() - SHEET_ARROW_HEIGHT.ConvertToPx();
739     float finalOffsetY = targetOffset.GetY() + (targetSize.Height() - sheetHeight_) / DOUBLE_SIZE;
740     RestrictOffsetInSpaceLeft(finalOffsetX, finalOffsetY);
741     if (!sheetPopupInfo_.showArrow) {
742         return OffsetF(finalOffsetX, finalOffsetY);
743     }
744     // if sheetHeight < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show
745     if (LessNotEqual(sheetHeight_, sheetRadius_.radiusTopRight->ConvertToPx() +
746         sheetRadius_.radiusBottomRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
747         sheetPopupInfo_.showArrow = false;
748         return OffsetF(finalOffsetX, finalOffsetY);
749     }
750     SetArrowOffsetInRightOrLeft(targetSize, targetOffset, finalOffsetY);
751     CheckIsArrowOverlapSheetRadius();
752     return OffsetF(finalOffsetX, finalOffsetY);
753 }
754 
GetOffsetWithLeftTop(const SizeF & targetSize,const OffsetF & targetOffset)755 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetWithLeftTop(const SizeF& targetSize, const OffsetF& targetOffset)
756 {
757     float finalOffsetX = targetOffset.GetX() -
758         sheetWidth_ - SHEET_TARGET_SPACE.ConvertToPx() - SHEET_ARROW_HEIGHT.ConvertToPx();
759     float finalOffsetY = targetOffset.GetY();
760     RestrictOffsetInSpaceLeft(finalOffsetX, finalOffsetY);
761     if (!sheetPopupInfo_.showArrow) {
762         return OffsetF(finalOffsetX, finalOffsetY);
763     }
764     // if sheetHeight < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show
765     if (LessNotEqual(sheetHeight_, sheetRadius_.radiusTopRight->ConvertToPx() +
766         sheetRadius_.radiusBottomRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
767         sheetPopupInfo_.showArrow = false;
768         return OffsetF(finalOffsetX, finalOffsetY);
769     }
770     SetArrowOffsetInRightOrLeft(targetSize, targetOffset, finalOffsetY);
771     CheckIsArrowOverlapSheetRadius();
772     return OffsetF(finalOffsetX, finalOffsetY);
773 }
774 
GetOffsetWithLeftBottom(const SizeF & targetSize,const OffsetF & targetOffset)775 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetWithLeftBottom(
776     const SizeF& targetSize, const OffsetF& targetOffset)
777 {
778     float finalOffsetX = targetOffset.GetX() -
779         sheetWidth_ - SHEET_TARGET_SPACE.ConvertToPx() - SHEET_ARROW_HEIGHT.ConvertToPx();
780     float finalOffsetY = targetOffset.GetY() + targetSize.Height() - sheetHeight_;
781     RestrictOffsetInSpaceLeft(finalOffsetX, finalOffsetY);
782     if (!sheetPopupInfo_.showArrow) {
783         return OffsetF(finalOffsetX, finalOffsetY);
784     }
785     // if sheetHeight < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show
786     if (LessNotEqual(sheetHeight_, sheetRadius_.radiusTopRight->ConvertToPx() +
787         sheetRadius_.radiusBottomRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
788         sheetPopupInfo_.showArrow = false;
789         return OffsetF(finalOffsetX, finalOffsetY);
790     }
791     SetArrowOffsetInRightOrLeft(targetSize, targetOffset, finalOffsetY);
792     CheckIsArrowOverlapSheetRadius();
793     return OffsetF(finalOffsetX, finalOffsetY);
794 }
795 
GetOffsetWithRight(const SizeF & targetSize,const OffsetF & targetOffset)796 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetWithRight(const SizeF& targetSize, const OffsetF& targetOffset)
797 {
798     float finalOffsetX = targetOffset.GetX() + targetSize.Width() + SHEET_TARGET_SPACE.ConvertToPx();
799     float finalOffsetY = targetOffset.GetY() + (targetSize.Height() - sheetHeight_) / DOUBLE_SIZE;
800     RestrictOffsetInSpaceRight(finalOffsetX, finalOffsetY);
801     if (!sheetPopupInfo_.showArrow) {
802         return OffsetF(finalOffsetX, finalOffsetY);
803     }
804     // if sheetHeight < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show
805     if (LessNotEqual(sheetHeight_, sheetRadius_.radiusTopLeft->ConvertToPx() +
806         sheetRadius_.radiusBottomLeft->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
807         sheetPopupInfo_.showArrow = false;
808         return OffsetF(finalOffsetX, finalOffsetY);
809     }
810     SetArrowOffsetInRightOrLeft(targetSize, targetOffset, finalOffsetY);
811     CheckIsArrowOverlapSheetRadius();
812     return OffsetF(finalOffsetX, finalOffsetY);
813 }
814 
GetOffsetWithRightTop(const SizeF & targetSize,const OffsetF & targetOffset)815 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetWithRightTop(const SizeF& targetSize, const OffsetF& targetOffset)
816 {
817     float finalOffsetX = targetOffset.GetX() + targetSize.Width() + SHEET_TARGET_SPACE.ConvertToPx();
818     float finalOffsetY = targetOffset.GetY();
819     RestrictOffsetInSpaceRight(finalOffsetX, finalOffsetY);
820     if (!sheetPopupInfo_.showArrow) {
821         return OffsetF(finalOffsetX, finalOffsetY);
822     }
823     // if sheetHeight < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show
824     if (LessNotEqual(sheetHeight_, sheetRadius_.radiusTopLeft->ConvertToPx() +
825         sheetRadius_.radiusBottomLeft->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
826         sheetPopupInfo_.showArrow = false;
827         return OffsetF(finalOffsetX, finalOffsetY);
828     }
829     SetArrowOffsetInRightOrLeft(targetSize, targetOffset, finalOffsetY);
830     CheckIsArrowOverlapSheetRadius();
831     return OffsetF(finalOffsetX, finalOffsetY);
832 }
833 
GetOffsetWithRightBottom(const SizeF & targetSize,const OffsetF & targetOffset)834 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetWithRightBottom(
835     const SizeF& targetSize, const OffsetF& targetOffset)
836 {
837     float finalOffsetX = targetOffset.GetX() + targetSize.Width() + SHEET_TARGET_SPACE.ConvertToPx();
838     float finalOffsetY = targetOffset.GetY() + targetSize.Height() - sheetHeight_;
839     RestrictOffsetInSpaceRight(finalOffsetX, finalOffsetY);
840     if (!sheetPopupInfo_.showArrow) {
841         return OffsetF(finalOffsetX, finalOffsetY);
842     }
843     // if sheetHeight < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show
844     if (LessNotEqual(sheetHeight_, sheetRadius_.radiusTopLeft->ConvertToPx() +
845         sheetRadius_.radiusBottomLeft->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
846         sheetPopupInfo_.showArrow = false;
847         return OffsetF(finalOffsetX, finalOffsetY);
848     }
849     SetArrowOffsetInRightOrLeft(targetSize, targetOffset, finalOffsetY);
850     CheckIsArrowOverlapSheetRadius();
851     return OffsetF(finalOffsetX, finalOffsetY);
852 }
853 
SetArrowOffsetInBottomOrTop(const SizeF & targetSize,const OffsetF & targetOffset,float sheetOffset)854 void SheetWrapperLayoutAlgorithm::SetArrowOffsetInBottomOrTop(
855     const SizeF& targetSize, const OffsetF& targetOffset, float sheetOffset)
856 {
857     sheetPopupInfo_.arrowOffsetX = targetOffset.GetX() + targetSize.Width() / DOUBLE_SIZE - sheetOffset;
858 
859     sheetPopupInfo_.arrowOffsetX = std::clamp(static_cast<double>(sheetPopupInfo_.arrowOffsetX),
860         ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(),
861         sheetWidth_ - ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx());
862 }
863 
SetArrowOffsetInRightOrLeft(const SizeF & targetSize,const OffsetF & targetOffset,float sheetOffset)864 void SheetWrapperLayoutAlgorithm::SetArrowOffsetInRightOrLeft(
865     const SizeF& targetSize, const OffsetF& targetOffset, float sheetOffset)
866 {
867     sheetPopupInfo_.arrowOffsetY = targetOffset.GetY() + targetSize.Height() / DOUBLE_SIZE - sheetOffset;
868 
869     sheetPopupInfo_.arrowOffsetY = std::clamp(static_cast<double>(sheetPopupInfo_.arrowOffsetY),
870         ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(),
871         sheetHeight_ - ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx());
872 }
873 
RestrictOffsetInSpaceBottom(float & offsetX,float & offsetY)874 void SheetWrapperLayoutAlgorithm::RestrictOffsetInSpaceBottom(float& offsetX, float& offsetY)
875 {
876     offsetX = std::clamp(static_cast<double>(offsetX), static_cast<double>(windowEdgeWidth_),
877         windowGlobalRect_.Width() - sheetWidth_ - windowEdgeWidth_);
878 
879     offsetY = std::clamp(static_cast<double>(offsetY), windowGlobalRect_.Top() + WINDOW_EDGE_SPACE.ConvertToPx(),
880         windowGlobalRect_.Height() + windowGlobalRect_.Top() - sheetHeight_ - WINDOW_EDGE_SPACE.ConvertToPx());
881 
882     if (sheetPopupInfo_.placementRechecked && sheetPopupInfo_.placementOnTarget) {
883         sheetPopupInfo_.showArrow = false;
884         offsetY =
885             windowGlobalRect_.Height() + windowGlobalRect_.Top() - sheetHeight_ - WINDOW_EDGE_SPACE.ConvertToPx();
886     }
887 }
888 
RestrictOffsetInSpaceTop(float & offsetX,float & offsetY)889 void SheetWrapperLayoutAlgorithm::RestrictOffsetInSpaceTop(float& offsetX, float& offsetY)
890 {
891     offsetX = std::clamp(static_cast<double>(offsetX), static_cast<double>(windowEdgeWidth_),
892         windowGlobalRect_.Width() - sheetWidth_ - windowEdgeWidth_);
893 
894     offsetY = std::clamp(static_cast<double>(offsetY), windowGlobalRect_.Top() + WINDOW_EDGE_SPACE.ConvertToPx(),
895         windowGlobalRect_.Height() + windowGlobalRect_.Top() - sheetHeight_ - WINDOW_EDGE_SPACE.ConvertToPx());
896 
897     if (sheetPopupInfo_.placementRechecked && sheetPopupInfo_.placementOnTarget) {
898         sheetPopupInfo_.showArrow = false;
899         offsetY = windowGlobalRect_.Top() + WINDOW_EDGE_SPACE.ConvertToPx();
900     }
901 }
902 
RestrictOffsetInSpaceLeft(float & offsetX,float & offsetY)903 void SheetWrapperLayoutAlgorithm::RestrictOffsetInSpaceLeft(float& offsetX, float& offsetY)
904 {
905     offsetX = std::clamp(static_cast<double>(offsetX), static_cast<double>(windowEdgeWidth_),
906         windowGlobalRect_.Width() - sheetWidth_ - windowEdgeWidth_);
907 
908     offsetY = std::clamp(static_cast<double>(offsetY), windowGlobalRect_.Top() + WINDOW_EDGE_SPACE.ConvertToPx(),
909         windowGlobalRect_.Height() + windowGlobalRect_.Top() - sheetHeight_ - WINDOW_EDGE_SPACE.ConvertToPx());
910 
911     if (sheetPopupInfo_.placementRechecked && sheetPopupInfo_.placementOnTarget) {
912         sheetPopupInfo_.showArrow = false;
913         offsetX = windowEdgeWidth_;
914     }
915 }
916 
RestrictOffsetInSpaceRight(float & offsetX,float & offsetY)917 void SheetWrapperLayoutAlgorithm::RestrictOffsetInSpaceRight(float& offsetX, float& offsetY)
918 {
919     offsetX = std::clamp(static_cast<double>(offsetX), static_cast<double>(windowEdgeWidth_),
920         windowGlobalRect_.Width() - sheetWidth_ - windowEdgeWidth_);
921 
922     offsetY = std::clamp(static_cast<double>(offsetY), windowGlobalRect_.Top() + WINDOW_EDGE_SPACE.ConvertToPx(),
923         windowGlobalRect_.Height() + windowGlobalRect_.Top() - sheetHeight_ - WINDOW_EDGE_SPACE.ConvertToPx());
924 
925     if (sheetPopupInfo_.placementRechecked && sheetPopupInfo_.placementOnTarget) {
926         sheetPopupInfo_.showArrow = false;
927         offsetX = windowGlobalRect_.Width() - windowEdgeWidth_ - sheetWidth_;
928     }
929 }
930 
CheckIsArrowOverlapSheetRadius()931 void SheetWrapperLayoutAlgorithm::CheckIsArrowOverlapSheetRadius()
932 {
933     sheetPopupInfo_.arrowPosition = SheetArrowPosition::NONE;
934     switch (sheetPopupInfo_.finalPlacement) {
935         case Placement::BOTTOM_LEFT:
936             [[fallthrough]];
937         case Placement::BOTTOM_RIGHT:
938             [[fallthrough]];
939         case Placement::BOTTOM: {
940             if (LessNotEqual(sheetPopupInfo_.arrowOffsetX - ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(),
941                 sheetRadius_.radiusTopLeft->ConvertToPx())) {
942                 sheetPopupInfo_.arrowPosition = SheetArrowPosition::BOTTOM_LEFT;
943             } else if (GreatNotEqual(sheetPopupInfo_.arrowOffsetX + ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx(),
944                 sheetWidth_ - sheetRadius_.radiusTopRight->ConvertToPx())) {
945                 sheetPopupInfo_.arrowPosition = SheetArrowPosition::BOTTOM_RIGHT;
946             }
947             break;
948         }
949         case Placement::TOP_LEFT:
950             [[fallthrough]];
951         case Placement::TOP_RIGHT:
952             [[fallthrough]];
953         case Placement::TOP: {
954             if (LessNotEqual(sheetPopupInfo_.arrowOffsetX - ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx(),
955                 sheetRadius_.radiusBottomLeft->ConvertToPx())) {
956                 sheetPopupInfo_.arrowPosition = SheetArrowPosition::TOP_LEFT;
957             } else if (GreatNotEqual(sheetPopupInfo_.arrowOffsetX + ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(),
958                 sheetWidth_ - sheetRadius_.radiusBottomRight->ConvertToPx())) {
959                 sheetPopupInfo_.arrowPosition = SheetArrowPosition::TOP_RIGHT;
960             }
961             break;
962         }
963         case Placement::RIGHT_TOP:
964             [[fallthrough]];
965         case Placement::RIGHT_BOTTOM:
966             [[fallthrough]];
967         case Placement::RIGHT: {
968             if (LessNotEqual(sheetPopupInfo_.arrowOffsetY - ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(),
969                 sheetRadius_.radiusTopLeft->ConvertToPx())) {
970                 sheetPopupInfo_.arrowPosition = SheetArrowPosition::RIGHT_TOP;
971             } else if (GreatNotEqual(sheetPopupInfo_.arrowOffsetY + ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx(),
972                 sheetHeight_ - sheetRadius_.radiusBottomLeft->ConvertToPx())) {
973                 sheetPopupInfo_.arrowPosition = SheetArrowPosition::RIGHT_BOTTOM;
974             }
975             break;
976         }
977         case Placement::LEFT_TOP:
978             [[fallthrough]];
979         case Placement::LEFT_BOTTOM:
980             [[fallthrough]];
981         case Placement::LEFT: {
982             if (LessNotEqual(sheetPopupInfo_.arrowOffsetY - ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx(),
983                 sheetRadius_.radiusTopRight->ConvertToPx())) {
984                 sheetPopupInfo_.arrowPosition = SheetArrowPosition::LEFT_TOP;
985             } else if (GreatNotEqual(sheetPopupInfo_.arrowOffsetY + ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(),
986                 sheetHeight_ - sheetRadius_.radiusBottomRight->ConvertToPx())) {
987                 sheetPopupInfo_.arrowPosition = SheetArrowPosition::LEFT_BOTTOM;
988             }
989             break;
990         }
991         default:
992             sheetPopupInfo_.arrowPosition = SheetArrowPosition::NONE;
993             break;
994     }
995 }
996 
Layout(LayoutWrapper * layoutWrapper)997 void SheetWrapperLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
998 {
999     BoxLayoutAlgorithm::PerformLayout(layoutWrapper);
1000     auto host = layoutWrapper->GetHostNode();
1001     CHECK_NULL_VOID(host);
1002     auto sheetWrapperPattern = host->GetPattern<SheetWrapperPattern>();
1003     CHECK_NULL_VOID(sheetWrapperPattern);
1004     auto sheetPageNode = sheetWrapperPattern->GetSheetPageNode();
1005     CHECK_NULL_VOID(sheetPageNode);
1006     auto sheetPagePattern = sheetPageNode->GetPattern<SheetPresentationPattern>();
1007     CHECK_NULL_VOID(sheetPagePattern);
1008     auto sheetType = sheetPagePattern->GetSheetType();
1009     if (sheetType == SheetType::SHEET_POPUP) {
1010         TAG_LOGI(AceLogTag::ACE_SHEET, "before popup sheet page, origin size [%{public}f, %{public}f]",
1011             sheetWidth_, sheetHeight_);
1012         OffsetF popupOffset = GetPopupStyleSheetOffset(layoutWrapper);
1013         auto hostNode = layoutWrapper->GetHostNode();
1014         CHECK_NULL_VOID(hostNode);
1015         auto wrapperOffset = hostNode->GetPaintRectOffset();
1016         sheetPopupInfo_.sheetOffsetX = popupOffset.GetX() - wrapperOffset.GetX();
1017         sheetPopupInfo_.sheetOffsetY = popupOffset.GetY() - wrapperOffset.GetY();
1018         TAG_LOGI(AceLogTag::ACE_SHEET, "checked offset (%{public}f, %{public}f), checked size [%{public}f, %{public}f]",
1019             sheetPopupInfo_.sheetOffsetX, sheetPopupInfo_.sheetOffsetY, sheetWidth_, sheetHeight_);
1020         RemeasureForPopup(layoutWrapper);
1021     }
1022     auto index = host->GetChildIndexById(sheetPageNode->GetId());
1023     auto sheetPageWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1024     CHECK_NULL_VOID(sheetPageWrapper);
1025     sheetPageWrapper->Layout();
1026     LayoutMaskNode(layoutWrapper);
1027 }
1028 
LayoutMaskNode(LayoutWrapper * layoutWrapper)1029 void SheetWrapperLayoutAlgorithm::LayoutMaskNode(LayoutWrapper* layoutWrapper)
1030 {
1031     auto host = layoutWrapper->GetHostNode();
1032     CHECK_NULL_VOID(host);
1033     auto sheetWrapperPattern = host->GetPattern<SheetWrapperPattern>();
1034     CHECK_NULL_VOID(sheetWrapperPattern);
1035     if (!sheetWrapperPattern->ShowInUEC()) {
1036         return;
1037     }
1038     auto maskNode = sheetWrapperPattern->GetSheetMaskNode();
1039     CHECK_NULL_VOID(maskNode);
1040     auto maskPattern = maskNode->GetPattern<SheetMaskPattern>();
1041     CHECK_NULL_VOID(maskPattern);
1042     auto index = host->GetChildIndexById(maskNode->GetId());
1043     auto maskWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1044     CHECK_NULL_VOID(maskWrapper);
1045     auto rect = sheetWrapperPattern->GetMainWindowRect();
1046     auto contentOffset = OffsetF(rect.GetX(), rect.GetY());
1047     auto geometryNode = maskWrapper->GetGeometryNode();
1048     CHECK_NULL_VOID(geometryNode);
1049     geometryNode->SetMarginFrameOffset(contentOffset);
1050 
1051     auto currentId = Container::CurrentId();
1052     SubwindowManager::GetInstance()->DeleteHotAreas(currentId, maskNode->GetId(), SubwindowType::TYPE_SHEET);
1053     if (maskPattern->GetIsMaskInteractive()) {
1054         std::vector<Rect> rects;
1055         auto maskHotRect = Rect(rect.GetX(), rect.GetY(),
1056             maskNode->GetGeometryNode()->GetFrameSize().Width(), maskNode->GetGeometryNode()->GetFrameSize().Height());
1057         rects.emplace_back(maskHotRect);
1058         auto subWindowMgr = SubwindowManager::GetInstance();
1059         subWindowMgr->SetHotAreas(rects, SubwindowType::TYPE_SHEET, maskNode->GetId(), currentId);
1060     }
1061     maskWrapper->Layout();
1062 }
1063 
RemeasureForPopup(LayoutWrapper * layoutWrapper)1064 void SheetWrapperLayoutAlgorithm::RemeasureForPopup(LayoutWrapper* layoutWrapper)
1065 {
1066     UpdateSheetNodePopupInfo(layoutWrapper);
1067 }
1068 
UpdateSheetNodePopupInfo(LayoutWrapper * layoutWrapper)1069 void SheetWrapperLayoutAlgorithm::UpdateSheetNodePopupInfo(LayoutWrapper* layoutWrapper)
1070 {
1071     auto host = layoutWrapper->GetHostNode();
1072     CHECK_NULL_VOID(host);
1073     auto sheetWrapperPattern = host->GetPattern<SheetWrapperPattern>();
1074     CHECK_NULL_VOID(sheetWrapperPattern);
1075     auto sheetNode = sheetWrapperPattern->GetSheetPageNode();
1076     CHECK_NULL_VOID(sheetNode);
1077     auto index = host->GetChildIndexById(sheetNode->GetId());
1078     auto sheetPageWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1079     CHECK_NULL_VOID(sheetPageWrapper);
1080     auto sheetPageNode = sheetPageWrapper->GetHostNode();
1081     CHECK_NULL_VOID(sheetPageNode);
1082     auto sheetPagePattern = sheetPageNode->GetPattern<SheetPresentationPattern>();
1083     CHECK_NULL_VOID(sheetPagePattern);
1084     sheetPagePattern->UpdateSheetPopupInfo(sheetPopupInfo_);
1085     auto sheetPageLayoutAlgorithmWrapper = sheetPageWrapper->GetLayoutAlgorithm();
1086     CHECK_NULL_VOID(sheetPageLayoutAlgorithmWrapper);
1087     auto sheetPageLayoutAlgorithm =
1088         DynamicCast<SheetPresentationLayoutAlgorithm>(sheetPageLayoutAlgorithmWrapper->GetLayoutAlgorithm());
1089     CHECK_NULL_VOID(sheetPageLayoutAlgorithm);
1090     sheetPageLayoutAlgorithm->UpdatePopupInfoAndRemeasure(layoutWrapper, sheetPopupInfo_, sheetWidth_, sheetHeight_);
1091 }
1092 } // namespace OHOS::Ace::NG
1093