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