• 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 layoutProp = layoutWrapper->GetLayoutProperty();
170     CHECK_NULL_VOID(layoutProp);
171     auto constraint = layoutProp->CreateChildConstraint();
172     maskWrapper->Measure(constraint);
173 }
174 
InitParameter(LayoutWrapper * layoutWrapper)175 void SheetWrapperLayoutAlgorithm::InitParameter(LayoutWrapper* layoutWrapper)
176 {
177     auto host = layoutWrapper->GetHostNode();
178     CHECK_NULL_VOID(host);
179     auto pipeline = host->GetContext();
180     CHECK_NULL_VOID(pipeline);
181     auto sheetTheme = pipeline->GetTheme<SheetTheme>();
182     CHECK_NULL_VOID(sheetTheme);
183 
184     auto sheetWrapperPattern = host->GetPattern<SheetWrapperPattern>();
185     CHECK_NULL_VOID(sheetWrapperPattern);
186     auto sheetPage = sheetWrapperPattern->GetSheetPageNode();
187     CHECK_NULL_VOID(sheetPage);
188     auto sheetPattern = DynamicCast<SheetPresentationPattern>(sheetPage->GetPattern());
189     CHECK_NULL_VOID(sheetPattern);
190     sheetRadius_ = BorderRadiusProperty(sheetTheme->GetSheetRadius());
191     sheetPattern->CalculateSheetRadius(sheetRadius_);
192     auto layoutProperty = sheetPage->GetLayoutProperty<SheetPresentationProperty>();
193     CHECK_NULL_VOID(layoutProperty);
194     auto sheetStyle = layoutProperty->GetSheetStyleValue();
195     placement_ = sheetStyle.placement.value_or(Placement::BOTTOM);
196     sheetPopupInfo_.Reset();    // everytime sheetWrapper changed, we need to reset sheetPopupInfo to default value
197     sheetPopupInfo_.finalPlacement = placement_;
198     sheetPopupInfo_.placementOnTarget = sheetStyle.placementOnTarget.value_or(true);
199     windowGlobalRect_ = pipeline->GetDisplayWindowRectInfo();
200     windowEdgeWidth_ = WINDOW_EDGE_SPACE.ConvertToPx();
201 #ifndef PREVIEW
202     if (host->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         auto manager = pipeline->GetSafeAreaManager();
213         CHECK_NULL_VOID(manager);
214         auto keyboardInset = manager->GetKeyboardInset();
215         auto focusHub = sheetPage->GetFocusHub();
216         CHECK_NULL_VOID(focusHub);
217         auto isFocused = focusHub->IsCurrentFocus();
218         auto needAvoidKeyboard = sheetStyle.sheetKeyboardAvoidMode == SheetKeyboardAvoidMode::POPUP_SHEET;
219         if (keyboardInset.Length() != 0 && isFocused && needAvoidKeyboard) {
220             sheetPopupInfo_.keyboardShow = true;
221             height -= keyboardInset.Length() - safeArea.bottom_.Length();
222             sheetPopupInfo_.showArrow =
223                 !IsTargetNodeHideByKeyboard(keyboardInset, sheetWrapperPattern->GetTargetNode());
224         }
225         // windowRect neet to set as origin point, because sheet offset is relative to window rect
226         windowGlobalRect_ = Rect(0.f, offsetY, windowGlobalRect_.Width(), height);
227     }
228 #endif
229 }
230 
GetSheetPageSize(LayoutWrapper * layoutWrapper)231 void SheetWrapperLayoutAlgorithm::GetSheetPageSize(LayoutWrapper* layoutWrapper)
232 {
233     CHECK_NULL_VOID(layoutWrapper);
234     auto host = layoutWrapper->GetHostNode();
235     CHECK_NULL_VOID(host);
236     auto sheetWrapperPattern = host->GetPattern<SheetWrapperPattern>();
237     CHECK_NULL_VOID(sheetWrapperPattern);
238     auto sheetPage = sheetWrapperPattern->GetSheetPageNode();
239     CHECK_NULL_VOID(sheetPage);
240     auto sheetGeometryNode = sheetPage->GetGeometryNode();
241     CHECK_NULL_VOID(sheetGeometryNode);
242     sheetWidth_ = sheetGeometryNode->GetFrameSize().Width();
243     sheetHeight_ = sheetGeometryNode->GetFrameSize().Height();
244     DecreaseArrowHeightWhenArrowIsShown(sheetPage);
245     // when sheetWidth > global rect - 2 * windowEdgeSpace, windowEdgeSpace is set to the half of left space
246     if (GreatNotEqual(sheetWidth_, windowGlobalRect_.Width() - DOUBLE_SIZE * WINDOW_EDGE_SPACE.ConvertToPx())) {
247         windowEdgeWidth_ = (windowGlobalRect_.Width() - sheetWidth_) / DOUBLE_SIZE;
248     }
249 }
250 
DecreaseArrowHeightWhenArrowIsShown(const RefPtr<FrameNode> & sheetNode)251 void SheetWrapperLayoutAlgorithm::DecreaseArrowHeightWhenArrowIsShown(const RefPtr<FrameNode>& sheetNode)
252 {
253     if (sheetNode->LessThanAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)) {
254         return;
255     }
256 
257     auto sheetPattern = sheetNode->GetPattern<SheetPresentationPattern>();
258     CHECK_NULL_VOID(sheetPattern);
259     auto prePopupInfo = sheetPattern->GetSheetPopupInfo();
260     if (!prePopupInfo.showArrow) {
261         return;
262     }
263 
264     switch (prePopupInfo.finalPlacement) {
265         case Placement::BOTTOM_LEFT:
266             [[fallthrough]];
267         case Placement::BOTTOM_RIGHT:
268             [[fallthrough]];
269         case Placement::BOTTOM:
270             [[fallthrough]];
271         case Placement::TOP_LEFT:
272             [[fallthrough]];
273         case Placement::TOP_RIGHT:
274             [[fallthrough]];
275         case Placement::TOP: {
276             sheetHeight_ -= SHEET_ARROW_HEIGHT.ConvertToPx();
277             break;
278         }
279         case Placement::RIGHT_TOP:
280             [[fallthrough]];
281         case Placement::RIGHT_BOTTOM:
282             [[fallthrough]];
283         case Placement::RIGHT:
284             [[fallthrough]];
285         case Placement::LEFT_TOP:
286             [[fallthrough]];
287         case Placement::LEFT_BOTTOM:
288             [[fallthrough]];
289         case Placement::LEFT: {
290             sheetWidth_ -= SHEET_ARROW_HEIGHT.ConvertToPx();
291             break;
292         }
293         default:
294             break;
295     }
296 }
297 
GetPopupStyleSheetOffset(LayoutWrapper * layoutWrapper)298 OffsetF SheetWrapperLayoutAlgorithm::GetPopupStyleSheetOffset(LayoutWrapper* layoutWrapper)
299 {
300     auto host = layoutWrapper->GetHostNode();
301     CHECK_NULL_RETURN(host, OffsetF());
302     auto sheetWrapperPattern = host->GetPattern<SheetWrapperPattern>();
303     CHECK_NULL_RETURN(sheetWrapperPattern, OffsetF());
304     auto targetNode = sheetWrapperPattern->GetTargetNode();
305     CHECK_NULL_RETURN(targetNode, OffsetF());
306     auto geometryNode = targetNode->GetGeometryNode();
307     CHECK_NULL_RETURN(geometryNode, OffsetF());
308     auto targetSize = geometryNode->GetFrameSize();
309     if (host->GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)) {
310         targetSize = targetNode->GetPaintRectWithTransform().GetSize();
311     }
312     auto targetOffset = targetNode->GetPaintRectOffset();
313     if (sheetWrapperPattern->GetSubWindowId() != INVALID_SUBWINDOW_ID) {
314         if (sheetWrapperPattern->ShowInUEC()) {
315             auto UECId = SubwindowManager::GetInstance()->GetParentContainerId(sheetWrapperPattern->GetSubWindowId());
316             auto container = AceEngine::Get().GetContainer(UECId);
317             CHECK_NULL_RETURN(container, OffsetF());
318             auto mainWindowContext = AceType::DynamicCast<NG::PipelineContext>(container->GetPipelineContext());
319             CHECK_NULL_RETURN(mainWindowContext, OffsetF());
320             auto UECWindowGlobalRect = mainWindowContext->GetDisplayWindowRectInfo();
321             targetOffset = OffsetF(targetNode->GetPaintRectOffset().GetX() + UECWindowGlobalRect.Left(),
322                 targetNode->GetPaintRectOffset().GetY() + UECWindowGlobalRect.Top());
323         } else {
324             auto mainWindowRect = sheetWrapperPattern->GetMainWindowRect();
325             targetOffset = OffsetF(targetNode->GetPaintRectOffset().GetX() + mainWindowRect.Left(),
326                 targetNode->GetPaintRectOffset().GetY() + mainWindowRect.Top());
327         }
328     }
329     return GetOffsetInAvoidanceRule(layoutWrapper, targetSize, targetOffset);
330 }
331 
GetOffsetInAvoidanceRule(LayoutWrapper * layoutWrapper,const SizeF & targetSize,const OffsetF & targetOffset)332 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetInAvoidanceRule(
333     LayoutWrapper* layoutWrapper, const SizeF& targetSize, const OffsetF& targetOffset)
334 {
335     auto host = layoutWrapper->GetHostNode();
336     CHECK_NULL_RETURN(host, OffsetF());
337     if (host->GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)) {
338         sheetPopupInfo_.finalPlacement = AvoidanceRuleOfPlacement(layoutWrapper, targetSize, targetOffset);
339     } else {
340         // before api 16, only placement bottom is used
341         sheetPopupInfo_.finalPlacement = AvoidanceRuleBottom(Placement::BOTTOM, targetSize, targetOffset);
342     }
343     TAG_LOGI(AceLogTag::ACE_SHEET, "finalPlacement %{public}s",
344         PlacementUtils::ConvertPlacementToString(sheetPopupInfo_.finalPlacement).c_str());
345     if (getOffsetFunc_.find(sheetPopupInfo_.finalPlacement) == getOffsetFunc_.end()) {
346         TAG_LOGW(AceLogTag::ACE_SHEET, "It is an invalid Placement for current PopSheet.");
347         return OffsetF(0.f, 0.f);
348     }
349     auto offsetFunc = getOffsetFunc_[sheetPopupInfo_.finalPlacement];
350     CHECK_NULL_RETURN(offsetFunc, OffsetF());
351     /*
352      * steps 1. get sheet offset and restrict in window global rect
353      * steps 2. check whether need to show arrow
354      * steps 3. get arrow offset and check whether it is overlap sheet radius
355      */
356     return (this->*offsetFunc)(targetSize, targetOffset);
357 }
358 
AvoidanceRuleBottom(const Placement & currentPlacement,const SizeF & targetSize,const OffsetF & targetOffset)359 Placement SheetWrapperLayoutAlgorithm::AvoidanceRuleBottom(
360     const Placement& currentPlacement, const SizeF& targetSize, const OffsetF& targetOffset)
361 {
362     static std::map<Placement, std::vector<Placement>> PLACEMENT_STATES_BOTTOM = {
363         { Placement::BOTTOM,
364             {
365                 Placement::BOTTOM,
366                 Placement::BOTTOM_RIGHT,
367                 Placement::BOTTOM_LEFT,
368             } },
369     };
370     Placement targetPlacement = currentPlacement;
371     TAG_LOGD(AceLogTag::ACE_SHEET, "Init PopupSheet placement: %{public}s",
372         PlacementUtils::ConvertPlacementToString(targetPlacement).c_str());
373     // Step1: Determine the Placement in direction Bottom
374     auto& placementVec = PLACEMENT_STATES_BOTTOM[targetPlacement];
375     for (auto placement : placementVec) {
376         auto& placementFunc = placementCheckFunc_[placement];
377         if (placementFunc == nullptr) {
378             continue;
379         }
380         if ((this->*placementFunc)(targetSize, targetOffset)) {
381             targetPlacement = placement;
382             break;
383         }
384     }
385     TAG_LOGD(AceLogTag::ACE_SHEET, "After placementCheck, placement: %{public}s",
386         PlacementUtils::ConvertPlacementToString(targetPlacement).c_str());
387     return targetPlacement;
388 }
389 
AvoidanceRuleOfPlacement(LayoutWrapper * layoutWrapper,const SizeF & targetSize,const OffsetF & targetOffset)390 Placement SheetWrapperLayoutAlgorithm::AvoidanceRuleOfPlacement(
391     LayoutWrapper* layoutWrapper, const SizeF& targetSize, const OffsetF& targetOffset)
392 {
393     auto finalPlacement = placement_;
394     // step1: confirm the direction to place popup
395     TAG_LOGD(AceLogTag::ACE_SHEET, "Init PopupSheet placement: %{public}s",
396         PlacementUtils::ConvertPlacementToString(finalPlacement).c_str());
397     auto& placementStateVec = PLACEMENT_STATES[finalPlacement];
398     for (auto placement : placementStateVec) {
399         finalPlacement = placement;
400         auto& directionFunc = directionCheckFunc_[placement];
401         if (directionFunc == nullptr) {
402             continue;
403         }
404         if ((this->*directionFunc)(targetSize, targetOffset)) {
405             break;
406         }
407     }
408     TAG_LOGD(AceLogTag::ACE_SHEET, "After directionCheck, placement: %{public}s",
409         PlacementUtils::ConvertPlacementToString(finalPlacement).c_str());
410     // step2: check whether placement direction is NONE;
411     // if finalPlacement == None, it means nowhere can place the sheetPage after direction check.
412     if (finalPlacement == Placement::NONE) {
413         sheetPopupInfo_.placementRechecked = true;
414         if (sheetPopupInfo_.placementOnTarget) {
415             finalPlacement = placement_;
416             // when placementOnTarget and avoid keyboard is set true, the best place is user set placement.
417             if (sheetPopupInfo_.keyboardShow) {
418                 // aovid keyboard need decrease sheetHeight.
419                 sheetHeight_ = std::min(static_cast<double>(sheetHeight_),
420                     (windowGlobalRect_.Height() - DOUBLE_SIZE * WINDOW_EDGE_SPACE.ConvertToPx()));
421             }
422         } else {
423             SizeF bestSize = SizeF(0.f, 0.f);
424             finalPlacement = RecheckBestPlacementWithInsufficientSpace(targetSize, targetOffset, bestSize);
425             sheetHeight_ = bestSize.Height();
426         }
427     } else {
428         sheetPopupInfo_.placementRechecked = false;
429     }
430     return finalPlacement;
431 }
432 
CheckDirectionBottom(const SizeF & targetSize,const OffsetF & targetOffset)433 bool SheetWrapperLayoutAlgorithm::CheckDirectionBottom(const SizeF& targetSize, const OffsetF& targetOffset)
434 {
435     float sheetPageAvoidHeight = (SHEET_TARGET_SPACE + WINDOW_EDGE_SPACE).ConvertToPx();
436     /* if sheetWidth < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show,
437      * so that arrowHeight is no need to avoid.
438      */
439     if (GreatOrEqual(sheetWidth_, sheetRadius_.radiusTopLeft->ConvertToPx() +
440         sheetRadius_.radiusTopRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
441         sheetPageAvoidHeight += SHEET_ARROW_HEIGHT.ConvertToPx();
442     }
443     return GreatOrEqual(windowGlobalRect_.Width(), sheetWidth_ + DOUBLE_SIZE * windowEdgeWidth_) &&
444         GreatOrEqual(windowGlobalRect_.Height() + windowGlobalRect_.Top() - targetOffset.GetY() - targetSize.Height(),
445             sheetHeight_ + sheetPageAvoidHeight);
446 }
447 
CheckDirectionTop(const SizeF & targetSize,const OffsetF & targetOffset)448 bool SheetWrapperLayoutAlgorithm::CheckDirectionTop(const SizeF& targetSize, const OffsetF& targetOffset)
449 {
450     float sheetPageAvoidHeight = (SHEET_TARGET_SPACE + WINDOW_EDGE_SPACE).ConvertToPx();
451     /* if sheetWidth < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show,
452      * so that arrowHeight is no need to avoid.
453      */
454     if (GreatOrEqual(sheetWidth_, sheetRadius_.radiusBottomLeft->ConvertToPx() +
455         sheetRadius_.radiusBottomRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
456         sheetPageAvoidHeight += SHEET_ARROW_HEIGHT.ConvertToPx();
457     }
458     return GreatOrEqual(windowGlobalRect_.Width(), sheetWidth_ + DOUBLE_SIZE * windowEdgeWidth_) &&
459         GreatOrEqual(targetOffset.GetY() - windowGlobalRect_.Top(),
460             sheetHeight_ + sheetPageAvoidHeight);
461 }
462 
CheckDirectionRight(const SizeF & targetSize,const OffsetF & targetOffset)463 bool SheetWrapperLayoutAlgorithm::CheckDirectionRight(const SizeF& targetSize, const OffsetF& targetOffset)
464 {
465     float sheetPageAvoidWidth = (SHEET_TARGET_SPACE + WINDOW_EDGE_SPACE).ConvertToPx();
466     /* if sheetHeight < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show,
467      * so that arrowHeight is no need to avoid.
468      */
469     if (GreatOrEqual(sheetHeight_, sheetRadius_.radiusTopLeft->ConvertToPx() +
470         sheetRadius_.radiusBottomLeft->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
471         sheetPageAvoidWidth += SHEET_ARROW_HEIGHT.ConvertToPx();
472     }
473     return GreatOrEqual(windowGlobalRect_.Width() - targetOffset.GetX() - targetSize.Width(),
474         sheetWidth_ + sheetPageAvoidWidth) &&
475            GreatOrEqual(windowGlobalRect_.Height(), sheetHeight_ + DOUBLE_SIZE * WINDOW_EDGE_SPACE.ConvertToPx());
476 }
477 
CheckDirectionLeft(const SizeF & targetSize,const OffsetF & targetOffset)478 bool SheetWrapperLayoutAlgorithm::CheckDirectionLeft(const SizeF& targetSize, const OffsetF& targetOffset)
479 {
480     float sheetPageAvoidWidth = (SHEET_TARGET_SPACE + WINDOW_EDGE_SPACE).ConvertToPx();
481     /* if sheetHeight < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show,
482      * so that arrowHeight is no need to avoid.
483      */
484     if (GreatOrEqual(sheetHeight_, sheetRadius_.radiusTopRight->ConvertToPx() +
485         sheetRadius_.radiusBottomRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
486         sheetPageAvoidWidth += SHEET_ARROW_HEIGHT.ConvertToPx();
487     }
488     return GreatOrEqual(targetOffset.GetX(), sheetWidth_ + sheetPageAvoidWidth) &&
489         GreatOrEqual(windowGlobalRect_.Height(),
490             sheetHeight_ + DOUBLE_SIZE * WINDOW_EDGE_SPACE.ConvertToPx());
491 }
492 
RecheckBestPlacementWithInsufficientSpace(const SizeF & targetSize,const OffsetF & targetOffset,SizeF & bestSize)493 Placement SheetWrapperLayoutAlgorithm::RecheckBestPlacementWithInsufficientSpace(
494     const SizeF& targetSize, const OffsetF& targetOffset, SizeF& bestSize)
495 {
496     auto expectedPlacement = Placement::NONE;
497     auto& recheckPlacementVec = PLACEMENT_STATES[placement_];
498     for (auto placement : recheckPlacementVec) {
499         auto curLeftSpace = GetLeftSpaceWithPlacement(placement, targetSize, targetOffset);
500         /* best placement need to meet the following conditions:
501          * 1.space width >= sheetWidth
502          * 2.space height is the biggest in every placement
503          */
504         if (curLeftSpace.Width() >= sheetWidth_ && curLeftSpace.Height() < sheetHeight_) {
505             if (curLeftSpace.Height() > bestSize.Height()) {
506                 bestSize = curLeftSpace;
507                 expectedPlacement = placement;
508             }
509         }
510     }
511     TAG_LOGI(AceLogTag::ACE_SHEET, "get best size in insufficient space: %{public}s", bestSize.ToString().c_str());
512     return expectedPlacement;
513 }
514 
CheckPlacementBottom(const SizeF & targetSize,const OffsetF & targetOffset)515 bool SheetWrapperLayoutAlgorithm::CheckPlacementBottom(const SizeF& targetSize, const OffsetF& targetOffset)
516 {
517     return GreatOrEqual(
518         windowGlobalRect_.Width() - WINDOW_EDGE_SPACE.ConvertToPx(),
519         targetOffset.GetX() + targetSize.Width() / DOUBLE_SIZE + sheetWidth_ / DOUBLE_SIZE) &&
520         LessOrEqual(
521             WINDOW_EDGE_SPACE.ConvertToPx(),
522             targetOffset.GetX() + targetSize.Width() / DOUBLE_SIZE - sheetWidth_ / DOUBLE_SIZE);
523 }
524 
CheckPlacementBottomLeft(const SizeF & targetSize,const OffsetF & targetOffset)525 bool SheetWrapperLayoutAlgorithm::CheckPlacementBottomLeft(const SizeF& targetSize, const OffsetF& targetOffset)
526 {
527     return LessOrEqual(WINDOW_EDGE_SPACE.ConvertToPx(), targetOffset.GetX()) &&
528            GreatOrEqual(windowGlobalRect_.Width() - WINDOW_EDGE_SPACE.ConvertToPx(), targetOffset.GetX() + sheetWidth_);
529 }
530 
CheckPlacementBottomRight(const SizeF & targetSize,const OffsetF & targetOffset)531 bool SheetWrapperLayoutAlgorithm::CheckPlacementBottomRight(const SizeF& targetSize, const OffsetF& targetOffset)
532 {
533     return LessOrEqual(WINDOW_EDGE_SPACE.ConvertToPx(), targetOffset.GetX() + targetSize.Width() - sheetWidth_) &&
534            GreatOrEqual(
535                windowGlobalRect_.Width() - WINDOW_EDGE_SPACE.ConvertToPx(), targetOffset.GetX() + targetSize.Width());
536 }
537 
GetLeftSpaceWithPlacement(const Placement & placement,const SizeF & targetSize,const OffsetF & targetOffset)538 SizeF SheetWrapperLayoutAlgorithm::GetLeftSpaceWithPlacement(
539     const Placement& placement, const SizeF& targetSize, const OffsetF& targetOffset)
540 {
541     float width = 0.f;
542     float height = 0.f;
543     float sheetPageAvoidHeight = (SHEET_TARGET_SPACE + WINDOW_EDGE_SPACE).ConvertToPx();
544     float sheetPageAvoidWidth = (SHEET_TARGET_SPACE + WINDOW_EDGE_SPACE).ConvertToPx();
545     switch (placement) {
546         case Placement::BOTTOM_LEFT:
547             [[fallthrough]];
548         case Placement::BOTTOM_RIGHT:
549             [[fallthrough]];
550         case Placement::BOTTOM: {
551             if (GreatOrEqual(sheetWidth_, sheetRadius_.radiusTopLeft->ConvertToPx() +
552                 sheetRadius_.radiusTopRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
553                 sheetPageAvoidHeight += SHEET_ARROW_HEIGHT.ConvertToPx();
554             }
555             width = windowGlobalRect_.Width() - DOUBLE_SIZE * windowEdgeWidth_;
556             height = windowGlobalRect_.Height() + windowGlobalRect_.Top() -
557                 targetOffset.GetY() - targetSize.Height() - sheetPageAvoidHeight;
558             break;
559         }
560         case Placement::TOP_LEFT:
561             [[fallthrough]];
562         case Placement::TOP_RIGHT:
563             [[fallthrough]];
564         case Placement::TOP: {
565             if (GreatOrEqual(sheetWidth_, sheetRadius_.radiusBottomLeft->ConvertToPx() +
566                 sheetRadius_.radiusBottomRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
567                 sheetPageAvoidHeight += SHEET_ARROW_HEIGHT.ConvertToPx();
568             }
569             width = windowGlobalRect_.Width() - DOUBLE_SIZE * windowEdgeWidth_;
570             height = targetOffset.GetY() - windowGlobalRect_.Top() - sheetPageAvoidHeight;
571             break;
572         }
573         case Placement::RIGHT_TOP:
574             [[fallthrough]];
575         case Placement::RIGHT_BOTTOM:
576             [[fallthrough]];
577         case Placement::RIGHT: {
578             if (GreatOrEqual(sheetHeight_, sheetRadius_.radiusTopLeft->ConvertToPx() +
579                 sheetRadius_.radiusBottomLeft->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
580                 sheetPageAvoidWidth += SHEET_ARROW_HEIGHT.ConvertToPx();
581             }
582             width = windowGlobalRect_.Width() - targetOffset.GetX() - targetSize.Width() - sheetPageAvoidWidth;
583             height = windowGlobalRect_.Height() - DOUBLE_SIZE * WINDOW_EDGE_SPACE.ConvertToPx();
584             break;
585         }
586         case Placement::LEFT_TOP:
587             [[fallthrough]];
588         case Placement::LEFT_BOTTOM:
589             [[fallthrough]];
590         case Placement::LEFT: {
591             if (GreatOrEqual(sheetHeight_, sheetRadius_.radiusTopRight->ConvertToPx() +
592                 sheetRadius_.radiusBottomRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
593                 sheetPageAvoidWidth += SHEET_ARROW_HEIGHT.ConvertToPx();
594             }
595             width = targetOffset.GetX() - sheetPageAvoidWidth;
596             height = windowGlobalRect_.Height() - DOUBLE_SIZE * WINDOW_EDGE_SPACE.ConvertToPx();
597             break;
598         }
599         default:
600             break;
601     }
602     return SizeF(width, height);
603 }
604 
GetOffsetWithBottom(const SizeF & targetSize,const OffsetF & targetOffset)605 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetWithBottom(
606     const SizeF& targetSize, const OffsetF& targetOffset)
607 {
608     if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)) {
609         sheetPopupInfo_.arrowOffsetX = sheetWidth_ / DOUBLE_SIZE;
610         return OffsetF(targetOffset.GetX() + (targetSize.Width() - sheetWidth_) / DOUBLE_SIZE,
611             targetOffset.GetY() + targetSize.Height() + SHEET_TARGET_SPACE.ConvertToPx());
612     }
613 
614     float finalOffsetX = targetOffset.GetX() + (targetSize.Width() - sheetWidth_) / DOUBLE_SIZE;
615     float finalOffsetY = targetOffset.GetY() + targetSize.Height() + SHEET_TARGET_SPACE.ConvertToPx();
616     RestrictOffsetInSpaceBottom(finalOffsetX, finalOffsetY);
617     if (!sheetPopupInfo_.showArrow) {
618         return OffsetF(finalOffsetX, finalOffsetY);
619     }
620     // if sheetWidth < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show
621     if (LessNotEqual(sheetWidth_, sheetRadius_.radiusTopLeft->ConvertToPx() +
622         sheetRadius_.radiusTopRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
623         sheetPopupInfo_.showArrow = false;
624         return OffsetF(finalOffsetX, finalOffsetY);
625     }
626     SetArrowOffsetInBottomOrTop(targetSize, targetOffset, finalOffsetX);
627     CheckIsArrowOverlapSheetRadius();
628     return OffsetF(finalOffsetX, finalOffsetY);
629 }
630 
GetOffsetWithBottomLeft(const SizeF & targetSize,const OffsetF & targetOffset)631 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetWithBottomLeft(
632     const SizeF& targetSize, const OffsetF& targetOffset)
633 {
634     if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)) {
635         sheetPopupInfo_.arrowOffsetX = targetSize.Width() / DOUBLE_SIZE;
636         auto sheetOffset =
637             OffsetF(targetOffset.GetX(), targetOffset.GetY() + targetSize.Height() + SHEET_TARGET_SPACE.ConvertToPx());
638 
639         // if the arrow overlaps the sheet left corner, move sheet to the 6vp from the left edge
640         if (LessNotEqual(sheetPopupInfo_.arrowOffsetX - ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(),
641             sheetRadius_.radiusTopLeft->ConvertToPx())) {
642             sheetOffset.SetX(WINDOW_EDGE_SPACE.ConvertToPx());
643             sheetPopupInfo_.arrowOffsetX = targetOffset.GetX() + targetSize.Width() / DOUBLE_SIZE - sheetOffset.GetX();
644             TAG_LOGD(AceLogTag::ACE_SHEET, "Adjust sheet to the left boundary of the screen");
645         }
646 
647         // if the arrow still overlaps the sheet left corner, the arrow will become a right angle.
648         if (LessNotEqual(sheetPopupInfo_.arrowOffsetX - ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(),
649             sheetRadius_.radiusTopLeft->ConvertToPx())) {
650             TAG_LOGD(AceLogTag::ACE_SHEET, "Need to switch the arrow into the right-angle arrow");
651         }
652         return sheetOffset;
653     }
654 
655     float finalOffsetX = targetOffset.GetX();
656     float finalOffsetY = targetOffset.GetY() + targetSize.Height() + SHEET_TARGET_SPACE.ConvertToPx();
657     RestrictOffsetInSpaceBottom(finalOffsetX, finalOffsetY);
658     if (!sheetPopupInfo_.showArrow) {
659         return OffsetF(finalOffsetX, finalOffsetY);
660     }
661     // if sheetWidth < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show
662     if (LessNotEqual(sheetWidth_, sheetRadius_.radiusTopLeft->ConvertToPx() +
663         sheetRadius_.radiusTopRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
664         sheetPopupInfo_.showArrow = false;
665         return OffsetF(finalOffsetX, finalOffsetY);
666     }
667     SetArrowOffsetInBottomOrTop(targetSize, targetOffset, finalOffsetX);
668     CheckIsArrowOverlapSheetRadius();
669     return OffsetF(finalOffsetX, finalOffsetY);
670 }
671 
GetOffsetWithBottomRight(const SizeF & targetSize,const OffsetF & targetOffset)672 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetWithBottomRight(
673     const SizeF& targetSize, const OffsetF& targetOffset)
674 {
675     if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)) {
676         sheetPopupInfo_.arrowOffsetX = sheetWidth_ - targetSize.Width() / DOUBLE_SIZE;
677         auto sheetOffset = OffsetF(targetOffset.GetX() + targetSize.Width() - sheetWidth_,
678             targetOffset.GetY() + targetSize.Height() + SHEET_TARGET_SPACE.ConvertToPx());
679 
680         // if the arrow overlaps the sheet right corner, move sheet to the 6vp from the right edge
681         if (GreatNotEqual(sheetPopupInfo_.arrowOffsetX + sheetRadius_.radiusTopRight->ConvertToPx() +
682             ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx(), sheetWidth_)) {
683             sheetOffset.SetX(windowGlobalRect_.Width() - WINDOW_EDGE_SPACE.ConvertToPx() - sheetWidth_);
684             sheetPopupInfo_.arrowOffsetX = targetOffset.GetX() + targetSize.Width() / DOUBLE_SIZE - sheetOffset.GetX();
685             TAG_LOGD(AceLogTag::ACE_SHEET, "Adjust sheet to the right boundary of the screen");
686         }
687 
688         // if the arrow still overlaps the sheet right corner, the arrow will become a right angle.
689         if (GreatNotEqual(sheetPopupInfo_.arrowOffsetX + sheetRadius_.radiusTopRight->ConvertToPx() +
690             ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx(), sheetWidth_)) {
691             TAG_LOGD(AceLogTag::ACE_SHEET, "Need to switch the arrow into the right angle arrow");
692         }
693         return sheetOffset;
694     }
695 
696     float finalOffsetX = targetOffset.GetX() + targetSize.Width() - sheetWidth_;
697     float finalOffsetY = targetOffset.GetY() + targetSize.Height() + SHEET_TARGET_SPACE.ConvertToPx();
698     RestrictOffsetInSpaceBottom(finalOffsetX, finalOffsetY);
699     if (!sheetPopupInfo_.showArrow) {
700         return OffsetF(finalOffsetX, finalOffsetY);
701     }
702     // if sheetWidth < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show
703     if (LessNotEqual(sheetWidth_, sheetRadius_.radiusTopLeft->ConvertToPx() +
704         sheetRadius_.radiusTopRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
705         sheetPopupInfo_.showArrow = false;
706         return OffsetF(finalOffsetX, finalOffsetY);
707     }
708     SetArrowOffsetInBottomOrTop(targetSize, targetOffset, finalOffsetX);
709     CheckIsArrowOverlapSheetRadius();
710     return OffsetF(finalOffsetX, finalOffsetY);
711 }
712 
GetOffsetWithTop(const SizeF & targetSize,const OffsetF & targetOffset)713 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetWithTop(const SizeF& targetSize, const OffsetF& targetOffset)
714 {
715     float finalOffsetX = targetOffset.GetX() + (targetSize.Width() - sheetWidth_) / DOUBLE_SIZE;
716     float finalOffsetY = targetOffset.GetY() -
717         SHEET_TARGET_SPACE.ConvertToPx() - sheetHeight_ - SHEET_ARROW_HEIGHT.ConvertToPx();
718     RestrictOffsetInSpaceTop(finalOffsetX, finalOffsetY);
719     if (!sheetPopupInfo_.showArrow) {
720         return OffsetF(finalOffsetX, finalOffsetY);
721     }
722     // if sheetWidth < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show
723     if (LessNotEqual(sheetWidth_, sheetRadius_.radiusBottomLeft->ConvertToPx() +
724         sheetRadius_.radiusBottomRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
725         sheetPopupInfo_.showArrow = false;
726         return OffsetF(finalOffsetX, finalOffsetY);
727     }
728     SetArrowOffsetInBottomOrTop(targetSize, targetOffset, finalOffsetX);
729     CheckIsArrowOverlapSheetRadius();
730     return OffsetF(finalOffsetX, finalOffsetY);
731 }
732 
GetOffsetWithTopLeft(const SizeF & targetSize,const OffsetF & targetOffset)733 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetWithTopLeft(const SizeF& targetSize, const OffsetF& targetOffset)
734 {
735     float finalOffsetX = targetOffset.GetX();
736     float finalOffsetY = targetOffset.GetY() -
737         SHEET_TARGET_SPACE.ConvertToPx() - sheetHeight_ - SHEET_ARROW_HEIGHT.ConvertToPx();
738     RestrictOffsetInSpaceTop(finalOffsetX, finalOffsetY);
739     if (!sheetPopupInfo_.showArrow) {
740         return OffsetF(finalOffsetX, finalOffsetY);
741     }
742     // if sheetWidth < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show
743     if (LessNotEqual(sheetWidth_, sheetRadius_.radiusBottomLeft->ConvertToPx() +
744         sheetRadius_.radiusBottomRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
745         sheetPopupInfo_.showArrow = false;
746         return OffsetF(finalOffsetX, finalOffsetY);
747     }
748     SetArrowOffsetInBottomOrTop(targetSize, targetOffset, finalOffsetX);
749     CheckIsArrowOverlapSheetRadius();
750     return OffsetF(finalOffsetX, finalOffsetY);
751 }
752 
GetOffsetWithTopRight(const SizeF & targetSize,const OffsetF & targetOffset)753 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetWithTopRight(
754     const SizeF& targetSize, const OffsetF& targetOffset)
755 {
756     float finalOffsetX = targetOffset.GetX() + targetSize.Width() - sheetWidth_;
757     float finalOffsetY = targetOffset.GetY() -
758         SHEET_TARGET_SPACE.ConvertToPx() - sheetHeight_ - SHEET_ARROW_HEIGHT.ConvertToPx();
759     RestrictOffsetInSpaceTop(finalOffsetX, finalOffsetY);
760     if (!sheetPopupInfo_.showArrow) {
761         return OffsetF(finalOffsetX, finalOffsetY);
762     }
763     // if sheetWidth < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show
764     if (LessNotEqual(sheetWidth_, sheetRadius_.radiusBottomLeft->ConvertToPx() +
765         sheetRadius_.radiusBottomRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
766         sheetPopupInfo_.showArrow = false;
767         return OffsetF(finalOffsetX, finalOffsetY);
768     }
769     SetArrowOffsetInBottomOrTop(targetSize, targetOffset, finalOffsetX);
770     CheckIsArrowOverlapSheetRadius();
771     return OffsetF(finalOffsetX, finalOffsetY);
772 }
773 
GetOffsetWithLeft(const SizeF & targetSize,const OffsetF & targetOffset)774 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetWithLeft(const SizeF& targetSize, const OffsetF& targetOffset)
775 {
776     float finalOffsetX = targetOffset.GetX() -
777         sheetWidth_ - SHEET_TARGET_SPACE.ConvertToPx() - SHEET_ARROW_HEIGHT.ConvertToPx();
778     float finalOffsetY = targetOffset.GetY() + (targetSize.Height() - sheetHeight_) / DOUBLE_SIZE;
779     RestrictOffsetInSpaceLeft(finalOffsetX, finalOffsetY);
780     if (!sheetPopupInfo_.showArrow) {
781         return OffsetF(finalOffsetX, finalOffsetY);
782     }
783     // if sheetHeight < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show
784     if (LessNotEqual(sheetHeight_, sheetRadius_.radiusTopRight->ConvertToPx() +
785         sheetRadius_.radiusBottomRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
786         sheetPopupInfo_.showArrow = false;
787         return OffsetF(finalOffsetX, finalOffsetY);
788     }
789     SetArrowOffsetInRightOrLeft(targetSize, targetOffset, finalOffsetY);
790     CheckIsArrowOverlapSheetRadius();
791     return OffsetF(finalOffsetX, finalOffsetY);
792 }
793 
GetOffsetWithLeftTop(const SizeF & targetSize,const OffsetF & targetOffset)794 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetWithLeftTop(const SizeF& targetSize, const OffsetF& targetOffset)
795 {
796     float finalOffsetX = targetOffset.GetX() -
797         sheetWidth_ - SHEET_TARGET_SPACE.ConvertToPx() - SHEET_ARROW_HEIGHT.ConvertToPx();
798     float finalOffsetY = targetOffset.GetY();
799     RestrictOffsetInSpaceLeft(finalOffsetX, finalOffsetY);
800     if (!sheetPopupInfo_.showArrow) {
801         return OffsetF(finalOffsetX, finalOffsetY);
802     }
803     // if sheetHeight < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show
804     if (LessNotEqual(sheetHeight_, sheetRadius_.radiusTopRight->ConvertToPx() +
805         sheetRadius_.radiusBottomRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
806         sheetPopupInfo_.showArrow = false;
807         return OffsetF(finalOffsetX, finalOffsetY);
808     }
809     SetArrowOffsetInRightOrLeft(targetSize, targetOffset, finalOffsetY);
810     CheckIsArrowOverlapSheetRadius();
811     return OffsetF(finalOffsetX, finalOffsetY);
812 }
813 
GetOffsetWithLeftBottom(const SizeF & targetSize,const OffsetF & targetOffset)814 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetWithLeftBottom(
815     const SizeF& targetSize, const OffsetF& targetOffset)
816 {
817     float finalOffsetX = targetOffset.GetX() -
818         sheetWidth_ - SHEET_TARGET_SPACE.ConvertToPx() - SHEET_ARROW_HEIGHT.ConvertToPx();
819     float finalOffsetY = targetOffset.GetY() + targetSize.Height() - sheetHeight_;
820     RestrictOffsetInSpaceLeft(finalOffsetX, finalOffsetY);
821     if (!sheetPopupInfo_.showArrow) {
822         return OffsetF(finalOffsetX, finalOffsetY);
823     }
824     // if sheetHeight < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show
825     if (LessNotEqual(sheetHeight_, sheetRadius_.radiusTopRight->ConvertToPx() +
826         sheetRadius_.radiusBottomRight->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
827         sheetPopupInfo_.showArrow = false;
828         return OffsetF(finalOffsetX, finalOffsetY);
829     }
830     SetArrowOffsetInRightOrLeft(targetSize, targetOffset, finalOffsetY);
831     CheckIsArrowOverlapSheetRadius();
832     return OffsetF(finalOffsetX, finalOffsetY);
833 }
834 
GetOffsetWithRight(const SizeF & targetSize,const OffsetF & targetOffset)835 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetWithRight(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_) / DOUBLE_SIZE;
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 
GetOffsetWithRightTop(const SizeF & targetSize,const OffsetF & targetOffset)854 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetWithRightTop(const SizeF& targetSize, const OffsetF& targetOffset)
855 {
856     float finalOffsetX = targetOffset.GetX() + targetSize.Width() + SHEET_TARGET_SPACE.ConvertToPx();
857     float finalOffsetY = targetOffset.GetY();
858     RestrictOffsetInSpaceRight(finalOffsetX, finalOffsetY);
859     if (!sheetPopupInfo_.showArrow) {
860         return OffsetF(finalOffsetX, finalOffsetY);
861     }
862     // if sheetHeight < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show
863     if (LessNotEqual(sheetHeight_, sheetRadius_.radiusTopLeft->ConvertToPx() +
864         sheetRadius_.radiusBottomLeft->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
865         sheetPopupInfo_.showArrow = false;
866         return OffsetF(finalOffsetX, finalOffsetY);
867     }
868     SetArrowOffsetInRightOrLeft(targetSize, targetOffset, finalOffsetY);
869     CheckIsArrowOverlapSheetRadius();
870     return OffsetF(finalOffsetX, finalOffsetY);
871 }
872 
GetOffsetWithRightBottom(const SizeF & targetSize,const OffsetF & targetOffset)873 OffsetF SheetWrapperLayoutAlgorithm::GetOffsetWithRightBottom(
874     const SizeF& targetSize, const OffsetF& targetOffset)
875 {
876     float finalOffsetX = targetOffset.GetX() + targetSize.Width() + SHEET_TARGET_SPACE.ConvertToPx();
877     float finalOffsetY = targetOffset.GetY() + targetSize.Height() - sheetHeight_;
878     RestrictOffsetInSpaceRight(finalOffsetX, finalOffsetY);
879     if (!sheetPopupInfo_.showArrow) {
880         return OffsetF(finalOffsetX, finalOffsetY);
881     }
882     // if sheetHeight < sheetRadius * 2 + sheetArrowWidth, arrow is no need to show
883     if (LessNotEqual(sheetHeight_, sheetRadius_.radiusTopLeft->ConvertToPx() +
884         sheetRadius_.radiusBottomLeft->ConvertToPx() + SHEET_ARROW_WIDTH.ConvertToPx())) {
885         sheetPopupInfo_.showArrow = false;
886         return OffsetF(finalOffsetX, finalOffsetY);
887     }
888     SetArrowOffsetInRightOrLeft(targetSize, targetOffset, finalOffsetY);
889     CheckIsArrowOverlapSheetRadius();
890     return OffsetF(finalOffsetX, finalOffsetY);
891 }
892 
SetArrowOffsetInBottomOrTop(const SizeF & targetSize,const OffsetF & targetOffset,float sheetOffset)893 void SheetWrapperLayoutAlgorithm::SetArrowOffsetInBottomOrTop(
894     const SizeF& targetSize, const OffsetF& targetOffset, float sheetOffset)
895 {
896     sheetPopupInfo_.arrowOffsetX = targetOffset.GetX() + targetSize.Width() / DOUBLE_SIZE - sheetOffset;
897 
898     sheetPopupInfo_.arrowOffsetX = std::clamp(static_cast<double>(sheetPopupInfo_.arrowOffsetX),
899         ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(),
900         sheetWidth_ - ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx());
901 }
902 
SetArrowOffsetInRightOrLeft(const SizeF & targetSize,const OffsetF & targetOffset,float sheetOffset)903 void SheetWrapperLayoutAlgorithm::SetArrowOffsetInRightOrLeft(
904     const SizeF& targetSize, const OffsetF& targetOffset, float sheetOffset)
905 {
906     sheetPopupInfo_.arrowOffsetY = targetOffset.GetY() + targetSize.Height() / DOUBLE_SIZE - sheetOffset;
907 
908     sheetPopupInfo_.arrowOffsetY = std::clamp(static_cast<double>(sheetPopupInfo_.arrowOffsetY),
909         ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(),
910         sheetHeight_ - ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx());
911 }
912 
RestrictOffsetInSpaceBottom(float & offsetX,float & offsetY)913 void SheetWrapperLayoutAlgorithm::RestrictOffsetInSpaceBottom(float& offsetX, float& offsetY)
914 {
915     offsetX = std::clamp(static_cast<double>(offsetX), static_cast<double>(windowEdgeWidth_),
916         windowGlobalRect_.Width() - sheetWidth_ - windowEdgeWidth_);
917 
918     offsetY = std::clamp(static_cast<double>(offsetY), windowGlobalRect_.Top() + WINDOW_EDGE_SPACE.ConvertToPx(),
919         std::max(windowGlobalRect_.Height() + windowGlobalRect_.Top() - sheetHeight_ - WINDOW_EDGE_SPACE.ConvertToPx(),
920             windowGlobalRect_.Top() + WINDOW_EDGE_SPACE.ConvertToPx()));
921 
922     if (sheetPopupInfo_.placementRechecked && sheetPopupInfo_.placementOnTarget) {
923         sheetPopupInfo_.showArrow = false;
924         offsetY = std::max(
925             windowGlobalRect_.Height() + windowGlobalRect_.Top() - sheetHeight_ - WINDOW_EDGE_SPACE.ConvertToPx(),
926             windowGlobalRect_.Top() + WINDOW_EDGE_SPACE.ConvertToPx());
927     }
928 }
929 
RestrictOffsetInSpaceTop(float & offsetX,float & offsetY)930 void SheetWrapperLayoutAlgorithm::RestrictOffsetInSpaceTop(float& offsetX, float& offsetY)
931 {
932     offsetX = std::clamp(static_cast<double>(offsetX), static_cast<double>(windowEdgeWidth_),
933         windowGlobalRect_.Width() - sheetWidth_ - windowEdgeWidth_);
934 
935     offsetY = std::clamp(static_cast<double>(offsetY), windowGlobalRect_.Top() + WINDOW_EDGE_SPACE.ConvertToPx(),
936         std::max(windowGlobalRect_.Height() + windowGlobalRect_.Top() - sheetHeight_ - WINDOW_EDGE_SPACE.ConvertToPx(),
937             windowGlobalRect_.Top() + WINDOW_EDGE_SPACE.ConvertToPx()));
938 
939     if (sheetPopupInfo_.placementRechecked && sheetPopupInfo_.placementOnTarget) {
940         sheetPopupInfo_.showArrow = false;
941         offsetY = windowGlobalRect_.Top() + WINDOW_EDGE_SPACE.ConvertToPx();
942     }
943 }
944 
RestrictOffsetInSpaceLeft(float & offsetX,float & offsetY)945 void SheetWrapperLayoutAlgorithm::RestrictOffsetInSpaceLeft(float& offsetX, float& offsetY)
946 {
947     offsetX = std::clamp(static_cast<double>(offsetX), static_cast<double>(windowEdgeWidth_),
948         windowGlobalRect_.Width() - sheetWidth_ - windowEdgeWidth_);
949 
950     offsetY = std::clamp(static_cast<double>(offsetY), windowGlobalRect_.Top() + WINDOW_EDGE_SPACE.ConvertToPx(),
951         windowGlobalRect_.Height() + windowGlobalRect_.Top() - sheetHeight_ - WINDOW_EDGE_SPACE.ConvertToPx());
952 
953     if (sheetPopupInfo_.placementRechecked && sheetPopupInfo_.placementOnTarget) {
954         sheetPopupInfo_.showArrow = false;
955         offsetX = windowEdgeWidth_;
956     }
957 }
958 
RestrictOffsetInSpaceRight(float & offsetX,float & offsetY)959 void SheetWrapperLayoutAlgorithm::RestrictOffsetInSpaceRight(float& offsetX, float& offsetY)
960 {
961     offsetX = std::clamp(static_cast<double>(offsetX), static_cast<double>(windowEdgeWidth_),
962         windowGlobalRect_.Width() - sheetWidth_ - windowEdgeWidth_);
963 
964     offsetY = std::clamp(static_cast<double>(offsetY), windowGlobalRect_.Top() + WINDOW_EDGE_SPACE.ConvertToPx(),
965         windowGlobalRect_.Height() + windowGlobalRect_.Top() - sheetHeight_ - WINDOW_EDGE_SPACE.ConvertToPx());
966 
967     if (sheetPopupInfo_.placementRechecked && sheetPopupInfo_.placementOnTarget) {
968         sheetPopupInfo_.showArrow = false;
969         offsetX = windowGlobalRect_.Width() - windowEdgeWidth_ - sheetWidth_;
970     }
971 }
972 
CheckIsArrowOverlapSheetRadius()973 void SheetWrapperLayoutAlgorithm::CheckIsArrowOverlapSheetRadius()
974 {
975     sheetPopupInfo_.arrowPosition = SheetArrowPosition::NONE;
976     switch (sheetPopupInfo_.finalPlacement) {
977         case Placement::BOTTOM_LEFT:
978             [[fallthrough]];
979         case Placement::BOTTOM_RIGHT:
980             [[fallthrough]];
981         case Placement::BOTTOM: {
982             if (LessNotEqual(sheetPopupInfo_.arrowOffsetX - ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(),
983                 sheetRadius_.radiusTopLeft->ConvertToPx())) {
984                 sheetPopupInfo_.arrowPosition = SheetArrowPosition::BOTTOM_LEFT;
985             } else if (GreatNotEqual(sheetPopupInfo_.arrowOffsetX + ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx(),
986                 sheetWidth_ - sheetRadius_.radiusTopRight->ConvertToPx())) {
987                 sheetPopupInfo_.arrowPosition = SheetArrowPosition::BOTTOM_RIGHT;
988             }
989             break;
990         }
991         case Placement::TOP_LEFT:
992             [[fallthrough]];
993         case Placement::TOP_RIGHT:
994             [[fallthrough]];
995         case Placement::TOP: {
996             if (LessNotEqual(sheetPopupInfo_.arrowOffsetX - ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx(),
997                 sheetRadius_.radiusBottomLeft->ConvertToPx())) {
998                 sheetPopupInfo_.arrowPosition = SheetArrowPosition::TOP_LEFT;
999             } else if (GreatNotEqual(sheetPopupInfo_.arrowOffsetX + ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(),
1000                 sheetWidth_ - sheetRadius_.radiusBottomRight->ConvertToPx())) {
1001                 sheetPopupInfo_.arrowPosition = SheetArrowPosition::TOP_RIGHT;
1002             }
1003             break;
1004         }
1005         case Placement::RIGHT_TOP:
1006             [[fallthrough]];
1007         case Placement::RIGHT_BOTTOM:
1008             [[fallthrough]];
1009         case Placement::RIGHT: {
1010             if (LessNotEqual(sheetPopupInfo_.arrowOffsetY - ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(),
1011                 sheetRadius_.radiusTopLeft->ConvertToPx())) {
1012                 sheetPopupInfo_.arrowPosition = SheetArrowPosition::RIGHT_TOP;
1013             } else if (GreatNotEqual(sheetPopupInfo_.arrowOffsetY + ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx(),
1014                 sheetHeight_ - sheetRadius_.radiusBottomLeft->ConvertToPx())) {
1015                 sheetPopupInfo_.arrowPosition = SheetArrowPosition::RIGHT_BOTTOM;
1016             }
1017             break;
1018         }
1019         case Placement::LEFT_TOP:
1020             [[fallthrough]];
1021         case Placement::LEFT_BOTTOM:
1022             [[fallthrough]];
1023         case Placement::LEFT: {
1024             if (LessNotEqual(sheetPopupInfo_.arrowOffsetY - ARROW_VERTICAL_P5_OFFSET_X.ConvertToPx(),
1025                 sheetRadius_.radiusTopRight->ConvertToPx())) {
1026                 sheetPopupInfo_.arrowPosition = SheetArrowPosition::LEFT_TOP;
1027             } else if (GreatNotEqual(sheetPopupInfo_.arrowOffsetY + ARROW_VERTICAL_P1_OFFSET_X.ConvertToPx(),
1028                 sheetHeight_ - sheetRadius_.radiusBottomRight->ConvertToPx())) {
1029                 sheetPopupInfo_.arrowPosition = SheetArrowPosition::LEFT_BOTTOM;
1030             }
1031             break;
1032         }
1033         default:
1034             sheetPopupInfo_.arrowPosition = SheetArrowPosition::NONE;
1035             break;
1036     }
1037 }
1038 
Layout(LayoutWrapper * layoutWrapper)1039 void SheetWrapperLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
1040 {
1041     BoxLayoutAlgorithm::PerformLayout(layoutWrapper);
1042     auto host = layoutWrapper->GetHostNode();
1043     CHECK_NULL_VOID(host);
1044     auto sheetWrapperPattern = host->GetPattern<SheetWrapperPattern>();
1045     CHECK_NULL_VOID(sheetWrapperPattern);
1046     auto sheetPageNode = sheetWrapperPattern->GetSheetPageNode();
1047     CHECK_NULL_VOID(sheetPageNode);
1048     auto sheetPagePattern = sheetPageNode->GetPattern<SheetPresentationPattern>();
1049     CHECK_NULL_VOID(sheetPagePattern);
1050     auto sheetType = sheetPagePattern->GetSheetTypeNoProcess();
1051     if (sheetType == SheetType::SHEET_POPUP) {
1052         TAG_LOGI(AceLogTag::ACE_SHEET, "before popup sheet page, origin size [%{public}f, %{public}f]",
1053             sheetWidth_, sheetHeight_);
1054         OffsetF popupOffset = GetPopupStyleSheetOffset(layoutWrapper);
1055         auto hostNode = layoutWrapper->GetHostNode();
1056         CHECK_NULL_VOID(hostNode);
1057         auto wrapperOffset = hostNode->GetPaintRectOffset();
1058         sheetPopupInfo_.sheetOffsetX = popupOffset.GetX() - wrapperOffset.GetX();
1059         sheetPopupInfo_.sheetOffsetY = popupOffset.GetY() - wrapperOffset.GetY();
1060         TAG_LOGI(AceLogTag::ACE_SHEET, "checked offset (%{public}f, %{public}f), checked size [%{public}f, %{public}f]",
1061             sheetPopupInfo_.sheetOffsetX, sheetPopupInfo_.sheetOffsetY, sheetWidth_, sheetHeight_);
1062         RemeasureForPopup(layoutWrapper);
1063     }
1064     auto index = host->GetChildIndexById(sheetPageNode->GetId());
1065     auto sheetPageWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1066     CHECK_NULL_VOID(sheetPageWrapper);
1067     sheetPageWrapper->Layout();
1068     LayoutMaskNode(layoutWrapper);
1069 }
1070 
LayoutMaskNode(LayoutWrapper * layoutWrapper)1071 void SheetWrapperLayoutAlgorithm::LayoutMaskNode(LayoutWrapper* layoutWrapper)
1072 {
1073     auto host = layoutWrapper->GetHostNode();
1074     CHECK_NULL_VOID(host);
1075     auto sheetWrapperPattern = host->GetPattern<SheetWrapperPattern>();
1076     CHECK_NULL_VOID(sheetWrapperPattern);
1077     if (!sheetWrapperPattern->ShowInUEC()) {
1078         return;
1079     }
1080     auto maskNode = sheetWrapperPattern->GetSheetMaskNode();
1081     CHECK_NULL_VOID(maskNode);
1082     auto maskPattern = maskNode->GetPattern<SheetMaskPattern>();
1083     CHECK_NULL_VOID(maskPattern);
1084     auto index = host->GetChildIndexById(maskNode->GetId());
1085     auto maskWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1086     CHECK_NULL_VOID(maskWrapper);
1087     auto subContainer = AceEngine::Get().GetContainer(sheetWrapperPattern->GetSubWindowId());
1088     CHECK_NULL_VOID(subContainer);
1089     auto subWindowContext = AceType::DynamicCast<NG::PipelineContext>(subContainer->GetPipelineContext());
1090     CHECK_NULL_VOID(subWindowContext);
1091     auto geometryNode = maskWrapper->GetGeometryNode();
1092     CHECK_NULL_VOID(geometryNode);
1093 
1094     auto currentId = Container::CurrentId();
1095     SubwindowManager::GetInstance()->DeleteHotAreas(currentId, maskNode->GetId(), SubwindowType::TYPE_SHEET);
1096     // true means that the mask layer takes effect in response to the click event
1097     if (maskPattern->GetIsMaskInteractive()) {
1098         auto maskFrameRect = geometryNode->GetFrameRect();
1099         std::vector<Rect> rects;
1100         auto maskHotRect =
1101             Rect(maskFrameRect.GetX(), maskFrameRect.GetY(), maskFrameRect.Width(), maskFrameRect.Height());
1102         rects.emplace_back(maskHotRect);
1103         auto subWindowMgr = SubwindowManager::GetInstance();
1104         subWindowMgr->SetHotAreas(rects, SubwindowType::TYPE_SHEET, maskNode->GetId(), currentId);
1105     }
1106     maskWrapper->Layout();
1107 }
1108 
RemeasureForPopup(LayoutWrapper * layoutWrapper)1109 void SheetWrapperLayoutAlgorithm::RemeasureForPopup(LayoutWrapper* layoutWrapper)
1110 {
1111     UpdateSheetNodePopupInfo(layoutWrapper);
1112 }
1113 
IsTargetNodeHideByKeyboard(const SafeAreaInsets::Inset & keyboardInset,const RefPtr<FrameNode> & targetNode)1114 bool SheetWrapperLayoutAlgorithm::IsTargetNodeHideByKeyboard(
1115     const SafeAreaInsets::Inset& keyboardInset, const RefPtr<FrameNode>& targetNode)
1116 {
1117     CHECK_NULL_RETURN(targetNode, false);
1118     auto windowHeight = windowGlobalRect_.Height();
1119     auto keyboardHeight = keyboardInset.Length();
1120     auto targetRect = targetNode->GetPaintRectToWindowWithTransform();
1121     return GreatOrEqual(targetRect.Top(), windowHeight - keyboardHeight);
1122 }
1123 
UpdateSheetNodePopupInfo(LayoutWrapper * layoutWrapper)1124 void SheetWrapperLayoutAlgorithm::UpdateSheetNodePopupInfo(LayoutWrapper* layoutWrapper)
1125 {
1126     auto host = layoutWrapper->GetHostNode();
1127     CHECK_NULL_VOID(host);
1128     auto sheetWrapperPattern = host->GetPattern<SheetWrapperPattern>();
1129     CHECK_NULL_VOID(sheetWrapperPattern);
1130     auto sheetNode = sheetWrapperPattern->GetSheetPageNode();
1131     CHECK_NULL_VOID(sheetNode);
1132     auto index = host->GetChildIndexById(sheetNode->GetId());
1133     auto sheetPageWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1134     CHECK_NULL_VOID(sheetPageWrapper);
1135     auto sheetPageNode = sheetPageWrapper->GetHostNode();
1136     CHECK_NULL_VOID(sheetPageNode);
1137     auto sheetPagePattern = sheetPageNode->GetPattern<SheetPresentationPattern>();
1138     CHECK_NULL_VOID(sheetPagePattern);
1139     sheetPagePattern->UpdateSheetPopupInfo(sheetPopupInfo_);
1140     auto sheetPageLayoutAlgorithmWrapper = sheetPageWrapper->GetLayoutAlgorithm();
1141     CHECK_NULL_VOID(sheetPageLayoutAlgorithmWrapper);
1142     auto sheetPageLayoutAlgorithm =
1143         DynamicCast<SheetPresentationLayoutAlgorithm>(sheetPageLayoutAlgorithmWrapper->GetLayoutAlgorithm());
1144     CHECK_NULL_VOID(sheetPageLayoutAlgorithm);
1145     sheetPageLayoutAlgorithm->UpdatePopupInfoAndRemeasure(layoutWrapper, sheetPopupInfo_, sheetWidth_, sheetHeight_);
1146 }
1147 } // namespace OHOS::Ace::NG
1148