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