• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/menu/menu_layout_algorithm.h"
17 
18 #include "base/subwindow/subwindow_manager.h"
19 #include "core/common/ace_engine.h"
20 #include "core/components/common/layout/grid_system_manager.h"
21 #include "core/components/container_modal/container_modal_constants.h"
22 #include "core/components_ng/pattern/menu/menu_theme.h"
23 #include "core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.h"
24 #include "core/components_ng/pattern/overlay/overlay_manager.h"
25 #include "core/components_ng/pattern/overlay/dialog_manager.h"
26 #include "core/components_ng/pattern/select_overlay/select_overlay_pattern.h"
27 #include "core/components_ng/property/measure_utils.h"
28 
29 namespace OHOS::Ace::NG {
30 
31 namespace {
32 constexpr uint32_t MIN_GRID_COUNTS = 2;
33 constexpr uint32_t GRID_COUNTS_4 = 4;
34 constexpr uint32_t GRID_COUNTS_6 = 6;
35 constexpr uint32_t GRID_COUNTS_8 = 8;
36 constexpr uint32_t GRID_COUNTS_12 = 12;
37 constexpr size_t ALIGNMENT_STEP_OFFSET = 2;
38 constexpr float HEIGHT_CONSTRAINT_FACTOR = 0.8;
39 constexpr float ARROW_WIDTH_FACTOR = 2.0;
40 constexpr float HALF = 2.0;
41 
42 constexpr Dimension ARROW_RADIUS = 2.0_vp;
43 constexpr Dimension ARROW_P1_OFFSET_X = 8.0_vp;
44 constexpr Dimension ARROW_P2_OFFSET_X = 1.5_vp;
45 constexpr Dimension ARROW_P1_OFFSET_Y = 8.0_vp;
46 constexpr Dimension ARROW_P2_OFFSET_Y = 0.68_vp;
47 
48 const std::map<Placement, std::vector<Placement>> PLACEMENT_STATES = {
49     { Placement::BOTTOM_LEFT,
50         {
51             Placement::BOTTOM_LEFT,
52             Placement::BOTTOM_RIGHT,
53             Placement::TOP_LEFT,
54             Placement::TOP_RIGHT,
55             Placement::RIGHT_TOP,
56             Placement::RIGHT_BOTTOM,
57             Placement::LEFT_TOP,
58             Placement::LEFT_BOTTOM,
59             Placement::NONE,
60         } },
61     { Placement::BOTTOM,
62         {
63             Placement::BOTTOM,
64             Placement::BOTTOM_LEFT,
65             Placement::BOTTOM_RIGHT,
66             Placement::TOP,
67             Placement::TOP_LEFT,
68             Placement::TOP_RIGHT,
69             Placement::RIGHT,
70             Placement::RIGHT_TOP,
71             Placement::RIGHT_BOTTOM,
72             Placement::LEFT,
73             Placement::LEFT_TOP,
74             Placement::LEFT_BOTTOM,
75             Placement::NONE,
76         } },
77     { Placement::BOTTOM_RIGHT,
78         {
79             Placement::BOTTOM_RIGHT,
80             Placement::BOTTOM_LEFT,
81             Placement::TOP_RIGHT,
82             Placement::TOP_LEFT,
83             Placement::RIGHT_BOTTOM,
84             Placement::RIGHT_TOP,
85             Placement::LEFT_BOTTOM,
86             Placement::LEFT_TOP,
87             Placement::NONE,
88         } },
89     { Placement::TOP_LEFT,
90         {
91             Placement::TOP_LEFT,
92             Placement::TOP_RIGHT,
93             Placement::BOTTOM_LEFT,
94             Placement::BOTTOM_RIGHT,
95             Placement::RIGHT_TOP,
96             Placement::RIGHT_BOTTOM,
97             Placement::LEFT_TOP,
98             Placement::LEFT_BOTTOM,
99             Placement::NONE,
100         } },
101     { Placement::TOP,
102         {
103             Placement::TOP,
104             Placement::TOP_LEFT,
105             Placement::TOP_RIGHT,
106             Placement::BOTTOM,
107             Placement::BOTTOM_LEFT,
108             Placement::BOTTOM_RIGHT,
109             Placement::RIGHT,
110             Placement::RIGHT_TOP,
111             Placement::RIGHT_BOTTOM,
112             Placement::LEFT,
113             Placement::LEFT_TOP,
114             Placement::LEFT_BOTTOM,
115             Placement::NONE,
116         } },
117     { Placement::TOP_RIGHT,
118         {
119             Placement::TOP_RIGHT,
120             Placement::TOP_LEFT,
121             Placement::BOTTOM_RIGHT,
122             Placement::BOTTOM_LEFT,
123             Placement::RIGHT_BOTTOM,
124             Placement::RIGHT_TOP,
125             Placement::LEFT_BOTTOM,
126             Placement::LEFT_TOP,
127             Placement::NONE,
128         } },
129     { Placement::LEFT_TOP,
130         {
131             Placement::LEFT_TOP,
132             Placement::LEFT_BOTTOM,
133             Placement::RIGHT_TOP,
134             Placement::RIGHT_BOTTOM,
135             Placement::BOTTOM_LEFT,
136             Placement::BOTTOM_RIGHT,
137             Placement::TOP_LEFT,
138             Placement::TOP_RIGHT,
139             Placement::NONE,
140         } },
141     { Placement::LEFT,
142         {
143             Placement::LEFT,
144             Placement::LEFT_TOP,
145             Placement::LEFT_BOTTOM,
146             Placement::RIGHT,
147             Placement::RIGHT_TOP,
148             Placement::RIGHT_BOTTOM,
149             Placement::BOTTOM,
150             Placement::BOTTOM_LEFT,
151             Placement::BOTTOM_RIGHT,
152             Placement::TOP,
153             Placement::TOP_LEFT,
154             Placement::TOP_RIGHT,
155             Placement::NONE,
156         } },
157     { Placement::LEFT_BOTTOM,
158         {
159             Placement::LEFT_BOTTOM,
160             Placement::LEFT_TOP,
161             Placement::RIGHT_BOTTOM,
162             Placement::RIGHT_TOP,
163             Placement::BOTTOM_RIGHT,
164             Placement::BOTTOM_LEFT,
165             Placement::TOP_RIGHT,
166             Placement::TOP_LEFT,
167             Placement::NONE,
168         } },
169     { Placement::RIGHT_TOP,
170         {
171             Placement::RIGHT_TOP,
172             Placement::RIGHT_BOTTOM,
173             Placement::LEFT_TOP,
174             Placement::LEFT_BOTTOM,
175             Placement::BOTTOM_LEFT,
176             Placement::BOTTOM_RIGHT,
177             Placement::TOP_LEFT,
178             Placement::TOP_RIGHT,
179             Placement::NONE,
180         } },
181     { Placement::RIGHT,
182         {
183             Placement::RIGHT,
184             Placement::RIGHT_TOP,
185             Placement::RIGHT_BOTTOM,
186             Placement::LEFT,
187             Placement::LEFT_TOP,
188             Placement::LEFT_BOTTOM,
189             Placement::BOTTOM,
190             Placement::BOTTOM_LEFT,
191             Placement::BOTTOM_RIGHT,
192             Placement::TOP,
193             Placement::TOP_LEFT,
194             Placement::TOP_RIGHT,
195             Placement::NONE,
196         } },
197     { Placement::RIGHT_BOTTOM,
198         {
199             Placement::RIGHT_BOTTOM,
200             Placement::RIGHT_TOP,
201             Placement::LEFT_BOTTOM,
202             Placement::LEFT_TOP,
203             Placement::BOTTOM_RIGHT,
204             Placement::BOTTOM_LEFT,
205             Placement::TOP_RIGHT,
206             Placement::TOP_LEFT,
207             Placement::NONE,
208         } },
209 };
210 
211 // Provide positional enumeration order for select avoidance mode.
212 const std::map<Placement, std::vector<Placement>> SELECT_PLACEMENT_STATES = {
213     { Placement::BOTTOM_LEFT,
214         {
215             Placement::BOTTOM_LEFT,
216             Placement::BOTTOM_RIGHT,
217             Placement::TOP_LEFT,
218             Placement::TOP_RIGHT,
219             Placement::RIGHT_TOP,
220             Placement::RIGHT_BOTTOM,
221             Placement::LEFT_TOP,
222             Placement::LEFT_BOTTOM,
223             Placement::NONE,
224         } },
225     { Placement::BOTTOM,
226         {
227             Placement::BOTTOM,
228             Placement::BOTTOM_LEFT,
229             Placement::BOTTOM_RIGHT,
230             Placement::TOP,
231             Placement::TOP_LEFT,
232             Placement::TOP_RIGHT,
233             Placement::RIGHT_TOP,
234             Placement::RIGHT_BOTTOM,
235             Placement::LEFT_TOP,
236             Placement::LEFT_BOTTOM,
237             Placement::NONE,
238         } },
239     { Placement::BOTTOM_RIGHT,
240         {
241             Placement::BOTTOM_RIGHT,
242             Placement::BOTTOM_LEFT,
243             Placement::TOP_RIGHT,
244             Placement::TOP_LEFT,
245             Placement::RIGHT_TOP,
246             Placement::RIGHT_BOTTOM,
247             Placement::LEFT_TOP,
248             Placement::LEFT_BOTTOM,
249             Placement::NONE,
250         } },
251 };
252 
GetMaxGridCounts(const RefPtr<GridColumnInfo> & columnInfo)253 uint32_t GetMaxGridCounts(const RefPtr<GridColumnInfo>& columnInfo)
254 {
255     CHECK_NULL_RETURN(columnInfo, GRID_COUNTS_8);
256     auto currentColumns = columnInfo->GetParent()->GetColumns();
257     auto maxGridCounts = GRID_COUNTS_8;
258     switch (currentColumns) {
259         case GRID_COUNTS_4:
260             maxGridCounts = GRID_COUNTS_4;
261             break;
262         case GRID_COUNTS_8:
263             maxGridCounts = GRID_COUNTS_6;
264             break;
265         case GRID_COUNTS_12:
266             maxGridCounts = GRID_COUNTS_8;
267             break;
268         case MIN_GRID_COUNTS:
269             maxGridCounts = MIN_GRID_COUNTS;
270             break;
271         default:
272             break;
273     }
274     return maxGridCounts;
275 }
276 
GetMenuTheme(const RefPtr<FrameNode> & frameNode)277 RefPtr<NG::MenuTheme> GetMenuTheme(const RefPtr<FrameNode>& frameNode)
278 {
279     CHECK_NULL_RETURN(frameNode, nullptr);
280     auto pipelineContext = frameNode->GetContext();
281     CHECK_NULL_RETURN(pipelineContext, nullptr);
282     return pipelineContext->GetTheme<NG::MenuTheme>();
283 }
284 
PrepareExtensionMenuConstraint(LayoutWrapper * layoutWrapper,LayoutConstraintF & childConstraint)285 void PrepareExtensionMenuConstraint(LayoutWrapper* layoutWrapper, LayoutConstraintF& childConstraint)
286 {
287     auto host = layoutWrapper->GetHostNode();
288     CHECK_NULL_VOID(host);
289     auto pattern = host->GetPattern<MenuPattern>();
290     CHECK_NULL_VOID(pattern);
291     auto props = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
292     if (pattern->IsSelectOverlayExtensionMenu() && props && props->HasMenuWidth() &&
293         props->GetMenuWidth()->ConvertToPx() < childConstraint.maxSize.Width()) {
294         auto minSizeWidth = childConstraint.minSize.Width();
295         minSizeWidth = fmax(static_cast<double>(minSizeWidth), props->GetMenuWidth()->ConvertToPx());
296         minSizeWidth = fmin(minSizeWidth, childConstraint.maxSize.Width());
297         childConstraint.minSize.SetWidth(minSizeWidth);
298     }
299 }
300 } // namespace
301 
MenuLayoutAlgorithm(int32_t id,const std::string & tag,const std::optional<OffsetF> & lastPosition)302 MenuLayoutAlgorithm::MenuLayoutAlgorithm(int32_t id, const std::string& tag,
303     const std::optional<OffsetF>& lastPosition) : targetNodeId_(id), targetTag_(tag)
304 {
305     if (lastPosition.has_value()) {
306         lastPosition_ = lastPosition;
307     }
308     placementFuncMap_[Placement::TOP] = &MenuLayoutAlgorithm::GetPositionWithPlacementTop;
309     placementFuncMap_[Placement::TOP_LEFT] = &MenuLayoutAlgorithm::GetPositionWithPlacementTopLeft;
310     placementFuncMap_[Placement::TOP_RIGHT] = &MenuLayoutAlgorithm::GetPositionWithPlacementTopRight;
311     placementFuncMap_[Placement::BOTTOM] = &MenuLayoutAlgorithm::GetPositionWithPlacementBottom;
312     placementFuncMap_[Placement::BOTTOM_LEFT] = &MenuLayoutAlgorithm::GetPositionWithPlacementBottomLeft;
313     placementFuncMap_[Placement::BOTTOM_RIGHT] = &MenuLayoutAlgorithm::GetPositionWithPlacementBottomRight;
314     placementFuncMap_[Placement::LEFT] = &MenuLayoutAlgorithm::GetPositionWithPlacementLeft;
315     placementFuncMap_[Placement::LEFT_TOP] = &MenuLayoutAlgorithm::GetPositionWithPlacementLeftTop;
316     placementFuncMap_[Placement::LEFT_BOTTOM] = &MenuLayoutAlgorithm::GetPositionWithPlacementLeftBottom;
317     placementFuncMap_[Placement::RIGHT] = &MenuLayoutAlgorithm::GetPositionWithPlacementRight;
318     placementFuncMap_[Placement::RIGHT_TOP] = &MenuLayoutAlgorithm::GetPositionWithPlacementRightTop;
319     placementFuncMap_[Placement::RIGHT_BOTTOM] = &MenuLayoutAlgorithm::GetPositionWithPlacementRightBottom;
320 
321     setHorizontal_ = { Placement::LEFT, Placement::LEFT_BOTTOM, Placement::LEFT_TOP,
322         Placement::RIGHT, Placement::RIGHT_BOTTOM, Placement::RIGHT_TOP };
323     setVertical_ = { Placement::TOP, Placement::TOP_LEFT, Placement::TOP_RIGHT,
324         Placement::BOTTOM, Placement::BOTTOM_LEFT, Placement::BOTTOM_RIGHT };
325 
326     auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
327     CHECK_NULL_VOID(pipeline);
328     auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
329     CHECK_NULL_VOID(menuTheme);
330     previewScale_ = menuTheme->GetPreviewAfterAnimationScale();
331     if (LessOrEqual(previewScale_, 0.0f)) {
332         previewScale_ = 1.0f;
333     }
334 
335     auto theme = pipeline->GetTheme<SelectTheme>();
336     CHECK_NULL_VOID(theme);
337     targetSecurity_ = static_cast<float>(theme->GetMenuTargetSecuritySpace().ConvertToPx());
338 }
339 
~MenuLayoutAlgorithm()340 MenuLayoutAlgorithm::~MenuLayoutAlgorithm()
341 {
342     placementFuncMap_.clear();
343     setHorizontal_.clear();
344     setVertical_.clear();
345 }
346 
ModifyPreviewMenuPlacement(LayoutWrapper * layoutWrapper)347 void MenuLayoutAlgorithm::ModifyPreviewMenuPlacement(LayoutWrapper* layoutWrapper)
348 {
349     CHECK_NULL_VOID(layoutWrapper);
350     auto props = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
351     CHECK_NULL_VOID(props);
352     auto hostNode = layoutWrapper->GetHostNode();
353     CHECK_NULL_VOID(hostNode);
354     auto pipeline = hostNode->GetContext();
355     CHECK_NULL_VOID(pipeline);
356     auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
357     CHECK_NULL_VOID(menuTheme);
358     auto hasPlacement = props->GetMenuPlacement().has_value();
359     if (!hasPlacement) {
360         if (menuTheme->GetNormalPlacement() &&
361             SystemProperties::GetDeviceOrientation() == DeviceOrientation::PORTRAIT) {
362             // for Phone with PORTRAIT orientation, default placement is BOTTOM_LEFT
363             placement_ = Placement::BOTTOM_LEFT;
364             props->UpdateMenuPlacement(placement_);
365         } else {
366             placement_ = Placement::RIGHT_TOP;
367             props->UpdateMenuPlacement(placement_);
368         }
369     }
370 }
371 
Initialize(LayoutWrapper * layoutWrapper)372 void MenuLayoutAlgorithm::Initialize(LayoutWrapper* layoutWrapper)
373 {
374     CHECK_NULL_VOID(layoutWrapper);
375     // currently using click point as menu position
376     auto props = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
377     CHECK_NULL_VOID(props);
378     auto menuNode = layoutWrapper->GetHostNode();
379     CHECK_NULL_VOID(menuNode);
380     auto menuPattern = menuNode->GetPattern<MenuPattern>();
381     CHECK_NULL_VOID(menuPattern);
382     auto menuTheme = GetMenuTheme(menuNode);
383     CHECK_NULL_VOID(menuTheme);
384     auto beforeAnimationScale = menuPattern->GetPreviewBeforeAnimationScale();
385     auto afterAnimationScale = menuPattern->GetPreviewAfterAnimationScale();
386     dumpInfo_.previewBeginScale =
387         LessOrEqual(beforeAnimationScale, 0.0f) ? menuTheme->GetPreviewBeforeAnimationScale() : beforeAnimationScale;
388     dumpInfo_.previewEndScale =
389         LessOrEqual(afterAnimationScale, 0.0f) ? menuTheme->GetPreviewAfterAnimationScale() : afterAnimationScale;
390     previewScale_ = LessOrEqual(afterAnimationScale, 0.0f) ? previewScale_ : afterAnimationScale;
391     position_ = props->GetMenuOffset().value_or(OffsetF());
392     anchorPosition_ = props->GetAnchorPosition();
393     dumpInfo_.globalLocation = position_;
394     // user-set offset
395     positionOffset_ = props->GetPositionOffset().value_or(OffsetF());
396     dumpInfo_.offset = positionOffset_;
397     InitializePadding(layoutWrapper);
398     InitializeParam(layoutWrapper, menuPattern);
399     auto needModify = !menuPattern->IsSelectMenu() && !menuPattern->IsSelectOverlayDefaultModeRightClickMenu();
400     if (needModify) {
401         if (canExpandCurrentWindow_ && isExpandDisplay_) {
402             position_ += displayWindowRect_.GetOffset();
403             TAG_LOGI(AceLogTag::ACE_MENU, "original postion after applying displayWindowRect : %{public}s",
404                 position_.ToString().c_str());
405         } else if (isUIExtensionSubWindow_ && !isExpandDisplay_) {
406             position_ += displayWindowRect_.GetOffset() - UIExtensionHostWindowRect_.GetOffset();
407             TAG_LOGI(AceLogTag::ACE_MENU, "original postion after applying UIExtensionHostWindowRect : %{public}s",
408                 position_.ToString().c_str());
409         }
410     }
411     dumpInfo_.originPlacement =
412         PlacementUtils::ConvertPlacementToString(props->GetMenuPlacement().value_or(Placement::NONE));
413     placement_ = props->GetMenuPlacement().value_or(Placement::BOTTOM_LEFT);
414     if (menuPattern->IsSubMenu() && Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
415         placement_ = props->GetMenuPlacement().value_or(Placement::BOTTOM_RIGHT);
416     }
417     ModifyPositionToWrapper(layoutWrapper, position_);
418     if (menuPattern->GetPreviewMode() != MenuPreviewMode::NONE) {
419         ModifyPreviewMenuPlacement(layoutWrapper);
420     }
421     dumpInfo_.defaultPlacement = PlacementUtils::ConvertPlacementToString(placement_);
422     InitSpace(props, menuPattern);
423     holdEmbeddedMenuPosition_ = HoldEmbeddedMenuPosition(layoutWrapper);
424     auto previewRect = menuPattern->GetPreviewRect();
425     previewOriginOffset_ = menuPattern->GetPreviewOriginOffset();
426     previewOffset_ = previewRect.GetOffset();
427     previewSize_ = previewRect.GetSize();
428 }
429 
InitializeSecurityPadding()430 void MenuLayoutAlgorithm::InitializeSecurityPadding()
431 {
432     float topSecurity = 0.0f;
433     float bottomSecurity = 0.0f;
434     if (SystemProperties::GetDeviceOrientation() == DeviceOrientation::PORTRAIT) {
435         if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TWELVE)) {
436             topSecurity = static_cast<float>(PORTRAIT_TOP_SECURITY_API12.ConvertToPx());
437             bottomSecurity = static_cast<float>(PORTRAIT_BOTTOM_SECURITY_API12.ConvertToPx());
438         } else {
439             topSecurity = static_cast<float>(PORTRAIT_TOP_SECURITY.ConvertToPx());
440             bottomSecurity = static_cast<float>(PORTRAIT_BOTTOM_SECURITY.ConvertToPx());
441         }
442     } else {
443         topSecurity = static_cast<float>(LANDSCAPE_TOP_SECURITY.ConvertToPx());
444         bottomSecurity = static_cast<float>(LANDSCAPE_BOTTOM_SECURITY.ConvertToPx());
445     }
446     param_.topSecurity = topSecurity;
447     param_.bottomSecurity = bottomSecurity;
448 }
449 
InitializeParam(LayoutWrapper * layoutWrapper,const RefPtr<MenuPattern> & menuPattern)450 void MenuLayoutAlgorithm::InitializeParam(LayoutWrapper* layoutWrapper, const RefPtr<MenuPattern>& menuPattern)
451 {
452     CHECK_NULL_VOID(layoutWrapper);
453     auto hostNode = layoutWrapper->GetHostNode();
454     CHECK_NULL_VOID(hostNode);
455     auto pipelineContext = DialogManager::GetMainPipelineContext(hostNode);
456     CHECK_NULL_VOID(pipelineContext);
457     auto safeAreaManager = pipelineContext->GetSafeAreaManager();
458     CHECK_NULL_VOID(safeAreaManager);
459     CHECK_NULL_VOID(menuPattern);
460     auto safeAreaInsets = OverlayManager::GetSafeAreaInsets(menuPattern->GetHost());
461     auto top = safeAreaInsets.top_.Length();
462     auto props = menuPattern->GetLayoutProperty<MenuLayoutProperty>();
463     CHECK_NULL_VOID(props);
464     auto bottom = GetBottomBySafeAreaManager(safeAreaManager, props, menuPattern);
465     InitializeSecurityPadding();
466     auto menuWindowRect = GetMenuWindowRectInfo(menuPattern);
467     float windowsOffsetX = static_cast<float>(menuWindowRect.GetOffset().GetX());
468     float windowsOffsetY = static_cast<float>(menuWindowRect.GetOffset().GetY());
469     SizeF windowGlobalSizeF(menuWindowRect.Width(), menuWindowRect.Height());
470     if (canExpandCurrentWindow_) {
471         param_.windowsOffsetX = windowsOffsetX;
472         param_.windowsOffsetY = windowsOffsetY;
473     } else {
474         param_.windowsOffsetX = 0;
475         param_.windowsOffsetY = 0;
476     }
477     param_.menuWindowRect = menuWindowRect;
478     param_.windowGlobalSizeF = windowGlobalSizeF;
479     param_.top = top;
480     param_.bottom = bottom;
481     param_.left = safeAreaInsets.left_.Length();
482     param_.right = safeAreaInsets.right_.Length();
483     param_.previewMenuGap = targetSecurity_;
484 
485     InitWrapperRect(props, menuPattern);
486     InitializeLayoutRegionMargin(menuPattern);
487 
488     param_.leftSecurity = paddingStart_;
489     param_.rightSecurity = paddingEnd_;
490     param_.wrapperRect = wrapperRect_;
491     menuPattern->SetMenuLayoutParam(param_);
492 }
493 
InitializeLayoutRegionMargin(const RefPtr<MenuPattern> & menuPattern)494 void MenuLayoutAlgorithm::InitializeLayoutRegionMargin(const RefPtr<MenuPattern>& menuPattern)
495 {
496     CHECK_NULL_VOID(menuPattern);
497     auto menuWrapper = menuPattern->GetMenuWrapper();
498     CHECK_NULL_VOID(menuWrapper);
499     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
500     CHECK_NULL_VOID(menuWrapperPattern);
501 
502     auto menuParam = menuWrapperPattern->GetMenuParam();
503     isPreviewContainScale_ = menuParam.isPreviewContainScale ||
504                              menuParam.previewScaleMode.value_or(PreviewScaleMode::AUTO) == PreviewScaleMode::MAINTAIN;
505     previewScaleMode_ = menuParam.previewScaleMode;
506     availableLayoutAreaMode_ = menuParam.availableLayoutAreaMode;
507     if (!menuParam.layoutRegionMargin.has_value()) {
508         return;
509     }
510 
511     auto marginProps = menuParam.layoutRegionMargin.value();
512     float left = marginProps.start.has_value()
513                      ? marginProps.start.value().GetDimension().ConvertToPxWithSize(wrapperSize_.Width())
514                      : paddingStart_;
515     float right = marginProps.end.has_value()
516                       ? marginProps.end.value().GetDimension().ConvertToPxWithSize(wrapperSize_.Width())
517                       : paddingEnd_;
518     float top = marginProps.top.has_value()
519                     ? marginProps.top.value().GetDimension().ConvertToPxWithSize(wrapperSize_.Height())
520                     : param_.topSecurity;
521     float bottom = marginProps.bottom.has_value()
522                        ? marginProps.bottom.value().GetDimension().ConvertToPxWithSize(wrapperSize_.Height())
523                        : param_.bottomSecurity;
524 
525     if (LessNotEqual(left + right, wrapperSize_.Width())) {
526         paddingStart_ = left;
527         paddingEnd_ = right;
528         if (marginProps.start.has_value()) {
529             layoutRegionMargin_.left = left;
530         }
531         if (marginProps.end.has_value()) {
532             layoutRegionMargin_.right = right;
533         }
534     }
535 
536     if (LessNotEqual(top + bottom, wrapperSize_.Height())) {
537         param_.topSecurity = top;
538         param_.bottomSecurity = bottom;
539         if (marginProps.top.has_value()) {
540             layoutRegionMargin_.top = top;
541         }
542         if (marginProps.bottom.has_value()) {
543             layoutRegionMargin_.bottom = bottom;
544         }
545     }
546 }
547 
InitWrapperRect(const RefPtr<MenuLayoutProperty> & props,const RefPtr<MenuPattern> & menuPattern)548 void MenuLayoutAlgorithm::InitWrapperRect(
549     const RefPtr<MenuLayoutProperty>& props, const RefPtr<MenuPattern>& menuPattern)
550 {
551     if (canExpandCurrentWindow_ && isExpandDisplay_ && !isTargetNodeInSubwindow_) {
552         wrapperRect_ = param_.menuWindowRect;
553         wrapperSize_ = SizeF(wrapperRect_.Width(), wrapperRect_.Height());
554         dumpInfo_.wrapperRect = wrapperRect_;
555         width_ = wrapperRect_.Width();
556         height_ = wrapperRect_.Height();
557         TAG_LOGI(
558             AceLogTag::ACE_MENU, "InitWrapperRect with menuWindowRect : %{public}s", wrapperRect_.ToString().c_str());
559         return;
560     }
561     wrapperRect_.SetRect(0, 0, param_.menuWindowRect.Width(), param_.menuWindowRect.Height());
562     CHECK_NULL_VOID(menuPattern);
563     auto host = menuPattern->GetHost();
564     CHECK_NULL_VOID(host);
565     auto pipelineContext = DialogManager::GetMainPipelineContext(host);
566     CHECK_NULL_VOID(pipelineContext);
567     auto safeAreaManager = pipelineContext->GetSafeAreaManager();
568     CHECK_NULL_VOID(safeAreaManager);
569     // system safeArea(AvoidAreaType.TYPE_SYSTEM) only include status bar,now the bottom is 0
570     auto safeAreaInsets = OverlayManager::GetSafeAreaInsets(menuPattern->GetHost());
571     bottom_ = static_cast<double>(GetBottomBySafeAreaManager(safeAreaManager, props, menuPattern));
572     top_ = static_cast<double>(safeAreaInsets.top_.Length());
573     left_ = static_cast<double>(safeAreaInsets.left_.Length());
574     right_ = static_cast<double>(safeAreaInsets.right_.Length());
575     width_ = wrapperRect_.Width();
576     height_ = wrapperRect_.Height();
577     dumpInfo_.top = top_;
578     dumpInfo_.bottom = bottom_;
579     dumpInfo_.left = left_;
580     dumpInfo_.right = right_;
581     TAG_LOGI(AceLogTag::ACE_MENU,
582         "safeAreaInsets: (top: %{public}f, bottom: %{public}f, left: %{public}f, right: %{public}f)", top_, bottom_,
583         left_, right_);
584     auto windowManager = pipelineContext->GetWindowManager();
585     auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL && windowManager &&
586                             windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING;
587 
588     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
589         if (!canExpandCurrentWindow_ && isContainerModal) {
590             LimitContainerModalMenuRect(width_, height_, menuPattern);
591         }
592     }
593     wrapperRect_.SetRect(left_, top_, width_ - left_ - right_, height_ - top_ - bottom_);
594     wrapperSize_ = SizeF(wrapperRect_.Width(), wrapperRect_.Height());
595     dumpInfo_.wrapperRect = wrapperRect_;
596     TAG_LOGI(AceLogTag::ACE_MENU, "InitWrapperRect with safeAreaInsets : %{public}s", wrapperRect_.ToString().c_str());
597 }
598 
UpdateWrapperRectForHoverMode(const RefPtr<MenuLayoutProperty> & props,const RefPtr<MenuPattern> & menuPattern,double creaseHeightOffset)599 void MenuLayoutAlgorithm::UpdateWrapperRectForHoverMode(
600     const RefPtr<MenuLayoutProperty>& props, const RefPtr<MenuPattern>& menuPattern, double creaseHeightOffset)
601 {
602     auto container = Container::CurrentSafelyWithCheck();
603     CHECK_NULL_VOID(container);
604     auto displayInfo = container->GetDisplayInfo();
605     CHECK_NULL_VOID(displayInfo);
606     auto foldCreaseRects = displayInfo->GetCurrentFoldCreaseRegion();
607     double creaseTop = 0.0;
608     double creaseBottom = 0.0;
609     double creaseHeight = 0.0;
610     if (!foldCreaseRects.empty()) {
611         auto foldCrease = foldCreaseRects.front();
612         creaseTop = foldCrease.Top() - creaseHeightOffset;
613         creaseBottom = foldCrease.Bottom() - creaseHeightOffset;
614         creaseHeight = foldCrease.Height();
615     }
616     double offsetY = 0.0;
617     if (props->GetMenuPlacement().has_value()) {
618         offsetY = targetOffset_.GetY();
619     } else {
620         offsetY = position_.GetY();
621     }
622     auto isShowInSubWindow = canExpandCurrentWindow_ && isExpandDisplay_ && !isTargetNodeInSubwindow_;
623     auto windowsOffsetY = isShowInSubWindow ? param_.windowsOffsetY : 0;
624     if (LessNotEqual(offsetY, creaseTop)) {
625         wrapperRect_.SetRect(left_, top_ + windowsOffsetY, width_ - left_ - right_, creaseTop - top_);
626     } else if (GreatNotEqual(offsetY, creaseBottom)) {
627         wrapperRect_.SetRect(left_, creaseBottom, width_ - left_ - right_,
628         windowsOffsetY + height_ - creaseBottom - bottom_);
629     } else {
630         wrapperRect_.SetRect(left_, top_ + windowsOffsetY, width_ - left_ - right_, height_ - top_ - bottom_);
631     }
632     dumpInfo_.wrapperRect = wrapperRect_;
633     TAG_LOGI(AceLogTag::ACE_MENU, "Update wrapperRect for hoverMode : %{public}s", wrapperRect_.ToString().c_str());
634 }
635 
GetBottomBySafeAreaManager(const RefPtr<SafeAreaManager> & safeAreaManager,const RefPtr<MenuLayoutProperty> & props,const RefPtr<MenuPattern> & menuPattern)636 uint32_t MenuLayoutAlgorithm::GetBottomBySafeAreaManager(const RefPtr<SafeAreaManager>& safeAreaManager,
637     const RefPtr<MenuLayoutProperty>& props, const RefPtr<MenuPattern>& menuPattern)
638 {
639     CHECK_NULL_RETURN(menuPattern, 0);
640     auto safeAreaInsets = OverlayManager::GetSafeAreaInsets(menuPattern->GetHost());
641     auto bottom = safeAreaInsets.bottom_.Length();
642     CHECK_NULL_RETURN(safeAreaManager, 0);
643     auto keyboardHeight = safeAreaManager->GetKeyboardInsetImpl().Length();
644     if ((menuPattern->IsSelectOverlayExtensionMenu() || menuPattern->IsSelectOverlayRightClickMenu()) &&
645         GreatNotEqual(keyboardHeight, 0)) {
646         bottom = keyboardHeight;
647     }
648 
649     CHECK_NULL_RETURN(props, 0);
650     // Determine whether the menu is an AI menu
651     if (props->GetIsRectInTargetValue(false)) {
652         if (LessOrEqual(keyboardHeight, 0)) {
653             keyboardHeight = safeAreaManager->GetkeyboardHeightConsideringUIExtension();
654         }
655         if (GreatNotEqual(keyboardHeight, 0)) {
656             bottom = keyboardHeight;
657         }
658     }
659     return bottom;
660 }
661 
InitSpace(const RefPtr<MenuLayoutProperty> & props,const RefPtr<MenuPattern> & menuPattern)662 void MenuLayoutAlgorithm::InitSpace(const RefPtr<MenuLayoutProperty>& props, const RefPtr<MenuPattern>& menuPattern)
663 {
664     auto targetSize = props->GetTargetSizeValue(SizeF());
665     if (props->GetMenuPlacement().has_value()) {
666         auto targetSecurity = targetSecurity_;
667         topSpace_ = std::max(0.0, targetOffset_.GetY() - targetSecurity - paddingTop_ - wrapperRect_.Top());
668         bottomSpace_ = std::max(0.0,
669             wrapperRect_.Bottom() - targetOffset_.GetY() - targetSize_.Height() - targetSecurity - paddingBottom_);
670         if (NearZero(topSpace_) && NearZero(bottomSpace_)) {
671             bottomSpace_ = wrapperRect_.Bottom() - position_.GetY() - paddingTop_;
672         }
673         leftSpace_ = std::max(0.0, wrapperRect_.Left() + targetOffset_.GetX() - paddingStart_ - targetSecurity);
674         rightSpace_ = std::max(
675             0.0, wrapperRect_.Right() - targetSize_.Width() - targetSecurity - paddingStart_ - paddingEnd_);
676         if (NearZero(leftSpace_) && NearZero(rightSpace_)) {
677             leftSpace_ = position_.GetX();
678             rightSpace_ = wrapperRect_.Right() - leftSpace_;
679         }
680     } else {
681         if (canExpandCurrentWindow_ || !Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
682             topSpace_ = position_.GetY() - targetSize.Height() - paddingTop_ - wrapperRect_.Top();
683             bottomSpace_ = wrapperRect_.Bottom() - position_.GetY() - paddingBottom_;
684         } else {
685             topSpace_ = position_.GetY() - wrapperRect_.Top() - paddingTop_;
686             bottomSpace_ = wrapperRect_.Bottom() - position_.GetY() - paddingTop_;
687         }
688         leftSpace_ = position_.GetX() - paddingStart_;
689         rightSpace_ = wrapperRect_.Right() - position_.GetX() - paddingEnd_;
690     }
691 }
692 
InitializePadding(LayoutWrapper * layoutWrapper)693 void MenuLayoutAlgorithm::InitializePadding(LayoutWrapper* layoutWrapper)
694 {
695     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
696         InitializePaddingAPI12(layoutWrapper);
697         return;
698     }
699     auto menuNode = layoutWrapper->GetHostNode();
700     CHECK_NULL_VOID(menuNode);
701     auto menuPattern = menuNode->GetPattern<MenuPattern>();
702     CHECK_NULL_VOID(menuPattern);
703     auto theme = GetCurrentSelectTheme(menuNode);
704     CHECK_NULL_VOID(theme);
705     margin_ = static_cast<float>(theme->GetMenuPadding().ConvertToPx());
706     optionPadding_ = margin_;
707     paddingStart_ = static_cast<float>(theme->GetDefaultPaddingStart().ConvertToPx());
708     paddingEnd_ = static_cast<float>(theme->GetDefaultPaddingEnd().ConvertToPx());
709     paddingTop_ = static_cast<float>(theme->GetDefaultPaddingTop().ConvertToPx());
710     paddingBottom_ = static_cast<float>(theme->GetDefaultPaddingBottomFixed().ConvertToPx());
711 }
712 
InitializePaddingAPI12(LayoutWrapper * layoutWrapper)713 void MenuLayoutAlgorithm::InitializePaddingAPI12(LayoutWrapper* layoutWrapper)
714 {
715     auto menuNode = layoutWrapper->GetHostNode();
716     CHECK_NULL_VOID(menuNode);
717     auto menuPattern = menuNode->GetPattern<MenuPattern>();
718     CHECK_NULL_VOID(menuPattern);
719     auto theme = GetCurrentSelectTheme(menuNode);
720     CHECK_NULL_VOID(theme);
721 
722     margin_ = static_cast<float>(theme->GetMenuPadding().ConvertToPx());
723     optionPadding_ = margin_;
724     if (!canExpandCurrentWindow_) {
725         paddingStart_ = static_cast<float>(theme->GetMenuLargeMargin().ConvertToPx());
726         paddingEnd_ = static_cast<float>(theme->GetMenuLargeMargin().ConvertToPx());
727     } else {
728         paddingStart_ = static_cast<float>(theme->GetMenuMediumMargin().ConvertToPx());
729         paddingEnd_ = static_cast<float>(theme->GetMenuMediumMargin().ConvertToPx());
730     }
731 }
732 
ModifyPositionToWrapper(LayoutWrapper * layoutWrapper,OffsetF & position)733 void MenuLayoutAlgorithm::ModifyPositionToWrapper(LayoutWrapper* layoutWrapper, OffsetF& position)
734 {
735     auto menu = layoutWrapper->GetHostNode();
736     CHECK_NULL_VOID(menu);
737     auto wrapper = AceType::DynamicCast<FrameNode>(menu->GetParent());
738     CHECK_NULL_VOID(wrapper);
739 
740     OffsetF wrapperOffset;
741     // minus wrapper offset in LayoutFullScreen
742     auto wrapperLayoutProps = wrapper->GetLayoutProperty();
743     CHECK_NULL_VOID(wrapperLayoutProps);
744     auto&& safeAreaInsets = wrapperLayoutProps->GetSafeAreaInsets();
745     if (safeAreaInsets) {
746         wrapperOffset +=
747             OffsetF(static_cast<float>(safeAreaInsets->left_.end), static_cast<float>(safeAreaInsets->top_.end));
748         position -= wrapperOffset;
749     }
750 
751     auto menuPattern = menu->GetPattern<MenuPattern>();
752     CHECK_NULL_VOID(menuPattern);
753     bool isSubMenu = menuPattern->IsSubMenu() || menuPattern->IsSelectOverlaySubMenu();
754     if ((menuPattern->IsContextMenu() || (isSubMenu && Container::CurrentId() >= MIN_SUBCONTAINER_ID) ||
755             canExpandCurrentWindow_) &&
756         (targetTag_ != V2::SELECT_ETS_TAG)) {
757         // no need to modify for context menu, because context menu wrapper is full screen.
758         return;
759     }
760     // minus wrapper offset in floating window
761     auto pipelineContext = DialogManager::GetMainPipelineContext(menu);
762     CHECK_NULL_VOID(pipelineContext);
763     auto windowManager = pipelineContext->GetWindowManager();
764     auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL;
765     if (isContainerModal) {
766         wrapperOffset = OffsetF(0.0f, static_cast<float>(pipelineContext->GetCustomTitleHeight().ConvertToPx()));
767         if (windowManager && windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING) {
768             wrapperOffset += OffsetF(static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()),
769                 static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()));
770         }
771         position -= wrapperOffset;
772     }
773 }
774 
IsNodeOnRootTree(const RefPtr<FrameNode> & frameNode)775 bool IsNodeOnRootTree(const RefPtr<FrameNode>& frameNode)
776 {
777     auto parent = frameNode->GetParent();
778     while (parent) {
779         if (parent->GetTag() == V2::ROOT_ETS_TAG) {
780             return true;
781         }
782         parent = parent->GetParent();
783     }
784     TAG_LOGW(AceLogTag::ACE_MENU, "node %{public}d not no root tree", frameNode->GetId());
785     return false;
786 }
787 
788 // Called to perform layout render node and child.
Measure(LayoutWrapper * layoutWrapper)789 void MenuLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
790 {
791     auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
792     // if targetNode == nullptr, it means the menu is subMenu or multiMenu
793     if (targetNode && !IsNodeOnRootTree(targetNode)) {
794         TAG_LOGW(AceLogTag::ACE_MENU, "measure return because targetNode %{public}d not no root tree", targetNodeId_);
795         return;
796     }
797     // initialize screen size and menu position
798     CHECK_NULL_VOID(layoutWrapper);
799     MenuDumpInfo dumpInfo;
800     auto menuNode = layoutWrapper->GetHostNode();
801     CHECK_NULL_VOID(menuNode);
802     auto menuPattern = menuNode->GetPattern<MenuPattern>();
803     CHECK_NULL_VOID(menuPattern);
804     auto menuLayoutProperty = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
805     CHECK_NULL_VOID(menuLayoutProperty);
806     auto isContextMenu = menuPattern->IsContextMenu();
807     auto isShowInSubWindow = menuLayoutProperty->GetShowInSubWindowValue(true) || isContextMenu;
808     InitCanExpandCurrentWindow(isShowInSubWindow, layoutWrapper);
809     Initialize(layoutWrapper);
810     if (!targetTag_.empty()) {
811         InitTargetSizeAndPosition(layoutWrapper, menuPattern->IsContextMenu(), menuPattern);
812     }
813     CalcWrapperRectForHoverMode(menuPattern);
814 
815     const auto& constraint = menuLayoutProperty->GetLayoutConstraint();
816     if (!constraint) {
817         return;
818     }
819 
820     auto idealSize = CreateIdealSize(
821         constraint.value(), Axis::VERTICAL, menuLayoutProperty->GetMeasureType(MeasureType::MATCH_CONTENT), true);
822     const auto& padding = menuLayoutProperty->CreatePaddingAndBorder();
823     MinusPaddingToSize(padding, idealSize);
824 
825     // calculate menu main size
826     auto childConstraint = CreateChildConstraint(layoutWrapper);
827     if (menuPattern->IsSelectMenu() && menuPattern->GetHasOptionWidth()) {
828         auto selectMenuWidth = menuPattern->GetSelectMenuWidth();
829         childConstraint.maxSize.SetWidth(selectMenuWidth);
830         childConstraint.parentIdealSize.SetWidth(selectMenuWidth);
831         childConstraint.selfIdealSize.SetWidth(selectMenuWidth);
832     }
833 
834     // The menu width child Constraint is added to the 2in1 device in API13
835     UpdateChildConstraintByDevice(menuPattern, childConstraint, constraint.value());
836 
837     auto parentItem = menuPattern->GetParentMenuItem();
838     CalculateIdealSize(layoutWrapper, childConstraint, padding, idealSize, parentItem);
839 }
840 
CalcWrapperRectForHoverMode(const RefPtr<MenuPattern> & menuPattern)841 void MenuLayoutAlgorithm::CalcWrapperRectForHoverMode(const RefPtr<MenuPattern>& menuPattern)
842 {
843     CHECK_NULL_VOID(menuPattern);
844     auto menuNode = menuPattern->GetHost();
845     CHECK_NULL_VOID(menuNode);
846     auto pipelineContext = menuNode->GetContext();
847     CHECK_NULL_VOID(pipelineContext);
848     auto menuWrapper = menuPattern->GetMenuWrapper();
849     CHECK_NULL_VOID(menuWrapper);
850     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
851     CHECK_NULL_VOID(menuWrapperPattern);
852     auto menuLayoutProperty = menuNode->GetLayoutProperty<MenuLayoutProperty>();
853     CHECK_NULL_VOID(menuLayoutProperty);
854     if (OverlayManager::IsNeedAvoidFoldCrease(menuNode, true, isExpandDisplay_, menuWrapperPattern->GetHoverMode())) {
855         auto creaseHeightOffset =
856             pipelineContext->GetDisplayAvailableRect().Top() + menuNode->GetParentGlobalOffsetWithSafeArea().GetY();
857         UpdateWrapperRectForHoverMode(menuLayoutProperty, menuPattern, creaseHeightOffset);
858     }
859 }
860 
CheckChildConstraintCondition(const RefPtr<MenuPattern> & menuPattern)861 bool MenuLayoutAlgorithm::CheckChildConstraintCondition(const RefPtr<MenuPattern>& menuPattern)
862 {
863     CHECK_NULL_RETURN(menuPattern, false);
864     CHECK_NULL_RETURN(Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_THIRTEEN), false);
865     if (menuPattern->IsSubMenu()) {
866         auto parentItem = menuPattern->GetParentMenuItem();
867         CHECK_NULL_RETURN(parentItem, false);
868         auto parentPattern = parentItem->GetPattern<MenuItemPattern>();
869         CHECK_NULL_RETURN(parentPattern, false);
870         auto expandingMode = parentPattern->GetExpandingMode();
871         if (expandingMode == SubMenuExpandingMode::SIDE) {
872             return true;
873         }
874         return false;
875     }
876 
877     if (menuPattern->IsMenu() || menuPattern->IsContextMenu()) {
878         return true;
879     }
880     return false;
881 }
882 
UpdateChildConstraintByDevice(const RefPtr<MenuPattern> & menuPattern,LayoutConstraintF & childConstraint,const LayoutConstraintF & layoutConstraint)883 void MenuLayoutAlgorithm::UpdateChildConstraintByDevice(const RefPtr<MenuPattern>& menuPattern,
884     LayoutConstraintF& childConstraint, const LayoutConstraintF& layoutConstraint)
885 {
886     CHECK_NULL_VOID(menuPattern);
887 
888     // only 2in1 device has restrictions on the menu width in API13
889     if (!CheckChildConstraintCondition(menuPattern)) {
890         return;
891     }
892     auto host = menuPattern->GetHost();
893     CHECK_NULL_VOID(host);
894     auto pipeline = host->GetContext();
895     CHECK_NULL_VOID(pipeline);
896     auto theme = pipeline->GetTheme<SelectTheme>();
897     CHECK_NULL_VOID(theme);
898 
899     auto expandDisplay = theme->GetExpandDisplay();
900     CHECK_NULL_VOID(expandDisplay);
901 
902     auto menuMinWidth = theme->GetMenuMinWidth().ConvertToPx();
903     auto menuDefaultWidth = theme->GetMenuDefaultWidth().ConvertToPx();
904     auto menuMaxWidth = theme->GetMenuMaxWidthRatio() * pipeline->GetDisplayWindowRectInfo().Width();
905     double minWidth = 0.0f;
906     double maxWidth = 0.0f;
907 
908     auto firstMenu = menuPattern->GetFirstInnerMenu();
909     CHECK_NULL_VOID(firstMenu);
910     auto layoutProperty = firstMenu->GetLayoutProperty<MenuLayoutProperty>();
911     CHECK_NULL_VOID(layoutProperty);
912     if (layoutProperty->HasMenuWidth()) {
913         auto menuWidth = layoutProperty->GetMenuWidthValue();
914         auto menuWidthPX = (menuWidth.Unit() == DimensionUnit::PERCENT) ?
915             menuWidth.Value() * layoutConstraint.percentReference.Width() : menuWidth.ConvertToPx();
916         if (LessNotEqual(menuWidthPX, menuMinWidth) || GreatNotEqual(menuWidthPX, menuMaxWidth)) {
917             minWidth = menuDefaultWidth;
918             maxWidth = menuMaxWidth;
919         } else {
920             minWidth = menuWidthPX;
921             maxWidth = menuWidthPX;
922         }
923     } else {
924         minWidth = menuDefaultWidth;
925         maxWidth = menuMaxWidth;
926     }
927     childConstraint.minSize.SetWidth(minWidth);
928     childConstraint.maxSize.SetWidth(maxWidth);
929 }
930 
UpdateSelectFocus(LayoutWrapper * layoutWrapper,LayoutConstraintF & childConstraint)931 void MenuLayoutAlgorithm::UpdateSelectFocus(LayoutWrapper* layoutWrapper, LayoutConstraintF& childConstraint)
932 {
933     auto host = layoutWrapper->GetHostNode();
934     CHECK_NULL_VOID(host);
935     auto pattern = host->GetPattern<MenuPattern>();
936     CHECK_NULL_VOID(pattern);
937     if (!pattern->IsSelectMenu()) {
938         return;
939     }
940     for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
941         auto childHost = child->GetHostNode();
942         CHECK_NULL_VOID(childHost);
943         // when use contentModifier the origin scroll node should be hide.
944         auto needHide = pattern->UseContentModifier() && childHost->GetId() != pattern->GetBuilderId();
945         auto layoutProperty = childHost->GetLayoutProperty();
946         CHECK_NULL_VOID(layoutProperty);
947         layoutProperty->UpdateVisibility(needHide ? VisibleType::GONE : VisibleType::VISIBLE);
948         auto focusHub = childHost->GetOrCreateFocusHub();
949         CHECK_NULL_VOID(focusHub);
950         focusHub->SetShow(!needHide);
951         // when use contentModifier the default focus should change to the first custom node.
952         if (childHost->GetChildren().empty()) {
953             return;
954         }
955         auto columnChild = childHost->GetChildAtIndex(0);
956         CHECK_NULL_VOID(columnChild);
957         if (columnChild->GetChildren().empty()) {
958             return;
959         }
960         auto optionChild = columnChild->GetChildAtIndex(0);
961         CHECK_NULL_VOID(optionChild);
962         auto optionNode = AceType::DynamicCast<FrameNode>(optionChild);
963         CHECK_NULL_VOID(optionNode);
964         auto optionFocusHub = optionNode->GetOrCreateFocusHub();
965         CHECK_NULL_VOID(optionFocusHub);
966         optionFocusHub->SetIsDefaultFocus(!needHide);
967     }
968 }
969 
GetFirstItemBottomPositionY(const RefPtr<FrameNode> & menu)970 float MenuLayoutAlgorithm::GetFirstItemBottomPositionY(const RefPtr<FrameNode>& menu)
971 {
972     CHECK_NULL_RETURN(menu, 0.0f);
973     auto menuPattern = menu->GetPattern<MenuPattern>();
974     CHECK_NULL_RETURN(menuPattern, 0.0f);
975     auto firstMenuItem = menuPattern->GetFirstMenuItem();
976     CHECK_NULL_RETURN(firstMenuItem, 0.0f);
977     auto geometryNode = firstMenuItem->GetGeometryNode();
978     CHECK_NULL_RETURN(geometryNode, 0.0f);
979     return firstMenuItem->GetPaintRectOffset(false, true).GetY() + geometryNode->GetMarginFrameSize().Height();
980 }
981 
GetLastItemTopPositionY(const RefPtr<FrameNode> & menu)982 float MenuLayoutAlgorithm::GetLastItemTopPositionY(const RefPtr<FrameNode>& menu)
983 {
984     CHECK_NULL_RETURN(menu, 0.0f);
985     auto menuPattern = menu->GetPattern<MenuPattern>();
986     CHECK_NULL_RETURN(menuPattern, 0.0f);
987     auto lastMenuItem = menuPattern->GetLastMenuItem();
988     CHECK_NULL_RETURN(lastMenuItem, 0.0f);
989     return lastMenuItem->GetPaintRectOffset(false, true).GetY();
990 }
991 
GetMenuBottomPositionY(const RefPtr<FrameNode> & menu)992 float MenuLayoutAlgorithm::GetMenuBottomPositionY(const RefPtr<FrameNode>& menu)
993 {
994     CHECK_NULL_RETURN(menu, 0.0f);
995     auto geometryNode = menu->GetGeometryNode();
996     CHECK_NULL_RETURN(geometryNode, 0.0f);
997     return menu->GetPaintRectOffset(false, true).GetY() + geometryNode->GetMarginFrameSize().Height();
998 }
999 
isContainerModal(const RefPtr<FrameNode> & node)1000 bool MenuLayoutAlgorithm::isContainerModal(const RefPtr<FrameNode>& node)
1001 {
1002     CHECK_NULL_RETURN(node, false);
1003     auto pipeline = node->GetContext();
1004     CHECK_NULL_RETURN(pipeline, false);
1005     return (!canExpandCurrentWindow_) && pipeline->GetWindowModal() == WindowModal::CONTAINER_MODAL;
1006 }
1007 
GetContainerModalOffsetY(const RefPtr<FrameNode> & node)1008 float MenuLayoutAlgorithm::GetContainerModalOffsetY(const RefPtr<FrameNode>& node)
1009 {
1010     float defaultOffset = 0.0f;
1011     CHECK_NULL_RETURN(node, defaultOffset);
1012     auto pipeline = node->GetContext();
1013     CHECK_NULL_RETURN(pipeline, defaultOffset);
1014     return static_cast<float>(pipeline->GetCustomTitleHeight().ConvertToPx())
1015         + static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx());
1016 }
1017 
CalcSubMenuMaxHeightWithPreview(RefPtr<FrameNode> parentMenu,LayoutConstraintF & childConstraint,float lastItemTopPositionY,float firstItemBottomPositionY,float parentMenuPositionY)1018 float MenuLayoutAlgorithm::CalcSubMenuMaxHeightWithPreview(RefPtr<FrameNode> parentMenu,
1019     LayoutConstraintF& childConstraint,
1020     float lastItemTopPositionY, float firstItemBottomPositionY, float parentMenuPositionY)
1021 {
1022     float subMenuMaxHeight = childConstraint.maxSize.Height();
1023     CHECK_NULL_RETURN(parentMenu, subMenuMaxHeight);
1024     auto parentMenuPattern = parentMenu->GetPattern<MenuPattern>();
1025     CHECK_NULL_RETURN(parentMenuPattern, subMenuMaxHeight);
1026     auto parentPlacement = parentMenuPattern->GetLastPlacement().value_or(Placement::NONE);
1027     auto isContainerModalBool = isContainerModal(parentMenu);
1028     auto containerModalOffsetY = GetContainerModalOffsetY(parentMenu);
1029     auto [previewTopPositionY, previewBottomPositionY] = parentMenuPattern->GetPreviewPositionY();
1030     //correct position when window modal is containerModal
1031     if (isContainerModalBool) {
1032         previewTopPositionY -= containerModalOffsetY;
1033         previewBottomPositionY -= containerModalOffsetY;
1034     }
1035     if (parentPlacement == Placement::TOP_LEFT || parentPlacement == Placement::TOP ||
1036         parentPlacement == Placement::TOP_RIGHT) {
1037         subMenuMaxHeight = wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity
1038             - (previewBottomPositionY - lastItemTopPositionY);
1039     } else if (parentPlacement == Placement::BOTTOM_LEFT || parentPlacement == Placement::BOTTOM ||
1040             parentPlacement == Placement::BOTTOM_RIGHT) {
1041         subMenuMaxHeight = wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity
1042             - (firstItemBottomPositionY - previewTopPositionY);
1043     } else {
1044         subMenuMaxHeight = wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity
1045             - (firstItemBottomPositionY - parentMenuPositionY);
1046     }
1047     return subMenuMaxHeight;
1048 }
1049 
CalcSubMenuMaxHeightNoPreview(RefPtr<FrameNode> parentItem,LayoutConstraintF & childConstraint,float lastItemTopPositionY,float firstItemBottomPositionY,float parentMenuPositionY)1050 float MenuLayoutAlgorithm::CalcSubMenuMaxHeightNoPreview(RefPtr<FrameNode> parentItem,
1051     LayoutConstraintF& childConstraint,
1052     float lastItemTopPositionY, float firstItemBottomPositionY, float parentMenuPositionY)
1053 {
1054     float subMenuMaxHeight = childConstraint.maxSize.Height();
1055     CHECK_NULL_RETURN(parentItem, subMenuMaxHeight);
1056     auto parentItemGeometryNode = parentItem->GetGeometryNode();
1057     CHECK_NULL_RETURN(parentItemGeometryNode, subMenuMaxHeight);
1058     auto parentItemPattern = parentItem->GetPattern<MenuItemPattern>();
1059     CHECK_NULL_RETURN(parentItemPattern, subMenuMaxHeight);
1060     auto parentMenu = parentItemPattern->GetMenu(true);
1061     CHECK_NULL_RETURN(parentMenu, subMenuMaxHeight);
1062     auto parentMenuPattern = parentMenu->GetPattern<MenuPattern>();
1063     CHECK_NULL_RETURN(parentMenuPattern, subMenuMaxHeight);
1064     auto parentPlacement = parentMenuPattern->GetLastPlacement().value_or(Placement::NONE);
1065     if (parentPlacement == Placement::TOP_LEFT || parentPlacement == Placement::TOP ||
1066             parentPlacement == Placement::TOP_RIGHT) {
1067         auto parentMenuBottomY = GetMenuBottomPositionY(parentMenu);
1068         auto parentItemBottomY = parentItem->GetPaintRectOffset(false, true).GetY()
1069             + parentItemGeometryNode->GetMarginFrameSize().Height();
1070         float bottomSpace1 = parentMenuBottomY - parentItemBottomY;
1071         float bottomSpace2 = lastItemTopPositionY - wrapperRect_.Top() - param_.topSecurity;
1072         subMenuMaxHeight = std::max(bottomSpace1, bottomSpace2);
1073     } else {
1074         subMenuMaxHeight = wrapperRect_.Bottom() - param_.bottomSecurity - firstItemBottomPositionY;
1075         if (subMenuMaxHeight < parentItemGeometryNode->GetMarginFrameSize().Height()) {
1076             subMenuMaxHeight = wrapperRect_.Bottom() - param_.bottomSecurity - parentMenuPositionY;
1077         }
1078     }
1079     return subMenuMaxHeight;
1080 }
1081 
CalcSubMenuMaxHeightConstraint(LayoutConstraintF & childConstraint,RefPtr<FrameNode> parentItem)1082 float MenuLayoutAlgorithm::CalcSubMenuMaxHeightConstraint(LayoutConstraintF& childConstraint,
1083     RefPtr<FrameNode> parentItem)
1084 {
1085     float subMenuMaxHeight = childConstraint.maxSize.Height();
1086     CHECK_NULL_RETURN(parentItem, subMenuMaxHeight);
1087     auto parentItemPattern = parentItem->GetPattern<MenuItemPattern>();
1088     CHECK_NULL_RETURN(parentItemPattern, subMenuMaxHeight);
1089     auto parentMenu = parentItemPattern->GetMenu(true);
1090     CHECK_NULL_RETURN(parentMenu, subMenuMaxHeight);
1091     auto parentMenuPattern = parentMenu->GetPattern<MenuPattern>();
1092     CHECK_NULL_RETURN(parentMenuPattern, subMenuMaxHeight);
1093 
1094     auto parentFirstMenuItemPositionY = GetFirstItemBottomPositionY(parentMenu);
1095     auto lastMenuItemPositionY = GetLastItemTopPositionY(parentMenu);
1096     auto parentMenuPositionY = parentMenu->GetPaintRectOffset(false, true).GetY();
1097     auto containerModalOffsetY = GetContainerModalOffsetY(parentItem);
1098     if (isContainerModal(parentItem)) {
1099         parentMenuPositionY -= containerModalOffsetY;
1100         parentFirstMenuItemPositionY -= containerModalOffsetY;
1101         lastMenuItemPositionY -= containerModalOffsetY;
1102     }
1103 
1104     if (parentMenuPattern->GetPreviewMode() != MenuPreviewMode::NONE) {
1105         subMenuMaxHeight = CalcSubMenuMaxHeightWithPreview(parentMenu, childConstraint, lastMenuItemPositionY,
1106             parentFirstMenuItemPositionY, parentMenuPositionY);
1107     } else {
1108         subMenuMaxHeight = CalcSubMenuMaxHeightNoPreview(parentItem, childConstraint, lastMenuItemPositionY,
1109             parentFirstMenuItemPositionY, parentMenuPositionY);
1110     }
1111     return subMenuMaxHeight;
1112 }
1113 
CalculateIdealSize(LayoutWrapper * layoutWrapper,LayoutConstraintF & childConstraint,PaddingPropertyF padding,SizeF & idealSize,RefPtr<FrameNode> parentItem)1114 void MenuLayoutAlgorithm::CalculateIdealSize(LayoutWrapper* layoutWrapper,
1115     LayoutConstraintF& childConstraint, PaddingPropertyF padding, SizeF& idealSize,
1116     RefPtr<FrameNode> parentItem)
1117 {
1118     if (parentItem != nullptr) {
1119         auto parentPattern = parentItem->GetPattern<MenuItemPattern>();
1120         CHECK_NULL_VOID(parentPattern);
1121         auto expandingMode = parentPattern->GetExpandingMode();
1122         if (expandingMode == SubMenuExpandingMode::STACK) {
1123             auto parentPattern = parentItem->GetPattern<MenuItemPattern>();
1124             CHECK_NULL_VOID(parentPattern);
1125             auto parentMenu = parentPattern->GetMenu();
1126             auto parentWidth = parentMenu->GetGeometryNode()->GetFrameSize().Width();
1127             childConstraint.minSize.SetWidth(parentWidth);
1128             childConstraint.maxSize.SetWidth(parentWidth);
1129             childConstraint.selfIdealSize.SetWidth(parentWidth);
1130             auto subMenuMaxHeight = CalcSubMenuMaxHeightConstraint(childConstraint, parentItem);
1131             childConstraint.maxSize.SetHeight(std::min(subMenuMaxHeight, childConstraint.maxSize.Height()));
1132         }
1133     }
1134     PrepareExtensionMenuConstraint(layoutWrapper, childConstraint);
1135 
1136     auto host = layoutWrapper->GetHostNode();
1137     CHECK_NULL_VOID(host);
1138     auto pattern = host->GetPattern<MenuPattern>();
1139     CHECK_NULL_VOID(pattern);
1140     float idealHeight = 0.0f;
1141     float idealWidth = 0.0f;
1142     std::list<RefPtr<LayoutWrapper>> builderChildList;
1143     for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
1144         if (pattern->UseContentModifier()) {
1145             if (child->GetHostNode()->GetId() != pattern->GetBuilderId()) {
1146                 child->GetGeometryNode()->Reset();
1147                 child->GetGeometryNode()->SetContentSize(SizeF());
1148             } else {
1149                 child->Measure(childConstraint);
1150                 builderChildList.push_back(child);
1151             }
1152             BoxLayoutAlgorithm::PerformMeasureSelfWithChildList(layoutWrapper, builderChildList);
1153         } else {
1154             child->Measure(childConstraint);
1155         }
1156         auto childSize = child->GetGeometryNode()->GetMarginFrameSize();
1157         idealHeight += childSize.Height();
1158         idealWidth = std::max(idealWidth, childSize.Width());
1159     }
1160     idealSize.SetHeight(idealHeight);
1161     idealSize.SetWidth(idealWidth);
1162     AddPaddingToSize(padding, idealSize);
1163 
1164     auto geometryNode = layoutWrapper->GetGeometryNode();
1165     CHECK_NULL_VOID(geometryNode);
1166     geometryNode->SetFrameSize(idealSize);
1167     childMarginFrameSize_ = idealSize;
1168 }
1169 
CheckPreviewConstraintForConstant(const RefPtr<GeometryNode> & previewGeometryNode)1170 void MenuLayoutAlgorithm::CheckPreviewConstraintForConstant(const RefPtr<GeometryNode>& previewGeometryNode)
1171 {
1172     CHECK_NULL_VOID(previewGeometryNode);
1173     auto maxWidth = (wrapperRect_.Width() - paddingStart_ - paddingEnd_) / previewScale_;
1174     auto maxHeight = (wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity) / previewScale_;
1175     auto previewSize = previewGeometryNode->GetMarginFrameSize();
1176 
1177     auto width = std::min<float>(previewSize.Width(), maxWidth);
1178     auto height = std::min<float>(previewSize.Height(), maxHeight);
1179     previewGeometryNode->SetFrameSize(SizeF(width, height));
1180 }
1181 
CheckPreviewConstraint(const RefPtr<FrameNode> & frameNode,const Rect & menuWindowRect)1182 void MenuLayoutAlgorithm::CheckPreviewConstraint(const RefPtr<FrameNode>& frameNode, const Rect& menuWindowRect)
1183 {
1184     CHECK_NULL_VOID(frameNode &&
1185         (frameNode->GetTag() == V2::MENU_PREVIEW_ETS_TAG || frameNode->GetTag() == V2::FLEX_ETS_TAG));
1186     auto geometryNode = frameNode->GetGeometryNode();
1187     CHECK_NULL_VOID(geometryNode);
1188 
1189     if (previewScaleMode_.value_or(PreviewScaleMode::AUTO) == PreviewScaleMode::CONSTANT) {
1190         CheckPreviewConstraintForConstant(geometryNode);
1191         return;
1192     }
1193 
1194     auto maxWidth = wrapperSize_.Width();
1195     auto hasLayoutMargin = layoutRegionMargin_.left.has_value() || layoutRegionMargin_.right.has_value();
1196     if (hasLayoutMargin || previewScaleMode_.value_or(PreviewScaleMode::AUTO) == PreviewScaleMode::MAINTAIN) {
1197         maxWidth = std::max(0.0f, wrapperSize_.Width() - paddingStart_ - paddingEnd_) / previewScale_;
1198     } else {
1199         RefPtr<GridColumnInfo> columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
1200         CHECK_NULL_VOID(columnInfo);
1201         auto parent = columnInfo->GetParent();
1202         CHECK_NULL_VOID(parent);
1203         parent->BuildColumnWidth(std::min(menuWindowRect.Width(), menuWindowRect.Height()));
1204         maxWidth = static_cast<float>(columnInfo->GetWidth(GRID_COUNTS_4)) / previewScale_;
1205     }
1206 
1207     auto frameSize = geometryNode->GetMarginFrameSize();
1208     if (!isPreviewContainScale_) {
1209         static SizeF previewSize;
1210         static int32_t hostId = -1;
1211         if (previewSize == SizeF(0.0f, 0.0f) || hostId != frameNode->GetId()) {
1212             previewSize = frameSize;
1213             hostId = frameNode->GetId();
1214         } else {
1215             frameSize = previewSize;
1216         }
1217     }
1218 
1219     if (LessOrEqual(frameSize.Width(), maxWidth)) {
1220         geometryNode->SetFrameSize(SizeF(frameSize.Width(), frameSize.Height()));
1221     } else if (isPreviewContainScale_) {
1222         geometryNode->SetFrameSize(SizeF(maxWidth, frameSize.Height() * (maxWidth / frameSize.Width())));
1223     } else {
1224         geometryNode->SetFrameSize(SizeF(maxWidth, frameSize.Height()));
1225     }
1226 }
1227 
CheckPreviewSize(const RefPtr<LayoutWrapper> & previewLayoutWrapper,const RefPtr<MenuPattern> & menuPattern)1228 void MenuLayoutAlgorithm::CheckPreviewSize(
1229     const RefPtr<LayoutWrapper>& previewLayoutWrapper, const RefPtr<MenuPattern>& menuPattern)
1230 {
1231     CHECK_NULL_VOID(previewLayoutWrapper && menuPattern);
1232     auto previewNode = previewLayoutWrapper->GetHostNode();
1233     CHECK_NULL_VOID(previewNode);
1234     auto tag = previewNode->GetTag();
1235     auto isPreview = tag == V2::IMAGE_ETS_TAG || tag == V2::MENU_PREVIEW_ETS_TAG || tag == V2::FLEX_ETS_TAG;
1236     CHECK_NULL_VOID(isPreview);
1237 
1238     auto previewGeometryNode = previewNode->GetGeometryNode();
1239     CHECK_NULL_VOID(previewGeometryNode);
1240     auto previewSize = previewGeometryNode->GetMarginFrameSize();
1241 
1242     if (menuPattern->GetIsFirstShow()) {
1243         menuPattern->SetPreviewIdealSize(previewSize);
1244         return;
1245     }
1246 
1247     if (previewSize != menuPattern->GetPreviewIdealSize()) {
1248         auto menuWrapper = menuPattern->GetMenuWrapper();
1249         CHECK_NULL_VOID(menuWrapper);
1250         auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
1251         CHECK_NULL_VOID(menuWrapperPattern);
1252         auto constraint = menuWrapperPattern->GetChildLayoutConstraint();
1253         CHECK_NULL_VOID(constraint.maxSize.IsPositive() && constraint.percentReference.IsPositive());
1254         auto layoutProperty = previewLayoutWrapper->GetLayoutProperty();
1255         CHECK_NULL_VOID(layoutProperty);
1256         layoutProperty->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE);
1257         previewLayoutWrapper->Measure(constraint);
1258         menuPattern->SetPreviewIdealSize(previewGeometryNode->GetMarginFrameSize());
1259     }
1260 }
1261 
GetPreviewNodeTotalSize(const RefPtr<LayoutWrapper> & child,const Rect & menuWindowRect,RefPtr<LayoutWrapper> & previewLayoutWrapper,SizeF & size,const RefPtr<LayoutWrapper> & menuLayoutWrapper)1262 void MenuLayoutAlgorithm::GetPreviewNodeTotalSize(const RefPtr<LayoutWrapper>& child, const Rect& menuWindowRect,
1263     RefPtr<LayoutWrapper>& previewLayoutWrapper, SizeF& size, const RefPtr<LayoutWrapper>& menuLayoutWrapper)
1264 {
1265     CHECK_NULL_VOID(child);
1266     auto hostNode = child->GetHostNode();
1267     auto geometryNode = child->GetGeometryNode();
1268     if (!hostNode || !geometryNode) {
1269         return;
1270     }
1271 
1272     bool isImageNode = hostNode->GetTag() == V2::IMAGE_ETS_TAG;
1273     bool isPreviewNode = hostNode->GetTag() == V2::MENU_PREVIEW_ETS_TAG;
1274     bool isFlexNode = hostNode->GetTag() == V2::FLEX_ETS_TAG;
1275     if (!isPreviewNode && !isImageNode && !isFlexNode) {
1276         return;
1277     }
1278 
1279     CHECK_NULL_VOID(menuLayoutWrapper);
1280     auto menuNode = menuLayoutWrapper->GetHostNode();
1281     CHECK_NULL_VOID(menuNode);
1282     auto menuPattern = menuNode->GetPattern<MenuPattern>();
1283     CHECK_NULL_VOID(menuPattern);
1284     CheckPreviewSize(child, menuPattern);
1285 
1286     if (isImageNode && menuPattern->GetIsShowHoverImage()) {
1287         return;
1288     }
1289 
1290     auto frameSize = geometryNode->GetMarginFrameSize();
1291     if (isPreviewNode || isFlexNode) {
1292         CheckPreviewConstraint(hostNode, menuWindowRect);
1293     } else if (isImageNode && previewScaleMode_.value_or(PreviewScaleMode::AUTO) == PreviewScaleMode::CONSTANT) {
1294         CheckPreviewConstraintForConstant(geometryNode);
1295     } else {
1296         geometryNode->SetFrameSize(frameSize);
1297     }
1298     frameSize = geometryNode->GetMarginFrameSize() * previewScale_;
1299     auto widthLeftSpace = menuWindowRect.Width() - paddingStart_ - paddingEnd_;
1300     if (GreatNotEqual(frameSize.Width(), widthLeftSpace)) {
1301         auto unitSpace = widthLeftSpace / frameSize.Width() / previewScale_;
1302         geometryNode->SetFrameSize(SizeF(widthLeftSpace / previewScale_, unitSpace * frameSize.Height()));
1303         frameSize = geometryNode->GetMarginFrameSize() * previewScale_;
1304     }
1305     previewLayoutWrapper = child;
1306     size += frameSize;
1307 }
1308 
GetPreviewNodeAndMenuNodeTotalSize(const RefPtr<FrameNode> & frameNode,RefPtr<LayoutWrapper> & previewLayoutWrapper,RefPtr<LayoutWrapper> & menuLayoutWrapper)1309 SizeF MenuLayoutAlgorithm::GetPreviewNodeAndMenuNodeTotalSize(const RefPtr<FrameNode>& frameNode,
1310     RefPtr<LayoutWrapper>& previewLayoutWrapper, RefPtr<LayoutWrapper>& menuLayoutWrapper)
1311 {
1312     SizeF size;
1313     CHECK_NULL_RETURN(frameNode, size);
1314     auto pipelineContext = DialogManager::GetMainPipelineContext(frameNode);
1315     CHECK_NULL_RETURN(pipelineContext, size);
1316     for (auto& child : frameNode->GetAllChildrenWithBuild()) {
1317         auto hostNode = child->GetHostNode();
1318         auto geometryNode = child->GetGeometryNode();
1319         if (!hostNode || !geometryNode) {
1320             continue;
1321         }
1322         GetPreviewNodeTotalSize(child, param_.menuWindowRect, previewLayoutWrapper, size, menuLayoutWrapper);
1323         auto menuPattern = hostNode->GetPattern<MenuPattern>();
1324         if (hostNode->GetTag() == V2::MENU_ETS_TAG && menuPattern && !menuPattern->IsSubMenu()) {
1325             menuLayoutWrapper = child;
1326             size += geometryNode->GetMarginFrameSize();
1327         }
1328     }
1329     return size;
1330 }
1331 
LayoutNormalTopPreviewBottomMenuLessThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize)1332 void MenuLayoutAlgorithm::LayoutNormalTopPreviewBottomMenuLessThan(
1333     const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize)
1334 {
1335     CHECK_NULL_VOID(previewGeometryNode);
1336     CHECK_NULL_VOID(menuGeometryNode);
1337 
1338     OffsetF center(
1339         targetOffset_.GetX() + targetSize_.Width() / HALF, targetOffset_.GetY() + targetSize_.Height() / HALF);
1340     targetCenterOffset_ = center;
1341     auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1342     OffsetF offset(center.GetX() - previewSize.Width() / HALF,
1343         std::min<float>(center.GetY() - previewSize.Height() / HALF,
1344             wrapperRect_.Bottom() - param_.bottomSecurity - totalSize.Height() - param_.previewMenuGap));
1345     auto x = std::clamp(offset.GetX(), static_cast<float>(wrapperRect_.Left()) + paddingStart_,
1346         static_cast<float>(wrapperRect_.Right()) - paddingEnd_ - previewSize.Width());
1347     auto y = std::clamp(offset.GetY(), static_cast<float>(wrapperRect_.Top()) + param_.topSecurity,
1348         static_cast<float>(wrapperRect_.Bottom()) - param_.bottomSecurity - previewSize.Height());
1349     x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / HALF;
1350     y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / HALF;
1351     previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1352 }
1353 
LayoutNormalTopPreviewBottomMenuGreateThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize)1354 void MenuLayoutAlgorithm::LayoutNormalTopPreviewBottomMenuGreateThan(
1355     const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize)
1356 {
1357     CHECK_NULL_VOID(previewGeometryNode);
1358     CHECK_NULL_VOID(menuGeometryNode);
1359 
1360     OffsetF center(targetOffset_.GetX() + targetSize_.Width() / 2, targetOffset_.GetY() + targetSize_.Height() / 2);
1361     targetCenterOffset_ = center;
1362     auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1363     auto menuHeight = totalSize.Height() - previewSize.Height();
1364 
1365     if (previewScaleMode_.value_or(PreviewScaleMode::AUTO) == PreviewScaleMode::CONSTANT) {
1366         LayoutPreviewMenuGreateThanForConstant(previewGeometryNode, menuGeometryNode);
1367         return;
1368     }
1369     auto previewHalfHeight = previewSize.Height() / 2;
1370     if (LessNotEqual(menuHeight, previewHalfHeight)) {
1371         auto menuSize = menuGeometryNode->GetMarginFrameSize();
1372         if (GreatNotEqual(param_.menuItemTotalHeight, previewHalfHeight)) {
1373             menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), previewHalfHeight));
1374             totalSize = SizeF(totalSize.Width(), previewHalfHeight + previewSize.Height());
1375         } else {
1376             menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), param_.menuItemTotalHeight));
1377             totalSize = SizeF(totalSize.Width(), param_.menuItemTotalHeight + previewSize.Height());
1378         }
1379     }
1380 
1381     auto heightLeftSpace = wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity - param_.previewMenuGap;
1382     auto delta = totalSize.Height() - heightLeftSpace;
1383     if (GreatNotEqual(delta, 0.0f)) {
1384         menuHeight = totalSize.Height() - previewSize.Height();
1385         float unitSpace = 0.0f;
1386         if (LessNotEqual(menuHeight, previewHalfHeight)) {
1387             unitSpace = delta / previewSize.Height();
1388         } else {
1389             unitSpace = delta / totalSize.Height();
1390             auto menuSize = menuGeometryNode->GetMarginFrameSize();
1391             menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), (1 - unitSpace) * menuSize.Height()));
1392         }
1393         previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
1394             (1 - unitSpace) * previewSize.Height() / previewScale_));
1395         totalSize = totalSize - SizeF(0.0f, delta);
1396     }
1397     previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1398     OffsetF offset(center.GetX() - previewSize.Width() / 2, 0.0f);
1399     auto x = std::clamp(offset.GetX(), static_cast<float>(wrapperRect_.Left()) + paddingStart_,
1400         static_cast<float>(wrapperRect_.Right()) - paddingEnd_ - previewSize.Width());
1401     auto y = std::clamp(offset.GetY(), static_cast<float>(wrapperRect_.Top()) + param_.topSecurity,
1402         static_cast<float>(wrapperRect_.Bottom()) - param_.bottomSecurity - previewSize.Height());
1403     x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / 2;
1404     y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / 2;
1405     previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1406 }
1407 
LayoutNormalTopPreviewBottomMenu(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize,float menuItemTotalHeight,LayoutWrapper * layoutWrapper)1408 void MenuLayoutAlgorithm::LayoutNormalTopPreviewBottomMenu(const RefPtr<GeometryNode>& previewGeometryNode,
1409     const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize, float menuItemTotalHeight,
1410     LayoutWrapper* layoutWrapper)
1411 {
1412     CHECK_NULL_VOID(previewGeometryNode);
1413     CHECK_NULL_VOID(menuGeometryNode);
1414     param_.menuItemTotalHeight = menuItemTotalHeight;
1415     CHECK_NULL_VOID(layoutWrapper);
1416     auto hostNode = layoutWrapper->GetHostNode();
1417     CHECK_NULL_VOID(hostNode);
1418     auto pipelineContext = DialogManager::GetMainPipelineContext(hostNode);
1419     CHECK_NULL_VOID(pipelineContext);
1420     if (LessNotEqual(totalSize.Height() + targetSecurity_,
1421         wrapperRect_.Height() - paddingTop_ - paddingBottom_ - param_.topSecurity - param_.bottomSecurity)) {
1422         LayoutNormalTopPreviewBottomMenuLessThan(previewGeometryNode, menuGeometryNode, totalSize);
1423     } else {
1424         LayoutNormalTopPreviewBottomMenuGreateThan(previewGeometryNode, menuGeometryNode, totalSize);
1425     }
1426     auto previewSize = previewGeometryNode->GetMarginFrameSize();
1427     auto securityHeight = wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity;
1428     if (GreatNotEqual(param_.windowGlobalSizeF.Height(), param_.menuWindowRect.Height()) &&
1429         GreatNotEqual(previewSize.Height(), securityHeight)) {
1430         previewGeometryNode->SetFrameSize(SizeF(previewSize.Width(), securityHeight));
1431     }
1432 }
1433 
LayoutNormalBottomPreviewTopMenuLessThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize)1434 void MenuLayoutAlgorithm::LayoutNormalBottomPreviewTopMenuLessThan(
1435     const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize)
1436 {
1437     CHECK_NULL_VOID(previewGeometryNode);
1438     CHECK_NULL_VOID(menuGeometryNode);
1439 
1440     OffsetF center(
1441         targetOffset_.GetX() + targetSize_.Width() / HALF, targetOffset_.GetY() + targetSize_.Height() / HALF);
1442     targetCenterOffset_ = center;
1443     auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1444     OffsetF offset(center.GetX() - previewSize.Width() / HALF,
1445         std::max<float>(center.GetY() - previewSize.Height() / HALF, wrapperRect_.Top() + param_.topSecurity +
1446                                                                       totalSize.Height() - previewSize.Height() +
1447                                                                       param_.previewMenuGap));
1448     auto x = std::clamp(offset.GetX(), static_cast<float>(wrapperRect_.Left()) + paddingStart_,
1449         static_cast<float>(wrapperRect_.Right()) - previewSize.Width() - paddingEnd_);
1450     auto y = std::clamp(offset.GetY(), static_cast<float>(wrapperRect_.Top()) + param_.topSecurity,
1451         static_cast<float>(wrapperRect_.Bottom()) - param_.bottomSecurity - previewSize.Height());
1452     x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / HALF;
1453     y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / HALF;
1454     previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1455 }
1456 
LayoutNormalBottomPreviewTopMenuGreateThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize)1457 void MenuLayoutAlgorithm::LayoutNormalBottomPreviewTopMenuGreateThan(
1458     const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize)
1459 {
1460     CHECK_NULL_VOID(previewGeometryNode);
1461     CHECK_NULL_VOID(menuGeometryNode);
1462 
1463     OffsetF center(targetOffset_.GetX() + targetSize_.Width() / 2, targetOffset_.GetY() + targetSize_.Height() / 2);
1464     targetCenterOffset_ = center;
1465     auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1466     if (previewScaleMode_.value_or(PreviewScaleMode::AUTO) == PreviewScaleMode::CONSTANT) {
1467         LayoutPreviewMenuGreateThanForConstant(previewGeometryNode, menuGeometryNode);
1468         return;
1469     }
1470     auto menuHeight = totalSize.Height() - previewSize.Height();
1471     auto previewHalfHeight = previewSize.Height() / 2;
1472     if (LessNotEqual(menuHeight, previewHalfHeight)) {
1473         auto menuSize = menuGeometryNode->GetMarginFrameSize();
1474         if (GreatNotEqual(param_.menuItemTotalHeight, previewHalfHeight)) {
1475             menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), previewHalfHeight));
1476             totalSize = SizeF(totalSize.Width(), previewHalfHeight + previewSize.Height());
1477         } else {
1478             menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), param_.menuItemTotalHeight));
1479             totalSize = SizeF(totalSize.Width(), param_.menuItemTotalHeight + previewSize.Height());
1480         }
1481     }
1482 
1483     auto heightLeftSpace = wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity - param_.previewMenuGap;
1484     auto delta = totalSize.Height() - heightLeftSpace;
1485     if (GreatNotEqual(delta, 0.0f)) {
1486         menuHeight = totalSize.Height() - previewSize.Height();
1487         float unitSpace = 0.0f;
1488         if (LessNotEqual(menuHeight, previewHalfHeight)) {
1489             unitSpace = delta / previewSize.Height();
1490         } else {
1491             unitSpace = delta / totalSize.Height();
1492             auto menuSize = menuGeometryNode->GetMarginFrameSize();
1493             menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), (1 - unitSpace) * menuSize.Height()));
1494         }
1495         previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
1496             (1 - unitSpace) * previewSize.Height() / previewScale_));
1497         totalSize = totalSize - SizeF(0.0f, delta);
1498     }
1499     previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1500     OffsetF offset(center.GetX() - previewSize.Width() / 2,
1501         wrapperRect_.Bottom() - param_.bottomSecurity - previewSize.Height());
1502 
1503     auto x = std::clamp(offset.GetX(), static_cast<float>(wrapperRect_.Left()) + paddingStart_,
1504         static_cast<float>(wrapperRect_.Right()) - previewSize.Width() - paddingEnd_);
1505     auto y = std::clamp(offset.GetY(), static_cast<float>(wrapperRect_.Top()) + param_.topSecurity,
1506         static_cast<float>(wrapperRect_.Bottom()) - param_.bottomSecurity - previewSize.Height());
1507     x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / 2;
1508     y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / 2;
1509     previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1510 }
1511 
LayoutNormalBottomPreviewTopMenu(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize,float menuItemTotalHeight,LayoutWrapper * layoutWrapper)1512 void MenuLayoutAlgorithm::LayoutNormalBottomPreviewTopMenu(const RefPtr<GeometryNode>& previewGeometryNode,
1513     const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize, float menuItemTotalHeight,
1514     LayoutWrapper* layoutWrapper)
1515 {
1516     CHECK_NULL_VOID(previewGeometryNode);
1517     CHECK_NULL_VOID(menuGeometryNode);
1518     param_.menuItemTotalHeight = menuItemTotalHeight;
1519     CHECK_NULL_VOID(layoutWrapper);
1520     auto hostNode = layoutWrapper->GetHostNode();
1521     CHECK_NULL_VOID(hostNode);
1522     auto pipelineContext = DialogManager::GetMainPipelineContext(hostNode);
1523     CHECK_NULL_VOID(pipelineContext);
1524     if (LessNotEqual(totalSize.Height() + targetSecurity_,
1525         wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity)) {
1526         LayoutNormalBottomPreviewTopMenuLessThan(previewGeometryNode, menuGeometryNode, totalSize);
1527     } else {
1528         LayoutNormalBottomPreviewTopMenuGreateThan(previewGeometryNode, menuGeometryNode, totalSize);
1529     }
1530     auto previewSize = previewGeometryNode->GetMarginFrameSize();
1531     auto securityHeight =
1532         param_.menuWindowRect.Height() - param_.topSecurity - param_.top - param_.bottomSecurity - param_.bottom;
1533     if (GreatNotEqual(param_.windowGlobalSizeF.Height(), param_.menuWindowRect.Height()) &&
1534         GreatNotEqual(previewSize.Height(), securityHeight)) {
1535         previewGeometryNode->SetFrameSize(SizeF(previewSize.Width(), securityHeight));
1536     }
1537 }
1538 
UpdateScrollAndColumnLayoutConstraint(const RefPtr<LayoutWrapper> & previewLayoutWrapper,const RefPtr<LayoutWrapper> & menuLayoutWrapper)1539 void MenuLayoutAlgorithm::UpdateScrollAndColumnLayoutConstraint(
1540     const RefPtr<LayoutWrapper>& previewLayoutWrapper, const RefPtr<LayoutWrapper>& menuLayoutWrapper)
1541 {
1542     CHECK_NULL_VOID(menuLayoutWrapper);
1543     CHECK_NULL_VOID(previewLayoutWrapper);
1544     RefPtr<GeometryNode> menuGeometryNode = menuLayoutWrapper->GetGeometryNode();
1545     RefPtr<GeometryNode> previewGeometryNode = previewLayoutWrapper->GetGeometryNode();
1546     CHECK_NULL_VOID(menuGeometryNode);
1547     CHECK_NULL_VOID(previewGeometryNode);
1548 
1549     for (auto& child : menuLayoutWrapper->GetAllChildrenWithBuild()) {
1550         auto geometryNode = child->GetGeometryNode();
1551         if (!geometryNode) {
1552             continue;
1553         }
1554         auto frameSize = menuGeometryNode->GetMarginFrameSize();
1555         geometryNode->SetFrameSize(SizeF(frameSize.Width(), frameSize.Height()));
1556         auto layoutProperty = child->GetLayoutProperty();
1557         CHECK_NULL_VOID(layoutProperty);
1558         auto constraint = layoutProperty->GetLayoutConstraint();
1559         if (constraint.has_value()) {
1560             constraint.value().maxSize.SetWidth(frameSize.Width());
1561             constraint.value().maxSize.SetHeight(frameSize.Height());
1562             constraint.value().selfIdealSize.UpdateSizeWithCheck(SizeF(frameSize.Width(), frameSize.Height()));
1563             layoutProperty->UpdateLayoutConstraint(constraint.value());
1564             child->Measure(constraint);
1565         }
1566     }
1567 
1568     for (auto& child : previewLayoutWrapper->GetAllChildrenWithBuild()) {
1569         auto hostNode = child->GetHostNode();
1570         auto geometryNode = child->GetGeometryNode();
1571         if (!hostNode || !geometryNode) {
1572             continue;
1573         }
1574         auto frameSize = previewGeometryNode->GetMarginFrameSize();
1575         geometryNode->SetFrameSize(SizeF(frameSize.Width(), frameSize.Height()));
1576         auto layoutProperty = child->GetLayoutProperty();
1577         CHECK_NULL_VOID(layoutProperty);
1578         auto constraint = layoutProperty->GetLayoutConstraint();
1579         if (constraint.has_value()) {
1580             constraint.value().maxSize.SetWidth(frameSize.Width());
1581             constraint.value().maxSize.SetHeight(frameSize.Height());
1582             constraint.value().selfIdealSize.UpdateSizeWithCheck(SizeF(frameSize.Width(), frameSize.Height()));
1583             layoutProperty->UpdateLayoutConstraint(constraint.value());
1584             hostNode->GetRenderContext()->SetClipToBounds(true);
1585             child->Measure(constraint);
1586         }
1587     }
1588 }
1589 
GetMenuItemTotalHeight(const RefPtr<LayoutWrapper> & menuLayoutWrapper)1590 float MenuLayoutAlgorithm::GetMenuItemTotalHeight(const RefPtr<LayoutWrapper>& menuLayoutWrapper)
1591 {
1592     CHECK_NULL_RETURN(menuLayoutWrapper, 0.0f);
1593     RefPtr<GeometryNode> menuGeometryNode = menuLayoutWrapper->GetGeometryNode();
1594     CHECK_NULL_RETURN(menuGeometryNode, 0.0f);
1595     float height = 0.0f;
1596 
1597     for (auto& child : menuLayoutWrapper->GetAllChildrenWithBuild()) {
1598         auto geometryNode = child->GetGeometryNode();
1599         if (!geometryNode) {
1600             continue;
1601         }
1602         for (auto& menuItem : child->GetAllChildrenWithBuild()) {
1603             auto itemHostnode = menuItem->GetHostNode();
1604             auto itemGeometryNode = menuItem->GetGeometryNode();
1605             if (!itemHostnode || !itemGeometryNode) {
1606                 continue;
1607             }
1608             height += itemGeometryNode->GetMarginFrameSize().Height();
1609         }
1610     }
1611     auto menuHeight = menuGeometryNode->GetMarginFrameSize().Height();
1612     if (LessNotEqual(height, menuHeight)) {
1613         height = menuHeight;
1614     }
1615     return height;
1616 }
1617 
CheckHorizontalLayoutPreviewOffsetX(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,float offsetX)1618 float MenuLayoutAlgorithm::CheckHorizontalLayoutPreviewOffsetX(
1619     const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, float offsetX)
1620 {
1621     if (SystemProperties::GetDeviceOrientation() != DeviceOrientation::LANDSCAPE) {
1622         return offsetX;
1623     }
1624     CHECK_NULL_RETURN(previewGeometryNode, offsetX);
1625     auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1626     CHECK_NULL_RETURN(menuGeometryNode, offsetX);
1627     auto menuSize = menuGeometryNode->GetMarginFrameSize();
1628     // left menu right preview
1629     auto x_min = wrapperRect_.Left() + paddingStart_ + menuSize.Width() + targetSecurity_;
1630     // left preview right menu
1631     auto x_max = wrapperRect_.Right() - paddingEnd_ - menuSize.Width() - previewSize.Width() - targetSecurity_;
1632     auto needAvoid = GreatNotEqual(offsetX, x_max) && LessNotEqual(offsetX, x_min);
1633     return needAvoid ? x_max : offsetX;
1634 }
1635 
LayoutOtherDeviceLeftPreviewRightMenuLessThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize)1636 void MenuLayoutAlgorithm::LayoutOtherDeviceLeftPreviewRightMenuLessThan(
1637     const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize)
1638 {
1639     CHECK_NULL_VOID(previewGeometryNode);
1640     CHECK_NULL_VOID(menuGeometryNode);
1641 
1642     OffsetF targetCenterOffset(
1643         targetOffset_.GetX() + targetSize_.Width() / HALF, targetOffset_.GetY() + targetSize_.Height() / HALF);
1644     targetCenterOffset_ = targetCenterOffset;
1645     auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1646     auto heightLeftSpace = wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity;
1647     auto delta = previewSize.Height() - heightLeftSpace;
1648     if (GreatNotEqual(delta, 0.0f)) {
1649         auto unitSpace = delta / previewSize.Height();
1650         previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
1651             (1 - unitSpace) * previewSize.Height() / previewScale_));
1652         totalSize = totalSize - SizeF(unitSpace * previewSize.Width(), unitSpace * previewSize.Height());
1653     }
1654     previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1655     auto menuSize = menuGeometryNode->GetMarginFrameSize();
1656     menuGeometryNode->SetFrameSize(
1657         SizeF(menuSize.Width(), std::min<float>(param_.menuItemTotalHeight, heightLeftSpace)));
1658     menuSize = menuGeometryNode->GetMarginFrameSize();
1659     auto offsetX = targetCenterOffset.GetX() - previewSize.Width() / HALF;
1660     auto offsetY = std::min<float>(targetCenterOffset.GetY() - previewSize.Height() / HALF,
1661         wrapperRect_.Bottom() - param_.bottomSecurity - menuSize.Height());
1662     offsetX = CheckHorizontalLayoutPreviewOffsetX(previewGeometryNode, menuGeometryNode, offsetX);
1663     auto x = std::clamp(offsetX, static_cast<float>(wrapperRect_.Left()) + paddingStart_,
1664         static_cast<float>(wrapperRect_.Right()) - previewSize.Width() - paddingEnd_);
1665     auto yMax = static_cast<float>(wrapperRect_.Bottom()) - param_.bottomSecurity - previewSize.Height();
1666     if (placement_ == Placement::LEFT_BOTTOM || placement_ == Placement::RIGHT_BOTTOM) {
1667         yMax += previewSize.Height();
1668         offsetY = std::min<float>(targetCenterOffset.GetY() - previewSize.Height() / HALF, yMax);
1669     }
1670     auto y = std::clamp(offsetY, static_cast<float>(wrapperRect_.Top()) + param_.topSecurity, yMax);
1671     x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / HALF;
1672     y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / HALF;
1673     previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1674 }
1675 
LayoutOtherDeviceLeftPreviewRightMenuGreateThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize)1676 void MenuLayoutAlgorithm::LayoutOtherDeviceLeftPreviewRightMenuGreateThan(
1677     const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize)
1678 {
1679     CHECK_NULL_VOID(previewGeometryNode);
1680     CHECK_NULL_VOID(menuGeometryNode);
1681 
1682     OffsetF targetCenterOffset(
1683         targetOffset_.GetX() + targetSize_.Width() / 2, targetOffset_.GetY() + targetSize_.Height() / 2);
1684     targetCenterOffset_ = targetCenterOffset;
1685     auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1686     auto widthLeftSpace = wrapperRect_.Width() - paddingStart_ - paddingEnd_ - param_.previewMenuGap;
1687     if (previewScaleMode_.value_or(PreviewScaleMode::AUTO) == PreviewScaleMode::CONSTANT) {
1688         LayoutPreviewMenuGreateThanForConstant(previewGeometryNode, menuGeometryNode);
1689         return;
1690     }
1691     auto delta = totalSize.Width() - widthLeftSpace;
1692     if (GreatNotEqual(delta, 0.0f)) {
1693         auto unitSpace = delta / previewSize.Width();
1694         previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
1695             (1 - unitSpace) * previewSize.Height() / previewScale_));
1696         totalSize = totalSize - SizeF(unitSpace * previewSize.Width(), unitSpace * previewSize.Height());
1697     }
1698     previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1699     auto heightLeftSpace = wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity;
1700     delta = previewSize.Height() - heightLeftSpace;
1701     if (GreatNotEqual(delta, 0.0f)) {
1702         auto unitSpace = delta / previewSize.Height();
1703         previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
1704             (1 - unitSpace) * previewSize.Height() / previewScale_));
1705         totalSize = totalSize - SizeF(unitSpace * previewSize.Width(), unitSpace * previewSize.Height());
1706     }
1707     previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1708     auto menuSize = menuGeometryNode->GetMarginFrameSize();
1709     menuGeometryNode->SetFrameSize(
1710         SizeF(menuSize.Width(), std::min<float>(param_.menuItemTotalHeight, heightLeftSpace)));
1711     menuSize = menuGeometryNode->GetMarginFrameSize();
1712     auto offsetX = 0.0f;
1713     auto offsetY = std::min<float>(targetCenterOffset.GetY() - previewSize.Height() / 2,
1714         wrapperRect_.Bottom() - param_.bottomSecurity - menuSize.Height());
1715     auto x = std::clamp(offsetX, static_cast<float>(wrapperRect_.Left()) + paddingStart_,
1716         static_cast<float>(wrapperRect_.Right()) - previewSize.Width() - paddingEnd_);
1717     auto y = std::clamp(offsetY, static_cast<float>(wrapperRect_.Top()) + param_.topSecurity,
1718         static_cast<float>(wrapperRect_.Bottom()) - param_.bottomSecurity - previewSize.Height());
1719     x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / 2;
1720     y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / 2;
1721     previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1722 }
1723 
LayoutPreviewMenuGreateThanForConstant(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode)1724 void MenuLayoutAlgorithm::LayoutPreviewMenuGreateThanForConstant(
1725     const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode)
1726 {
1727     CHECK_NULL_VOID(previewGeometryNode);
1728     CHECK_NULL_VOID(menuGeometryNode);
1729     auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1730     auto menuSize = menuGeometryNode->GetMarginFrameSize();
1731     OffsetF center(
1732         targetOffset_.GetX() + targetSize_.Width() / HALF, targetOffset_.GetY() + targetSize_.Height() / HALF);
1733     targetCenterOffset_ = center;
1734 
1735     RectF contentRect;
1736     contentRect.SetLeft(wrapperRect_.Left() + paddingStart_);
1737     contentRect.SetTop(wrapperRect_.Top() + param_.topSecurity);
1738     contentRect.SetWidth(wrapperRect_.Width() - paddingStart_ - paddingEnd_);
1739     contentRect.SetHeight(wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity);
1740 
1741     auto x = std::clamp(
1742         center.GetX() - previewSize.Width() / HALF, contentRect.Left(), contentRect.Right() - previewSize.Width());
1743     auto y = std::clamp(
1744         center.GetY() - previewSize.Height() / HALF, contentRect.Top(), contentRect.Bottom() - previewSize.Height());
1745 
1746     auto heightLeftSpace = 0.0f;
1747     if (placement_ == Placement::BOTTOM_LEFT || placement_ == Placement::BOTTOM ||
1748         placement_ == Placement::BOTTOM_RIGHT) {
1749         y = contentRect.Top();
1750         heightLeftSpace = contentRect.Height() - previewSize.Height() - param_.previewMenuGap;
1751     } else if (placement_ == Placement::TOP_LEFT || placement_ == Placement::TOP ||
1752                placement_ == Placement::TOP_RIGHT) {
1753         y = contentRect.Bottom() - previewSize.Height();
1754         heightLeftSpace = contentRect.Height() - previewSize.Height() - param_.previewMenuGap;
1755     } else { // leftPreviewRightMenu
1756         x = contentRect.Left();
1757         heightLeftSpace = contentRect.Height();
1758     }
1759 
1760     if (GreatNotEqual(heightLeftSpace, 0.0f) && GreatNotEqual(menuSize.Height(), heightLeftSpace)) {
1761         menuGeometryNode->SetFrameSize(
1762             SizeF(menuSize.Width(), std::min<float>(param_.menuItemTotalHeight, heightLeftSpace)));
1763     }
1764 
1765     x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / HALF;
1766     y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / HALF;
1767     previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1768 }
1769 
LayoutOtherDeviceLeftPreviewRightMenu(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize,float menuItemTotalHeight)1770 void MenuLayoutAlgorithm::LayoutOtherDeviceLeftPreviewRightMenu(const RefPtr<GeometryNode>& previewGeometryNode,
1771     const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize, float menuItemTotalHeight)
1772 {
1773     CHECK_NULL_VOID(previewGeometryNode);
1774     CHECK_NULL_VOID(menuGeometryNode);
1775     param_.menuItemTotalHeight = menuItemTotalHeight;
1776     auto widthLeftSpace = wrapperRect_.Width() - paddingStart_ - paddingEnd_;
1777     if (LessNotEqual(totalSize.Width() + targetSecurity_, widthLeftSpace)) {
1778         LayoutOtherDeviceLeftPreviewRightMenuLessThan(previewGeometryNode, menuGeometryNode, totalSize);
1779     } else {
1780         LayoutOtherDeviceLeftPreviewRightMenuGreateThan(previewGeometryNode, menuGeometryNode, totalSize);
1781     }
1782     auto previewSize = previewGeometryNode->GetMarginFrameSize();
1783     auto securityHeight =
1784         param_.menuWindowRect.Height() - param_.topSecurity - param_.top - param_.bottomSecurity - param_.bottom;
1785     if (GreatNotEqual(param_.windowGlobalSizeF.Height(), param_.menuWindowRect.Height()) &&
1786         GreatNotEqual(previewSize.Height(), securityHeight)) {
1787         previewGeometryNode->SetFrameSize(SizeF(previewSize.Width(), securityHeight));
1788     }
1789 }
1790 
LayoutPreviewMenu(LayoutWrapper * layoutWrapper)1791 void MenuLayoutAlgorithm::LayoutPreviewMenu(LayoutWrapper* layoutWrapper)
1792 {
1793     CHECK_NULL_VOID(layoutWrapper);
1794     auto paintProperty = GetPaintProperty(layoutWrapper);
1795     CHECK_NULL_VOID(paintProperty);
1796     paintProperty->UpdateEnableArrow(false);
1797     auto menuNode = layoutWrapper->GetHostNode();
1798     CHECK_NULL_VOID(menuNode);
1799     auto parentNode = AceType::DynamicCast<FrameNode>(menuNode->GetParent());
1800     CHECK_NULL_VOID(parentNode);
1801     RefPtr<LayoutWrapper> menuLayoutWrapper;
1802     RefPtr<LayoutWrapper> previewLayoutWrapper;
1803     SizeF totalSize = GetPreviewNodeAndMenuNodeTotalSize(parentNode, previewLayoutWrapper, menuLayoutWrapper);
1804     CHECK_NULL_VOID(menuLayoutWrapper);
1805     CHECK_NULL_VOID(previewLayoutWrapper);
1806     RefPtr<GeometryNode> menuGeometryNode = menuLayoutWrapper->GetGeometryNode();
1807     RefPtr<GeometryNode> previewGeometryNode = previewLayoutWrapper->GetGeometryNode();
1808     CHECK_NULL_VOID(menuGeometryNode);
1809     CHECK_NULL_VOID(previewGeometryNode);
1810     auto menuItemTotalHeight = GetMenuItemTotalHeight(menuLayoutWrapper);
1811     if (placement_ == Placement::BOTTOM_LEFT || placement_ == Placement::BOTTOM ||
1812         placement_ == Placement::BOTTOM_RIGHT) {
1813         LayoutNormalTopPreviewBottomMenu(
1814             previewGeometryNode, menuGeometryNode, totalSize, menuItemTotalHeight, layoutWrapper);
1815     } else if (placement_ == Placement::TOP_LEFT || placement_ == Placement::TOP ||
1816                placement_ == Placement::TOP_RIGHT) {
1817         LayoutNormalBottomPreviewTopMenu(
1818             previewGeometryNode, menuGeometryNode, totalSize, menuItemTotalHeight, layoutWrapper);
1819     } else {
1820         LayoutOtherDeviceLeftPreviewRightMenu(previewGeometryNode, menuGeometryNode, totalSize, menuItemTotalHeight);
1821     }
1822     UpdateScrollAndColumnLayoutConstraint(previewLayoutWrapper, menuLayoutWrapper);
1823     UpdatePreviewPositionAndOffset(previewLayoutWrapper, menuLayoutWrapper);
1824 }
1825 
UpdatePreviewPositionAndOffset(RefPtr<LayoutWrapper> & previewLayoutWrapper,RefPtr<LayoutWrapper> & menuLayoutWrapper)1826 void MenuLayoutAlgorithm::UpdatePreviewPositionAndOffset(
1827     RefPtr<LayoutWrapper>& previewLayoutWrapper, RefPtr<LayoutWrapper>& menuLayoutWrapper)
1828 {
1829     CHECK_NULL_VOID(previewLayoutWrapper);
1830     CHECK_NULL_VOID(menuLayoutWrapper);
1831     RefPtr<GeometryNode> previewGeometryNode = previewLayoutWrapper->GetGeometryNode();
1832     RefPtr<GeometryNode> menuGeometryNode = menuLayoutWrapper->GetGeometryNode();
1833     CHECK_NULL_VOID(previewGeometryNode);
1834     CHECK_NULL_VOID(menuGeometryNode);
1835 
1836     auto previewSize = previewGeometryNode->GetMarginFrameSize();
1837     previewOffset_ = previewGeometryNode->GetFrameOffset();
1838     auto previewOffsetX = previewOffset_.GetX();
1839     auto previewOffsetY = previewOffset_.GetY();
1840     if (previewSize.IsPositive()) {
1841         targetSize_ = previewSize * previewScale_;
1842         targetOffset_ = OffsetF(previewOffsetX + (previewSize.Width() - targetSize_.Width()) / HALF,
1843             previewOffsetY + (previewSize.Height() - targetSize_.Height()) / HALF);
1844     }
1845     auto previewHostNode = previewLayoutWrapper->GetHostNode();
1846     CHECK_NULL_VOID(previewHostNode);
1847     auto renderContext = previewHostNode->GetRenderContext();
1848     CHECK_NULL_VOID(renderContext);
1849 
1850     auto menuHostNode = menuLayoutWrapper->GetHostNode();
1851     CHECK_NULL_VOID(menuHostNode);
1852     previewOriginOffset_ = targetCenterOffset_ - OffsetF(previewSize.Width() / HALF, previewSize.Height() / HALF);
1853     previewSize_ = previewSize;
1854     auto menuPattern = menuHostNode->GetPattern<MenuPattern>();
1855     CHECK_NULL_VOID(menuPattern);
1856     auto lastPreviewOffset = menuPattern->GetPreviewRect().GetOffset();
1857     menuPattern->SetPreviewOriginOffset(previewOriginOffset_);
1858     menuPattern->SetPreviewRect(RectF(previewOffset_, previewSize_));
1859 
1860     if (previewOffset_ != lastPreviewOffset) {
1861         renderContext->UpdatePosition(OffsetT<Dimension>(Dimension(previewOffsetX), Dimension(previewOffsetY)));
1862     }
1863 
1864     TAG_LOGD(AceLogTag::ACE_MENU, "previewOriginOffset: %{public}s currentPreviewRect: %{public}s",
1865         previewOriginOffset_.ToString().c_str(), menuPattern->GetPreviewRect().ToString().c_str());
1866 }
1867 
FixMenuOriginOffset(float beforeAnimationScale,float afterAnimationScale)1868 OffsetF MenuLayoutAlgorithm::FixMenuOriginOffset(float beforeAnimationScale, float afterAnimationScale)
1869 {
1870     auto beforeRate = (1.0f - beforeAnimationScale) / 2;
1871     auto beforeScalePreviewOffset = OffsetF((previewSize_ * beforeRate).Width(),
1872         (previewSize_ * beforeRate).Height());
1873     auto afterScalePreviewOffset = OffsetF((previewSize_ * ((afterAnimationScale - 1.0f) / 2)).Width(),
1874         (previewSize_ * ((afterAnimationScale - 1.0f) / 2)).Height());
1875     auto scaleOffset = afterScalePreviewOffset + beforeScalePreviewOffset;
1876     float x = 0.0f;
1877     float y = 0.0f;
1878     switch (placement_) {
1879         case Placement::BOTTOM_LEFT:
1880         case Placement::LEFT_BOTTOM:
1881             x += scaleOffset.GetX();
1882             y -= scaleOffset.GetY();
1883             break;
1884         case Placement::TOP_RIGHT:
1885         case Placement::RIGHT_TOP:
1886             x -= scaleOffset.GetX();
1887             y += scaleOffset.GetY();
1888             break;
1889         case Placement::TOP_LEFT:
1890         case Placement::LEFT_TOP:
1891             x += scaleOffset.GetX();
1892             y += scaleOffset.GetY();
1893             break;
1894         case Placement::BOTTOM_RIGHT:
1895         case Placement::RIGHT_BOTTOM:
1896             x -= scaleOffset.GetX();
1897             y -= scaleOffset.GetY();
1898             break;
1899         case Placement::BOTTOM:
1900             y -= scaleOffset.GetY();
1901             break;
1902         case Placement::TOP:
1903             y += scaleOffset.GetY();
1904             break;
1905         case Placement::LEFT:
1906             x += scaleOffset.GetX();
1907             break;
1908         case Placement::RIGHT:
1909             x -= scaleOffset.GetX();
1910             break;
1911         default:
1912             x += scaleOffset.GetX();
1913             y -= scaleOffset.GetY();
1914             break;
1915     }
1916     return OffsetF(x, y);
1917 }
1918 
CalculateChildOffset(bool didNeedArrow)1919 void MenuLayoutAlgorithm::CalculateChildOffset(bool didNeedArrow)
1920 {
1921     childOffset_.Reset();
1922     if (didNeedArrow) {
1923         switch (arrowPlacement_) {
1924             case Placement::RIGHT:
1925             case Placement::RIGHT_TOP:
1926             case Placement::RIGHT_BOTTOM:
1927                 childOffset_.SetX(ARROW_HIGHT.ConvertToPx());
1928                 break;
1929             case Placement::BOTTOM:
1930             case Placement::BOTTOM_LEFT:
1931             case Placement::BOTTOM_RIGHT:
1932                 childOffset_.SetY(ARROW_HIGHT.ConvertToPx());
1933                 break;
1934             default:
1935                 break;
1936         }
1937     }
1938 }
1939 
CalculateMenuPositionWithArrow(const OffsetF & menuPosition,bool didNeedArrow)1940 OffsetF MenuLayoutAlgorithm::CalculateMenuPositionWithArrow(const OffsetF& menuPosition, bool didNeedArrow)
1941 {
1942     OffsetF menuPositionWithArrow = menuPosition;
1943     if (didNeedArrow) {
1944         switch (arrowPlacement_) {
1945             case Placement::RIGHT:
1946             case Placement::RIGHT_TOP:
1947             case Placement::RIGHT_BOTTOM:
1948                 menuPositionWithArrow -= OffsetF(ARROW_HIGHT.ConvertToPx(), 0);
1949                 break;
1950             case Placement::BOTTOM:
1951             case Placement::BOTTOM_LEFT:
1952             case Placement::BOTTOM_RIGHT:
1953                 menuPositionWithArrow -= OffsetF(0, ARROW_HIGHT.ConvertToPx());
1954                 break;
1955             default:
1956                 break;
1957         }
1958     }
1959     return menuPositionWithArrow;
1960 }
1961 
UpdateMenuFrameSizeWithArrow(const RefPtr<GeometryNode> & geometryNode,bool didNeedArrow)1962 void MenuLayoutAlgorithm::UpdateMenuFrameSizeWithArrow(const RefPtr<GeometryNode>& geometryNode,
1963     bool didNeedArrow)
1964 {
1965     if (didNeedArrow && arrowPlacement_ != Placement::NONE) {
1966         switch (arrowPlacement_) {
1967             case Placement::LEFT:
1968             case Placement::LEFT_TOP:
1969             case Placement::LEFT_BOTTOM:
1970             case Placement::RIGHT:
1971             case Placement::RIGHT_TOP:
1972             case Placement::RIGHT_BOTTOM:
1973                 geometryNode->SetFrameSize(geometryNode->GetFrameSize() + SizeF(ARROW_HIGHT.ConvertToPx(), 0));
1974                 break;
1975             case Placement::TOP:
1976             case Placement::TOP_LEFT:
1977             case Placement::TOP_RIGHT:
1978             case Placement::BOTTOM:
1979             case Placement::BOTTOM_LEFT:
1980             case Placement::BOTTOM_RIGHT:
1981                 geometryNode->SetFrameSize(geometryNode->GetFrameSize() + SizeF(0, ARROW_HIGHT.ConvertToPx()));
1982                 break;
1983             default:
1984                 break;
1985         }
1986     }
1987 }
1988 
Layout(LayoutWrapper * layoutWrapper)1989 void MenuLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
1990 {
1991     auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
1992     // if targetNode == nullptr, it means the menu is subMenu or multiMenu
1993     if (targetNode && !IsNodeOnRootTree(targetNode)) {
1994         TAG_LOGW(AceLogTag::ACE_MENU, "layout return because targetNode %{public}d not no root tree", targetNodeId_);
1995         return;
1996     }
1997     CHECK_NULL_VOID(layoutWrapper);
1998     auto geometryNode = layoutWrapper->GetGeometryNode();
1999     CHECK_NULL_VOID(geometryNode);
2000     auto menuNode = layoutWrapper->GetHostNode();
2001     CHECK_NULL_VOID(menuNode);
2002     auto menuPattern = menuNode->GetPattern<MenuPattern>();
2003     CHECK_NULL_VOID(menuPattern);
2004 
2005     if (menuPattern->GetPreviewMode() != MenuPreviewMode::NONE && !holdEmbeddedMenuPosition_) {
2006         LayoutPreviewMenu(layoutWrapper);
2007     }
2008     if (!menuPattern->IsSelectOverlayCustomMenu()) {
2009         auto menuProp = DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
2010         CHECK_NULL_VOID(menuProp);
2011         auto menuPosition = UpdateMenuPosition(layoutWrapper, menuNode, menuPattern, menuProp);
2012         auto pipeline = menuNode->GetContext();
2013         CHECK_NULL_VOID(pipeline);
2014         auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
2015         CHECK_NULL_VOID(menuTheme);
2016         auto beforeAnimationScale = menuTheme->GetPreviewBeforeAnimationScale();
2017         auto afterAnimationScale = menuTheme->GetPreviewAfterAnimationScale();
2018         auto menuOriginOffset = menuPosition - (previewOffset_ - previewOriginOffset_) +
2019                                 FixMenuOriginOffset(beforeAnimationScale, afterAnimationScale);
2020         menuPattern->SetOriginOffset(menuOriginOffset);
2021         auto previewScale = 1.0f;
2022         if (menuPattern->GetPreviewMode() == MenuPreviewMode::IMAGE &&
2023             !NearEqual(menuPattern->GetTargetSize().Width(), previewSize_.Width())) {
2024             previewScale = menuPattern->GetTargetSize().Width() / previewSize_.Width();
2025         }
2026         auto menuEndOffset = menuPosition - (previewOffset_ - previewOriginOffset_) +
2027                              FixMenuOriginOffset(previewScale, afterAnimationScale);
2028         menuPattern->SetEndOffset(menuEndOffset);
2029         menuPattern->SetHasLaid(true);
2030         dumpInfo_.menuPreviewMode = static_cast<uint32_t>(menuPattern->GetPreviewMode());
2031         dumpInfo_.menuType = static_cast<uint32_t>(menuPattern->GetMenuType());
2032         auto menuWrapper = menuPattern->GetMenuWrapper();
2033         CHECK_NULL_VOID(menuWrapper);
2034         auto wrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
2035         CHECK_NULL_VOID(wrapperPattern);
2036         dumpInfo_.anchorPosition = menuProp->GetAnchorPosition().value_or(OffsetF());
2037         wrapperPattern->SetDumpInfo(dumpInfo_);
2038         UpdateMenuFrameSizeWithArrow(geometryNode, didNeedArrow_);
2039     }
2040 
2041     TranslateOptions(layoutWrapper);
2042     ClipMenuPath(layoutWrapper);
2043 }
2044 
UpdateMenuPosition(LayoutWrapper * layoutWrapper,const RefPtr<FrameNode> & menuNode,RefPtr<MenuPattern> menuPattern,const RefPtr<MenuLayoutProperty> & menuProp)2045 OffsetF MenuLayoutAlgorithm::UpdateMenuPosition(LayoutWrapper* layoutWrapper, const RefPtr<FrameNode>& menuNode,
2046     RefPtr<MenuPattern> menuPattern, const RefPtr<MenuLayoutProperty>& menuProp)
2047 {
2048     OffsetF menuPosition = OffsetF();
2049     auto geometryNode = layoutWrapper->GetGeometryNode();
2050     CHECK_NULL_RETURN(geometryNode, menuPosition);
2051     auto size = geometryNode->GetMarginFrameSize();
2052     didNeedArrow_ = GetIfNeedArrow(layoutWrapper, size);
2053     if (menuPattern->IsSelectMenu()) {
2054         ComputeMenuPositionByAlignType(menuProp, size);
2055         auto offset = ComputeMenuPositionByOffset(menuProp, geometryNode);
2056         position_ += offset;
2057     }
2058     auto useLastPosition = lastPosition_.has_value() && holdEmbeddedMenuPosition_;
2059     auto avoidanceMode = menuProp->GetSelectAvoidanceMode().value_or(AvoidanceMode::COVER_TARGET);
2060     if (useLastPosition) {
2061         menuPosition = lastPosition_.value();
2062         auto lastPlacement = menuPattern->GetLastPlacement();
2063         if (lastPlacement.has_value()) {
2064             placement_ = lastPlacement.value();
2065             arrowPlacement_ = lastPlacement.value();
2066         }
2067     } else if (menuPattern->IsSelectMenu() && avoidanceMode == AvoidanceMode::AVOID_AROUND_TARGET &&
2068                !IsSelectMenuShowInSubWindow(layoutWrapper, menuNode)) {
2069         menuPosition = SelectLayoutAvoidAlgorithm(menuProp, menuPattern, size, didNeedArrow_, layoutWrapper);
2070     } else {
2071         menuPosition = MenuLayoutAvoidAlgorithm(menuProp, menuPattern, size, didNeedArrow_, layoutWrapper);
2072     }
2073     menuPattern->UpdateLastPosition(menuPosition);
2074     menuPattern->UpdateLastPlacement(placement_);
2075     CalculateChildOffset(didNeedArrow_);
2076     OffsetF menuPositionWithArrow = CalculateMenuPositionWithArrow(menuPosition, didNeedArrow_);
2077     TAG_LOGD(AceLogTag::ACE_MENU, "update menu postion: %{public}s", menuPositionWithArrow.ToString().c_str());
2078     auto renderContext = menuNode->GetRenderContext();
2079     CHECK_NULL_RETURN(renderContext, menuPosition);
2080     // show animation will be interrupted by repeated update
2081     if (lastPosition_.value_or(OffsetF()) != menuPositionWithArrow) {
2082         renderContext->UpdatePosition(
2083             OffsetT<Dimension>(Dimension(menuPositionWithArrow.GetX()), Dimension(menuPositionWithArrow.GetY())));
2084     }
2085     dumpInfo_.finalPlacement = PlacementUtils::ConvertPlacementToString(placement_);
2086     dumpInfo_.finalPosition = menuPosition;
2087     SetMenuPlacementForAnimation(layoutWrapper);
2088     if (didNeedArrow_ && arrowPlacement_ != Placement::NONE) {
2089         arrowPosition_ = GetArrowPositionWithPlacement(size, layoutWrapper);
2090         LayoutArrow(layoutWrapper);
2091     }
2092     geometryNode->SetFrameOffset(menuPositionWithArrow);
2093     return menuPosition;
2094 }
2095 
IsSelectMenuShowInSubWindow(LayoutWrapper * layoutWrapper,const RefPtr<FrameNode> & menuNode)2096 bool MenuLayoutAlgorithm::IsSelectMenuShowInSubWindow(LayoutWrapper* layoutWrapper, const RefPtr<FrameNode>& menuNode)
2097 {
2098     CHECK_NULL_RETURN(layoutWrapper, false);
2099     CHECK_NULL_RETURN(menuNode, false);
2100     auto pipelineContext = menuNode->GetContext();
2101     CHECK_NULL_RETURN(pipelineContext, false);
2102     auto theme = pipelineContext->GetTheme<SelectTheme>();
2103     CHECK_NULL_RETURN(theme, false);
2104     auto menuLayoutProperty = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
2105     CHECK_NULL_RETURN(menuLayoutProperty, false);
2106     auto isSelectMenuShowInSubWindow = theme->GetExpandDisplay() && menuLayoutProperty->GetShowInSubWindowValue(false);
2107     return isSelectMenuShowInSubWindow;
2108 }
2109 
TranslateOptions(LayoutWrapper * layoutWrapper)2110 void MenuLayoutAlgorithm::TranslateOptions(LayoutWrapper* layoutWrapper)
2111 {
2112     // translate each option by the height of previous options
2113     OffsetF translate = childOffset_;
2114     for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
2115         child->GetGeometryNode()->SetMarginFrameOffset(translate);
2116         child->Layout();
2117         translate += OffsetF(0, child->GetGeometryNode()->GetFrameSize().Height());
2118     }
2119 }
2120 
SetMenuPlacementForAnimation(LayoutWrapper * layoutWrapper)2121 void MenuLayoutAlgorithm::SetMenuPlacementForAnimation(LayoutWrapper* layoutWrapper)
2122 {
2123     auto menu = layoutWrapper->GetHostNode();
2124     CHECK_NULL_VOID(menu);
2125     auto menuPattern = menu->GetPattern<MenuPattern>();
2126     CHECK_NULL_VOID(menuPattern);
2127     auto menuWrapper = menuPattern->GetMenuWrapper();
2128     CHECK_NULL_VOID(menuWrapper);
2129     auto wrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
2130     CHECK_NULL_VOID(wrapperPattern);
2131     wrapperPattern->SetMenuPlacementAfterLayout(placement_);
2132 }
2133 
LayoutArrow(const LayoutWrapper * layoutWrapper)2134 void MenuLayoutAlgorithm::LayoutArrow(const LayoutWrapper* layoutWrapper)
2135 {
2136     auto paintProperty = GetPaintProperty(layoutWrapper);
2137     CHECK_NULL_VOID(paintProperty);
2138     paintProperty->UpdateArrowPosition(arrowPosition_);
2139     paintProperty->UpdateArrowPlacement(arrowPlacement_);
2140     dumpInfo_.enableArrow = true;
2141 }
2142 
GetPaintProperty(const LayoutWrapper * layoutWrapper)2143 RefPtr<MenuPaintProperty> MenuLayoutAlgorithm::GetPaintProperty(const LayoutWrapper* layoutWrapper)
2144 {
2145     auto menuNode = layoutWrapper->GetHostNode();
2146     CHECK_NULL_RETURN(menuNode, nullptr);
2147     auto paintProperty = menuNode->GetPaintProperty<MenuPaintProperty>();
2148     CHECK_NULL_RETURN(paintProperty, nullptr);
2149     return paintProperty;
2150 }
2151 
GetMenuRadius(const LayoutWrapper * layoutWrapper,const SizeF & menuSize)2152 BorderRadiusProperty MenuLayoutAlgorithm::GetMenuRadius(const LayoutWrapper* layoutWrapper, const SizeF& menuSize)
2153 {
2154     Dimension defaultDimension(0);
2155     BorderRadiusProperty radius = { defaultDimension, defaultDimension, defaultDimension, defaultDimension };
2156     auto hostNode = layoutWrapper->GetHostNode();
2157     CHECK_NULL_RETURN(hostNode, radius);
2158     auto theme = GetCurrentSelectTheme(hostNode);
2159     CHECK_NULL_RETURN(theme, radius);
2160     auto defaultRadius = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)
2161                              ? theme->GetMenuDefaultRadius()
2162                              : theme->GetMenuBorderRadius();
2163     radius.SetRadius(defaultRadius);
2164     auto menuLayoutProp = DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
2165     CHECK_NULL_RETURN(menuLayoutProp, radius);
2166     if (menuLayoutProp->GetBorderRadius().has_value()) {
2167         auto menuNode = layoutWrapper->GetHostNode();
2168         CHECK_NULL_RETURN(menuNode, radius);
2169         auto menuPattern = menuNode->GetPattern<MenuPattern>();
2170         CHECK_NULL_RETURN(menuPattern, radius);
2171         radius = menuPattern->CalcIdealBorderRadius(menuLayoutProp->GetBorderRadiusValue(), menuSize);
2172     }
2173 
2174     return radius;
2175 }
2176 
GetIfNeedArrow(const LayoutWrapper * layoutWrapper,const SizeF & menuSize)2177 bool MenuLayoutAlgorithm::GetIfNeedArrow(const LayoutWrapper* layoutWrapper, const SizeF& menuSize)
2178 {
2179     CHECK_NULL_RETURN(layoutWrapper, false);
2180     auto menuNode = layoutWrapper->GetHostNode();
2181     CHECK_NULL_RETURN(menuNode, false);
2182     auto menuPattern = menuNode->GetPattern<MenuPattern>();
2183     CHECK_NULL_RETURN(menuPattern, false);
2184     auto menuProp = DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
2185     CHECK_NULL_RETURN(menuProp, false);
2186     auto paintProperty = GetPaintProperty(layoutWrapper);
2187     CHECK_NULL_RETURN(paintProperty, false);
2188     propNeedArrow_ = paintProperty->GetEnableArrow().value_or(false);
2189 
2190     auto hostNode = layoutWrapper->GetHostNode();
2191     CHECK_NULL_RETURN(hostNode, false);
2192     auto selectThemePtr = GetCurrentSelectTheme(menuNode);
2193     CHECK_NULL_RETURN(selectThemePtr, false);
2194     if (!propNeedArrow_ || !menuProp->GetMenuPlacement().has_value()) {
2195         return false;
2196     }
2197 
2198     propArrowOffset_ = paintProperty->GetArrowOffset().value_or(Dimension(0));
2199     ProcessArrowParams(layoutWrapper, menuSize);
2200 
2201     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
2202         return (menuPattern->IsContextMenu() || menuPattern->IsMenu()) && !targetTag_.empty() && arrowInMenu_;
2203     }
2204 
2205     return menuPattern->IsContextMenu() && !targetTag_.empty() && arrowInMenu_;
2206 }
2207 
ProcessArrowParams(const LayoutWrapper * layoutWrapper,const SizeF & menuSize)2208 void MenuLayoutAlgorithm::ProcessArrowParams(const LayoutWrapper* layoutWrapper, const SizeF& menuSize)
2209 {
2210     BorderRadiusProperty menuBorderRadius = GetMenuRadius(layoutWrapper, menuSize);
2211     auto radiusTopLeft = menuBorderRadius.radiusTopLeft.value_or(Dimension()).ConvertToPx();
2212     auto radiusTopRight = menuBorderRadius.radiusTopRight.value_or(Dimension()).ConvertToPx();
2213     auto radiusBottomLeft = menuBorderRadius.radiusBottomLeft.value_or(Dimension()).ConvertToPx();
2214     auto radiusBottomRight = menuBorderRadius.radiusBottomRight.value_or(Dimension()).ConvertToPx();
2215     arrowWidth_ = ARROW_WIDTH.ConvertToPx();
2216 
2217     switch (placement_) {
2218         case Placement::LEFT:
2219         case Placement::LEFT_TOP:
2220         case Placement::LEFT_BOTTOM:
2221             if (GreatOrEqual(menuSize.Height(), radiusTopRight + radiusBottomRight + arrowWidth_)) {
2222                 arrowInMenu_ = true;
2223             }
2224             break;
2225         case Placement::RIGHT:
2226         case Placement::RIGHT_TOP:
2227         case Placement::RIGHT_BOTTOM:
2228             if (GreatOrEqual(menuSize.Height(), radiusTopLeft + radiusBottomLeft + arrowWidth_)) {
2229                 arrowInMenu_ = true;
2230             }
2231             break;
2232         case Placement::TOP:
2233         case Placement::TOP_LEFT:
2234         case Placement::TOP_RIGHT:
2235             if (GreatOrEqual(menuSize.Width(), radiusBottomLeft + radiusBottomRight + arrowWidth_)) {
2236                 arrowInMenu_ = true;
2237             }
2238             break;
2239         case Placement::BOTTOM:
2240         case Placement::BOTTOM_LEFT:
2241         case Placement::BOTTOM_RIGHT:
2242             if (GreatOrEqual(menuSize.Width(), radiusTopLeft + radiusTopRight + arrowWidth_)) {
2243                 arrowInMenu_ = true;
2244             }
2245             break;
2246         default:
2247             break;
2248     }
2249 
2250     if (arrowInMenu_) {
2251         targetSpace_ = TARGET_SPACE.ConvertToPx();
2252     }
2253 }
2254 
UpdatePropArrowOffset()2255 void MenuLayoutAlgorithm::UpdatePropArrowOffset()
2256 {
2257     if (propArrowOffset_.IsValid()) {
2258         if (propArrowOffset_.Unit() == DimensionUnit::PERCENT) {
2259             propArrowOffset_.SetValue(std::clamp(propArrowOffset_.Value(), 0.0, 1.0));
2260         }
2261         return;
2262     }
2263     switch (arrowPlacement_) {
2264         case Placement::LEFT:
2265         case Placement::RIGHT:
2266         case Placement::TOP:
2267         case Placement::BOTTOM:
2268             propArrowOffset_ = ARROW_HALF_PERCENT_VALUE;
2269             break;
2270         case Placement::TOP_LEFT:
2271         case Placement::BOTTOM_LEFT:
2272         case Placement::LEFT_TOP:
2273         case Placement::RIGHT_TOP:
2274             propArrowOffset_ = ARROW_ZERO_PERCENT_VALUE;
2275             break;
2276         case Placement::TOP_RIGHT:
2277         case Placement::BOTTOM_RIGHT:
2278         case Placement::LEFT_BOTTOM:
2279         case Placement::RIGHT_BOTTOM:
2280             propArrowOffset_ = ARROW_ONE_HUNDRED_PERCENT_VALUE;
2281             break;
2282         default:
2283             break;
2284     }
2285 }
2286 
UpdateArrowOffsetWithMenuLimit(const SizeF & menuSize,const LayoutWrapper * layoutWrapper)2287 void MenuLayoutAlgorithm::UpdateArrowOffsetWithMenuLimit(const SizeF& menuSize, const LayoutWrapper* layoutWrapper)
2288 {
2289     UpdatePropArrowOffset();
2290     BorderRadiusProperty menuBorderRadius = GetMenuRadius(layoutWrapper, menuSize);
2291     auto radiusTopLeft = menuBorderRadius.radiusTopLeft.value_or(Dimension()).ConvertToPx();
2292     auto radiusTopRight = menuBorderRadius.radiusTopRight.value_or(Dimension()).ConvertToPx();
2293     auto radiusBottomLeft = menuBorderRadius.radiusBottomLeft.value_or(Dimension()).ConvertToPx();
2294     auto radiusBottomRight = menuBorderRadius.radiusBottomRight.value_or(Dimension()).ConvertToPx();
2295     float range = -1.0f;
2296 
2297     switch (arrowPlacement_) {
2298         case Placement::LEFT:
2299         case Placement::LEFT_TOP:
2300         case Placement::LEFT_BOTTOM:
2301             range = menuSize.Height() - radiusTopRight - radiusBottomRight - arrowWidth_;
2302             arrowMinLimit_ = radiusTopRight + ARROW_WIDTH.ConvertToPx() / ARROW_WIDTH_FACTOR;
2303             break;
2304         case Placement::RIGHT:
2305         case Placement::RIGHT_TOP:
2306         case Placement::RIGHT_BOTTOM:
2307             range = menuSize.Height() - radiusTopLeft - radiusBottomLeft - arrowWidth_;
2308             arrowMinLimit_ = radiusTopLeft + ARROW_WIDTH.ConvertToPx() / ARROW_WIDTH_FACTOR;
2309             break;
2310         case Placement::TOP:
2311         case Placement::TOP_LEFT:
2312         case Placement::TOP_RIGHT:
2313             range = menuSize.Width() - radiusBottomLeft - radiusBottomRight - arrowWidth_;
2314             arrowMinLimit_ = radiusBottomLeft + ARROW_WIDTH.ConvertToPx() / ARROW_WIDTH_FACTOR;
2315             break;
2316         case Placement::BOTTOM:
2317         case Placement::BOTTOM_LEFT:
2318         case Placement::BOTTOM_RIGHT:
2319             range = menuSize.Width() - radiusTopLeft - radiusTopRight - arrowWidth_;
2320             arrowMinLimit_ = radiusTopLeft + ARROW_WIDTH.ConvertToPx() / ARROW_WIDTH_FACTOR;
2321             break;
2322         default:
2323             break;
2324     }
2325     if (range >= 0) {
2326         float tempOffset = propArrowOffset_.Unit() == DimensionUnit::PERCENT ? propArrowOffset_.Value() * range
2327                                                                              : propArrowOffset_.ConvertToPx();
2328         arrowOffset_ = std::clamp(tempOffset, 0.0f, range);
2329     }
2330 }
2331 
ComputeMenuPositionByAlignType(const RefPtr<MenuLayoutProperty> & menuProp,const SizeF & menuSize)2332 void MenuLayoutAlgorithm::ComputeMenuPositionByAlignType(
2333     const RefPtr<MenuLayoutProperty>& menuProp, const SizeF& menuSize)
2334 {
2335     auto alignType = menuProp->GetAlignType().value_or(MenuAlignType::START);
2336     auto direction = menuProp->GetNonAutoLayoutDirection();
2337     auto targetSize = menuProp->GetTargetSizeValue(SizeF());
2338     switch (alignType) {
2339         case MenuAlignType::CENTER: {
2340             position_.AddX(targetSize.Width() / 2.0f - menuSize.Width() / 2.0f);
2341             break;
2342         }
2343         case MenuAlignType::END: {
2344             if (direction == TextDirection::RTL) {
2345                 return;
2346             }
2347             position_.AddX(targetSize.Width() - menuSize.Width());
2348             break;
2349         }
2350         case MenuAlignType::START: {
2351             if (direction != TextDirection::RTL) {
2352                 return;
2353             }
2354             position_.AddX(targetSize.Width() - menuSize.Width());
2355             break;
2356         }
2357         default:
2358             break;
2359     }
2360 }
2361 
ComputeMenuPositionByOffset(const RefPtr<MenuLayoutProperty> & menuProp,const RefPtr<GeometryNode> & geometryNode)2362 OffsetF MenuLayoutAlgorithm::ComputeMenuPositionByOffset(
2363     const RefPtr<MenuLayoutProperty>& menuProp, const RefPtr<GeometryNode>& geometryNode)
2364 {
2365     CHECK_NULL_RETURN(menuProp, OffsetF(0, 0));
2366     CHECK_NULL_RETURN(geometryNode, OffsetF(0, 0));
2367 
2368     const auto& layoutConstraint = menuProp->GetLayoutConstraint();
2369     CHECK_NULL_RETURN(layoutConstraint, OffsetF(0, 0));
2370     auto menuAlignOffset = menuProp->GetOffset().value_or(
2371         DimensionOffset(Dimension(0, DimensionUnit::VP), Dimension(0, DimensionUnit::VP)));
2372 
2373     auto menuSize = geometryNode->GetFrameSize();
2374     auto menuTrimOffsetX =
2375         ConvertToPx(CalcLength(menuAlignOffset.GetX()), layoutConstraint->scaleProperty, menuSize.Width());
2376     auto menuTrimOffsetY =
2377         ConvertToPx(CalcLength(menuAlignOffset.GetY()), layoutConstraint->scaleProperty, menuSize.Height());
2378     OffsetF menuTrimOffset = OffsetF(menuTrimOffsetX.value_or(0.0), menuTrimOffsetY.value_or(0.0));
2379     return menuTrimOffset;
2380 }
2381 
2382 // Adapt to the menualign specification in select avoidance mode.
ComputePlacementByAlignType(const RefPtr<MenuLayoutProperty> & menuProp)2383 void MenuLayoutAlgorithm::ComputePlacementByAlignType(const RefPtr<MenuLayoutProperty>& menuProp)
2384 {
2385     auto alignType = menuProp->GetAlignType().value_or(MenuAlignType::START);
2386     switch (alignType) {
2387         case MenuAlignType::START:
2388             placement_ = Placement::BOTTOM_LEFT;
2389             break;
2390         case MenuAlignType::CENTER:
2391             placement_ = Placement::BOTTOM;
2392             break;
2393         case MenuAlignType::END:
2394             placement_ = Placement::BOTTOM_RIGHT;
2395             break;
2396         default:
2397             break;
2398     }
2399 }
2400 
MenuLayoutAvoidAlgorithm(const RefPtr<MenuLayoutProperty> & menuProp,const RefPtr<MenuPattern> & menuPattern,const SizeF & size,bool didNeedArrow,LayoutWrapper * layoutWrapper)2401 OffsetF MenuLayoutAlgorithm::MenuLayoutAvoidAlgorithm(const RefPtr<MenuLayoutProperty>& menuProp,
2402     const RefPtr<MenuPattern>& menuPattern, const SizeF& size, bool didNeedArrow, LayoutWrapper* layoutWrapper)
2403 {
2404     CHECK_NULL_RETURN(menuProp, OffsetF(0.0f, 0.0f));
2405     CHECK_NULL_RETURN(menuPattern, OffsetF(0.0f, 0.0f));
2406     CHECK_NULL_RETURN(layoutWrapper, OffsetF(0.0f, 0.0f));
2407     auto hostNode = layoutWrapper->GetHostNode();
2408     CHECK_NULL_RETURN(hostNode, OffsetF(0.0f, 0.0f));
2409     auto pipelineContext = DialogManager::GetMainPipelineContext(hostNode);
2410     CHECK_NULL_RETURN(pipelineContext, OffsetF(0.0f, 0.0f));
2411     float x = 0.0f;
2412     float y = 0.0f;
2413     if (menuProp->GetMenuPlacement().has_value() &&
2414         (GreatNotEqual(targetSize_.Width(), 0.0) || GreatNotEqual(targetSize_.Height(), 0.0))) {
2415         placement_ = menuProp->GetMenuPlacement().value();
2416         if (layoutWrapper != nullptr) {
2417             PlacementRTL(layoutWrapper, placement_);
2418         }
2419         auto childOffset = GetChildPosition(size, didNeedArrow);
2420         x = childOffset.GetX();
2421         y = childOffset.GetY();
2422     } else {
2423         x = HorizontalLayout(size, position_.GetX(), menuPattern->IsSelectMenu());
2424         y = VerticalLayout(size, position_.GetY(), menuPattern->IsContextMenu());
2425     }
2426     x = std::clamp(static_cast<double>(x), static_cast<double>(paddingStart_),
2427         static_cast<double>(wrapperRect_.Right() - size.Width() - paddingEnd_));
2428     float yMinAvoid = wrapperRect_.Top() + paddingTop_;
2429     float yMaxAvoid = wrapperRect_.Bottom() - paddingBottom_ - size.Height();
2430     y = std::clamp(y, yMinAvoid, yMaxAvoid);
2431     return { x, y };
2432 }
2433 
SelectLayoutAvoidAlgorithm(const RefPtr<MenuLayoutProperty> & menuProp,const RefPtr<MenuPattern> & menuPattern,const SizeF & size,bool didNeedArrow,LayoutWrapper * layoutWrapper)2434 OffsetF MenuLayoutAlgorithm::SelectLayoutAvoidAlgorithm(const RefPtr<MenuLayoutProperty>& menuProp,
2435     const RefPtr<MenuPattern>& menuPattern, const SizeF& size, bool didNeedArrow, LayoutWrapper* layoutWrapper)
2436 {
2437     CHECK_NULL_RETURN(menuProp, OffsetF(0.0f, 0.0f));
2438     CHECK_NULL_RETURN(menuPattern, OffsetF(0.0f, 0.0f));
2439     CHECK_NULL_RETURN(layoutWrapper, OffsetF(0.0f, 0.0f));
2440     auto host = menuPattern->GetHost();
2441     CHECK_NULL_RETURN(host, OffsetF(0.0f, 0.0f));
2442     auto pipelineContext = DialogManager::GetMainPipelineContext(host);
2443     CHECK_NULL_RETURN(pipelineContext, OffsetF(0.0f, 0.0f));
2444     auto geometryNode = layoutWrapper->GetGeometryNode();
2445     CHECK_NULL_RETURN(geometryNode, OffsetF(0.0f, 0.0f));
2446     float x = 0.0f;
2447     float y = 0.0f;
2448     float selectMenuHeight = geometryNode->GetFrameSize().Height();
2449     Rect targetRect(targetOffset_.GetX(), targetOffset_.GetY(), targetSize_.Width(), targetSize_.Height());
2450     float bottomSpace = wrapperRect_.Bottom() - targetRect.Bottom() - targetSecurity_ - paddingBottom_;
2451 
2452     float xMinAvoid = paddingStart_;
2453     float xMaxAvoid = wrapperRect_.Right() - size.Width() - paddingEnd_;
2454     float yMinAvoid = wrapperRect_.Top() + paddingTop_;
2455     float yMaxAvoid = wrapperRect_.Bottom() - paddingBottom_ - size.Height();
2456 
2457     if (GreatNotEqual(targetSize_.Width(), 0.0) || GreatNotEqual(targetSize_.Height(), 0.0)) {
2458         placement_ = Placement::BOTTOM_LEFT;
2459         ComputePlacementByAlignType(menuProp);
2460         if (layoutWrapper != nullptr) {
2461             PlacementRTL(layoutWrapper, placement_);
2462         }
2463         OffsetF selectChildOffset = GetSelectChildPosition(size, didNeedArrow, layoutWrapper);
2464         x = selectChildOffset.GetX();
2465         y = selectChildOffset.GetY();
2466         OffsetF computedOffset = selectChildOffset + ComputeMenuPositionByOffset(menuProp, geometryNode);
2467         bool isXOut = LessOrEqual(computedOffset.GetX(), xMinAvoid) ||
2468             GreatOrEqual(computedOffset.GetX(), xMaxAvoid);
2469         bool isYOut = LessOrEqual(computedOffset.GetY(), yMinAvoid) ||
2470             GreatOrEqual(computedOffset.GetY(), yMaxAvoid);
2471         bool hasEnoughSpace = GreatOrEqual(bottomSpace, selectMenuHeight);
2472         if (!isXOut && !isYOut && hasEnoughSpace) {
2473             x = computedOffset.GetX();
2474             y = computedOffset.GetY();
2475             return { x, y };
2476         }
2477     }
2478     x = std::clamp(x, xMinAvoid, xMaxAvoid);
2479     y = std::clamp(y, yMinAvoid, yMaxAvoid);
2480     return { x, y };
2481 }
2482 
PlacementRTL(LayoutWrapper * layoutWrapper,Placement & placement_)2483 void MenuLayoutAlgorithm::PlacementRTL(LayoutWrapper* layoutWrapper, Placement& placement_)
2484 {
2485     auto menuLayoutProperty = layoutWrapper->GetLayoutProperty();
2486     CHECK_NULL_VOID(menuLayoutProperty);
2487     auto layoutDirection = menuLayoutProperty->GetNonAutoLayoutDirection();
2488     if (layoutDirection == TextDirection::RTL) {
2489         switch (placement_) {
2490             case Placement::LEFT:
2491                 placement_ = Placement::RIGHT;
2492                 break;
2493             case Placement::RIGHT:
2494                 placement_ = Placement::LEFT;
2495                 break;
2496             case Placement::TOP_LEFT:
2497                 placement_ = Placement::TOP_RIGHT;
2498                 break;
2499             case Placement::TOP_RIGHT:
2500                 placement_ = Placement::TOP_LEFT;
2501                 break;
2502             case Placement::BOTTOM_LEFT:
2503                 placement_ = Placement::BOTTOM_RIGHT;
2504                 break;
2505             case Placement::BOTTOM_RIGHT:
2506                 placement_ = Placement::BOTTOM_LEFT;
2507                 break;
2508             case Placement::LEFT_TOP:
2509                 placement_ = Placement::RIGHT_TOP;
2510                 break;
2511             case Placement::RIGHT_TOP:
2512                 placement_ = Placement::LEFT_TOP;
2513                 break;
2514             case Placement::LEFT_BOTTOM:
2515                 placement_ = Placement::RIGHT_BOTTOM;
2516                 break;
2517             case Placement::RIGHT_BOTTOM:
2518                 placement_ = Placement::LEFT_BOTTOM;
2519                 break;
2520             default:
2521                 break;
2522         }
2523     }
2524 }
2525 
LimitContainerModalMenuRect(double & rectWidth,double & rectHeight,const RefPtr<MenuPattern> & menuPattern)2526 void MenuLayoutAlgorithm::LimitContainerModalMenuRect(
2527     double& rectWidth, double& rectHeight, const RefPtr<MenuPattern>& menuPattern)
2528 {
2529     auto containerOffsetX = static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()) +
2530                             static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx());
2531     CHECK_NULL_VOID(menuPattern);
2532     auto host = menuPattern->GetHost();
2533     CHECK_NULL_VOID(host);
2534     auto pipeline = DialogManager::GetMainPipelineContext(host);
2535     CHECK_NULL_VOID(pipeline);
2536     auto containerOffsetY = static_cast<float>(pipeline->GetCustomTitleHeight().ConvertToPx()) +
2537                             static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()) +
2538                             static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx());
2539     rectWidth -= containerOffsetX;
2540     rectHeight -= containerOffsetY;
2541 }
2542 
UpdateConstraintWidth(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)2543 void MenuLayoutAlgorithm::UpdateConstraintWidth(LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
2544 {
2545     RefPtr<GridColumnInfo> columnInfo;
2546     columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
2547     auto menuNode = layoutWrapper->GetHostNode();
2548     CHECK_NULL_VOID(menuNode);
2549     auto menuPattern = menuNode->GetPattern<MenuPattern>();
2550     CHECK_NULL_VOID(menuPattern);
2551     columnInfo->GetParent()->BuildColumnWidth(wrapperSize_.Width());
2552     auto menuLayoutProperty = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
2553     CHECK_NULL_VOID(menuLayoutProperty);
2554     // set max width
2555     const auto& padding = menuLayoutProperty->CreatePaddingAndBorder();
2556     auto maxHorizontalSpace = std::max(leftSpace_, rightSpace_) - 2.0f * padding.Width();
2557     auto maxWidth = static_cast<float>(columnInfo->GetWidth(GetMaxGridCounts(columnInfo)));
2558     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
2559         maxWidth = std::min(maxHorizontalSpace, maxWidth);
2560     }
2561     maxWidth = std::min(constraint.maxSize.Width(), maxWidth);
2562     constraint.maxSize.SetWidth(maxWidth);
2563 }
2564 
UpdateConstraintHeight(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)2565 void MenuLayoutAlgorithm::UpdateConstraintHeight(LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
2566 {
2567     CHECK_NULL_VOID(layoutWrapper);
2568     auto hostNode = layoutWrapper->GetHostNode();
2569     CHECK_NULL_VOID(hostNode);
2570     auto pipelineContext = DialogManager::GetMainPipelineContext(hostNode);
2571     CHECK_NULL_VOID(pipelineContext);
2572     auto menuPattern = layoutWrapper->GetHostNode()->GetPattern<MenuPattern>();
2573     CHECK_NULL_VOID(menuPattern);
2574 
2575     float maxAvailableHeight = wrapperRect_.Height();
2576     float maxSpaceHeight = maxAvailableHeight * HEIGHT_CONSTRAINT_FACTOR;
2577     if (lastPosition_.has_value() && holdEmbeddedMenuPosition_) {
2578         auto spaceToBottom = GetMenuMaxBottom(menuPattern) - lastPosition_.value().GetY();
2579         maxSpaceHeight = std::min(maxSpaceHeight, spaceToBottom);
2580     }
2581     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
2582         if (menuPattern->IsHeightModifiedBySelect()) {
2583             auto menuLayoutProps = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
2584             auto selectModifiedHeight = menuLayoutProps->GetSelectModifiedHeight().value_or(0.0f);
2585             if (LessNotEqual(selectModifiedHeight, maxSpaceHeight)) {
2586                 maxSpaceHeight = selectModifiedHeight;
2587             }
2588         }
2589     }
2590     constraint.maxSize.SetHeight(maxSpaceHeight);
2591 }
2592 
UpdateConstraintSelectHeight(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)2593 void MenuLayoutAlgorithm::UpdateConstraintSelectHeight(LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
2594 {
2595     CHECK_NULL_VOID(layoutWrapper);
2596     auto hostNode = layoutWrapper->GetHostNode();
2597     CHECK_NULL_VOID(hostNode);
2598     auto pipelineContext = DialogManager::GetMainPipelineContext(hostNode);
2599     CHECK_NULL_VOID(pipelineContext);
2600     auto menuPattern = layoutWrapper->GetHostNode()->GetPattern<MenuPattern>();
2601     CHECK_NULL_VOID(menuPattern);
2602 
2603     float maxAvailableHeight = wrapperRect_.Height();
2604     float maxSpaceHeight = maxAvailableHeight * HEIGHT_CONSTRAINT_FACTOR;
2605     auto layoutProp = layoutWrapper->GetLayoutProperty();
2606     CHECK_NULL_VOID(layoutProp);
2607     auto menuLayoutProps = AceType::DynamicCast<MenuLayoutProperty>(layoutProp);
2608     CHECK_NULL_VOID(menuLayoutProps);
2609     auto geometryNode = layoutWrapper->GetGeometryNode();
2610     CHECK_NULL_VOID(geometryNode);
2611     float selectMenuWidth = geometryNode->GetFrameSize().Width();
2612     float xAvoid = wrapperRect_.Left() + paddingStart_;
2613     float yAvoid = wrapperRect_.Top() + paddingTop_ + param_.topSecurity;
2614     float maxWidth = wrapperSize_.Width() - paddingEnd_ - paddingStart_;
2615     Rect targetRect = Rect(targetOffset_.GetX(), targetOffset_.GetY(), targetSize_.Width(), targetSize_.Height());
2616     auto bottomSpace = std::min<float>(wrapperRect_.Bottom() - targetRect.Bottom() -
2617         targetSecurity_ - paddingBottom_, maxSpaceHeight);
2618     auto topSpace = std::min<float>(targetRect.Top() - yAvoid - targetSecurity_, maxSpaceHeight);
2619     auto rightSpace =
2620         std::min<float>(wrapperRect_.Right() - targetRect.Right() - targetSecurity_ - paddingEnd_, maxWidth);
2621     auto leftSpace = std::min<float>(targetRect.Left() - xAvoid - targetSecurity_, maxWidth);
2622     if (GreatOrEqual(selectMenuWidth, rightSpace) && GreatOrEqual(selectMenuWidth, leftSpace)) {
2623         maxSpaceHeight = std::max(topSpace, bottomSpace);
2624     }
2625     if (lastPosition_.has_value() && holdEmbeddedMenuPosition_) {
2626         auto spaceToBottom = GetMenuMaxBottom(menuPattern) - lastPosition_.value().GetY();
2627         maxSpaceHeight = std::min(maxSpaceHeight, spaceToBottom);
2628     }
2629     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
2630         if (menuPattern->IsHeightModifiedBySelect()) {
2631             auto menuLayoutProps = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
2632             auto selectModifiedHeight = menuLayoutProps->GetSelectModifiedHeight().value_or(0.0f);
2633             if (LessNotEqual(selectModifiedHeight, maxSpaceHeight)) {
2634                 maxSpaceHeight = selectModifiedHeight;
2635             }
2636         }
2637     }
2638     constraint.maxSize.SetHeight(maxSpaceHeight);
2639 }
2640 
GetMenuMaxBottom(const RefPtr<MenuPattern> & menuPattern)2641 float MenuLayoutAlgorithm::GetMenuMaxBottom(const RefPtr<MenuPattern>& menuPattern)
2642 {
2643     auto bottom = wrapperRect_.Bottom();
2644     CHECK_NULL_RETURN(menuPattern, bottom);
2645     if (menuPattern->GetPreviewMode() != MenuPreviewMode::NONE) {
2646         auto placement = menuPattern->GetLastPlacement().value_or(Placement::NONE);
2647         auto isTopMenuBottomPreview =
2648             placement == Placement::TOP || placement == Placement::TOP_LEFT || placement == Placement::TOP_RIGHT;
2649         auto previewTopSpace = bottom - previewSize_.Height() * previewScale_ - targetSecurity_;
2650         return std::max<float>(0.0f, (isTopMenuBottomPreview ? previewTopSpace : bottom) - param_.bottomSecurity);
2651     }
2652 
2653     return bottom;
2654 }
2655 
CreateChildConstraint(LayoutWrapper * layoutWrapper)2656 LayoutConstraintF MenuLayoutAlgorithm::CreateChildConstraint(LayoutWrapper* layoutWrapper)
2657 {
2658     auto menuLayoutProperty = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
2659     CHECK_NULL_RETURN(menuLayoutProperty, LayoutConstraintF());
2660     auto menuNode = layoutWrapper->GetHostNode();
2661     CHECK_NULL_RETURN(menuNode, LayoutConstraintF());
2662     auto menuPattern = menuNode->GetPattern<MenuPattern>();
2663     CHECK_NULL_RETURN(menuPattern, LayoutConstraintF());
2664 
2665     auto avoidanceMode = menuLayoutProperty->GetSelectAvoidanceMode().value_or(AvoidanceMode::COVER_TARGET);
2666     auto childConstraint = menuLayoutProperty->CreateChildConstraint();
2667     UpdateConstraintWidth(layoutWrapper, childConstraint);
2668     UpdateConstraintBaseOnOptions(layoutWrapper, childConstraint);
2669     if (menuPattern->IsSelectMenu() && avoidanceMode == AvoidanceMode::AVOID_AROUND_TARGET &&
2670         !IsSelectMenuShowInSubWindow(layoutWrapper, menuNode)) {
2671         UpdateConstraintSelectHeight(layoutWrapper, childConstraint);
2672     } else {
2673         UpdateConstraintHeight(layoutWrapper, childConstraint);
2674     }
2675     return childConstraint;
2676 }
2677 
UpdateConstraintBaseOnOptions(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)2678 void MenuLayoutAlgorithm::UpdateConstraintBaseOnOptions(LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
2679 {
2680     auto menuNode = layoutWrapper->GetHostNode();
2681     CHECK_NULL_VOID(menuNode);
2682     auto menuPattern = menuNode->GetPattern<MenuPattern>();
2683     CHECK_NULL_VOID(menuPattern);
2684     auto options = menuPattern->GetOptions();
2685     if (options.empty()) {
2686         return;
2687     }
2688     auto optionConstraint = constraint;
2689     RefPtr<GridColumnInfo> columnInfo;
2690     columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
2691     if (!UpdateSelectOverlayMenuColumnInfo(menuPattern, columnInfo)) {
2692         columnInfo->GetParent()->BuildColumnWidth(wrapperSize_.Width());
2693     }
2694     auto minWidth = static_cast<float>(columnInfo->GetWidth(MIN_GRID_COUNTS));
2695     optionConstraint.maxSize.MinusWidth(optionPadding_ * 2.0f);
2696     auto props = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
2697     if (menuPattern->IsSelectOverlayExtensionMenu() && props && props->HasMenuWidth()) {
2698         minWidth = props->GetMenuWidth()->ConvertToPx();
2699     }
2700     optionConstraint.minSize.SetWidth(minWidth - optionPadding_ * 2.0f);
2701     auto maxChildrenWidth = optionConstraint.minSize.Width();
2702     auto optionsLayoutWrapper = GetOptionsLayoutWrappper(layoutWrapper);
2703     for (const auto& optionWrapper : optionsLayoutWrapper) {
2704         optionWrapper->GetLayoutProperty()->ResetCalcMinSize();
2705         optionWrapper->Measure(optionConstraint);
2706         auto childSize = optionWrapper->GetGeometryNode()->GetMarginFrameSize();
2707         maxChildrenWidth = std::max(maxChildrenWidth, childSize.Width());
2708     }
2709     UpdateOptionConstraint(optionsLayoutWrapper, maxChildrenWidth);
2710     auto pipelineContext = menuNode->GetContext();
2711     CHECK_NULL_VOID(pipelineContext);
2712     auto selectTheme = pipelineContext->GetTheme<SelectTheme>();
2713     CHECK_NULL_VOID(selectTheme);
2714     auto textAlign = static_cast<TextAlign>(selectTheme->GetOptionContentNormalAlign());
2715     if (textAlign == TextAlign::CENTER) {
2716         for (const auto& optionWrapper : optionsLayoutWrapper) {
2717             optionWrapper->Measure(optionConstraint);
2718         }
2719     }
2720     constraint.minSize.SetWidth(maxChildrenWidth + optionPadding_ * 2.0f);
2721 }
2722 
GetOptionsLayoutWrappper(LayoutWrapper * layoutWrapper)2723 std::list<RefPtr<LayoutWrapper>> MenuLayoutAlgorithm::GetOptionsLayoutWrappper(LayoutWrapper* layoutWrapper)
2724 {
2725     std::list<RefPtr<LayoutWrapper>> optionsWrapper;
2726     auto scrollWrapper = layoutWrapper->GetOrCreateChildByIndex(0);
2727     CHECK_NULL_RETURN(scrollWrapper, optionsWrapper);
2728     auto columnWrapper = scrollWrapper->GetOrCreateChildByIndex(0);
2729     CHECK_NULL_RETURN(columnWrapper, optionsWrapper);
2730     optionsWrapper = columnWrapper->GetAllChildrenWithBuild();
2731     return optionsWrapper;
2732 }
2733 
UpdateOptionConstraint(std::list<RefPtr<LayoutWrapper>> & options,float width)2734 void MenuLayoutAlgorithm::UpdateOptionConstraint(std::list<RefPtr<LayoutWrapper>>& options, float width)
2735 {
2736     for (const auto& option : options) {
2737         auto optionLayoutProps = option->GetLayoutProperty();
2738         CHECK_NULL_VOID(optionLayoutProps);
2739         optionLayoutProps->UpdateCalcMinSize(CalcSize(CalcLength(width), std::nullopt));
2740     }
2741 }
2742 
2743 // return vertical offset
VerticalLayout(const SizeF & size,float position,bool isContextMenu)2744 float MenuLayoutAlgorithm::VerticalLayout(const SizeF& size, float position, bool isContextMenu)
2745 {
2746     placement_ = Placement::BOTTOM;
2747     // can put menu below click point
2748     if (GreatOrEqual(bottomSpace_, size.Height())) {
2749         return position + margin_;
2750     }
2751     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) && isContextMenu) {
2752         if (LessNotEqual(bottomSpace_, size.Height()) && LessNotEqual(size.Height(), wrapperRect_.Height())) {
2753             return wrapperRect_.Bottom() - size.Height() - paddingBottom_;
2754         }
2755         // can't fit in screen, line up with top of the screen
2756         return wrapperRect_.Top() + paddingTop_;
2757     } else if (anchorPosition_.has_value()) {
2758         // When the component height is less than the bottom margin and the menu height can be lowered,
2759         // or when the anchor point y coordinate is at the bottom of the screen and
2760         // the anchorPosition_ has a set value, the menu should be placed at the bottom of the screen.
2761         bool isOutBottom = GreatOrEqual(position + anchorPosition_->GetY() + size.Height(), wrapperRect_.Bottom());
2762         if (isOutBottom) {
2763             if (((LessNotEqual(bottomSpace_, size.Height()) || GreatOrEqual(position, wrapperRect_.Bottom())) &&
2764                 LessNotEqual(size.Height(), wrapperRect_.Height()))) {
2765                 return wrapperRect_.Bottom() - size.Height() - paddingBottom_;
2766             }
2767             // can't fit in screen, line up with top of the screen
2768             return wrapperRect_.Top() + paddingTop_;
2769         } else {
2770             return CalcVerticalPosition(size);
2771         }
2772     } else {
2773         return CalcVerticalPosition(size);
2774     }
2775 }
2776 
CalcVerticalPosition(const SizeF & size)2777 float MenuLayoutAlgorithm::CalcVerticalPosition(const SizeF& size)
2778 {
2779     float wrapperHeight = wrapperSize_.Height();
2780     // put menu above click point
2781     if (GreatOrEqual(topSpace_, size.Height())) {
2782         // menu show on top
2783         placement_ = Placement::TOP;
2784         return topSpace_ - size.Height() + margin_;
2785     }
2786     // line up bottom of menu with bottom of the screen
2787     if (LessNotEqual(size.Height(), wrapperHeight)) {
2788         return wrapperHeight - size.Height();
2789     }
2790     // can't fit in screen, line up with top of the screen
2791     return 0.0f;
2792 }
2793 
2794 // returns horizontal offset
HorizontalLayout(const SizeF & size,float position,bool isSelectMenu)2795 float MenuLayoutAlgorithm::HorizontalLayout(const SizeF& size, float position, bool isSelectMenu)
2796 {
2797     float wrapperWidth = wrapperSize_.Width();
2798     // can fit menu on the right side of position
2799     if (GreatOrEqual(rightSpace_, size.Width())) {
2800         return position + margin_;
2801     }
2802 
2803     // fit menu on the left side
2804     if (!isSelectMenu && GreatOrEqual(leftSpace_, size.Width())) {
2805         return position - size.Width();
2806     }
2807 
2808     // line up right side of menu with right boundary of the screen
2809     if (LessNotEqual(size.Width(), wrapperWidth)) {
2810         if (isSelectMenu) {
2811             return position + margin_;
2812         }
2813         return wrapperWidth - size.Width();
2814     }
2815 
2816     // can't fit in screen, line up with left side of the screen
2817     return 0.0f;
2818 }
2819 
GetPositionWithPlacement(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2820 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacement(
2821     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2822 {
2823     OffsetF childPosition;
2824 
2825     auto func = placementFuncMap_.find(placement_);
2826     if (func != placementFuncMap_.end()) {
2827         auto placementFunc = func->second;
2828         if (placementFunc != nullptr) {
2829             childPosition = (this->*placementFunc)(childSize, topPosition, bottomPosition);
2830         }
2831     }
2832     return childPosition;
2833 }
2834 
GetArrowPositionWithPlacement(const SizeF & menuSize,const LayoutWrapper * layoutWrapper)2835 OffsetF MenuLayoutAlgorithm::GetArrowPositionWithPlacement(const SizeF& menuSize, const LayoutWrapper* layoutWrapper)
2836 {
2837     UpdateArrowOffsetWithMenuLimit(menuSize, layoutWrapper);
2838     auto addArrowOffsetToArrowMin = arrowOffset_ + arrowMinLimit_;
2839     auto space_ = ARROW_HIGHT.ConvertToPx();
2840     OffsetF childPosition;
2841     switch (arrowPlacement_) {
2842         case Placement::TOP:
2843         case Placement::TOP_LEFT:
2844         case Placement::TOP_RIGHT:
2845             childPosition = OffsetF(addArrowOffsetToArrowMin, menuSize.Height() + space_);
2846             break;
2847         case Placement::BOTTOM:
2848         case Placement::BOTTOM_LEFT:
2849         case Placement::BOTTOM_RIGHT:
2850             childPosition = OffsetF(addArrowOffsetToArrowMin, 0);
2851             break;
2852         case Placement::LEFT:
2853         case Placement::LEFT_TOP:
2854         case Placement::LEFT_BOTTOM:
2855             childPosition = OffsetF(menuSize.Width() + space_, addArrowOffsetToArrowMin);
2856             break;
2857         case Placement::RIGHT:
2858         case Placement::RIGHT_TOP:
2859         case Placement::RIGHT_BOTTOM:
2860             childPosition = OffsetF(0, addArrowOffsetToArrowMin);
2861             break;
2862         default:
2863             break;
2864     }
2865     return childPosition;
2866 }
2867 
GetMenuWrapperOffset(const LayoutWrapper * layoutWrapper)2868 OffsetF MenuLayoutAlgorithm::GetMenuWrapperOffset(const LayoutWrapper* layoutWrapper)
2869 {
2870     CHECK_NULL_RETURN(layoutWrapper, OffsetF());
2871     auto menuNode = layoutWrapper->GetHostNode();
2872     CHECK_NULL_RETURN(menuNode, OffsetF());
2873     auto menuLayoutProperty = layoutWrapper->GetLayoutProperty();
2874     if (menuLayoutProperty && menuLayoutProperty->GetNonAutoLayoutDirection() == TextDirection::RTL) {
2875         return menuNode->GetPaintRectOffset(true, true);
2876     }
2877     return menuNode->GetParentGlobalOffsetDuringLayout();
2878 }
2879 
NeedHoldTargetOffset(const RefPtr<FrameNode> & targetNode,const RefPtr<MenuPattern> & menuPattern)2880 bool MenuLayoutAlgorithm::NeedHoldTargetOffset(
2881     const RefPtr<FrameNode>& targetNode, const RefPtr<MenuPattern>& menuPattern)
2882 {
2883     CHECK_NULL_RETURN(menuPattern, false);
2884     if (!menuPattern->GetTargetSize().IsPositive()) {
2885         return false;
2886     }
2887     auto menuWrapper = menuPattern->GetMenuWrapper();
2888     CHECK_NULL_RETURN(menuWrapper, false);
2889     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
2890     CHECK_NULL_RETURN(menuWrapperPattern, false);
2891     if (menuWrapperPattern->IsHide()) {
2892         TAG_LOGI(AceLogTag::ACE_MENU, "offset hold by menu hide");
2893         return true;
2894     }
2895     if (!targetNode) {
2896         TAG_LOGI(AceLogTag::ACE_MENU, "offset hold by target not exist");
2897         return true;
2898     }
2899     auto targetRenderContext = targetNode->GetRenderContext();
2900     CHECK_NULL_RETURN(targetRenderContext, false);
2901     auto targetOpacity = targetRenderContext->GetOpacityValue(0.0);
2902     if (NearZero(targetOpacity)) {
2903         TAG_LOGI(AceLogTag::ACE_MENU, "offset hold by target opacity");
2904         return true;
2905     }
2906     auto targetVisible = targetNode->IsVisible();
2907     if (!targetVisible) {
2908         TAG_LOGI(AceLogTag::ACE_MENU, "offset hold by target unvisible");
2909         return true;
2910     }
2911     auto targetSize = targetNode->GetPaintRectWithTransform().GetSize();
2912     if (!targetSize.IsPositive()) {
2913         TAG_LOGI(AceLogTag::ACE_MENU, "offset hold by target size");
2914         return true;
2915     }
2916     return false;
2917 }
2918 
SkipUpdateTargetNodeSize(const RefPtr<FrameNode> & targetNode,const RefPtr<MenuPattern> & menuPattern)2919 bool MenuLayoutAlgorithm::SkipUpdateTargetNodeSize(
2920     const RefPtr<FrameNode>& targetNode, const RefPtr<MenuPattern>& menuPattern)
2921 {
2922     CHECK_NULL_RETURN(menuPattern, false);
2923     if (NeedHoldTargetOffset(targetNode, menuPattern)) {
2924         targetSize_ = menuPattern->GetTargetSize();
2925         targetOffset_ = menuPattern->GetTargetOffset();
2926         return true;
2927     }
2928     return false;
2929 }
2930 
InitTargetSizeAndPosition(const LayoutWrapper * layoutWrapper,bool isContextMenu,const RefPtr<MenuPattern> & menuPattern)2931 void MenuLayoutAlgorithm::InitTargetSizeAndPosition(
2932     const LayoutWrapper* layoutWrapper, bool isContextMenu, const RefPtr<MenuPattern>& menuPattern)
2933 {
2934     CHECK_NULL_VOID(layoutWrapper && menuPattern);
2935     auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
2936     bool holdTargetOffset = SkipUpdateTargetNodeSize(targetNode, menuPattern);
2937     if (!holdTargetOffset) {
2938         CHECK_NULL_VOID(targetNode);
2939         dumpInfo_.targetNode = targetNode->GetTag();
2940         auto props = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
2941         CHECK_NULL_VOID(props);
2942         if (props->GetIsRectInTargetValue(false)) {
2943             targetSize_ = props->GetTargetSizeValue(SizeF());
2944             targetOffset_ = props->GetMenuOffsetValue(OffsetF());
2945         } else {
2946             targetSize_ = targetNode->GetPaintRectWithTransform().GetSize();
2947             targetOffset_ = targetNode->GetPaintRectOffset(false, true);
2948         }
2949     }
2950     dumpInfo_.targetSize = targetSize_;
2951     dumpInfo_.targetOffset = targetOffset_;
2952     menuPattern->SetTargetSize(targetSize_);
2953     menuPattern->SetTargetOffset(targetOffset_);
2954     TAG_LOGI(AceLogTag::ACE_MENU,
2955         "targetId: %{public}d targetNode: %{public}s, targetSize: %{public}s, targetOffset: %{public}s", targetNodeId_,
2956         targetTag_.c_str(), targetSize_.ToString().c_str(), targetOffset_.ToString().c_str());
2957     auto hostNode = layoutWrapper->GetHostNode();
2958     CHECK_NULL_VOID(hostNode);
2959     auto pipelineContext = DialogManager::GetMainPipelineContext(hostNode);
2960     CHECK_NULL_VOID(pipelineContext);
2961     if (canExpandCurrentWindow_ && targetTag_ != V2::SELECT_ETS_TAG) {
2962         if (!holdTargetOffset) {
2963             ModifyTargetOffset();
2964             menuPattern->SetTargetOffset(targetOffset_);
2965         }
2966         OffsetF offset = GetMenuWrapperOffset(layoutWrapper);
2967         targetOffset_ -= offset;
2968         return;
2969     }
2970 
2971     auto windowManager = pipelineContext->GetWindowManager();
2972     auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL && windowManager &&
2973                             windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING;
2974     if (isContainerModal) {
2975         auto newOffsetX = static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx());
2976         auto newOffsetY = static_cast<float>(pipelineContext->GetCustomTitleHeight().ConvertToPx()) +
2977                           static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx());
2978         targetOffset_ -= OffsetF(newOffsetX, newOffsetY);
2979     } else {
2980         OffsetF offset = GetMenuWrapperOffset(layoutWrapper);
2981         targetOffset_ -= offset;
2982     }
2983 }
2984 
FitToScreen(const OffsetF & position,const SizeF & childSize,bool didNeedArrow)2985 OffsetF MenuLayoutAlgorithm::FitToScreen(const OffsetF& position, const SizeF& childSize, bool didNeedArrow)
2986 {
2987     OffsetF afterOffsetPosition;
2988     auto originPosition = position;
2989 
2990     // add space between targetNode and menu using default value or user-set offset
2991     if (NearEqual(positionOffset_, OffsetF(0.0f, 0.0f))) {
2992         afterOffsetPosition = AddTargetSpace(originPosition);
2993     } else {
2994         afterOffsetPosition = AddOffset(originPosition);
2995     }
2996 
2997     if (!CheckPosition(afterOffsetPosition, childSize) || flag_) {
2998         flag_ = false;
2999         return OffsetF(0.0f, 0.0f);
3000     }
3001 
3002     return afterOffsetPosition;
3003 }
3004 
GetChildPosition(const SizeF & childSize,bool didNeedArrow)3005 OffsetF MenuLayoutAlgorithm::GetChildPosition(const SizeF& childSize, bool didNeedArrow)
3006 {
3007     // add space between arrow and targetNode
3008     OffsetF bottomPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
3009         targetOffset_.GetY() + targetSize_.Height() + targetSpace_);
3010     OffsetF topPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
3011         targetOffset_.GetY() - childSize.Height() - targetSpace_);
3012     // when failed to find place for menu, put menu above targetNode with center-aligned
3013     OffsetF defaultPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
3014         targetOffset_.GetY() + (targetSize_.Height() - childSize.Height()) / 2.0);
3015 
3016     OffsetF childPosition;
3017     OffsetF position = defaultPosition;
3018     auto positionOffset = positionOffset_;
3019     std::vector<Placement> currentPlacementStates = PLACEMENT_STATES.find(Placement::BOTTOM_LEFT)->second;
3020     auto it = PLACEMENT_STATES.find(placement_);
3021     if (it != PLACEMENT_STATES.end()) {
3022         currentPlacementStates = it->second;
3023     }
3024     size_t step = ALIGNMENT_STEP_OFFSET;
3025     if (placement_ <= Placement::BOTTOM) {
3026         step += 1;
3027     }
3028     for (size_t i = 0, len = currentPlacementStates.size(); i < len; i++) {
3029         placement_ = currentPlacementStates[i];
3030         if (placement_ == Placement::NONE) {
3031             break;
3032         }
3033         if (i >= step) {
3034             positionOffset_ = OffsetF(0.0f, 0.0f);
3035         }
3036         childPosition = GetPositionWithPlacement(childSize, topPosition, bottomPosition);
3037         position = FitToScreen(childPosition, childSize, didNeedArrow);
3038         if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
3039             continue;
3040         }
3041         break;
3042     }
3043     if (placement_ == Placement::NONE) {
3044         position = GetAdjustPosition(currentPlacementStates, step, childSize, topPosition, bottomPosition);
3045         if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
3046             position = defaultPosition;
3047         }
3048     }
3049     positionOffset_ = positionOffset;
3050     arrowPlacement_ = placement_;
3051 
3052     return position;
3053 }
3054 
GetSelectChildPosition(const SizeF & childSize,bool didNeedArrow,LayoutWrapper * layoutWrapper)3055 OffsetF MenuLayoutAlgorithm::GetSelectChildPosition(const SizeF& childSize, bool didNeedArrow,
3056     LayoutWrapper* layoutWrapper)
3057 {
3058     OffsetF bottomPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / HALF,
3059         targetOffset_.GetY() + targetSize_.Height() + targetSpace_);
3060     OffsetF topPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / HALF,
3061         targetOffset_.GetY() - childSize.Height() - targetSpace_);
3062 
3063     OffsetF childPosition;
3064     OffsetF position;
3065     std::vector<Placement> currentPlacementStates = SELECT_PLACEMENT_STATES.find(Placement::BOTTOM_LEFT)->second;
3066     auto it = SELECT_PLACEMENT_STATES.find(placement_);
3067     if (it != SELECT_PLACEMENT_STATES.end()) {
3068         currentPlacementStates = it->second;
3069     }
3070     for (size_t i = 0, len = currentPlacementStates.size(); i < len; i++) {
3071         placement_ = currentPlacementStates[i];
3072         if (placement_ == Placement::NONE) {
3073             break;
3074         }
3075         childPosition = GetPositionWithPlacement(childSize, topPosition, bottomPosition);
3076         position = FitToScreen(childPosition, childSize, didNeedArrow);
3077         if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
3078             continue;
3079         }
3080         break;
3081     }
3082     if (placement_ == Placement::NONE) {
3083         position = GetSelectAdjustPosition(currentPlacementStates, childSize, topPosition, bottomPosition);
3084     }
3085     return position;
3086 }
3087 
GetAdjustPosition(std::vector<Placement> & currentPlacementStates,size_t step,const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3088 OffsetF MenuLayoutAlgorithm::GetAdjustPosition(std::vector<Placement>& currentPlacementStates, size_t step,
3089     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3090 {
3091     OffsetF childPosition;
3092     OffsetF position;
3093     size_t len = currentPlacementStates.size();
3094     for (size_t i = 0; i < len;) {
3095         placement_ = currentPlacementStates[i];
3096         if (placement_ == Placement::NONE) {
3097             break;
3098         }
3099         childPosition = GetPositionWithPlacement(childSize, topPosition, bottomPosition);
3100         position = AdjustPosition(childPosition, childSize.Width(), childSize.Height(), targetSecurity_);
3101         if (!NearEqual(position, OffsetF(0.0f, 0.0f))) {
3102             break;
3103         }
3104         if (len - i <= step) {
3105             break;
3106         }
3107         i += step;
3108     }
3109     return position;
3110 }
3111 
GetSelectAdjustPosition(std::vector<Placement> & currentPlacementStates,const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3112 OffsetF MenuLayoutAlgorithm::GetSelectAdjustPosition(std::vector<Placement>& currentPlacementStates,
3113     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3114 {
3115     OffsetF childPosition;
3116     OffsetF position;
3117     Placement firstPlacement = currentPlacementStates[0];
3118     size_t step = firstPlacement == Placement::BOTTOM ? ALIGNMENT_STEP_OFFSET + 1 : ALIGNMENT_STEP_OFFSET;
3119 
3120     for (size_t i = 0, len = currentPlacementStates.size(); i < len;) {
3121         placement_ = currentPlacementStates[i];
3122         if (placement_ == Placement::NONE) {
3123             break;
3124         }
3125         if (firstPlacement == Placement::BOTTOM && placement_ == Placement::RIGHT_TOP) {
3126             step = ALIGNMENT_STEP_OFFSET;
3127         }
3128         childPosition = GetPositionWithPlacement(childSize, topPosition, bottomPosition);
3129         position = AdjustPosition(childPosition, childSize.Width(), childSize.Height(), targetSecurity_);
3130         if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
3131             i += step;
3132             continue;
3133         }
3134         break;
3135     }
3136     return position;
3137 }
3138 
AdjustPosition(const OffsetF & position,float width,float height,float space)3139 OffsetF MenuLayoutAlgorithm::AdjustPosition(const OffsetF& position, float width, float height, float space)
3140 {
3141     float xMax = 0.0f;
3142     float yMax = 0.0f;
3143     float xMin = wrapperRect_.Left() + paddingStart_;
3144     float yMin = std::max(1.0f, static_cast<float>(wrapperRect_.Top()) + paddingTop_);
3145     float wrapperRight = wrapperRect_.Right();
3146     float wrapperBottom = wrapperRect_.Bottom();
3147     switch (placement_) {
3148         case Placement::LEFT_TOP:
3149         case Placement::LEFT_BOTTOM:
3150         case Placement::LEFT: {
3151             xMax = std::min(targetOffset_.GetX() - width - space, wrapperRight - paddingEnd_ - width);
3152             yMax = wrapperBottom - height - paddingBottom_;
3153             break;
3154         }
3155         case Placement::RIGHT_TOP:
3156         case Placement::RIGHT_BOTTOM:
3157         case Placement::RIGHT: {
3158             xMin = std::max(targetOffset_.GetX() + targetSize_.Width() + space, xMin);
3159             xMax = wrapperRight - width - paddingEnd_;
3160             yMax = wrapperBottom - height - paddingBottom_;
3161             break;
3162         }
3163         case Placement::TOP_LEFT:
3164         case Placement::TOP_RIGHT:
3165         case Placement::TOP: {
3166             xMax = wrapperRight - width - paddingEnd_;
3167             yMax = std::min(targetOffset_.GetY() - height - space, wrapperBottom - paddingBottom_ - height);
3168             break;
3169         }
3170         case Placement::BOTTOM_LEFT:
3171         case Placement::BOTTOM_RIGHT:
3172         case Placement::BOTTOM: {
3173             xMax = wrapperRight - width - paddingEnd_;
3174             yMin = std::max(targetOffset_.GetY() + targetSize_.Height() + space, yMin);
3175             yMax = wrapperBottom - height - paddingBottom_;
3176             break;
3177         }
3178         default:
3179             break;
3180     }
3181     if (LessNotEqual(xMax, xMin) || LessNotEqual(yMax, yMin)) {
3182         return OffsetF(0.0f, 0.0f);
3183     }
3184     auto x = std::clamp(position.GetX(), xMin, xMax);
3185     auto y = std::clamp(position.GetY(), yMin, yMax);
3186     return OffsetF(x, y);
3187 }
3188 
AddTargetSpace(const OffsetF & position)3189 OffsetF MenuLayoutAlgorithm::AddTargetSpace(const OffsetF& position)
3190 {
3191     auto x = position.GetX();
3192     auto y = position.GetY();
3193     switch (placement_) {
3194         case Placement::BOTTOM_LEFT:
3195         case Placement::BOTTOM_RIGHT:
3196         case Placement::BOTTOM: {
3197             y += targetSecurity_;
3198             break;
3199         }
3200         case Placement::TOP_LEFT:
3201         case Placement::TOP_RIGHT:
3202         case Placement::TOP: {
3203             y -= targetSecurity_;
3204             break;
3205         }
3206         case Placement::RIGHT_TOP:
3207         case Placement::RIGHT_BOTTOM:
3208         case Placement::RIGHT: {
3209             x += targetSecurity_;
3210             break;
3211         }
3212         case Placement::LEFT_TOP:
3213         case Placement::LEFT_BOTTOM:
3214         case Placement::LEFT: {
3215             x -= targetSecurity_;
3216             break;
3217         }
3218         default: {
3219             y += targetSecurity_;
3220             break;
3221         }
3222     }
3223     return OffsetF(x, y);
3224 }
3225 
AddOffset(const OffsetF & position)3226 OffsetF MenuLayoutAlgorithm::AddOffset(const OffsetF& position)
3227 {
3228     auto x = position.GetX();
3229     auto y = position.GetY();
3230     switch (placement_) {
3231         case Placement::BOTTOM_LEFT:
3232         case Placement::BOTTOM_RIGHT:
3233         case Placement::BOTTOM: {
3234             x += positionOffset_.GetX();
3235             y += positionOffset_.GetY();
3236             break;
3237         }
3238         case Placement::TOP_LEFT:
3239         case Placement::TOP_RIGHT:
3240         case Placement::TOP: {
3241             x += positionOffset_.GetX();
3242             y -= positionOffset_.GetY();
3243             break;
3244         }
3245         case Placement::RIGHT_TOP:
3246         case Placement::RIGHT_BOTTOM:
3247         case Placement::RIGHT: {
3248             x += positionOffset_.GetX();
3249             y += positionOffset_.GetY();
3250             break;
3251         }
3252         case Placement::LEFT_TOP:
3253         case Placement::LEFT_BOTTOM:
3254         case Placement::LEFT: {
3255             x -= positionOffset_.GetX();
3256             y += positionOffset_.GetY();
3257             break;
3258         }
3259         default: {
3260             x += positionOffset_.GetX();
3261             y += positionOffset_.GetY();
3262             break;
3263         }
3264     }
3265     return OffsetF(x, y);
3266 }
3267 
CheckPositionInPlacementRect(const Rect & rect,const OffsetF & position,const SizeF & childSize)3268 bool MenuLayoutAlgorithm::CheckPositionInPlacementRect(
3269     const Rect& rect, const OffsetF& position, const SizeF& childSize)
3270 {
3271     auto x = position.GetX();
3272     auto y = position.GetY();
3273     auto leftOutside = LessNotEqual(x, rect.Left());
3274     auto rightOutside = GreatNotEqual(x + childSize.Width(), rect.Right());
3275     auto topOutside = LessNotEqual(y, rect.Top());
3276     auto bottomOutside = GreatNotEqual(y + childSize.Height(), rect.Bottom());
3277     TAG_LOGI(AceLogTag::ACE_MENU,
3278         "CheckPositionInPlacementRect, leftOutside : %{public}d, rightOutside : %{public}d, topOutside : %{public}d, "
3279         "bottomOutside : %{public}d",
3280         leftOutside, rightOutside, topOutside, bottomOutside);
3281     auto outside = leftOutside || rightOutside || topOutside || bottomOutside;
3282     if (state_ != prevState_) {
3283         if (prevState_ == -1) {
3284             prevState_ = state_;
3285             preOffset_ = position;
3286             preRect_ = rect;
3287             if (!outside) {
3288                 return true;
3289             }
3290             flag_ = true;
3291             positionOffset_ = { 0.0f, 0.0f };
3292             return false;
3293         }
3294         return CheckPlacement(childSize);
3295     }
3296     if (outside) {
3297         preOffset_ = position;
3298         preRect_ = rect;
3299         return false;
3300     }
3301     return true;
3302 }
3303 
CheckPlacement(const SizeF & childSize)3304 bool MenuLayoutAlgorithm::CheckPlacement(const SizeF& childSize)
3305 {
3306     auto x = preOffset_.GetX();
3307     auto y = preOffset_.GetY();
3308     auto leftOutside = LessNotEqual(x, preRect_.Left());
3309     auto rightOutside = GreatNotEqual(x + childSize.Width(), preRect_.Right());
3310     auto topOutside = LessNotEqual(y, preRect_.Top());
3311     auto bottomOutside = GreatNotEqual(y + childSize.Height(), preRect_.Bottom());
3312     TAG_LOGI(AceLogTag::ACE_MENU,
3313         "CheckPlacement, leftOutside : %{public}d, rightOutside : %{public}d, topOutside : %{public}d, "
3314         "bottomOutside : %{public}d",
3315         leftOutside, rightOutside, topOutside, bottomOutside);
3316 
3317     switch (prevState_) {
3318         case static_cast<int>(DirectionState::Bottom_Direction):
3319         case static_cast<int>(DirectionState::Top_Direction): {
3320             if ((leftOutside || rightOutside) && !(topOutside || bottomOutside)) {
3321                 placement_ = Placement::NONE;
3322                 return true;
3323             }
3324             break;
3325         }
3326         case static_cast<int>(DirectionState::Right_Direction):
3327         case static_cast<int>(DirectionState::Left_Direction): {
3328             if ((topOutside || bottomOutside) && !(leftOutside || rightOutside)) {
3329                 placement_ = Placement::NONE;
3330                 return true;
3331             }
3332             break;
3333         }
3334         default:
3335             return false;
3336     }
3337 
3338     return false;
3339 }
3340 
CheckPosition(const OffsetF & position,const SizeF & childSize)3341 bool MenuLayoutAlgorithm::CheckPosition(const OffsetF& position, const SizeF& childSize)
3342 {
3343     float xAvoid = wrapperRect_.Left() + paddingStart_;
3344     float yAvoid = wrapperRect_.Top() + paddingTop_ + param_.topSecurity;
3345     float maxWidth = wrapperSize_.Width() - paddingEnd_ - paddingStart_;
3346     float maxHeight = wrapperSize_.Height() - paddingTop_ - param_.topSecurity - paddingBottom_ - param_.bottomSecurity;
3347     Rect rect;
3348     Rect targetRect = Rect(targetOffset_.GetX(), targetOffset_.GetY(), targetSize_.Width(), targetSize_.Height());
3349     switch (placement_) {
3350         case Placement::BOTTOM_LEFT:
3351         case Placement::BOTTOM_RIGHT:
3352         case Placement::BOTTOM: {
3353             state_ = static_cast<int>(DirectionState::Bottom_Direction);
3354             auto y = std::max<float>(targetRect.Bottom(), yAvoid);
3355             auto height = std::min<float>(
3356                 wrapperRect_.Bottom() - targetRect.Bottom() - paddingBottom_ - param_.bottomSecurity, maxHeight);
3357             rect.SetRect(xAvoid, y, maxWidth, height);
3358             break;
3359         }
3360         case Placement::TOP_LEFT:
3361         case Placement::TOP_RIGHT:
3362         case Placement::TOP: {
3363             state_ = static_cast<int>(DirectionState::Top_Direction);
3364             auto height = std::min<float>(targetRect.Top() - yAvoid, maxHeight);
3365             rect.SetRect(xAvoid, yAvoid, maxWidth, height);
3366             break;
3367         }
3368         case Placement::RIGHT_TOP:
3369         case Placement::RIGHT_BOTTOM:
3370         case Placement::RIGHT: {
3371             state_ = static_cast<int>(DirectionState::Right_Direction);
3372             auto x = std::max<float>(targetRect.Right(), xAvoid);
3373             auto width = std::min<float>(wrapperRect_.Right() - targetRect.Right() - paddingEnd_, maxWidth);
3374             rect.SetRect(x, yAvoid, width, maxHeight);
3375             break;
3376         }
3377         case Placement::LEFT_TOP:
3378         case Placement::LEFT_BOTTOM:
3379         case Placement::LEFT: {
3380             state_ = static_cast<int>(DirectionState::Left_Direction);
3381             auto width = std::min<float>(targetRect.Left() - xAvoid, maxWidth);
3382             rect.SetRect(xAvoid, yAvoid, width, maxHeight);
3383             break;
3384         }
3385         default:
3386             state_ = static_cast<int>(DirectionState::None_Direction);
3387             return false;
3388     }
3389     return CheckPositionInPlacementRect(rect, position, childSize);
3390 }
3391 
GetPositionWithPlacementTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3392 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementTop(
3393     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3394 {
3395     TAG_LOGI(AceLogTag::ACE_MENU, "GetPositionWithPlacementTop : %{public}s", topPosition.ToString().c_str());
3396     return topPosition;
3397 }
3398 
GetPositionWithPlacementTopLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3399 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementTopLeft(
3400     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3401 {
3402     OffsetF childPosition;
3403     float marginRight = 0.0f;
3404     float marginBottom = 0.0f;
3405     childPosition = OffsetF(
3406         targetOffset_.GetX() - marginRight, targetOffset_.GetY() - childSize.Height() - marginBottom - targetSpace_);
3407     TAG_LOGI(AceLogTag::ACE_MENU, "GetPositionWithPlacementTopLeft : %{public}s", childPosition.ToString().c_str());
3408     return childPosition;
3409 }
3410 
GetPositionWithPlacementTopRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3411 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementTopRight(
3412     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3413 {
3414     OffsetF childPosition;
3415     float marginBottom = 0.0f;
3416     float marginLeft = 0.0f;
3417     childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
3418         targetOffset_.GetY() - childSize.Height() - targetSpace_ - marginBottom);
3419     TAG_LOGI(AceLogTag::ACE_MENU, "GetPositionWithPlacementTopRight : %{public}s", childPosition.ToString().c_str());
3420     return childPosition;
3421 }
3422 
GetPositionWithPlacementBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3423 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementBottom(
3424     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3425 {
3426     TAG_LOGI(AceLogTag::ACE_MENU, "GetPositionWithPlacementBottom : %{public}s", bottomPosition.ToString().c_str());
3427     return bottomPosition;
3428 }
3429 
GetPositionWithPlacementBottomLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3430 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementBottomLeft(
3431     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3432 {
3433     OffsetF childPosition;
3434     float marginRight = 0.0f;
3435     float marginTop = 0.0f;
3436     childPosition = OffsetF(
3437         targetOffset_.GetX() - marginRight, targetOffset_.GetY() + targetSize_.Height() + targetSpace_ + marginTop);
3438     TAG_LOGI(AceLogTag::ACE_MENU, "GetPositionWithPlacementBottomLeft : %{public}s", childPosition.ToString().c_str());
3439     return childPosition;
3440 }
3441 
GetPositionWithPlacementBottomRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3442 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementBottomRight(
3443     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3444 {
3445     OffsetF childPosition;
3446     float marginTop = 0.0f;
3447     float marginLeft = 0.0f;
3448     childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
3449         targetOffset_.GetY() + targetSize_.Height() + targetSpace_ + marginTop);
3450     TAG_LOGI(AceLogTag::ACE_MENU, "GetPositionWithPlacementBottomRight : %{public}s", childPosition.ToString().c_str());
3451     return childPosition;
3452 }
3453 
GetPositionWithPlacementLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3454 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementLeft(
3455     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3456 {
3457     OffsetF childPosition;
3458     float marginRight = 0.0f;
3459     childPosition = OffsetF(targetOffset_.GetX() - targetSpace_ - childSize.Width() - marginRight,
3460         targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
3461     TAG_LOGI(AceLogTag::ACE_MENU, "GetPositionWithPlacementLeft : %{public}s", childPosition.ToString().c_str());
3462     return childPosition;
3463 }
3464 
GetPositionWithPlacementLeftTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3465 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementLeftTop(
3466     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3467 {
3468     OffsetF childPosition;
3469     float marginRight = 0.0f;
3470     float marginBottom = 0.0f;
3471     childPosition = OffsetF(
3472         targetOffset_.GetX() - targetSpace_ - childSize.Width() - marginRight, targetOffset_.GetY() - marginBottom);
3473     TAG_LOGI(AceLogTag::ACE_MENU, "GetPositionWithPlacementLeftTop : %{public}s", childPosition.ToString().c_str());
3474     return childPosition;
3475 }
3476 
GetPositionWithPlacementLeftBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3477 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementLeftBottom(
3478     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3479 {
3480     OffsetF childPosition;
3481     float marginRight = 0.0f;
3482     float marginTop = 0.0f;
3483     childPosition = OffsetF(targetOffset_.GetX() - targetSpace_ - childSize.Width() - marginRight,
3484         targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
3485     TAG_LOGI(AceLogTag::ACE_MENU, "GetPositionWithPlacementLeftBottom : %{public}s", childPosition.ToString().c_str());
3486     return childPosition;
3487 }
3488 
GetPositionWithPlacementRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3489 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementRight(
3490     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3491 {
3492     OffsetF childPosition;
3493     float marginLeft = 0.0f;
3494     childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_ + marginLeft,
3495         targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
3496     TAG_LOGI(AceLogTag::ACE_MENU, "GetPositionWithPlacementRight : %{public}s", childPosition.ToString().c_str());
3497     return childPosition;
3498 }
3499 
GetPositionWithPlacementRightTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3500 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementRightTop(
3501     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3502 {
3503     OffsetF childPosition;
3504     float marginBottom = 0.0f;
3505     float marginLeft = 0.0f;
3506     childPosition = OffsetF(
3507         targetOffset_.GetX() + targetSize_.Width() + targetSpace_ + marginLeft, targetOffset_.GetY() - marginBottom);
3508     TAG_LOGI(AceLogTag::ACE_MENU, "GetPositionWithPlacementRightTop : %{public}s", childPosition.ToString().c_str());
3509     return childPosition;
3510 }
3511 
GetPositionWithPlacementRightBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3512 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementRightBottom(
3513     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3514 {
3515     OffsetF childPosition;
3516     float marginTop = 0.0f;
3517     float marginLeft = 0.0f;
3518     childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_ + marginLeft,
3519         targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
3520     TAG_LOGI(AceLogTag::ACE_MENU, "GetPositionWithPlacementRightBottom : %{public}s", childPosition.ToString().c_str());
3521     return childPosition;
3522 }
3523 
InitCanExpandCurrentWindow(bool isShowInSubWindow,LayoutWrapper * layoutWrapper)3524 void MenuLayoutAlgorithm::InitCanExpandCurrentWindow(bool isShowInSubWindow, LayoutWrapper* layoutWrapper)
3525 {
3526     CHECK_NULL_VOID(layoutWrapper);
3527     auto hostNode = layoutWrapper->GetHostNode();
3528     CHECK_NULL_VOID(hostNode);
3529     auto pipelineContext = DialogManager::GetMainPipelineContext(hostNode);
3530     CHECK_NULL_VOID(pipelineContext);
3531     auto containerId = Container::CurrentId();
3532     auto container = AceEngine::Get().GetContainer(containerId);
3533     if (containerId >= MIN_SUBCONTAINER_ID) {
3534         auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(containerId);
3535         container = AceEngine::Get().GetContainer(parentContainerId);
3536     }
3537     CHECK_NULL_VOID(container);
3538     // Get FreeMultiWindow status of main window or host window
3539     isFreeMultiWindow_ = container->IsFreeMultiWindow();
3540     auto theme = pipelineContext->GetTheme<SelectTheme>();
3541     CHECK_NULL_VOID(theme);
3542     // false for phone devices
3543     isExpandDisplay_ = theme->GetExpandDisplay() || isFreeMultiWindow_;
3544     auto isUIExtensionSubWindow = container->IsUIExtensionWindow();
3545     if ((isExpandDisplay_ || isUIExtensionSubWindow) && !isShowInSubWindow && containerId >= MIN_SUBCONTAINER_ID) {
3546         canExpandCurrentWindow_ = true;
3547         isTargetNodeInSubwindow_ = true;
3548         return;
3549     }
3550     canExpandCurrentWindow_ = isExpandDisplay_ && isShowInSubWindow;
3551     if (containerId >= MIN_SUBCONTAINER_ID) {
3552         isUIExtensionSubWindow_ = container->IsUIExtensionWindow();
3553         if (isUIExtensionSubWindow_) {
3554             canExpandCurrentWindow_ = true;
3555             auto subwindow = SubwindowManager::GetInstance()->GetSubwindowByType(
3556                 containerId, SubwindowType::TYPE_MENU);
3557             CHECK_NULL_VOID(subwindow);
3558             auto rect = subwindow->GetUIExtensionHostWindowRect();
3559             UIExtensionHostWindowRect_ = RectF(rect.Left(), rect.Top(), rect.Width(), rect.Height());
3560             TAG_LOGI(AceLogTag::ACE_MENU, "GetUIExtensionHostWindowRect : %{public}s",
3561                 UIExtensionHostWindowRect_.ToString().c_str());
3562         }
3563     }
3564 }
3565 
GetMenuWindowRectInfo(const RefPtr<MenuPattern> & menuPattern)3566 Rect MenuLayoutAlgorithm::GetMenuWindowRectInfo(const RefPtr<MenuPattern>& menuPattern)
3567 {
3568     auto menuWindowRect = Rect();
3569     CHECK_NULL_RETURN(menuPattern, menuWindowRect);
3570     auto host = menuPattern->GetHost();
3571     CHECK_NULL_RETURN(host, menuWindowRect);
3572     auto pipelineContext = DialogManager::GetMainPipelineContext(host, isTargetNodeInSubwindow_);
3573     CHECK_NULL_RETURN(pipelineContext, menuWindowRect);
3574     auto rect = pipelineContext->GetDisplayWindowRectInfo();
3575     displayWindowRect_ = RectF(rect.Left(), rect.Top(), rect.Width(), rect.Height());
3576     TAG_LOGI(AceLogTag::ACE_MENU, "GetDisplayWindowRectInfo : %{public}s", displayWindowRect_.ToString().c_str());
3577     menuWindowRect = Rect(rect.Left(), rect.Top(), rect.Width(), rect.Height());
3578     auto availableRect = OverlayManager::GetDisplayAvailableRect(
3579         menuPattern->GetHost(), static_cast<int32_t>(SubwindowType::TYPE_MENU));
3580     TAG_LOGI(AceLogTag::ACE_MENU, "GetDisplayAvailableRect : %{public}s", availableRect.ToString().c_str());
3581     if (canExpandCurrentWindow_ && isExpandDisplay_) {
3582         menuWindowRect = Rect(availableRect.Left(), availableRect.Top(), availableRect.Width(), availableRect.Height());
3583     } else if (isUIExtensionSubWindow_ && !isExpandDisplay_) {
3584         rect = Rect(UIExtensionHostWindowRect_.Left(), UIExtensionHostWindowRect_.Top(),
3585             UIExtensionHostWindowRect_.Width(), UIExtensionHostWindowRect_.Height());
3586         menuWindowRect = rect;
3587     }
3588     TAG_LOGI(AceLogTag::ACE_MENU, "GetMenuWindowRectInfo : %{public}s", menuWindowRect.ToString().c_str());
3589     dumpInfo_.menuWindowRect = menuWindowRect;
3590     menuPattern->SetMenuWindowRect(menuWindowRect);
3591     return menuWindowRect;
3592 }
3593 
ModifyTargetOffset()3594 void MenuLayoutAlgorithm::ModifyTargetOffset()
3595 {
3596     TAG_LOGI(AceLogTag::ACE_MENU, "original targetOffset is : %{public}s", targetOffset_.ToString().c_str());
3597     if (canExpandCurrentWindow_ && isExpandDisplay_ && !isTargetNodeInSubwindow_) {
3598         targetOffset_ += displayWindowRect_.GetOffset();
3599         TAG_LOGI(AceLogTag::ACE_MENU, "ModifyTargetOffset for displayAvailableRect : %{public}s",
3600             targetOffset_.ToString().c_str());
3601     } else if (isUIExtensionSubWindow_ && !isExpandDisplay_) {
3602         targetOffset_ += displayWindowRect_.GetOffset() - UIExtensionHostWindowRect_.GetOffset();
3603         TAG_LOGI(AceLogTag::ACE_MENU, "ModifyTargetOffset for UIExtensionHostWindowRect : %{public}s",
3604             targetOffset_.ToString().c_str());
3605     }
3606 }
3607 
HoldEmbeddedMenuPosition(LayoutWrapper * layoutWrapper)3608 bool MenuLayoutAlgorithm::HoldEmbeddedMenuPosition(LayoutWrapper* layoutWrapper)
3609 {
3610     auto menuNode = layoutWrapper->GetHostNode();
3611     CHECK_NULL_RETURN(menuNode, false);
3612     auto menuNodePattern = AceType::DynamicCast<MenuPattern>(menuNode->GetPattern());
3613     CHECK_NULL_RETURN(menuNodePattern, false);
3614     auto menuWrapper = menuNodePattern->GetMenuWrapper();
3615     CHECK_NULL_RETURN(menuWrapper, false);
3616     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
3617     CHECK_NULL_RETURN(menuWrapperPattern, false);
3618     auto innerMenu = menuWrapperPattern->GetMenuChild(menuNode);
3619     CHECK_NULL_RETURN(innerMenu, false);
3620     auto innerMenuPattern = AceType::DynamicCast<MenuPattern>(innerMenu->GetPattern());
3621     CHECK_NULL_RETURN(innerMenuPattern, false);
3622     auto layoutProps = innerMenuPattern->GetLayoutProperty<MenuLayoutProperty>();
3623     CHECK_NULL_RETURN(layoutProps, false);
3624     auto isEmbeddedMenu =
3625         layoutProps->GetExpandingMode().value_or(SubMenuExpandingMode::SIDE) == SubMenuExpandingMode::EMBEDDED;
3626     return isEmbeddedMenu && menuWrapperPattern->GetEmbeddedSubMenuExpandTotalCount() > 0 &&
3627            !menuWrapperPattern->GetForceUpdateEmbeddedMenu();
3628 }
3629 
MoveTo(double x,double y)3630 std::string MenuLayoutAlgorithm::MoveTo(double x, double y)
3631 {
3632     return "M" + std::to_string(x) + " " + std::to_string(y) + " ";
3633 }
3634 
LineTo(double x,double y)3635 std::string MenuLayoutAlgorithm::LineTo(double x, double y)
3636 {
3637     return "L" + std::to_string(x) + " " + std::to_string(y) + " ";
3638 }
3639 
ArcTo(double rx,double ry,double rotation,int32_t arc_flag,double x,double y)3640 std::string MenuLayoutAlgorithm::ArcTo(double rx, double ry, double rotation, int32_t arc_flag, double x, double y)
3641 {
3642     int32_t sweep_flag = 1;
3643     return "A" + std::to_string(rx) + " " + std::to_string(ry) + " " + std::to_string(rotation) + " " +
3644            std::to_string(arc_flag) + " " + std::to_string(sweep_flag) + " " + std::to_string(x) + " " +
3645            std::to_string(y) + " ";
3646 }
3647 
BuildBottomArrowPath(float arrowX,float arrowY,std::string & path)3648 void MenuLayoutAlgorithm::BuildBottomArrowPath(float arrowX, float arrowY, std::string& path)
3649 {
3650     path += LineTo(arrowX - ARROW_P1_OFFSET_X.ConvertToPx(), arrowY + ARROW_P1_OFFSET_Y.ConvertToPx()); //P1
3651     path += LineTo(arrowX - ARROW_P2_OFFSET_X.ConvertToPx(), arrowY + ARROW_P2_OFFSET_Y.ConvertToPx()); //P2
3652     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
3653         arrowX + ARROW_P2_OFFSET_X.ConvertToPx(), arrowY + ARROW_P2_OFFSET_Y.ConvertToPx()); //P4
3654     path += LineTo(arrowX + ARROW_P1_OFFSET_X.ConvertToPx(), arrowY + ARROW_P1_OFFSET_Y.ConvertToPx()); //P5
3655 }
3656 
BuildTopArrowPath(float arrowX,float arrowY,std::string & path)3657 void MenuLayoutAlgorithm::BuildTopArrowPath(float arrowX, float arrowY, std::string& path)
3658 {
3659     path += LineTo(arrowX + ARROW_P1_OFFSET_X.ConvertToPx(), arrowY - ARROW_P1_OFFSET_Y.ConvertToPx()); //P1
3660     path += LineTo(arrowX + ARROW_P2_OFFSET_X.ConvertToPx(), arrowY - ARROW_P2_OFFSET_Y.ConvertToPx()); //P2
3661     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
3662         arrowX - ARROW_P2_OFFSET_X.ConvertToPx(), arrowY - ARROW_P2_OFFSET_Y.ConvertToPx()); //P4
3663     path += LineTo(arrowX - ARROW_P1_OFFSET_X.ConvertToPx(), arrowY - ARROW_P1_OFFSET_Y.ConvertToPx()); //P5
3664 }
3665 
BuildRightArrowPath(float arrowX,float arrowY,std::string & path)3666 void MenuLayoutAlgorithm::BuildRightArrowPath(float arrowX, float arrowY, std::string& path)
3667 {
3668     path += LineTo(arrowX + ARROW_P1_OFFSET_Y.ConvertToPx(), arrowY + ARROW_P1_OFFSET_X.ConvertToPx()); //P1
3669     path += LineTo(arrowX + ARROW_P2_OFFSET_Y.ConvertToPx(), arrowY + ARROW_P2_OFFSET_X.ConvertToPx()); //P2
3670     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
3671         arrowX + ARROW_P2_OFFSET_Y.ConvertToPx(), arrowY - ARROW_P2_OFFSET_X.ConvertToPx()); //P4
3672     path += LineTo(arrowX + ARROW_P1_OFFSET_Y.ConvertToPx(), arrowY - ARROW_P1_OFFSET_X.ConvertToPx()); //P5
3673 }
3674 
BuildLeftArrowPath(float arrowX,float arrowY,std::string & path)3675 void MenuLayoutAlgorithm::BuildLeftArrowPath(float arrowX, float arrowY, std::string& path)
3676 {
3677     path += LineTo(arrowX - ARROW_P1_OFFSET_Y.ConvertToPx(), arrowY - ARROW_P1_OFFSET_X.ConvertToPx()); //P1
3678     path += LineTo(arrowX - ARROW_P2_OFFSET_Y.ConvertToPx(), arrowY - ARROW_P2_OFFSET_X.ConvertToPx()); //P2
3679     path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
3680         arrowX - ARROW_P2_OFFSET_Y.ConvertToPx(), arrowY + ARROW_P2_OFFSET_X.ConvertToPx()); //P4
3681     path += LineTo(arrowX - ARROW_P1_OFFSET_Y.ConvertToPx(), arrowY + ARROW_P1_OFFSET_X.ConvertToPx()); //P5
3682 }
3683 
BuildTopLinePath(const OffsetF & arrowPosition,float radiusPx,Placement arrowBuildPlacement,bool didNeedArrow)3684 std::string MenuLayoutAlgorithm::BuildTopLinePath(const OffsetF& arrowPosition, float radiusPx,
3685     Placement arrowBuildPlacement, bool didNeedArrow)
3686 {
3687     std::string path;
3688     if (didNeedArrow) {
3689         switch (arrowBuildPlacement) {
3690             case Placement::BOTTOM:
3691             case Placement::BOTTOM_LEFT:
3692             case Placement::BOTTOM_RIGHT:
3693                 BuildBottomArrowPath(arrowPosition.GetX(), arrowPosition.GetY(), path);
3694                 break;
3695             default:
3696                 break;
3697         }
3698     }
3699     path += LineTo(childOffset_.GetX() + childMarginFrameSize_.Width() - radiusPx, childOffset_.GetY());
3700     path += ArcTo(radiusPx, radiusPx, 0.0f, 0, childOffset_.GetX() + childMarginFrameSize_.Width(),
3701         childOffset_.GetY() + radiusPx);
3702     return path;
3703 }
3704 
BuildRightLinePath(const OffsetF & arrowPosition,float radiusPx,Placement arrowBuildPlacement,bool didNeedArrow)3705 std::string MenuLayoutAlgorithm::BuildRightLinePath(const OffsetF& arrowPosition, float radiusPx,
3706     Placement arrowBuildPlacement, bool didNeedArrow)
3707 {
3708     std::string path;
3709     if (didNeedArrow) {
3710         switch (arrowBuildPlacement) {
3711             case Placement::LEFT:
3712             case Placement::LEFT_TOP:
3713             case Placement::LEFT_BOTTOM:
3714                 BuildLeftArrowPath(arrowPosition.GetX(), arrowPosition.GetY(), path);
3715                 break;
3716             default:
3717                 break;
3718         }
3719     }
3720     path += LineTo(childOffset_.GetX() + childMarginFrameSize_.Width(),
3721         childOffset_.GetY() + childMarginFrameSize_.Height() - radiusPx);
3722     path += ArcTo(radiusPx, radiusPx, 0.0f, 0, childOffset_.GetX() + childMarginFrameSize_.Width() - radiusPx,
3723         childOffset_.GetY() + childMarginFrameSize_.Height());
3724     return path;
3725 }
3726 
BuildBottomLinePath(const OffsetF & arrowPosition,float radiusPx,Placement arrowBuildPlacement,bool didNeedArrow)3727 std::string MenuLayoutAlgorithm::BuildBottomLinePath(const OffsetF& arrowPosition, float radiusPx,
3728     Placement arrowBuildPlacement, bool didNeedArrow)
3729 {
3730     std::string path;
3731     if (didNeedArrow) {
3732         switch (arrowBuildPlacement) {
3733             case Placement::TOP:
3734             case Placement::TOP_LEFT:
3735             case Placement::TOP_RIGHT:
3736                 BuildTopArrowPath(arrowPosition.GetX(), arrowPosition.GetY(), path);
3737                 break;
3738             default:
3739                 break;
3740         }
3741     }
3742     path += LineTo(childOffset_.GetX() + radiusPx, childOffset_.GetY() + childMarginFrameSize_.Height());
3743     path += ArcTo(radiusPx, radiusPx, 0.0f, 0, childOffset_.GetX(),
3744         childOffset_.GetY() + childMarginFrameSize_.Height() - radiusPx);
3745     return path;
3746 }
3747 
BuildLeftLinePath(const OffsetF & arrowPosition,float radiusPx,Placement arrowBuildPlacement,bool didNeedArrow)3748 std::string MenuLayoutAlgorithm::BuildLeftLinePath(const OffsetF& arrowPosition, float radiusPx,
3749     Placement arrowBuildPlacement, bool didNeedArrow)
3750 {
3751     std::string path;
3752     if (didNeedArrow) {
3753         switch (arrowBuildPlacement) {
3754             case Placement::RIGHT:
3755             case Placement::RIGHT_TOP:
3756             case Placement::RIGHT_BOTTOM:
3757                 BuildRightArrowPath(arrowPosition.GetX(), arrowPosition.GetY(), path);
3758                 break;
3759             default:
3760                 break;
3761         }
3762     }
3763     path += LineTo(childOffset_.GetX(), childOffset_.GetY() + radiusPx);
3764     path += ArcTo(radiusPx, radiusPx, 0.0f, 0, childOffset_.GetX() + radiusPx, childOffset_.GetY());
3765     return path;
3766 }
3767 
NormalizeBorderRadius(float & radiusTopLeftPx,float & radiusTopRightPx,float & radiusBottomLeftPx,float & radiusBottomRightPx)3768 void MenuLayoutAlgorithm::NormalizeBorderRadius(float& radiusTopLeftPx, float& radiusTopRightPx,
3769     float& radiusBottomLeftPx, float& radiusBottomRightPx)
3770 {
3771     float childMarginFrameWidth = childMarginFrameSize_.Width();
3772     float childMarginFrameHeight = childMarginFrameSize_.Height();
3773     if (NearZero(childMarginFrameWidth) || NearZero(childMarginFrameHeight)) {
3774         radiusTopLeftPx = 0.0f;
3775         radiusTopRightPx = 0.0f;
3776         radiusBottomLeftPx = 0.0f;
3777         radiusBottomRightPx = 0.0f;
3778         return;
3779     }
3780     float topRatio = (radiusTopLeftPx + radiusTopRightPx) / childMarginFrameWidth;
3781     float bottomRatio = (radiusBottomLeftPx + radiusBottomRightPx) / childMarginFrameWidth;
3782     float leftRatio = (radiusTopLeftPx + radiusBottomLeftPx) / childMarginFrameHeight;
3783     float rightRatio = (radiusTopRightPx + radiusBottomRightPx) / childMarginFrameHeight;
3784     float maxRatio = std::max(std::max(topRatio, bottomRatio), std::max(leftRatio, rightRatio));
3785     if (GreatNotEqual(maxRatio, 1.0f)) {
3786         radiusTopLeftPx = radiusTopLeftPx / maxRatio;
3787         radiusTopRightPx = radiusTopRightPx / maxRatio;
3788         radiusBottomLeftPx = radiusBottomLeftPx / maxRatio;
3789         radiusBottomRightPx = radiusBottomRightPx / maxRatio;
3790     }
3791 }
3792 
CalculateMenuPath(LayoutWrapper * layoutWrapper,bool didNeedArrow)3793 std::string MenuLayoutAlgorithm::CalculateMenuPath(LayoutWrapper* layoutWrapper, bool didNeedArrow)
3794 {
3795     CHECK_NULL_RETURN(layoutWrapper, "");
3796     auto menuNode = layoutWrapper->GetHostNode();
3797     CHECK_NULL_RETURN(menuNode, "");
3798     auto menuPattern = menuNode->GetPattern<MenuPattern>();
3799     CHECK_NULL_RETURN(menuPattern, "");
3800     auto renderContext = menuNode->GetRenderContext();
3801     CHECK_NULL_RETURN(renderContext, "");
3802     auto menuPosition = renderContext->GetPositionValue(OffsetT<Dimension>());
3803     BorderRadiusProperty menuBorderRadius = GetMenuRadius(layoutWrapper, childMarginFrameSize_);
3804     float radiusTopLeftPx = menuBorderRadius.radiusTopLeft.value_or(Dimension())
3805         .ConvertToPxWithSize(childMarginFrameSize_.Width());
3806     float radiusTopRightPx = menuBorderRadius.radiusTopRight.value_or(Dimension())
3807         .ConvertToPxWithSize(childMarginFrameSize_.Width());
3808     float radiusBottomLeftPx = menuBorderRadius.radiusBottomLeft.value_or(Dimension())
3809         .ConvertToPxWithSize(childMarginFrameSize_.Width());
3810     float radiusBottomRightPx = menuBorderRadius.radiusBottomRight.value_or(Dimension())
3811         .ConvertToPxWithSize(childMarginFrameSize_.Width());
3812     NormalizeBorderRadius(radiusTopLeftPx, radiusTopRightPx, radiusBottomLeftPx, radiusBottomRightPx);
3813     auto targetOffset = OffsetF(menuPosition.GetX().ConvertToPx(), menuPosition.GetY().ConvertToPx());
3814     if (menuPattern->GetMenuType() == MenuType::SUB_MENU) {
3815         const auto& geometryNode = layoutWrapper->GetGeometryNode();
3816         CHECK_NULL_RETURN(geometryNode, "");
3817         targetOffset = geometryNode->GetMarginFrameOffset();
3818     }
3819     auto childOffset = targetOffset + childOffset_;
3820     auto arrowPosition = targetOffset + arrowPosition_;
3821     MenuPathParams params = {
3822         radiusTopLeftPx,
3823         radiusTopRightPx,
3824         radiusBottomLeftPx,
3825         radiusBottomRightPx,
3826         childOffset,
3827         childMarginFrameSize_,
3828         arrowPosition,
3829         arrowPlacement_,
3830         didNeedArrow,
3831     };
3832     menuPattern->UpdateMenuPathParams(params);
3833     if (!didNeedArrow) {
3834         return "";
3835     }
3836     std::string path;
3837     path += MoveTo(childOffset_.GetX() + radiusTopLeftPx, childOffset_.GetY());
3838     path += BuildTopLinePath(arrowPosition_, radiusTopRightPx, arrowPlacement_, didNeedArrow);
3839     path += BuildRightLinePath(arrowPosition_, radiusBottomRightPx, arrowPlacement_, didNeedArrow);
3840     path += BuildBottomLinePath(arrowPosition_, radiusBottomLeftPx, arrowPlacement_, didNeedArrow);
3841     path += BuildLeftLinePath(arrowPosition_, radiusTopLeftPx, arrowPlacement_, didNeedArrow);
3842     return path + "Z";
3843 }
3844 
ClipMenuPath(LayoutWrapper * layoutWrapper)3845 void MenuLayoutAlgorithm::ClipMenuPath(LayoutWrapper* layoutWrapper)
3846 {
3847     bool didNeedArrow = GetIfNeedArrow(layoutWrapper, childMarginFrameSize_);
3848     clipPath_ = CalculateMenuPath(layoutWrapper, didNeedArrow);
3849 }
3850 
UpdateSelectOverlayMenuColumnInfo(const RefPtr<MenuPattern> & pattern,const RefPtr<GridColumnInfo> & columnInfo)3851 bool MenuLayoutAlgorithm::UpdateSelectOverlayMenuColumnInfo(
3852     const RefPtr<MenuPattern>& pattern, const RefPtr<GridColumnInfo>& columnInfo)
3853 {
3854     CHECK_NULL_RETURN(pattern, false);
3855     CHECK_NULL_RETURN(pattern->IsSelectOverlayExtensionMenu(), false);
3856     auto menuWrapper = pattern->GetMenuWrapper();
3857     CHECK_NULL_RETURN(menuWrapper, false);
3858     if (menuWrapper->GetTag() != V2::SELECT_OVERLAY_ETS_TAG) {
3859         return false;
3860     }
3861     auto selectOverlayPattern = menuWrapper->GetPattern<SelectOverlayPattern>();
3862     CHECK_NULL_RETURN(selectOverlayPattern, false);
3863     CHECK_NULL_RETURN(selectOverlayPattern->GetIsMenuShowInSubWindow(), false);
3864     auto mainWindowContainerId = selectOverlayPattern->GetContainerId();
3865     auto container = Container::GetContainer(mainWindowContainerId);
3866     CHECK_NULL_RETURN(container, false);
3867     auto pipelineContext = AceType::DynamicCast<PipelineContext>(container->GetPipelineContext());
3868     CHECK_NULL_RETURN(pipelineContext, false);
3869     auto displayWindowRect = pipelineContext->GetDisplayWindowRectInfo();
3870     auto mainWindowWidth = displayWindowRect.Width();
3871     if (Positive(mainWindowWidth)) {
3872         auto parent = columnInfo->GetParent();
3873         CHECK_NULL_RETURN(parent, false);
3874         parent->BuildColumnWidth(mainWindowWidth);
3875         TAG_LOGD(
3876             AceLogTag::ACE_MENU, "Update select overlay extension menu min width with main window width constraint.");
3877         return true;
3878     }
3879     return false;
3880 }
3881 
GetCurrentSelectTheme(const RefPtr<FrameNode> & frameNode)3882 RefPtr<SelectTheme> MenuLayoutAlgorithm::GetCurrentSelectTheme(const RefPtr<FrameNode>& frameNode)
3883 {
3884     CHECK_NULL_RETURN(frameNode, nullptr);
3885     auto pipeline = frameNode->GetContext();
3886     CHECK_NULL_RETURN(pipeline, nullptr);
3887     auto theme = pipeline->GetTheme<SelectTheme>();
3888     CHECK_NULL_RETURN(theme, nullptr);
3889     return theme;
3890 }
3891 } // namespace OHOS::Ace::NG
3892