• 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 <optional>
19 #include <vector>
20 
21 #include "base/geometry/dimension.h"
22 #include "base/geometry/ng/offset_t.h"
23 #include "base/geometry/ng/size_t.h"
24 #include "base/geometry/size.h"
25 #include "base/memory/ace_type.h"
26 #include "base/memory/referenced.h"
27 #include "base/subwindow/subwindow_manager.h"
28 #include "base/utils/utils.h"
29 #include "core/common/ace_engine.h"
30 #include "core/components/common/layout/grid_system_manager.h"
31 #include "core/components/common/properties/placement.h"
32 #include "core/components/container_modal/container_modal_constants.h"
33 #include "core/components_ng/pattern/linear_layout/linear_layout_property.h"
34 #include "core/components_ng/pattern/menu/menu_layout_property.h"
35 #include "core/components_ng/pattern/menu/menu_pattern.h"
36 #include "core/components_ng/pattern/menu/menu_theme.h"
37 #include "core/components_ng/pattern/menu/preview/menu_preview_pattern.h"
38 #include "core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.h"
39 #include "core/components_ng/pattern/scroll/scroll_layout_property.h"
40 #include "core/components_ng/property/layout_constraint.h"
41 #include "core/components_ng/property/measure_property.h"
42 #include "core/pipeline/pipeline_base.h"
43 #include "core/pipeline_ng/pipeline_context.h"
44 namespace OHOS::Ace::NG {
45 
46 namespace {
47 constexpr uint32_t MIN_GRID_COUNTS = 2;
48 constexpr uint32_t GRID_COUNTS_4 = 4;
49 constexpr uint32_t GRID_COUNTS_6 = 6;
50 constexpr uint32_t GRID_COUNTS_8 = 8;
51 constexpr uint32_t GRID_COUNTS_12 = 12;
52 constexpr size_t ALIGNMENT_STEP_OFFSET = 2;
53 constexpr float HEIGHT_CONSTRAINT_FACTOR = 0.8;
54 
55 const std::map<Placement, std::vector<Placement>> PLACEMENT_STATES = {
56     { Placement::BOTTOM_LEFT,
57         {
58             Placement::BOTTOM_LEFT,
59             Placement::BOTTOM_RIGHT,
60             Placement::TOP_LEFT,
61             Placement::TOP_RIGHT,
62             Placement::RIGHT_TOP,
63             Placement::RIGHT_BOTTOM,
64             Placement::LEFT_TOP,
65             Placement::LEFT_BOTTOM,
66             Placement::NONE,
67         } },
68     { Placement::BOTTOM,
69         {
70             Placement::BOTTOM,
71             Placement::BOTTOM_LEFT,
72             Placement::BOTTOM_RIGHT,
73             Placement::TOP,
74             Placement::TOP_LEFT,
75             Placement::TOP_RIGHT,
76             Placement::RIGHT,
77             Placement::RIGHT_TOP,
78             Placement::RIGHT_BOTTOM,
79             Placement::LEFT,
80             Placement::LEFT_TOP,
81             Placement::LEFT_BOTTOM,
82             Placement::NONE,
83         } },
84     { Placement::BOTTOM_RIGHT,
85         {
86             Placement::BOTTOM_RIGHT,
87             Placement::BOTTOM_LEFT,
88             Placement::TOP_RIGHT,
89             Placement::TOP_LEFT,
90             Placement::RIGHT_BOTTOM,
91             Placement::RIGHT_TOP,
92             Placement::LEFT_BOTTOM,
93             Placement::LEFT_TOP,
94             Placement::NONE,
95         } },
96     { Placement::TOP_LEFT,
97         {
98             Placement::TOP_LEFT,
99             Placement::TOP_RIGHT,
100             Placement::BOTTOM_LEFT,
101             Placement::BOTTOM_RIGHT,
102             Placement::RIGHT_TOP,
103             Placement::RIGHT_BOTTOM,
104             Placement::LEFT_TOP,
105             Placement::LEFT_BOTTOM,
106             Placement::NONE,
107         } },
108     { Placement::TOP,
109         {
110             Placement::TOP,
111             Placement::TOP_LEFT,
112             Placement::TOP_RIGHT,
113             Placement::BOTTOM,
114             Placement::BOTTOM_LEFT,
115             Placement::BOTTOM_RIGHT,
116             Placement::RIGHT,
117             Placement::RIGHT_TOP,
118             Placement::RIGHT_BOTTOM,
119             Placement::LEFT,
120             Placement::LEFT_TOP,
121             Placement::LEFT_BOTTOM,
122             Placement::NONE,
123         } },
124     { Placement::TOP_RIGHT,
125         {
126             Placement::TOP_RIGHT,
127             Placement::TOP_LEFT,
128             Placement::BOTTOM_RIGHT,
129             Placement::BOTTOM_LEFT,
130             Placement::RIGHT_BOTTOM,
131             Placement::RIGHT_TOP,
132             Placement::LEFT_BOTTOM,
133             Placement::LEFT_TOP,
134             Placement::NONE,
135         } },
136     { Placement::LEFT_TOP,
137         {
138             Placement::LEFT_TOP,
139             Placement::LEFT_BOTTOM,
140             Placement::RIGHT_TOP,
141             Placement::RIGHT_BOTTOM,
142             Placement::BOTTOM_LEFT,
143             Placement::BOTTOM_RIGHT,
144             Placement::TOP_LEFT,
145             Placement::TOP_RIGHT,
146             Placement::NONE,
147         } },
148     { Placement::LEFT,
149         {
150             Placement::LEFT,
151             Placement::LEFT_TOP,
152             Placement::LEFT_BOTTOM,
153             Placement::RIGHT,
154             Placement::RIGHT_TOP,
155             Placement::RIGHT_BOTTOM,
156             Placement::BOTTOM,
157             Placement::BOTTOM_LEFT,
158             Placement::BOTTOM_RIGHT,
159             Placement::TOP,
160             Placement::TOP_LEFT,
161             Placement::TOP_RIGHT,
162             Placement::NONE,
163         } },
164     { Placement::LEFT_BOTTOM,
165         {
166             Placement::LEFT_BOTTOM,
167             Placement::LEFT_TOP,
168             Placement::RIGHT_BOTTOM,
169             Placement::RIGHT_TOP,
170             Placement::BOTTOM_RIGHT,
171             Placement::BOTTOM_LEFT,
172             Placement::TOP_RIGHT,
173             Placement::TOP_LEFT,
174             Placement::NONE,
175         } },
176     { Placement::RIGHT_TOP,
177         {
178             Placement::RIGHT_TOP,
179             Placement::RIGHT_BOTTOM,
180             Placement::LEFT_TOP,
181             Placement::LEFT_BOTTOM,
182             Placement::BOTTOM_LEFT,
183             Placement::BOTTOM_RIGHT,
184             Placement::TOP_LEFT,
185             Placement::TOP_RIGHT,
186             Placement::NONE,
187         } },
188     { Placement::RIGHT,
189         {
190             Placement::RIGHT,
191             Placement::RIGHT_TOP,
192             Placement::RIGHT_BOTTOM,
193             Placement::LEFT,
194             Placement::LEFT_TOP,
195             Placement::LEFT_BOTTOM,
196             Placement::BOTTOM,
197             Placement::BOTTOM_LEFT,
198             Placement::BOTTOM_RIGHT,
199             Placement::TOP,
200             Placement::TOP_LEFT,
201             Placement::TOP_RIGHT,
202             Placement::NONE,
203         } },
204     { Placement::RIGHT_BOTTOM,
205         {
206             Placement::RIGHT_BOTTOM,
207             Placement::RIGHT_TOP,
208             Placement::LEFT_BOTTOM,
209             Placement::LEFT_TOP,
210             Placement::BOTTOM_RIGHT,
211             Placement::BOTTOM_LEFT,
212             Placement::TOP_RIGHT,
213             Placement::TOP_LEFT,
214             Placement::NONE,
215         } },
216 };
217 
GetMaxGridCounts(const RefPtr<GridColumnInfo> & columnInfo)218 uint32_t GetMaxGridCounts(const RefPtr<GridColumnInfo>& columnInfo)
219 {
220     CHECK_NULL_RETURN(columnInfo, GRID_COUNTS_8);
221     auto currentColumns = columnInfo->GetParent()->GetColumns();
222     auto maxGridCounts = GRID_COUNTS_8;
223     switch (currentColumns) {
224         case GRID_COUNTS_4:
225             maxGridCounts = GRID_COUNTS_4;
226             break;
227         case GRID_COUNTS_8:
228             maxGridCounts = GRID_COUNTS_6;
229             break;
230         case GRID_COUNTS_12:
231             maxGridCounts = GRID_COUNTS_8;
232             break;
233         case MIN_GRID_COUNTS:
234             maxGridCounts = MIN_GRID_COUNTS;
235             break;
236         default:
237             break;
238     }
239     return maxGridCounts;
240 }
241 } // namespace
242 
MenuLayoutAlgorithm(int32_t id,const std::string & tag)243 MenuLayoutAlgorithm::MenuLayoutAlgorithm(int32_t id, const std::string& tag) : targetNodeId_(id), targetTag_(tag)
244 {
245     placementFuncMap_[Placement::TOP] = &MenuLayoutAlgorithm::GetPositionWithPlacementTop;
246     placementFuncMap_[Placement::TOP_LEFT] = &MenuLayoutAlgorithm::GetPositionWithPlacementTopLeft;
247     placementFuncMap_[Placement::TOP_RIGHT] = &MenuLayoutAlgorithm::GetPositionWithPlacementTopRight;
248     placementFuncMap_[Placement::BOTTOM] = &MenuLayoutAlgorithm::GetPositionWithPlacementBottom;
249     placementFuncMap_[Placement::BOTTOM_LEFT] = &MenuLayoutAlgorithm::GetPositionWithPlacementBottomLeft;
250     placementFuncMap_[Placement::BOTTOM_RIGHT] = &MenuLayoutAlgorithm::GetPositionWithPlacementBottomRight;
251     placementFuncMap_[Placement::LEFT] = &MenuLayoutAlgorithm::GetPositionWithPlacementLeft;
252     placementFuncMap_[Placement::LEFT_TOP] = &MenuLayoutAlgorithm::GetPositionWithPlacementLeftTop;
253     placementFuncMap_[Placement::LEFT_BOTTOM] = &MenuLayoutAlgorithm::GetPositionWithPlacementLeftBottom;
254     placementFuncMap_[Placement::RIGHT] = &MenuLayoutAlgorithm::GetPositionWithPlacementRight;
255     placementFuncMap_[Placement::RIGHT_TOP] = &MenuLayoutAlgorithm::GetPositionWithPlacementRightTop;
256     placementFuncMap_[Placement::RIGHT_BOTTOM] = &MenuLayoutAlgorithm::GetPositionWithPlacementRightBottom;
257 
258     setHorizontal_ = { Placement::LEFT, Placement::LEFT_BOTTOM, Placement::LEFT_TOP,
259         Placement::RIGHT, Placement::RIGHT_BOTTOM, Placement::RIGHT_TOP };
260     setVertical_ = { Placement::TOP, Placement::TOP_LEFT, Placement::TOP_RIGHT,
261         Placement::BOTTOM, Placement::BOTTOM_LEFT, Placement::BOTTOM_RIGHT };
262 
263     auto pipeline = PipelineBase::GetCurrentContext();
264     CHECK_NULL_VOID(pipeline);
265     auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
266     CHECK_NULL_VOID(menuTheme);
267     previewScale_ = menuTheme->GetPreviewAfterAnimationScale();
268     if (LessOrEqual(previewScale_, 0.0f)) {
269         previewScale_ = 1.0f;
270     }
271 }
272 
~MenuLayoutAlgorithm()273 MenuLayoutAlgorithm::~MenuLayoutAlgorithm()
274 {
275     placementFuncMap_.clear();
276     setHorizontal_.clear();
277     setVertical_.clear();
278 }
279 
ModifyNormalPreviewMenuPortraitPlacement(LayoutWrapper * layoutWrapper)280 void MenuLayoutAlgorithm::ModifyNormalPreviewMenuPortraitPlacement(LayoutWrapper* layoutWrapper)
281 {
282     CHECK_NULL_VOID(layoutWrapper);
283     auto props = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
284     CHECK_NULL_VOID(props);
285 
286     auto hasPlacement = props->GetMenuPlacement().has_value();
287     if (placement_ == Placement::TOP_LEFT || placement_ == Placement::TOP || placement_ == Placement::TOP_RIGHT) {
288         if (!hasPlacement) {
289             placement_ = Placement::TOP_LEFT;
290             props->UpdateMenuPlacement(placement_);
291         }
292     } else if (!hasPlacement) {
293         placement_ = Placement::BOTTOM_LEFT;
294         props->UpdateMenuPlacement(placement_);
295     }
296 }
297 
ModifyNormalPreviewMenuPlacement(LayoutWrapper * layoutWrapper)298 void MenuLayoutAlgorithm::ModifyNormalPreviewMenuPlacement(LayoutWrapper* layoutWrapper)
299 {
300     CHECK_NULL_VOID(layoutWrapper);
301     auto props = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
302     CHECK_NULL_VOID(props);
303 
304     auto hasPlacement = props->GetMenuPlacement().has_value();
305     if (SystemProperties::GetDeviceOrientation() == DeviceOrientation::PORTRAIT) {
306         ModifyNormalPreviewMenuPortraitPlacement(layoutWrapper);
307     } else if (!hasPlacement) {
308         placement_ = Placement::RIGHT_TOP;
309         props->UpdateMenuPlacement(placement_);
310     }
311 }
312 
ModifyPreviewMenuPlacement(LayoutWrapper * layoutWrapper)313 void MenuLayoutAlgorithm::ModifyPreviewMenuPlacement(LayoutWrapper* layoutWrapper)
314 {
315     CHECK_NULL_VOID(layoutWrapper);
316     auto props = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
317     CHECK_NULL_VOID(props);
318 
319     auto hasPlacement = props->GetMenuPlacement().has_value();
320     if (SystemProperties::GetDeviceType() == DeviceType::PHONE) {
321         ModifyNormalPreviewMenuPlacement(layoutWrapper);
322     } else {
323         if (!hasPlacement) {
324             placement_ = Placement::RIGHT_TOP;
325             props->UpdateMenuPlacement(placement_);
326         }
327     }
328 }
329 
Initialize(LayoutWrapper * layoutWrapper)330 void MenuLayoutAlgorithm::Initialize(LayoutWrapper* layoutWrapper)
331 {
332     CHECK_NULL_VOID(layoutWrapper);
333     // currently using click point as menu position
334     auto props = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
335     CHECK_NULL_VOID(props);
336     auto menuNode = layoutWrapper->GetHostNode();
337     CHECK_NULL_VOID(menuNode);
338     auto menuPattern = menuNode->GetPattern<MenuPattern>();
339     CHECK_NULL_VOID(menuPattern);
340     float scale = menuPattern->GetPreviewAfterAnimationScale();
341     previewScale_ = LessOrEqual(scale, 0.0f) ? previewScale_ : scale;
342     position_ = props->GetMenuOffset().value_or(OffsetF());
343     positionOffset_ = props->GetPositionOffset().value_or(OffsetF());
344     InitializePadding(layoutWrapper);
345     InitWrapperRect(props, menuPattern);
346     placement_ = props->GetMenuPlacement().value_or(Placement::BOTTOM_LEFT);
347     ModifyPositionToWrapper(layoutWrapper, position_);
348     if (!menuPattern->IsSelectOverlayExtensionMenu() && menuPattern->GetPreviewMode() != MenuPreviewMode::NONE) {
349         ModifyPreviewMenuPlacement(layoutWrapper);
350     }
351     InitSpace(props, menuPattern);
352 }
353 
InitWrapperRect(const RefPtr<MenuLayoutProperty> & props,const RefPtr<MenuPattern> & menuPattern)354 void MenuLayoutAlgorithm::InitWrapperRect(
355     const RefPtr<MenuLayoutProperty>& props, const RefPtr<MenuPattern>& menuPattern)
356 {
357     auto constraint = props->GetLayoutConstraint();
358     // has minus navgation bar height(AvoidAreaType.TYPE_NAVIGATION_INDICATOR)
359     auto wrapperIdealSize =
360         CreateIdealSize(constraint.value(), Axis::FREE, props->GetMeasureType(MeasureType::MATCH_PARENT), true);
361     auto pipelineContext = GetCurrentPipelineContext();
362     CHECK_NULL_VOID(pipelineContext);
363     auto windowGlobalRect = pipelineContext->GetDisplayWindowRectInfo();
364     wrapperRect_.SetRect(0, 0, wrapperIdealSize.Width(), wrapperIdealSize.Height());
365     auto safeAreaManager = pipelineContext->GetSafeAreaManager();
366     // system safeArea(AvoidAreaType.TYPE_SYSTEM) only include status bar,now the bottom is 0
367     auto bottom = safeAreaManager->GetSystemSafeArea().bottom_.Length();
368     auto top = safeAreaManager->GetSystemSafeArea().top_.Length();
369     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
370         if (hierarchicalParameters_) {
371             // wrapperRect_= windowGlobalRect- dock -statusbar
372             wrapperRect_ = pipelineContext->GetDisplayAvailableRect();
373         } else {
374             // wrapperIdealSize.Height = windowGlobalRect.Height()-navigation_indicator.height,no AR to avoid navigation
375             auto windowManager = pipelineContext->GetWindowManager();
376             auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL && windowManager &&
377                     windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING;
378             double width = windowGlobalRect.Width();
379             double height = windowGlobalRect.Height();
380             if (isContainerModal) {
381                 LimitContainerModalMenuRect(width, height);
382             }
383             wrapperRect_.SetRect(0, top, width, height - top - bottom);
384 
385         }
386     }
387 
388     if (!menuPattern->IsSelectOverlayExtensionMenu() && menuPattern->GetPreviewMode() != MenuPreviewMode::NONE) {
389         //  come from ModifyPreviewMenuPlacement
390         if (NearEqual(wrapperIdealSize.Height(), windowGlobalRect.Height())) {
391             wrapperRect_.SetRect(0, top, windowGlobalRect.Width(), windowGlobalRect.Height() - bottom);
392         }
393     }
394     wrapperSize_ = SizeF(wrapperRect_.Width(), wrapperRect_.Height());
395 }
396 
InitSpace(const RefPtr<MenuLayoutProperty> & props,const RefPtr<MenuPattern> & menuPattern)397 void MenuLayoutAlgorithm::InitSpace(const RefPtr<MenuLayoutProperty>& props, const RefPtr<MenuPattern>& menuPattern)
398 {
399     auto constraint = props->GetLayoutConstraint();
400     auto targetSize = props->GetTargetSizeValue(SizeF());
401     if (menuPattern->IsSelectOverlayExtensionMenu()) {
402         topSpace_ = 0.0f;
403         bottomSpace_ = constraint->maxSize.Height() - position_.GetY();
404         leftSpace_ = Infinity<float>();
405     } else {
406         if (props->GetMenuPlacement().has_value()) {
407             auto targetSecurity = targetSecurity_;
408             topSpace_ = std::max(0.0, targetOffset_.GetY() - targetSecurity - paddingTop_ - wrapperRect_.Top());
409             bottomSpace_ = std::max(0.0,
410                 wrapperRect_.Bottom() - targetOffset_.GetY() - targetSize_.Height() - targetSecurity - paddingBottom_);
411             if (NearZero(topSpace_) && NearZero(bottomSpace_)) {
412                 bottomSpace_ = wrapperRect_.Bottom() - position_.GetY() - paddingTop_;
413             }
414             leftSpace_ = std::max(0.0, wrapperRect_.Left() + targetOffset_.GetX() - paddingStart_ - targetSecurity);
415             rightSpace_ = std::max(
416                 0.0, wrapperRect_.Right() - targetSize_.Width() - targetSecurity - paddingStart_ - paddingEnd_);
417             if (NearZero(leftSpace_) && NearZero(rightSpace_)) {
418                 leftSpace_ = position_.GetX();
419                 rightSpace_ = wrapperRect_.Right() - leftSpace_;
420             }
421         } else {
422             if (hierarchicalParameters_ || !Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
423                 topSpace_ = position_.GetY() - targetSize.Height() - paddingTop_ - wrapperRect_.Top();
424                 bottomSpace_ = wrapperRect_.Bottom() - position_.GetY() - paddingBottom_;
425             } else {
426                 topSpace_ = position_.GetY() - wrapperRect_.Top() - paddingTop_;
427                 bottomSpace_ = wrapperRect_.Bottom() - position_.GetY() - paddingTop_;
428             }
429             leftSpace_ = position_.GetX() - paddingStart_;
430             rightSpace_ = wrapperRect_.Right() - leftSpace_ - paddingEnd_;
431         }
432     }
433 }
434 
InitializePadding(LayoutWrapper * layoutWrapper)435 void MenuLayoutAlgorithm::InitializePadding(LayoutWrapper* layoutWrapper)
436 {
437     auto menuNode = layoutWrapper->GetHostNode();
438     CHECK_NULL_VOID(menuNode);
439     auto menuPattern = menuNode->GetPattern<MenuPattern>();
440     CHECK_NULL_VOID(menuPattern);
441     auto pipeline = PipelineBase::GetCurrentContext();
442     CHECK_NULL_VOID(pipeline);
443     auto theme = pipeline->GetTheme<SelectTheme>();
444     CHECK_NULL_VOID(theme);
445     if (!menuPattern->IsSelectOverlayExtensionMenu()) {
446         margin_ = static_cast<float>(theme->GetOutPadding().ConvertToPx());
447         optionPadding_ = margin_;
448         paddingStart_ = static_cast<float>(theme->GetDefaultPaddingStart().ConvertToPx());
449         paddingEnd_ = static_cast<float>(theme->GetDefaultPaddingEnd().ConvertToPx());
450         paddingTop_ = static_cast<float>(theme->GetDefaultPaddingTop().ConvertToPx());
451         paddingBottom_ = static_cast<float>(theme->GetDefaultPaddingBottomFixed().ConvertToPx());
452     } else {
453         optionPadding_ = static_cast<float>(theme->GetOutPadding().ConvertToPx());
454     }
455 }
456 
InitializePaddingAPI11(LayoutWrapper * layoutWrapper)457 void MenuLayoutAlgorithm::InitializePaddingAPI11(LayoutWrapper* layoutWrapper)
458 {
459     auto menuNode = layoutWrapper->GetHostNode();
460     CHECK_NULL_VOID(menuNode);
461     auto menuPattern = menuNode->GetPattern<MenuPattern>();
462     CHECK_NULL_VOID(menuPattern);
463     auto pipeline = PipelineBase::GetCurrentContext();
464     CHECK_NULL_VOID(pipeline);
465     auto theme = pipeline->GetTheme<SelectTheme>();
466     CHECK_NULL_VOID(theme);
467 
468     if (!menuPattern->IsSelectOverlayExtensionMenu() && !hierarchicalParameters_) {
469         margin_ = static_cast<float>(theme->GetOutPadding().ConvertToPx());
470         optionPadding_ = margin_;
471         paddingStart_ = static_cast<float>(theme->GetMaxPaddingStart().ConvertToPx());
472         paddingEnd_ = static_cast<float>(theme->GetMaxPaddingEnd().ConvertToPx());
473         paddingTop_ = static_cast<float>(theme->GetDefaultPaddingTop().ConvertToPx());
474         paddingBottom_ = static_cast<float>(theme->GetDefaultPaddingBottomFixed().ConvertToPx());
475     } else {
476         margin_ = static_cast<float>(theme->GetOutPadding().ConvertToPx());
477         paddingStart_ = margin_;
478         paddingEnd_ = margin_;
479         paddingTop_ = margin_;
480         paddingBottom_ = margin_;
481         optionPadding_ = static_cast<float>(theme->GetOutPadding().ConvertToPx());
482     }
483 }
484 
ModifyPositionToWrapper(LayoutWrapper * layoutWrapper,OffsetF & position)485 void MenuLayoutAlgorithm::ModifyPositionToWrapper(LayoutWrapper* layoutWrapper, OffsetF& position)
486 {
487     auto menu = layoutWrapper->GetHostNode();
488     CHECK_NULL_VOID(menu);
489     auto wrapper = AceType::DynamicCast<FrameNode>(menu->GetParent());
490     CHECK_NULL_VOID(wrapper);
491 
492     OffsetF wrapperOffset;
493     // minus wrapper offset in LayoutFullScreen
494     auto wrapperLayoutProps = wrapper->GetLayoutProperty();
495     CHECK_NULL_VOID(wrapperLayoutProps);
496     auto&& safeAreaInsets = wrapperLayoutProps->GetSafeAreaInsets();
497     if (safeAreaInsets) {
498         wrapperOffset +=
499             OffsetF(static_cast<float>(safeAreaInsets->left_.end), static_cast<float>(safeAreaInsets->top_.end));
500         position -= wrapperOffset;
501     }
502 
503     auto menuPattern = menu->GetPattern<MenuPattern>();
504     CHECK_NULL_VOID(menuPattern);
505     bool isSubMenu = menuPattern->IsSubMenu() || menuPattern->IsSelectOverlaySubMenu();
506     if ((menuPattern->IsContextMenu() || (isSubMenu && Container::CurrentId() >= MIN_SUBCONTAINER_ID) ||
507             hierarchicalParameters_) &&
508         (targetTag_ != V2::SELECT_ETS_TAG)) {
509         // no need to modify for context menu, because context menu wrapper is full screen.
510         return;
511     }
512     // minus wrapper offset in floating window
513     auto pipelineContext = GetCurrentPipelineContext();
514     CHECK_NULL_VOID(pipelineContext);
515     auto windowManager = pipelineContext->GetWindowManager();
516     auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL && windowManager &&
517                             windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING;
518     if (isContainerModal) {
519         wrapperOffset = OffsetF(static_cast<float>((CONTAINER_BORDER_WIDTH + CONTENT_PADDING).ConvertToPx()),
520             static_cast<float>((CONTAINER_TITLE_HEIGHT + CONTAINER_BORDER_WIDTH).ConvertToPx()));
521         position -= wrapperOffset;
522     }
523 }
524 
525 // Called to perform layout render node and child.
Measure(LayoutWrapper * layoutWrapper)526 void MenuLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
527 {
528     // initialize screen size and menu position
529     CHECK_NULL_VOID(layoutWrapper);
530     auto menuNode = layoutWrapper->GetHostNode();
531     CHECK_NULL_VOID(menuNode);
532     auto menuPattern = menuNode->GetPattern<MenuPattern>();
533     CHECK_NULL_VOID(menuPattern);
534     auto menuLayoutProperty = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
535     CHECK_NULL_VOID(menuLayoutProperty);
536     auto isShowInSubWindow = menuLayoutProperty->GetShowInSubWindowValue(true);
537     InitHierarchicalParameters(isShowInSubWindow);
538     if (!targetTag_.empty()) {
539         InitTargetSizeAndPosition(layoutWrapper, menuPattern->IsContextMenu(), menuPattern);
540     }
541     Initialize(layoutWrapper);
542 
543     const auto& constraint = menuLayoutProperty->GetLayoutConstraint();
544     if (!constraint) {
545         return;
546     }
547     auto idealSize = CreateIdealSize(
548         constraint.value(), Axis::VERTICAL, menuLayoutProperty->GetMeasureType(MeasureType::MATCH_CONTENT), true);
549     const auto& padding = menuLayoutProperty->CreatePaddingAndBorder();
550     MinusPaddingToSize(padding, idealSize);
551 
552     // calculate menu main size
553     auto childConstraint = CreateChildConstraint(layoutWrapper);
554     auto container = Container::Current();
555     CHECK_NULL_VOID(container);
556     if (container->IsSubContainer()) {
557         auto parentId = SubwindowManager::GetInstance()->GetParentContainerId(Container::CurrentId());
558         auto parentContainer = AceEngine::Get().GetContainer(parentId);
559         CHECK_NULL_VOID(parentContainer);
560         auto pipeline = AceType::DynamicCast<NG::PipelineContext>(parentContainer->GetPipelineContext());
561         CHECK_NULL_VOID(pipeline);
562         auto childInsets = pipeline->GetSafeArea();
563         childInsets.top_.start = 0;
564         childInsets.top_.end = 0;
565         LayoutWrapper::ApplySafeArea(childInsets, childConstraint);
566     }
567     if (menuPattern->IsSelectMenu() && menuPattern->GetHasOptionWidth()) {
568         auto selectMenuWidth = menuPattern->GetSelectMenuWidth();
569         childConstraint.maxSize.SetWidth(selectMenuWidth);
570         childConstraint.parentIdealSize.SetWidth(selectMenuWidth);
571         childConstraint.selfIdealSize.SetWidth(selectMenuWidth);
572     }
573     float idealHeight = 0.0f;
574     float idealWidth = 0.0f;
575     for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
576         child->Measure(childConstraint);
577         auto childSize = child->GetGeometryNode()->GetMarginFrameSize();
578         idealHeight += childSize.Height();
579         idealWidth = std::max(idealWidth, childSize.Width());
580     }
581     idealSize.SetHeight(idealHeight);
582     idealSize.SetWidth(idealWidth);
583     AddPaddingToSize(padding, idealSize);
584 
585     auto geometryNode = layoutWrapper->GetGeometryNode();
586     CHECK_NULL_VOID(geometryNode);
587     geometryNode->SetFrameSize(idealSize);
588 }
589 
GetPreviewNodeAndMenuNodeTotalSize(const RefPtr<FrameNode> & frameNode,RefPtr<LayoutWrapper> & previewLayoutWrapper,RefPtr<LayoutWrapper> & menuLayoutWrapper)590 SizeF MenuLayoutAlgorithm::GetPreviewNodeAndMenuNodeTotalSize(const RefPtr<FrameNode>& frameNode,
591     RefPtr<LayoutWrapper>& previewLayoutWrapper, RefPtr<LayoutWrapper>& menuLayoutWrapper)
592 {
593     SizeF size;
594     CHECK_NULL_RETURN(frameNode, size);
595     auto pipelineContext = GetCurrentPipelineContext();
596     CHECK_NULL_RETURN(pipelineContext, size);
597     auto windowGlobalRect = pipelineContext->GetDisplayWindowRectInfo();
598     for (auto& child : frameNode->GetAllChildrenWithBuild()) {
599         auto hostNode = child->GetHostNode();
600         auto geometryNode = child->GetGeometryNode();
601         if (!hostNode || !geometryNode) {
602             continue;
603         }
604         if (hostNode->GetTag() == V2::MENU_PREVIEW_ETS_TAG || hostNode->GetTag() == V2::IMAGE_ETS_TAG) {
605             RefPtr<GridColumnInfo> columnInfo =
606                 GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
607             CHECK_NULL_RETURN(columnInfo, size);
608             auto parent = columnInfo->GetParent();
609             CHECK_NULL_RETURN(parent, size);
610             parent->BuildColumnWidth(std::min(windowGlobalRect.Width(), windowGlobalRect.Height()));
611             auto maxWidth = static_cast<float>(columnInfo->GetWidth(GRID_COUNTS_4)) / previewScale_;
612             auto frameSize = geometryNode->GetMarginFrameSize();
613             static SizeF previewSize;
614             static int32_t hostId = -1;
615             if (hostNode->GetTag() == V2::MENU_PREVIEW_ETS_TAG) {
616                 if (previewSize == SizeF(0.0f, 0.0f) || hostId != hostNode->GetId()) {
617                     previewSize = frameSize;
618                     hostId = hostNode->GetId();
619                 } else {
620                     frameSize = previewSize;
621                 }
622                 if (LessOrEqual(frameSize.Width(), maxWidth)) {
623                     geometryNode->SetFrameSize(SizeF(frameSize.Width(), frameSize.Height()));
624                 } else {
625                     geometryNode->SetFrameSize(SizeF(maxWidth, frameSize.Height()));
626                 }
627             } else {
628                 geometryNode->SetFrameSize(frameSize);
629             }
630             frameSize = geometryNode->GetMarginFrameSize() * previewScale_;
631             auto widthLeftSpace = windowGlobalRect.Width() - paddingStart_ - paddingEnd_;
632             if (GreatNotEqual(frameSize.Width(), widthLeftSpace)) {
633                 auto unitSpace = widthLeftSpace / frameSize.Width() / previewScale_;
634                 geometryNode->SetFrameSize(SizeF(widthLeftSpace / previewScale_, unitSpace * frameSize.Height()));
635             }
636             previewLayoutWrapper = child;
637             size += geometryNode->GetMarginFrameSize() * previewScale_;
638         }
639         auto menuPattern = hostNode->GetPattern<MenuPattern>();
640         if (hostNode->GetTag() == V2::MENU_ETS_TAG && menuPattern && !menuPattern->IsSubMenu()) {
641             menuLayoutWrapper = child;
642             size += geometryNode->GetMarginFrameSize();
643         }
644     }
645     return size;
646 }
647 
LayoutNormalTopPreviewBottomMenuLessThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,const PreviewMenuParam & param,SizeF & totalSize)648 void MenuLayoutAlgorithm::LayoutNormalTopPreviewBottomMenuLessThan(const RefPtr<GeometryNode>& previewGeometryNode,
649     const RefPtr<GeometryNode>& menuGeometryNode, const PreviewMenuParam& param, SizeF& totalSize)
650 {
651     CHECK_NULL_VOID(previewGeometryNode);
652     CHECK_NULL_VOID(menuGeometryNode);
653 
654     OffsetF center(targetOffset_.GetX() + targetSize_.Width() / 2, targetOffset_.GetY() + targetSize_.Height() / 2);
655     targetCenterOffset_ = center;
656     auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
657     OffsetF offset(center.GetX() - previewSize.Width() / 2,
658         std::min<float>(center.GetY() - previewSize.Height() / 2, param.windowGlobalSizeF.Height() -
659             param.bottomSecurity - param.bottom - totalSize.Height() - param.previewMenuGap));
660     auto x = std::clamp(offset.GetX(), param.windowsOffsetX + paddingStart_,
661         param.windowsOffsetX + param.windowGlobalSizeF.Width() - previewSize.Width() - paddingEnd_);
662     auto y = std::clamp(offset.GetY(), param.windowsOffsetY + param.top + param.topSecurity,
663         param.windowsOffsetY + param.windowGlobalSizeF.Height() - param.bottomSecurity - param.bottom -
664             previewSize.Height());
665     x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / 2;
666     y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / 2;
667     previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
668 }
669 
LayoutNormalTopPreviewBottomMenuGreateThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,const PreviewMenuParam & param,SizeF & totalSize)670 void MenuLayoutAlgorithm::LayoutNormalTopPreviewBottomMenuGreateThan(const RefPtr<GeometryNode>& previewGeometryNode,
671     const RefPtr<GeometryNode>& menuGeometryNode, const PreviewMenuParam& param, SizeF& totalSize)
672 {
673     CHECK_NULL_VOID(previewGeometryNode);
674     CHECK_NULL_VOID(menuGeometryNode);
675 
676     OffsetF center(targetOffset_.GetX() + targetSize_.Width() / 2, targetOffset_.GetY() + targetSize_.Height() / 2);
677     targetCenterOffset_ = center;
678     auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
679     auto menuHeight = totalSize.Height() - previewSize.Height();
680     auto previewHalfHeight = previewSize.Height() / 2;
681     if (LessNotEqual(menuHeight, previewHalfHeight)) {
682         auto menuSize = menuGeometryNode->GetMarginFrameSize();
683         if (GreatNotEqual(param.menuItemTotalHeight, previewHalfHeight)) {
684             menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), previewHalfHeight));
685             totalSize = SizeF(totalSize.Width(), previewHalfHeight + previewSize.Height());
686         } else {
687             menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), param.menuItemTotalHeight));
688             totalSize = SizeF(totalSize.Width(), param.menuItemTotalHeight + previewSize.Height());
689         }
690     }
691 
692     auto heightLeftSpace = param.windowGlobalSizeF.Height() - param.top - param.bottom - param.topSecurity -
693                            param.bottomSecurity - param.previewMenuGap;
694     auto delta = totalSize.Height() - heightLeftSpace;
695     if (GreatNotEqual(delta, 0.0f)) {
696         menuHeight = totalSize.Height() - previewSize.Height();
697         float unitSpace = 0.0f;
698         if (LessNotEqual(menuHeight, previewHalfHeight)) {
699             unitSpace = delta / previewSize.Height();
700         } else {
701             unitSpace = delta / totalSize.Height();
702             auto menuSize = menuGeometryNode->GetMarginFrameSize();
703             menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), (1 - unitSpace) * menuSize.Height()));
704         }
705         previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
706             (1 - unitSpace) * previewSize.Height() / previewScale_));
707         totalSize = totalSize - SizeF(0.0f, delta);
708     }
709     previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
710     OffsetF offset(center.GetX() - previewSize.Width() / 2, 0.0f);
711     auto x = std::clamp(offset.GetX(), param.windowsOffsetX + paddingStart_,
712         param.windowsOffsetX + param.windowGlobalSizeF.Width() - previewSize.Width() - paddingEnd_);
713     auto y = std::clamp(offset.GetY(), param.windowsOffsetY + param.top + param.topSecurity, param.windowsOffsetY +
714         param.windowGlobalSizeF.Height() - param.bottomSecurity - param.bottom - previewSize.Height());
715     x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / 2;
716     y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / 2;
717     previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
718 }
719 
LayoutNormalTopPreviewBottomMenu(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize,float menuItemTotalHeight)720 void MenuLayoutAlgorithm::LayoutNormalTopPreviewBottomMenu(const RefPtr<GeometryNode>& previewGeometryNode,
721     const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize, float menuItemTotalHeight)
722 {
723     CHECK_NULL_VOID(previewGeometryNode);
724     CHECK_NULL_VOID(menuGeometryNode);
725 
726     auto pipelineContext = GetCurrentPipelineContext();
727     CHECK_NULL_VOID(pipelineContext);
728     auto safeAreaManager = pipelineContext->GetSafeAreaManager();
729     CHECK_NULL_VOID(safeAreaManager);
730     auto top = safeAreaManager->GetSystemSafeArea().top_.Length();
731     auto bottom = safeAreaManager->GetSystemSafeArea().bottom_.Length();
732     auto windowGlobalRect = pipelineContext->GetDisplayWindowRectInfo();
733     float windowsOffsetX = static_cast<float>(windowGlobalRect.GetOffset().GetX());
734     float windowsOffsetY = static_cast<float>(windowGlobalRect.GetOffset().GetY());
735     float screenHeight = wrapperSize_.Height();
736     if (!NearEqual(wrapperSize_.Height(), windowGlobalRect.Height())) {
737         screenHeight += bottom;
738     }
739     SizeF windowGlobalSizeF(windowGlobalRect.Width(), screenHeight - windowsOffsetY);
740 
741     float topSecurity = static_cast<float>(PORTRAIT_TOP_SECURITY.ConvertToPx());
742     float bottomSecurity = static_cast<float>(PORTRAIT_BOTTOM_SECURITY.ConvertToPx());
743 
744     PreviewMenuParam param;
745     param.windowGlobalSizeF = windowGlobalSizeF;
746     param.windowsOffsetX = windowsOffsetX;
747     param.windowsOffsetY = windowsOffsetY;
748     param.top = top;
749     param.bottom = bottom;
750     param.topSecurity = topSecurity;
751     param.bottomSecurity = bottomSecurity;
752     param.previewMenuGap = targetSecurity_;
753     param.menuItemTotalHeight = menuItemTotalHeight;
754     if (LessNotEqual(totalSize.Height() + targetSecurity_,
755         windowGlobalSizeF.Height() - topSecurity - bottomSecurity - top - bottom)) {
756         LayoutNormalTopPreviewBottomMenuLessThan(previewGeometryNode, menuGeometryNode, param, totalSize);
757     } else {
758         LayoutNormalTopPreviewBottomMenuGreateThan(previewGeometryNode, menuGeometryNode, param, totalSize);
759     }
760     auto previewSize = previewGeometryNode->GetMarginFrameSize();
761     auto securityHeight = windowGlobalRect.Height() - topSecurity - top - bottomSecurity - bottom;
762     if (GreatNotEqual(windowGlobalSizeF.Height(), windowGlobalRect.Height()) &&
763         GreatNotEqual(previewSize.Height(), securityHeight)) {
764         previewGeometryNode->SetFrameSize(SizeF(previewSize.Width(), securityHeight));
765     }
766 }
767 
LayoutNormalBottomPreviewTopMenuLessThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,const PreviewMenuParam & param,SizeF & totalSize)768 void MenuLayoutAlgorithm::LayoutNormalBottomPreviewTopMenuLessThan(const RefPtr<GeometryNode>& previewGeometryNode,
769     const RefPtr<GeometryNode>& menuGeometryNode, const PreviewMenuParam& param, SizeF& totalSize)
770 {
771     CHECK_NULL_VOID(previewGeometryNode);
772     CHECK_NULL_VOID(menuGeometryNode);
773 
774     OffsetF center(targetOffset_.GetX() + targetSize_.Width() / 2, targetOffset_.GetY() + targetSize_.Height() / 2);
775     targetCenterOffset_ = center;
776     auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
777     OffsetF offset(center.GetX() - previewSize.Width() / 2,
778         std::max<float>(center.GetY() - previewSize.Height() / 2,
779             param.top + param.topSecurity + totalSize.Height() - previewSize.Height() + param.previewMenuGap));
780     auto x = std::clamp(offset.GetX(), param.windowsOffsetX + paddingStart_,
781         param.windowsOffsetX + param.windowGlobalSizeF.Width() - previewSize.Width() - paddingEnd_);
782     auto y = std::clamp(offset.GetY(), param.windowsOffsetY + param.top + param.topSecurity,
783         param.windowsOffsetY + param.windowGlobalSizeF.Height() - param.bottomSecurity - param.bottom -
784             previewSize.Height());
785     x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / 2;
786     y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / 2;
787     previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
788 }
789 
LayoutNormalBottomPreviewTopMenuGreateThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,const PreviewMenuParam & param,SizeF & totalSize)790 void MenuLayoutAlgorithm::LayoutNormalBottomPreviewTopMenuGreateThan(const RefPtr<GeometryNode>& previewGeometryNode,
791     const RefPtr<GeometryNode>& menuGeometryNode, const PreviewMenuParam& param, SizeF& totalSize)
792 {
793     CHECK_NULL_VOID(previewGeometryNode);
794     CHECK_NULL_VOID(menuGeometryNode);
795 
796     OffsetF center(targetOffset_.GetX() + targetSize_.Width() / 2, targetOffset_.GetY() + targetSize_.Height() / 2);
797     targetCenterOffset_ = center;
798     auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
799     auto menuHeight = totalSize.Height() - previewSize.Height();
800     auto previewHalfHeight = previewSize.Height() / 2;
801     if (LessNotEqual(menuHeight, previewHalfHeight)) {
802         auto menuSize = menuGeometryNode->GetMarginFrameSize();
803         if (GreatNotEqual(param.menuItemTotalHeight, previewHalfHeight)) {
804             menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), previewHalfHeight));
805             totalSize = SizeF(totalSize.Width(), previewHalfHeight + previewSize.Height());
806         } else {
807             menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), param.menuItemTotalHeight));
808             totalSize = SizeF(totalSize.Width(), param.menuItemTotalHeight + previewSize.Height());
809         }
810     }
811 
812     auto heightLeftSpace = param.windowGlobalSizeF.Height() - param.top - param.topSecurity - param.bottom -
813                            param.bottomSecurity - param.previewMenuGap;
814     auto delta = totalSize.Height() - heightLeftSpace;
815     if (GreatNotEqual(delta, 0.0f)) {
816         menuHeight = totalSize.Height() - previewSize.Height();
817         float unitSpace = 0.0f;
818         if (LessNotEqual(menuHeight, previewHalfHeight)) {
819             unitSpace = delta / previewSize.Height();
820         } else {
821             unitSpace = delta / totalSize.Height();
822             auto menuSize = menuGeometryNode->GetMarginFrameSize();
823             menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), (1 - unitSpace) * menuSize.Height()));
824         }
825         previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
826             (1 - unitSpace) * previewSize.Height() / previewScale_));
827         totalSize = totalSize - SizeF(0.0f, delta);
828     }
829     previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
830     OffsetF offset(center.GetX() - previewSize.Width() / 2,
831         param.windowGlobalSizeF.Height() - param.bottomSecurity - param.bottom - previewSize.Height());
832 
833     auto x = std::clamp(offset.GetX(), param.windowsOffsetX + paddingStart_,
834         param.windowsOffsetX + param.windowGlobalSizeF.Width() - previewSize.Width() - paddingEnd_);
835     auto y = std::clamp(offset.GetY(), param.windowsOffsetY + param.top + param.topSecurity, param.windowsOffsetY +
836         param.windowGlobalSizeF.Height() - param.bottomSecurity - param.bottom - previewSize.Height());
837     x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / 2;
838     y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / 2;
839     previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
840 }
841 
LayoutNormalBottomPreviewTopMenu(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize,float menuItemTotalHeight)842 void MenuLayoutAlgorithm::LayoutNormalBottomPreviewTopMenu(const RefPtr<GeometryNode>& previewGeometryNode,
843     const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize, float menuItemTotalHeight)
844 {
845     CHECK_NULL_VOID(previewGeometryNode);
846     CHECK_NULL_VOID(menuGeometryNode);
847 
848     auto pipelineContext = GetCurrentPipelineContext();
849     CHECK_NULL_VOID(pipelineContext);
850     auto safeAreaManager = pipelineContext->GetSafeAreaManager();
851     CHECK_NULL_VOID(safeAreaManager);
852     auto top = safeAreaManager->GetSystemSafeArea().top_.Length();
853     auto bottom = safeAreaManager->GetSystemSafeArea().bottom_.Length();
854     auto windowGlobalRect = pipelineContext->GetDisplayWindowRectInfo();
855     float windowsOffsetX = static_cast<float>(windowGlobalRect.GetOffset().GetX());
856     float windowsOffsetY = static_cast<float>(windowGlobalRect.GetOffset().GetY());
857     float screenHeight = wrapperSize_.Height();
858     if (!NearEqual(wrapperSize_.Height(), windowGlobalRect.Height())) {
859         screenHeight += bottom;
860     }
861     SizeF windowGlobalSizeF(windowGlobalRect.Width(), screenHeight - windowsOffsetY);
862 
863     float topSecurity = static_cast<float>(PORTRAIT_TOP_SECURITY.ConvertToPx());
864     float bottomSecurity = static_cast<float>(PORTRAIT_BOTTOM_SECURITY.ConvertToPx());
865 
866     PreviewMenuParam param;
867     param.windowGlobalSizeF = windowGlobalSizeF;
868     param.windowsOffsetX = windowsOffsetX;
869     param.windowsOffsetY = windowsOffsetY;
870     param.top = top;
871     param.bottom = bottom;
872     param.topSecurity = topSecurity;
873     param.bottomSecurity = bottomSecurity;
874     param.previewMenuGap = targetSecurity_;
875     param.menuItemTotalHeight = menuItemTotalHeight;
876     if (LessNotEqual(totalSize.Height() + targetSecurity_,
877         windowGlobalSizeF.Height() - topSecurity - bottomSecurity - top - bottom)) {
878         LayoutNormalBottomPreviewTopMenuLessThan(previewGeometryNode, menuGeometryNode, param, totalSize);
879     } else {
880         LayoutNormalBottomPreviewTopMenuGreateThan(previewGeometryNode, menuGeometryNode, param, totalSize);
881     }
882     auto previewSize = previewGeometryNode->GetMarginFrameSize();
883     auto securityHeight = windowGlobalRect.Height() - topSecurity - top - bottomSecurity - bottom;
884     if (GreatNotEqual(windowGlobalSizeF.Height(), windowGlobalRect.Height()) &&
885         GreatNotEqual(previewSize.Height(), securityHeight)) {
886         previewGeometryNode->SetFrameSize(SizeF(previewSize.Width(), securityHeight));
887     }
888 }
889 
UpdateScrollAndColumnLayoutConstraint(const RefPtr<LayoutWrapper> & previewLayoutWrapper,const RefPtr<LayoutWrapper> & menuLayoutWrapper)890 void MenuLayoutAlgorithm::UpdateScrollAndColumnLayoutConstraint(
891     const RefPtr<LayoutWrapper>& previewLayoutWrapper, const RefPtr<LayoutWrapper>& menuLayoutWrapper)
892 {
893     CHECK_NULL_VOID(menuLayoutWrapper);
894     CHECK_NULL_VOID(previewLayoutWrapper);
895     RefPtr<GeometryNode> menuGeometryNode = menuLayoutWrapper->GetGeometryNode();
896     RefPtr<GeometryNode> previewGeometryNode = previewLayoutWrapper->GetGeometryNode();
897     CHECK_NULL_VOID(menuGeometryNode);
898     CHECK_NULL_VOID(previewGeometryNode);
899 
900     for (auto& child : menuLayoutWrapper->GetAllChildrenWithBuild()) {
901         auto geometryNode = child->GetGeometryNode();
902         if (!geometryNode) {
903             continue;
904         }
905         auto frameSize = menuGeometryNode->GetMarginFrameSize();
906         geometryNode->SetFrameSize(SizeF(frameSize.Width(), frameSize.Height()));
907         auto layoutProperty = child->GetLayoutProperty();
908         CHECK_NULL_VOID(layoutProperty);
909         auto constraint = layoutProperty->GetLayoutConstraint();
910         if (constraint.has_value()) {
911             constraint.value().maxSize.SetWidth(frameSize.Width());
912             constraint.value().maxSize.SetHeight(frameSize.Height());
913             constraint.value().selfIdealSize.UpdateSizeWithCheck(SizeF(frameSize.Width(), frameSize.Height()));
914             layoutProperty->UpdateLayoutConstraint(constraint.value());
915             child->Measure(constraint);
916         }
917     }
918 
919     for (auto& child : previewLayoutWrapper->GetAllChildrenWithBuild()) {
920         auto hostNode = child->GetHostNode();
921         auto geometryNode = child->GetGeometryNode();
922         if (!hostNode || !geometryNode) {
923             continue;
924         }
925         auto frameSize = previewGeometryNode->GetMarginFrameSize();
926         geometryNode->SetFrameSize(SizeF(frameSize.Width(), frameSize.Height()));
927         auto layoutProperty = child->GetLayoutProperty();
928         CHECK_NULL_VOID(layoutProperty);
929         auto constraint = layoutProperty->GetLayoutConstraint();
930         if (constraint.has_value()) {
931             constraint.value().maxSize.SetWidth(frameSize.Width());
932             constraint.value().maxSize.SetHeight(frameSize.Height());
933             constraint.value().selfIdealSize.UpdateSizeWithCheck(SizeF(frameSize.Width(), frameSize.Height()));
934             layoutProperty->UpdateLayoutConstraint(constraint.value());
935             hostNode->GetRenderContext()->SetClipToBounds(true);
936             child->Measure(constraint);
937         }
938     }
939 }
940 
GetMenuItemTotalHeight(const RefPtr<LayoutWrapper> & menuLayoutWrapper)941 float MenuLayoutAlgorithm::GetMenuItemTotalHeight(const RefPtr<LayoutWrapper>& menuLayoutWrapper)
942 {
943     CHECK_NULL_RETURN(menuLayoutWrapper, 0.0f);
944     RefPtr<GeometryNode> menuGeometryNode = menuLayoutWrapper->GetGeometryNode();
945     CHECK_NULL_RETURN(menuGeometryNode, 0.0f);
946     float height = 0.0f;
947 
948     for (auto& child : menuLayoutWrapper->GetAllChildrenWithBuild()) {
949         auto geometryNode = child->GetGeometryNode();
950         if (!geometryNode) {
951             continue;
952         }
953         for (auto& menuItem : child->GetAllChildrenWithBuild()) {
954             auto itemHostnode = menuItem->GetHostNode();
955             auto itemGeometryNode = menuItem->GetGeometryNode();
956             if (!itemHostnode || !itemGeometryNode) {
957                 continue;
958             }
959             height += itemGeometryNode->GetMarginFrameSize().Height();
960         }
961     }
962     auto menuHeight = menuGeometryNode->GetMarginFrameSize().Height();
963     if (LessNotEqual(height, menuHeight)) {
964         height = menuHeight;
965     }
966     return height;
967 }
968 
LayoutNormalPreviewMenu(LayoutWrapper * layoutWrapper)969 void MenuLayoutAlgorithm::LayoutNormalPreviewMenu(LayoutWrapper* layoutWrapper)
970 {
971     CHECK_NULL_VOID(layoutWrapper);
972     auto menuNode = layoutWrapper->GetHostNode();
973     CHECK_NULL_VOID(menuNode);
974     auto parentNode = AceType::DynamicCast<FrameNode>(menuNode->GetParent());
975     CHECK_NULL_VOID(parentNode);
976     auto menuProp = DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
977     CHECK_NULL_VOID(menuProp);
978 
979     RefPtr<LayoutWrapper> menuLayoutWrapper;
980     RefPtr<LayoutWrapper> previewLayoutWrapper;
981     SizeF totalSize = GetPreviewNodeAndMenuNodeTotalSize(parentNode, previewLayoutWrapper, menuLayoutWrapper);
982     CHECK_NULL_VOID(menuLayoutWrapper);
983     CHECK_NULL_VOID(previewLayoutWrapper);
984     RefPtr<GeometryNode> menuGeometryNode = menuLayoutWrapper->GetGeometryNode();
985     RefPtr<GeometryNode> previewGeometryNode = previewLayoutWrapper->GetGeometryNode();
986     auto menuItemTotalHeight = GetMenuItemTotalHeight(menuLayoutWrapper);
987 
988     if (SystemProperties::GetDeviceOrientation() == DeviceOrientation::PORTRAIT) {
989         if (placement_ == Placement::TOP_LEFT || placement_ == Placement::TOP || placement_ == Placement::TOP_RIGHT) {
990             LayoutNormalBottomPreviewTopMenu(previewGeometryNode, menuGeometryNode, totalSize, menuItemTotalHeight);
991         } else {
992             LayoutNormalTopPreviewBottomMenu(previewGeometryNode, menuGeometryNode, totalSize, menuItemTotalHeight);
993         }
994     } else {
995         LayoutOtherDeviceLeftPreviewRightMenu(previewGeometryNode, menuGeometryNode, totalSize, menuItemTotalHeight);
996     }
997     UpdateScrollAndColumnLayoutConstraint(previewLayoutWrapper, menuLayoutWrapper);
998     auto previewSize = previewGeometryNode->GetMarginFrameSize();
999     previewOffset_ = previewGeometryNode->GetFrameOffset();
1000     auto previewOffsetX = previewOffset_.GetX();
1001     auto previewOffsetY = previewOffset_.GetY();
1002     targetSize_ = previewSize * previewScale_;
1003     targetOffset_ = OffsetF(previewOffsetX + (previewSize.Width() - targetSize_.Width()) / 2,
1004         previewOffsetY + (previewSize.Height() - targetSize_.Height()) / 2);
1005     auto previewHostNode = previewLayoutWrapper->GetHostNode();
1006     CHECK_NULL_VOID(previewHostNode);
1007     auto renderContext = previewHostNode->GetRenderContext();
1008     CHECK_NULL_VOID(renderContext);
1009     renderContext->UpdatePosition(OffsetT<Dimension>(Dimension(previewOffsetX), Dimension(previewOffsetY)));
1010 
1011     auto menuHostNode = menuLayoutWrapper->GetHostNode();
1012     CHECK_NULL_VOID(menuHostNode);
1013     previewOriginOffset_ = targetCenterOffset_ - OffsetF(previewSize.Width() / 2, previewSize.Height() / 2);
1014     previewSize_ = previewSize;
1015     auto menuPattern = menuHostNode->GetPattern<MenuPattern>();
1016     CHECK_NULL_VOID(menuPattern);
1017     menuPattern->SetPreviewOriginOffset(previewOriginOffset_);
1018 }
1019 
LayoutOtherDeviceLeftPreviewRightMenuLessThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,const PreviewMenuParam & param,SizeF & totalSize)1020 void MenuLayoutAlgorithm::LayoutOtherDeviceLeftPreviewRightMenuLessThan(const RefPtr<GeometryNode>& previewGeometryNode,
1021     const RefPtr<GeometryNode>& menuGeometryNode, const PreviewMenuParam& param, SizeF& totalSize)
1022 {
1023     CHECK_NULL_VOID(previewGeometryNode);
1024     CHECK_NULL_VOID(menuGeometryNode);
1025 
1026     OffsetF targetCenterOffset(
1027         targetOffset_.GetX() + targetSize_.Width() / 2, targetOffset_.GetY() + targetSize_.Height() / 2);
1028     targetCenterOffset_ = targetCenterOffset;
1029     auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1030     auto heightLeftSpace =
1031         param.windowGlobalSizeF.Height() - param.top - param.topSecurity - param.bottomSecurity - param.bottom;
1032     auto delta = previewSize.Height() - heightLeftSpace;
1033     if (GreatNotEqual(delta, 0.0f)) {
1034         auto unitSpace = delta / previewSize.Height();
1035         previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
1036             (1 - unitSpace) * previewSize.Height() / previewScale_));
1037         totalSize = totalSize - SizeF(unitSpace * previewSize.Width(), unitSpace * previewSize.Height());
1038     }
1039     previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1040     auto menuSize = menuGeometryNode->GetMarginFrameSize();
1041     menuGeometryNode->SetFrameSize(
1042         SizeF(menuSize.Width(), std::min<float>(param.menuItemTotalHeight, heightLeftSpace)));
1043     menuSize = menuGeometryNode->GetMarginFrameSize();
1044     auto offsetX = targetCenterOffset.GetX() - previewSize.Width() / 2;
1045     auto offsetY = std::min<float>(targetCenterOffset.GetY() - previewSize.Height() / 2,
1046         param.windowGlobalSizeF.Height() - param.bottomSecurity - param.bottom - menuSize.Height());
1047     auto x = std::clamp(offsetX, param.windowsOffsetX + paddingStart_,
1048         param.windowsOffsetX + param.windowGlobalSizeF.Width() - previewSize.Width() - paddingEnd_);
1049     auto y = std::clamp(offsetY, param.windowsOffsetY + param.top + param.topSecurity,
1050         param.windowsOffsetY + param.windowGlobalSizeF.Height() - param.bottomSecurity - param.bottom -
1051             previewSize.Height());
1052     x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / 2;
1053     y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / 2;
1054     previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1055 }
1056 
LayoutOtherDeviceLeftPreviewRightMenuGreateThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,const PreviewMenuParam & param,SizeF & totalSize)1057 void MenuLayoutAlgorithm::LayoutOtherDeviceLeftPreviewRightMenuGreateThan(
1058     const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode,
1059     const PreviewMenuParam& param, SizeF& totalSize)
1060 {
1061     CHECK_NULL_VOID(previewGeometryNode);
1062     CHECK_NULL_VOID(menuGeometryNode);
1063 
1064     OffsetF targetCenterOffset(
1065         targetOffset_.GetX() + targetSize_.Width() / 2, targetOffset_.GetY() + targetSize_.Height() / 2);
1066     targetCenterOffset_ = targetCenterOffset;
1067     auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1068     auto widthLeftSpace = param.windowGlobalSizeF.Width() - paddingStart_ - paddingEnd_ - param.previewMenuGap;
1069     auto delta = totalSize.Width() - widthLeftSpace;
1070     if (GreatNotEqual(delta, 0.0f)) {
1071         auto unitSpace = delta / previewSize.Width();
1072         previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
1073             (1 - unitSpace) * previewSize.Height() / previewScale_));
1074         totalSize = totalSize - SizeF(unitSpace * previewSize.Width(), unitSpace * previewSize.Height());
1075     }
1076     previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1077     auto heightLeftSpace =
1078         param.windowGlobalSizeF.Height() - param.topSecurity - param.top - param.bottomSecurity - param.bottom;
1079     delta = previewSize.Height() - heightLeftSpace;
1080     if (GreatNotEqual(delta, 0.0f)) {
1081         auto unitSpace = delta / previewSize.Height();
1082         previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
1083             (1 - unitSpace) * previewSize.Height() / previewScale_));
1084         totalSize = totalSize - SizeF(unitSpace * previewSize.Width(), unitSpace * previewSize.Height());
1085     }
1086     previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1087     auto menuSize = menuGeometryNode->GetMarginFrameSize();
1088     menuGeometryNode->SetFrameSize(
1089         SizeF(menuSize.Width(), std::min<float>(param.menuItemTotalHeight, heightLeftSpace)));
1090     menuSize = menuGeometryNode->GetMarginFrameSize();
1091     auto offsetX = 0.0f;
1092     auto offsetY = std::min<float>(targetCenterOffset.GetY() - previewSize.Height() / 2,
1093         param.windowGlobalSizeF.Height() - param.bottomSecurity - param.bottom - menuSize.Height());
1094     auto x = std::clamp(offsetX, param.windowsOffsetX + paddingStart_,
1095         param.windowsOffsetX + param.windowGlobalSizeF.Width() - previewSize.Width() - paddingEnd_);
1096     auto y = std::clamp(offsetY, param.windowsOffsetY + param.top + param.topSecurity,
1097         param.windowsOffsetY + param.windowGlobalSizeF.Height() - param.bottomSecurity - param.bottom -
1098             previewSize.Height());
1099     x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / 2;
1100     y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / 2;
1101     previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1102 }
1103 
LayoutOtherDeviceLeftPreviewRightMenu(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize,float menuItemTotalHeight)1104 void MenuLayoutAlgorithm::LayoutOtherDeviceLeftPreviewRightMenu(const RefPtr<GeometryNode>& previewGeometryNode,
1105     const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize, float menuItemTotalHeight)
1106 {
1107     CHECK_NULL_VOID(previewGeometryNode);
1108     CHECK_NULL_VOID(menuGeometryNode);
1109 
1110     auto pipelineContext = GetCurrentPipelineContext();
1111     CHECK_NULL_VOID(pipelineContext);
1112     auto safeAreaManager = pipelineContext->GetSafeAreaManager();
1113     CHECK_NULL_VOID(safeAreaManager);
1114     auto top = safeAreaManager->GetSystemSafeArea().top_.Length();
1115     auto bottom = safeAreaManager->GetSystemSafeArea().bottom_.Length();
1116     auto windowGlobalRect = pipelineContext->GetDisplayWindowRectInfo();
1117     float windowsOffsetX = static_cast<float>(windowGlobalRect.GetOffset().GetX());
1118     float windowsOffsetY = static_cast<float>(windowGlobalRect.GetOffset().GetY());
1119     float screenHeight = wrapperSize_.Height();
1120     if (!NearEqual(wrapperSize_.Height(), windowGlobalRect.Height())) {
1121         screenHeight += bottom;
1122     }
1123     SizeF windowGlobalSizeF(windowGlobalRect.Width(), screenHeight - windowsOffsetY);
1124     float topSecurity = 0.0f;
1125     float bottomSecurity = 0.0f;
1126     if (SystemProperties::GetDeviceOrientation() == DeviceOrientation::PORTRAIT) {
1127         topSecurity = static_cast<float>(PORTRAIT_TOP_SECURITY.ConvertToPx());
1128         bottomSecurity = static_cast<float>(PORTRAIT_BOTTOM_SECURITY.ConvertToPx());
1129     } else {
1130         topSecurity = static_cast<float>(LANDSCAPE_TOP_SECURITY.ConvertToPx());
1131         bottomSecurity = static_cast<float>(LANDSCAPE_BOTTOM_SECURITY.ConvertToPx());
1132     }
1133     PreviewMenuParam param;
1134     param.windowGlobalSizeF = windowGlobalSizeF;
1135     param.windowsOffsetX = windowsOffsetX;
1136     param.windowsOffsetY = windowsOffsetY;
1137     param.top = top;
1138     param.bottom = bottom;
1139     param.topSecurity = topSecurity;
1140     param.bottomSecurity = bottomSecurity;
1141     param.previewMenuGap = targetSecurity_;
1142     param.menuItemTotalHeight = menuItemTotalHeight;
1143     if (LessNotEqual(totalSize.Width() + targetSecurity_, windowGlobalSizeF.Width() - paddingStart_ - paddingEnd_)) {
1144         LayoutOtherDeviceLeftPreviewRightMenuLessThan(previewGeometryNode, menuGeometryNode, param, totalSize);
1145     } else {
1146         LayoutOtherDeviceLeftPreviewRightMenuGreateThan(previewGeometryNode, menuGeometryNode, param, totalSize);
1147     }
1148     auto previewSize = previewGeometryNode->GetMarginFrameSize();
1149     auto securityHeight = windowGlobalRect.Height() - topSecurity - top - bottomSecurity - bottom;
1150     if (GreatNotEqual(windowGlobalSizeF.Height(), windowGlobalRect.Height()) &&
1151         GreatNotEqual(previewSize.Height(), securityHeight)) {
1152         previewGeometryNode->SetFrameSize(SizeF(previewSize.Width(), securityHeight));
1153     }
1154 }
1155 
LayoutOtherDevicePreviewMenu(LayoutWrapper * layoutWrapper)1156 void MenuLayoutAlgorithm::LayoutOtherDevicePreviewMenu(LayoutWrapper* layoutWrapper)
1157 {
1158     CHECK_NULL_VOID(layoutWrapper);
1159     auto menuNode = layoutWrapper->GetHostNode();
1160     CHECK_NULL_VOID(menuNode);
1161     auto parentNode = AceType::DynamicCast<FrameNode>(menuNode->GetParent());
1162     CHECK_NULL_VOID(parentNode);
1163     auto menuProp = DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
1164     CHECK_NULL_VOID(menuProp);
1165 
1166     RefPtr<LayoutWrapper> menuLayoutWrapper;
1167     RefPtr<LayoutWrapper> previewLayoutWrapper;
1168     SizeF totalSize = GetPreviewNodeAndMenuNodeTotalSize(parentNode, previewLayoutWrapper, menuLayoutWrapper);
1169     CHECK_NULL_VOID(menuLayoutWrapper);
1170     CHECK_NULL_VOID(previewLayoutWrapper);
1171     RefPtr<GeometryNode> menuGeometryNode = menuLayoutWrapper->GetGeometryNode();
1172     RefPtr<GeometryNode> previewGeometryNode = previewLayoutWrapper->GetGeometryNode();
1173     CHECK_NULL_VOID(menuGeometryNode);
1174     CHECK_NULL_VOID(previewGeometryNode);
1175     auto menuItemTotalHeight = GetMenuItemTotalHeight(menuLayoutWrapper);
1176     LayoutOtherDeviceLeftPreviewRightMenu(previewGeometryNode, menuGeometryNode, totalSize, menuItemTotalHeight);
1177     UpdateScrollAndColumnLayoutConstraint(previewLayoutWrapper, menuLayoutWrapper);
1178     auto previewSize = previewGeometryNode->GetMarginFrameSize();
1179     previewOffset_ = previewGeometryNode->GetFrameOffset();
1180     auto previewOffsetX = previewGeometryNode->GetFrameOffset().GetX();
1181     auto previewOffsetY = previewGeometryNode->GetFrameOffset().GetY();
1182     targetSize_ = previewSize * previewScale_;
1183     targetOffset_ = OffsetF(previewOffsetX + (previewSize.Width() - targetSize_.Width()) / 2,
1184         previewOffsetY + (previewSize.Height() - targetSize_.Height()) / 2);
1185     auto previewHostNode = previewLayoutWrapper->GetHostNode();
1186     CHECK_NULL_VOID(previewHostNode);
1187     auto renderContext = previewHostNode->GetRenderContext();
1188     CHECK_NULL_VOID(renderContext);
1189     renderContext->UpdatePosition(OffsetT<Dimension>(Dimension(previewOffsetX), Dimension(previewOffsetY)));
1190 
1191     auto menuHostNode = menuLayoutWrapper->GetHostNode();
1192     CHECK_NULL_VOID(menuHostNode);
1193     previewOriginOffset_ = targetCenterOffset_ - OffsetF(previewSize.Width() / 2, previewSize.Height() / 2);
1194     previewSize_ = previewSize;
1195     auto menuPattern = menuHostNode->GetPattern<MenuPattern>();
1196     CHECK_NULL_VOID(menuPattern);
1197     menuPattern->SetPreviewOriginOffset(previewOriginOffset_);
1198 }
1199 
LayoutPreviewMenu(LayoutWrapper * layoutWrapper)1200 void MenuLayoutAlgorithm::LayoutPreviewMenu(LayoutWrapper* layoutWrapper)
1201 {
1202     auto paintProperty = GetPaintProperty(layoutWrapper);
1203     CHECK_NULL_VOID(paintProperty);
1204     paintProperty->UpdateEnableArrow(false);
1205     if (SystemProperties::GetDeviceType() == DeviceType::PHONE) {
1206         LayoutNormalPreviewMenu(layoutWrapper);
1207     } else {
1208         LayoutOtherDevicePreviewMenu(layoutWrapper);
1209     }
1210 }
1211 
FixMenuOriginOffset(float beforeAnimationScale,float afterAnimationScale)1212 OffsetF MenuLayoutAlgorithm::FixMenuOriginOffset(float beforeAnimationScale, float afterAnimationScale)
1213 {
1214     auto beforeScalePreviewOffset = OffsetF((previewSize_ * ((1.0f - beforeAnimationScale) / 2)).Width(),
1215         (previewSize_ * ((1.0f - beforeAnimationScale) / 2)).Height());
1216     auto afterScalePreviewOffset = OffsetF((previewSize_ * ((afterAnimationScale - 1.0f) / 2)).Width(),
1217         (previewSize_ * ((afterAnimationScale - 1.0f) / 2)).Height());
1218     auto scaleOffset = afterScalePreviewOffset + beforeScalePreviewOffset;
1219     float x = 0.0f;
1220     float y = 0.0f;
1221     switch (placement_) {
1222         case Placement::BOTTOM_LEFT:
1223         case Placement::LEFT_BOTTOM:
1224             x += scaleOffset.GetX();
1225             y -= scaleOffset.GetY();
1226             break;
1227         case Placement::TOP_RIGHT:
1228         case Placement::RIGHT_TOP:
1229             x -= scaleOffset.GetX();
1230             y += scaleOffset.GetY();
1231             break;
1232         case Placement::TOP_LEFT:
1233         case Placement::LEFT_TOP:
1234             x += scaleOffset.GetX();
1235             y += scaleOffset.GetY();
1236             break;
1237         case Placement::BOTTOM_RIGHT:
1238         case Placement::RIGHT_BOTTOM:
1239             x -= scaleOffset.GetX();
1240             y -= scaleOffset.GetY();
1241             break;
1242         case Placement::BOTTOM:
1243             y -= scaleOffset.GetY();
1244             break;
1245         case Placement::TOP:
1246             y += scaleOffset.GetY();
1247             break;
1248         case Placement::LEFT:
1249             x += scaleOffset.GetX();
1250             break;
1251         case Placement::RIGHT:
1252             x -= scaleOffset.GetX();
1253             break;
1254         default:
1255             x += scaleOffset.GetX();
1256             y -= scaleOffset.GetY();
1257             break;
1258     }
1259     return OffsetF(x, y);
1260 }
1261 
Layout(LayoutWrapper * layoutWrapper)1262 void MenuLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
1263 {
1264     CHECK_NULL_VOID(layoutWrapper);
1265     auto menuProp = DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
1266     CHECK_NULL_VOID(menuProp);
1267     auto menuNode = layoutWrapper->GetHostNode();
1268     CHECK_NULL_VOID(menuNode);
1269     auto menuPattern = menuNode->GetPattern<MenuPattern>();
1270     CHECK_NULL_VOID(menuPattern);
1271 
1272     if (menuPattern->GetPreviewMode() != MenuPreviewMode::NONE) {
1273         LayoutPreviewMenu(layoutWrapper);
1274     }
1275     if (!menuPattern->IsSelectOverlayExtensionMenu() && !menuPattern->IsSelectOverlayCustomMenu()) {
1276         auto geometryNode = layoutWrapper->GetGeometryNode();
1277         CHECK_NULL_VOID(geometryNode);
1278         auto size = geometryNode->GetMarginFrameSize();
1279         bool didNeedArrow = GetIfNeedArrow(layoutWrapper, size);
1280         if (menuPattern->IsSelectMenu()) {
1281             ComputeMenuPositionByAlignType(menuProp, size);
1282             auto offset = ComputeMenuPositionByOffset(menuProp, geometryNode);
1283             position_ += offset;
1284         }
1285         auto menuPosition = MenuLayoutAvoidAlgorithm(menuProp, menuPattern, size, didNeedArrow);
1286         if (menuPattern->IsSelectOverlayRightClickMenu()) {
1287             AdjustSelectOverlayMenuPosition(menuPosition, geometryNode);
1288         }
1289         SetMenuPlacementForAnimation(layoutWrapper);
1290         arrowPosition_ = GetArrowPositionWithPlacement(size);
1291         if (didNeedArrow && arrowPlacement_ != Placement::NONE) {
1292             LayoutArrow(layoutWrapper);
1293         }
1294         geometryNode->SetFrameOffset(menuPosition);
1295         auto pipeline = PipelineBase::GetCurrentContext();
1296         CHECK_NULL_VOID(pipeline);
1297         auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
1298         CHECK_NULL_VOID(menuTheme);
1299         auto beforeAnimationScale = menuTheme->GetPreviewBeforeAnimationScale();
1300         auto afterAnimationScale = menuTheme->GetPreviewAfterAnimationScale();
1301         auto menuOriginOffset = menuPosition - (previewOffset_ - previewOriginOffset_) +
1302                                 FixMenuOriginOffset(beforeAnimationScale, afterAnimationScale);
1303         menuPattern->SetOriginOffset(menuOriginOffset);
1304         auto previewScale = 1.0f;
1305         if (menuPattern->GetPreviewMode() == MenuPreviewMode::IMAGE &&
1306             !NearEqual(menuPattern->GetTargetSize().Width(), previewSize_.Width())) {
1307             previewScale = menuPattern->GetTargetSize().Width() / previewSize_.Width();
1308         }
1309         auto menuEndOffset = menuPosition -
1310             (previewOffset_ - previewOriginOffset_) + FixMenuOriginOffset(previewScale, afterAnimationScale);
1311         menuPattern->SetEndOffset(menuEndOffset);
1312         menuPattern->SetHasLaid(true);
1313     }
1314 
1315     // translate each option by the height of previous options
1316     OffsetF translate;
1317     for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
1318         child->GetGeometryNode()->SetMarginFrameOffset(translate);
1319         child->Layout();
1320         translate += OffsetF(0, child->GetGeometryNode()->GetFrameSize().Height());
1321     }
1322 }
1323 
AdjustSelectOverlayMenuPosition(OffsetF & menuPosition,const RefPtr<GeometryNode> & geometryNode)1324 void MenuLayoutAlgorithm::AdjustSelectOverlayMenuPosition(
1325     OffsetF& menuPosition, const RefPtr<GeometryNode>& geometryNode)
1326 {
1327     auto pipelineContext = GetCurrentPipelineContext();
1328     CHECK_NULL_VOID(pipelineContext);
1329     auto safeAreaManager = pipelineContext->GetSafeAreaManager();
1330     auto keyboardInsert = safeAreaManager->GetKeyboardInset();
1331     auto size = geometryNode->GetFrameSize();
1332     auto start = static_cast<float>(keyboardInsert.start);
1333     if (GreatNotEqual(menuPosition.GetY() + size.Height(), start) && GreatOrEqual(start, size.Height())) {
1334         menuPosition.SetY(menuPosition.GetY() - margin_ - size.Height());
1335     } else if (GreatNotEqual(menuPosition.GetY() + size.Height(), start) && LessNotEqual(start, size.Height()) &&
1336                GreatNotEqual(start, 0)) {
1337         menuPosition.SetY(menuPosition.GetY() - margin_ - size.Height() / 2);
1338     }
1339 }
1340 
SetMenuPlacementForAnimation(LayoutWrapper * layoutWrapper)1341 void MenuLayoutAlgorithm::SetMenuPlacementForAnimation(LayoutWrapper* layoutWrapper)
1342 {
1343     auto menu = layoutWrapper->GetHostNode();
1344     CHECK_NULL_VOID(menu);
1345     auto menuPattern = menu->GetPattern<MenuPattern>();
1346     CHECK_NULL_VOID(menuPattern);
1347     auto menuWrapper = menuPattern->GetMenuWrapper();
1348     CHECK_NULL_VOID(menuWrapper);
1349     auto wrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
1350     CHECK_NULL_VOID(wrapperPattern);
1351     wrapperPattern->SetMenuPlacementAfterLayout(placement_);
1352 }
1353 
LayoutArrow(const LayoutWrapper * layoutWrapper)1354 void MenuLayoutAlgorithm::LayoutArrow(const LayoutWrapper* layoutWrapper)
1355 {
1356     auto paintProperty = GetPaintProperty(layoutWrapper);
1357     CHECK_NULL_VOID(paintProperty);
1358     paintProperty->UpdateArrowPosition(arrowPosition_);
1359     paintProperty->UpdateArrowPlacement(arrowPlacement_);
1360 }
1361 
GetPaintProperty(const LayoutWrapper * layoutWrapper)1362 RefPtr<MenuPaintProperty> MenuLayoutAlgorithm::GetPaintProperty(const LayoutWrapper* layoutWrapper)
1363 {
1364     auto menuNode = layoutWrapper->GetHostNode();
1365     CHECK_NULL_RETURN(menuNode, nullptr);
1366     auto paintProperty = menuNode->GetPaintProperty<MenuPaintProperty>();
1367     CHECK_NULL_RETURN(paintProperty, nullptr);
1368     return paintProperty;
1369 }
1370 
GetIfNeedArrow(const LayoutWrapper * layoutWrapper,const SizeF & menuSize)1371 bool MenuLayoutAlgorithm::GetIfNeedArrow(const LayoutWrapper* layoutWrapper, const SizeF& menuSize)
1372 {
1373     CHECK_NULL_RETURN(layoutWrapper, false);
1374     auto menuNode = layoutWrapper->GetHostNode();
1375     CHECK_NULL_RETURN(menuNode, false);
1376     auto menuPattern = menuNode->GetPattern<MenuPattern>();
1377     CHECK_NULL_RETURN(menuPattern, false);
1378     auto menuProp = DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
1379     CHECK_NULL_RETURN(menuProp, false);
1380     auto paintProperty = GetPaintProperty(layoutWrapper);
1381     CHECK_NULL_RETURN(paintProperty, false);
1382     propNeedArrow_ = paintProperty->GetEnableArrow().value_or(false);
1383 
1384     auto pipeline = PipelineBase::GetCurrentContext();
1385     CHECK_NULL_RETURN(pipeline, false);
1386     auto selectThemePtr = pipeline->GetTheme<SelectTheme>();
1387     CHECK_NULL_RETURN(selectThemePtr, false);
1388     if (!propNeedArrow_ || !menuProp->GetMenuPlacement().has_value()) {
1389         return false;
1390     }
1391 
1392     propArrowOffset_ = paintProperty->GetArrowOffset().value_or(Dimension(0));
1393     menuRadius_ = selectThemePtr->GetMenuBorderRadius().ConvertToPx();
1394     arrowMinLimit_ = menuRadius_ + ARROW_WIDTH.ConvertToPx() / 2.0;
1395     arrowWidth_ = ARROW_WIDTH.ConvertToPx();
1396     auto targetSpaceReal = TARGET_SPACE.ConvertToPx();
1397 
1398     if (setHorizontal_.find(placement_) != setHorizontal_.end()) {
1399         if (menuSize.Height() >= menuRadius_ * 2 + arrowWidth_) {
1400             arrowInMenu_ = true;
1401             targetSpace_ = targetSpaceReal;
1402         }
1403     }
1404 
1405     if (setVertical_.find(placement_) != setVertical_.end()) {
1406         if (menuSize.Width() >= menuRadius_ * 2 + arrowWidth_) {
1407             arrowInMenu_ = true;
1408             targetSpace_ = targetSpaceReal;
1409         }
1410     }
1411 
1412     return menuPattern->IsContextMenu() && !targetTag_.empty() && arrowInMenu_;
1413 }
1414 
UpdatePropArrowOffset()1415 void MenuLayoutAlgorithm::UpdatePropArrowOffset()
1416 {
1417     if (propArrowOffset_.IsValid()) {
1418         if (propArrowOffset_.Unit() == DimensionUnit::PERCENT) {
1419             propArrowOffset_.SetValue(std::clamp(propArrowOffset_.Value(), 0.0, 1.0));
1420         }
1421         return;
1422     }
1423     switch (arrowPlacement_) {
1424         case Placement::LEFT:
1425         case Placement::RIGHT:
1426         case Placement::TOP:
1427         case Placement::BOTTOM:
1428             propArrowOffset_ = ARROW_HALF_PERCENT_VALUE;
1429             break;
1430         case Placement::TOP_LEFT:
1431         case Placement::BOTTOM_LEFT:
1432         case Placement::LEFT_TOP:
1433         case Placement::RIGHT_TOP:
1434             propArrowOffset_ = ARROW_ZERO_PERCENT_VALUE;
1435             break;
1436         case Placement::TOP_RIGHT:
1437         case Placement::BOTTOM_RIGHT:
1438         case Placement::LEFT_BOTTOM:
1439         case Placement::RIGHT_BOTTOM:
1440             propArrowOffset_ = ARROW_ONE_HUNDRED_PERCENT_VALUE;
1441             break;
1442         default:
1443             break;
1444     }
1445 }
1446 
UpdateArrowOffsetWithMenuLimit(const SizeF & menuSize)1447 void MenuLayoutAlgorithm::UpdateArrowOffsetWithMenuLimit(const SizeF& menuSize)
1448 {
1449     UpdatePropArrowOffset();
1450 
1451     if (setHorizontal_.find(arrowPlacement_) != setHorizontal_.end()) {
1452         if (menuSize.Height() >= menuRadius_ * 2 + arrowWidth_) {
1453             float range = menuSize.Height() - menuRadius_ * 2  - arrowWidth_;
1454             float tempOffset = propArrowOffset_.Unit() == DimensionUnit::PERCENT ? propArrowOffset_.Value() * range :
1455                 propArrowOffset_.ConvertToPx();
1456             arrowOffset_ = std::clamp(tempOffset, 0.0f, range);
1457         }
1458     }
1459 
1460     if (setVertical_.find(arrowPlacement_) != setVertical_.end()) {
1461         if (menuSize.Width() >= menuRadius_ * 2 + arrowWidth_) {
1462             arrowInMenu_ = true;
1463             float range = menuSize.Width() - menuRadius_ * 2  - arrowWidth_;
1464             float tempOffset = propArrowOffset_.Unit() == DimensionUnit::PERCENT ? propArrowOffset_.Value() * range :
1465                 propArrowOffset_.ConvertToPx();
1466             arrowOffset_ = std::clamp(tempOffset, 0.0f, range);
1467         }
1468     }
1469 }
1470 
ComputeMenuPositionByAlignType(const RefPtr<MenuLayoutProperty> & menuProp,const SizeF & menuSize)1471 void MenuLayoutAlgorithm::ComputeMenuPositionByAlignType(
1472     const RefPtr<MenuLayoutProperty>& menuProp, const SizeF& menuSize)
1473 {
1474     auto alignType = menuProp->GetAlignType().value_or(MenuAlignType::START);
1475     auto targetSize = menuProp->GetTargetSizeValue(SizeF());
1476     switch (alignType) {
1477         case MenuAlignType::CENTER: {
1478             position_.AddX(targetSize.Width() / 2.0f - menuSize.Width() / 2.0f);
1479             break;
1480         }
1481         case MenuAlignType::END: {
1482             position_.AddX(targetSize.Width() - menuSize.Width());
1483             break;
1484         }
1485         default:
1486             break;
1487     }
1488 }
1489 
ComputeMenuPositionByOffset(const RefPtr<MenuLayoutProperty> & menuProp,const RefPtr<GeometryNode> & geometryNode)1490 OffsetF MenuLayoutAlgorithm::ComputeMenuPositionByOffset(
1491     const RefPtr<MenuLayoutProperty>& menuProp, const RefPtr<GeometryNode>& geometryNode)
1492 {
1493     CHECK_NULL_RETURN(menuProp, OffsetF(0, 0));
1494     CHECK_NULL_RETURN(geometryNode, OffsetF(0, 0));
1495 
1496     const auto& layoutConstraint = menuProp->GetLayoutConstraint();
1497     CHECK_NULL_RETURN(layoutConstraint, OffsetF(0, 0));
1498     auto menuAlignOffset = menuProp->GetOffset().value_or(
1499         DimensionOffset(Dimension(0, DimensionUnit::VP), Dimension(0, DimensionUnit::VP)));
1500 
1501     auto menuSize = geometryNode->GetFrameSize();
1502     auto menuTrimOffsetX =
1503         ConvertToPx(CalcLength(menuAlignOffset.GetX()), layoutConstraint->scaleProperty, menuSize.Width());
1504     auto menuTrimOffsetY =
1505         ConvertToPx(CalcLength(menuAlignOffset.GetY()), layoutConstraint->scaleProperty, menuSize.Height());
1506     OffsetF menuTrimOffset = OffsetF(menuTrimOffsetX.value_or(0.0), menuTrimOffsetY.value_or(0.0));
1507     return menuTrimOffset;
1508 }
1509 
GetCurrentPipelineContext()1510 RefPtr<PipelineContext> MenuLayoutAlgorithm::GetCurrentPipelineContext()
1511 {
1512     auto containerId = Container::CurrentId();
1513     RefPtr<PipelineContext> context;
1514     if (containerId >= MIN_SUBCONTAINER_ID) {
1515         auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(containerId);
1516         auto parentContainer = AceEngine::Get().GetContainer(parentContainerId);
1517         CHECK_NULL_RETURN(parentContainer, nullptr);
1518         context = DynamicCast<PipelineContext>(parentContainer->GetPipelineContext());
1519     } else {
1520         context = PipelineContext::GetCurrentContext();
1521     }
1522     return context;
1523 }
1524 
MenuLayoutAvoidAlgorithm(const RefPtr<MenuLayoutProperty> & menuProp,const RefPtr<MenuPattern> & menuPattern,const SizeF & size,bool didNeedArrow)1525 OffsetF MenuLayoutAlgorithm::MenuLayoutAvoidAlgorithm(const RefPtr<MenuLayoutProperty>& menuProp,
1526     const RefPtr<MenuPattern>& menuPattern, const SizeF& size, bool didNeedArrow)
1527 {
1528     auto pipelineContext = GetCurrentPipelineContext();
1529     CHECK_NULL_RETURN(pipelineContext, OffsetF(0, 0));
1530     CHECK_NULL_RETURN(menuProp, OffsetF(0, 0));
1531     CHECK_NULL_RETURN(menuPattern, OffsetF(0, 0));
1532     float x = 0.0f;
1533     float y = 0.0f;
1534     if (menuProp->GetMenuPlacement().has_value() && (targetSize_.Width() > 0.0 || targetSize_.Height() > 0.0)) {
1535         placement_ = menuProp->GetMenuPlacement().value();
1536         auto childOffset = GetChildPosition(size, didNeedArrow);
1537         x = childOffset.GetX();
1538         y = childOffset.GetY();
1539     } else {
1540         x = HorizontalLayout(size, position_.GetX(), menuPattern->IsSelectMenu()) + positionOffset_.GetX();
1541         y = VerticalLayout(size, position_.GetY(), menuPattern->IsContextMenu()) + positionOffset_.GetY();
1542     }
1543     x = std::clamp(static_cast<double>(x), static_cast<double>(paddingStart_),
1544         static_cast<double>(wrapperRect_.Right() - size.Width() - paddingEnd_));
1545     float yMinAvoid = wrapperRect_.Top() + paddingTop_;
1546     float yMaxAvoid = wrapperRect_.Bottom() - paddingBottom_ - size.Height();
1547     y = std::clamp(y, yMinAvoid, yMaxAvoid);
1548     return { x, y };
1549 }
1550 
LimitContainerModalMenuRect(double & rectWidth,double & rectHeight)1551 void MenuLayoutAlgorithm::LimitContainerModalMenuRect(double& rectWidth, double& rectHeight)
1552 {
1553     auto containerOffsetX = static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()) +
1554                           static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()) +
1555                           static_cast<float>(CONTENT_PADDING.ConvertToPx());
1556     auto containerOffsetY = static_cast<float>(CONTAINER_TITLE_HEIGHT.ConvertToPx()) +
1557                           static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()) +
1558                           static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx());
1559     rectWidth -= containerOffsetX;
1560     rectHeight -= containerOffsetY;
1561 }
1562 
UpdateConstraintWidth(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)1563 void MenuLayoutAlgorithm::UpdateConstraintWidth(LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
1564 {
1565     RefPtr<GridColumnInfo> columnInfo;
1566     columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
1567     auto menuNode = layoutWrapper->GetHostNode();
1568     CHECK_NULL_VOID(menuNode);
1569     auto menuPattern = menuNode->GetPattern<MenuPattern>();
1570     CHECK_NULL_VOID(menuPattern);
1571     if (menuPattern && menuPattern->IsSelectOverlayExtensionMenu()) {
1572         columnInfo->GetParent()->BuildColumnWidth();
1573     } else {
1574         columnInfo->GetParent()->BuildColumnWidth(wrapperSize_.Width());
1575     }
1576     auto menuLayoutProperty = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
1577     CHECK_NULL_VOID(menuLayoutProperty);
1578     // set max width
1579     const auto& padding = menuLayoutProperty->CreatePaddingAndBorder();
1580     auto maxHorizontalSpace = std::max(leftSpace_, rightSpace_) - 2.0f * padding.Width();
1581     auto maxWidth = static_cast<float>(columnInfo->GetWidth(GetMaxGridCounts(columnInfo)));
1582     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
1583         maxWidth = std::min(maxHorizontalSpace, maxWidth);
1584     }
1585     maxWidth = std::min(constraint.maxSize.Width(), maxWidth);
1586     constraint.maxSize.SetWidth(maxWidth);
1587 }
1588 
UpdateConstraintHeight(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)1589 void MenuLayoutAlgorithm::UpdateConstraintHeight(LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
1590 {
1591     auto pipelineContext = GetCurrentPipelineContext();
1592     CHECK_NULL_VOID(pipelineContext);
1593     auto menuPattern = layoutWrapper->GetHostNode()->GetPattern<MenuPattern>();
1594     CHECK_NULL_VOID(menuPattern);
1595 
1596     float maxAvailableHeight = wrapperRect_.Height();
1597     float maxSpaceHeight = maxAvailableHeight * HEIGHT_CONSTRAINT_FACTOR;
1598     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
1599         if (menuPattern->IsHeightModifiedBySelect()) {
1600             auto menuLayoutProps = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
1601             auto selectModifiedHeight = menuLayoutProps->GetSelectModifiedHeight().value();
1602             if (selectModifiedHeight < maxSpaceHeight) {
1603                 maxSpaceHeight = selectModifiedHeight;
1604             }
1605         }
1606     }
1607     constraint.maxSize.SetHeight(maxSpaceHeight);
1608 }
1609 
CreateChildConstraint(LayoutWrapper * layoutWrapper)1610 LayoutConstraintF MenuLayoutAlgorithm::CreateChildConstraint(LayoutWrapper* layoutWrapper)
1611 {
1612     auto menuLayoutProperty = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
1613     CHECK_NULL_RETURN(menuLayoutProperty, LayoutConstraintF());
1614 
1615     auto childConstraint = menuLayoutProperty->CreateChildConstraint();
1616     UpdateConstraintWidth(layoutWrapper, childConstraint);
1617     UpdateConstraintHeight(layoutWrapper, childConstraint);
1618     UpdateConstraintBaseOnOptions(layoutWrapper, childConstraint);
1619     return childConstraint;
1620 }
1621 
UpdateConstraintBaseOnOptions(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)1622 void MenuLayoutAlgorithm::UpdateConstraintBaseOnOptions(LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
1623 {
1624     auto menuNode = layoutWrapper->GetHostNode();
1625     CHECK_NULL_VOID(menuNode);
1626     auto menuPattern = menuNode->GetPattern<MenuPattern>();
1627     CHECK_NULL_VOID(menuPattern);
1628     auto options = menuPattern->GetOptions();
1629     if (options.empty()) {
1630         return;
1631     }
1632     auto optionConstraint = constraint;
1633     RefPtr<GridColumnInfo> columnInfo;
1634     columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
1635     columnInfo->GetParent()->BuildColumnWidth(wrapperSize_.Width());
1636     auto minWidth = static_cast<float>(columnInfo->GetWidth(MIN_GRID_COUNTS));
1637     optionConstraint.maxSize.MinusWidth(optionPadding_ * 2.0f);
1638     optionConstraint.minSize.SetWidth(minWidth - optionPadding_ * 2.0f);
1639     auto maxChildrenWidth = optionConstraint.minSize.Width();
1640     auto optionsLayoutWrapper = GetOptionsLayoutWrappper(layoutWrapper);
1641     for (const auto& optionWrapper : optionsLayoutWrapper) {
1642         optionWrapper->GetLayoutProperty()->ResetCalcMinSize();
1643         optionWrapper->Measure(optionConstraint);
1644         auto childSize = optionWrapper->GetGeometryNode()->GetMarginFrameSize();
1645         maxChildrenWidth = std::max(maxChildrenWidth, childSize.Width());
1646     }
1647     if (menuPattern->IsSelectOverlayExtensionMenu()) {
1648         maxChildrenWidth = std::min(maxChildrenWidth, optionConstraint.maxSize.Width());
1649         UpdateOptionConstraint(optionsLayoutWrapper, maxChildrenWidth);
1650         constraint.minSize.SetWidth(maxChildrenWidth);
1651         return;
1652     }
1653     UpdateOptionConstraint(optionsLayoutWrapper, maxChildrenWidth);
1654     constraint.minSize.SetWidth(maxChildrenWidth + optionPadding_ * 2.0f);
1655 }
1656 
GetOptionsLayoutWrappper(LayoutWrapper * layoutWrapper)1657 std::list<RefPtr<LayoutWrapper>> MenuLayoutAlgorithm::GetOptionsLayoutWrappper(LayoutWrapper* layoutWrapper)
1658 {
1659     std::list<RefPtr<LayoutWrapper>> optionsWrapper;
1660     auto scrollWrapper = layoutWrapper->GetOrCreateChildByIndex(0);
1661     CHECK_NULL_RETURN(scrollWrapper, optionsWrapper);
1662     auto columnWrapper = scrollWrapper->GetOrCreateChildByIndex(0);
1663     CHECK_NULL_RETURN(columnWrapper, optionsWrapper);
1664     optionsWrapper = columnWrapper->GetAllChildrenWithBuild();
1665     return optionsWrapper;
1666 }
1667 
UpdateOptionConstraint(std::list<RefPtr<LayoutWrapper>> & options,float width)1668 void MenuLayoutAlgorithm::UpdateOptionConstraint(std::list<RefPtr<LayoutWrapper>>& options, float width)
1669 {
1670     for (const auto& option : options) {
1671         auto optionLayoutProps = option->GetLayoutProperty();
1672         CHECK_NULL_VOID(optionLayoutProps);
1673         optionLayoutProps->UpdateCalcMinSize(CalcSize(CalcLength(width), std::nullopt));
1674     }
1675 }
1676 
1677 // return vertical offset
VerticalLayout(const SizeF & size,float position,bool isContextMenu)1678 float MenuLayoutAlgorithm::VerticalLayout(const SizeF &size, float position, bool isContextMenu)
1679 {
1680     placement_ = Placement::BOTTOM;
1681     // can put menu below click point
1682     if (GreatOrEqual(bottomSpace_, size.Height())) {
1683         return position + margin_;
1684     }
1685     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) && isContextMenu) {
1686         if (LessNotEqual(bottomSpace_, size.Height()) && LessNotEqual(size.Height(), wrapperRect_.Height())) {
1687             return wrapperRect_.Bottom() - size.Height() - paddingBottom_;
1688         }
1689         // can't fit in screen, line up with top of the screen
1690         return wrapperRect_.Top() + paddingTop_;
1691     } else {
1692         float wrapperHeight = wrapperSize_.Height();
1693         // put menu above click point
1694         if (GreatOrEqual(topSpace_, size.Height())) {
1695             // menu show on top
1696             placement_ = Placement::TOP;
1697             return topSpace_ - size.Height() + margin_;
1698         }
1699         // line up bottom of menu with bottom of the screen
1700         if (LessNotEqual(size.Height(), wrapperHeight)) {
1701             return wrapperHeight - size.Height();
1702         }
1703         // can't fit in screen, line up with top of the screen
1704         return 0.0f;
1705     }
1706 }
1707 
1708 // returns horizontal offset
HorizontalLayout(const SizeF & size,float position,bool isSelectMenu)1709 float MenuLayoutAlgorithm::HorizontalLayout(const SizeF& size, float position, bool isSelectMenu)
1710 {
1711     float wrapperWidth = wrapperSize_.Width();
1712     // can fit menu on the right side of position
1713     if (rightSpace_ >= size.Width()) {
1714         return position + margin_;
1715     }
1716 
1717     // fit menu on the left side
1718     if (!isSelectMenu && leftSpace_ >= size.Width()) {
1719         return position - size.Width();
1720     }
1721 
1722     // line up right side of menu with right boundary of the screen
1723     if (size.Width() < wrapperWidth) {
1724         if (isSelectMenu) {
1725             return position;
1726         }
1727         return wrapperWidth - size.Width();
1728     }
1729 
1730     // can't fit in screen, line up with left side of the screen
1731     return 0.0f;
1732 }
1733 
GetPositionWithPlacement(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1734 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacement(
1735     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1736 {
1737     OffsetF childPosition;
1738 
1739     auto func = placementFuncMap_.find(placement_);
1740     if (func != placementFuncMap_.end()) {
1741         auto placementFunc = func->second;
1742         if (placementFunc != nullptr) {
1743             childPosition = (this->*placementFunc)(childSize, topPosition, bottomPosition);
1744         }
1745     }
1746     return childPosition;
1747 }
1748 
GetArrowPositionWithPlacement(const SizeF & menuSize)1749 OffsetF MenuLayoutAlgorithm::GetArrowPositionWithPlacement(const SizeF& menuSize)
1750 {
1751     UpdateArrowOffsetWithMenuLimit(menuSize);
1752     auto addArrowOffsetToArrowMin = arrowOffset_ + arrowMinLimit_;
1753     auto space_ = ARROW_HIGHT.ConvertToPx();
1754     OffsetF childPosition;
1755     switch (arrowPlacement_) {
1756         case Placement::TOP:
1757         case Placement::TOP_LEFT:
1758         case Placement::TOP_RIGHT:
1759             childPosition = OffsetF(addArrowOffsetToArrowMin, menuSize.Height() + space_);
1760             break;
1761         case Placement::BOTTOM:
1762         case Placement::BOTTOM_LEFT:
1763         case Placement::BOTTOM_RIGHT:
1764             childPosition = OffsetF(addArrowOffsetToArrowMin, -space_);
1765             break;
1766         case Placement::LEFT:
1767         case Placement::LEFT_TOP:
1768         case Placement::LEFT_BOTTOM:
1769             childPosition = OffsetF(menuSize.Width() + space_, addArrowOffsetToArrowMin);
1770             break;
1771         case Placement::RIGHT:
1772         case Placement::RIGHT_TOP:
1773         case Placement::RIGHT_BOTTOM:
1774             childPosition = OffsetF(-space_, addArrowOffsetToArrowMin);
1775             break;
1776         default:
1777             break;
1778     }
1779     return childPosition;
1780 }
1781 
GetMenuWrapperOffset(const LayoutWrapper * layoutWrapper)1782 OffsetF MenuLayoutAlgorithm::GetMenuWrapperOffset(const LayoutWrapper* layoutWrapper)
1783 {
1784     CHECK_NULL_RETURN(layoutWrapper, OffsetF());
1785     auto menuNode = layoutWrapper->GetHostNode();
1786     CHECK_NULL_RETURN(menuNode, OffsetF());
1787     return menuNode->GetParentGlobalOffsetDuringLayout();
1788 }
1789 
InitTargetSizeAndPosition(const LayoutWrapper * layoutWrapper,bool isContextMenu,const RefPtr<MenuPattern> & menuPattern)1790 void MenuLayoutAlgorithm::InitTargetSizeAndPosition(
1791     const LayoutWrapper* layoutWrapper, bool isContextMenu, const RefPtr<MenuPattern>& menuPattern)
1792 {
1793     CHECK_NULL_VOID(layoutWrapper);
1794     CHECK_NULL_VOID(menuPattern);
1795     auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
1796     CHECK_NULL_VOID(targetNode);
1797     auto geometryNode = targetNode->GetGeometryNode();
1798     CHECK_NULL_VOID(geometryNode);
1799     auto props = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
1800     CHECK_NULL_VOID(props);
1801     bool expandDisplay = menuPattern->GetMenuExpandDisplay();
1802     if (props->GetIsRectInTargetValue(false)) {
1803         targetSize_ = props->GetTargetSizeValue(SizeF());
1804         targetOffset_ = props->GetMenuOffsetValue(OffsetF());
1805     } else {
1806         targetSize_ = geometryNode->GetFrameSize();
1807         targetOffset_ = targetNode->GetPaintRectOffset();
1808     }
1809     menuPattern->SetTargetSize(targetSize_);
1810     auto pipelineContext = GetCurrentPipelineContext();
1811     CHECK_NULL_VOID(pipelineContext);
1812     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
1813         expandDisplay = true;
1814     }
1815     if (((isContextMenu && expandDisplay) || hierarchicalParameters_) && (targetTag_ != V2::SELECT_ETS_TAG)) {
1816         auto windowGlobalRect = pipelineContext->GetDisplayWindowRectInfo();
1817         float windowsOffsetX = static_cast<float>(windowGlobalRect.GetOffset().GetX());
1818         float windowsOffsetY = static_cast<float>(windowGlobalRect.GetOffset().GetY());
1819         targetOffset_ += OffsetF(windowsOffsetX, windowsOffsetY);
1820         OffsetF offset = GetMenuWrapperOffset(layoutWrapper);
1821         targetOffset_ -= offset;
1822         return;
1823     }
1824 
1825     auto windowManager = pipelineContext->GetWindowManager();
1826     auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL && windowManager &&
1827                             windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING;
1828     if (isContainerModal) {
1829         auto newOffsetX = static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()) +
1830                           static_cast<float>(CONTENT_PADDING.ConvertToPx());
1831         auto newOffsetY = static_cast<float>(CONTAINER_TITLE_HEIGHT.ConvertToPx()) +
1832                           static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx());
1833         targetOffset_ -= OffsetF(newOffsetX, newOffsetY);
1834     } else {
1835         OffsetF offset = GetMenuWrapperOffset(layoutWrapper);
1836         targetOffset_ -= offset;
1837     }
1838 }
1839 
FitToScreen(const OffsetF & position,const SizeF & childSize,bool didNeedArrow)1840 OffsetF MenuLayoutAlgorithm::FitToScreen(const OffsetF& position, const SizeF& childSize, bool didNeedArrow)
1841 {
1842     OffsetF afterOffsetPosition;
1843     auto originPosition = position;
1844 
1845     if (NearEqual(positionOffset_, OffsetF(0.0f, 0.0f)) && (!didNeedArrow || arrowPlacement_ == Placement::NONE)) {
1846         afterOffsetPosition = AddTargetSpace(originPosition);
1847     } else {
1848         afterOffsetPosition = AddOffset(originPosition);
1849     }
1850 
1851     if (!CheckPosition(afterOffsetPosition, childSize)) {
1852         return OffsetF(0.0f, 0.0f);
1853     }
1854 
1855     return afterOffsetPosition;
1856 }
1857 
GetChildPosition(const SizeF & childSize,bool didNeedArrow)1858 OffsetF MenuLayoutAlgorithm::GetChildPosition(const SizeF& childSize, bool didNeedArrow)
1859 {
1860     OffsetF bottomPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
1861         targetOffset_.GetY() + targetSize_.Height() + targetSpace_);
1862     OffsetF topPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
1863         targetOffset_.GetY() - childSize.Height() - targetSpace_);
1864     OffsetF defaultPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
1865         targetOffset_.GetY() + (targetSize_.Height() - childSize.Height()) / 2.0);
1866 
1867     OffsetF childPosition;
1868     OffsetF position = defaultPosition;
1869     auto positionOffset = positionOffset_;
1870     std::vector<Placement> currentPlacementStates = PLACEMENT_STATES.find(Placement::BOTTOM_LEFT)->second;
1871     if (PLACEMENT_STATES.find(placement_) != PLACEMENT_STATES.end()) {
1872         currentPlacementStates = PLACEMENT_STATES.find(placement_)->second;
1873     }
1874     size_t step = ALIGNMENT_STEP_OFFSET;
1875     if (placement_ <= Placement::BOTTOM) {
1876         step += 1;
1877     }
1878     for (size_t i = 0, len = currentPlacementStates.size(); i < len; i++) {
1879         placement_ = currentPlacementStates[i];
1880         if (placement_ == Placement::NONE) {
1881             break;
1882         }
1883         if (i >= step) {
1884             positionOffset_ = OffsetF(0.0f, 0.0f);
1885         }
1886         childPosition = GetPositionWithPlacement(childSize, topPosition, bottomPosition);
1887         position = FitToScreen(childPosition, childSize, didNeedArrow);
1888         if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
1889             continue;
1890         }
1891         break;
1892     }
1893     if (placement_ == Placement::NONE) {
1894         position = GetAdjustPosition(currentPlacementStates, step, childSize, topPosition, bottomPosition);
1895         if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
1896             position = defaultPosition;
1897         }
1898     }
1899     positionOffset_ = positionOffset;
1900     arrowPlacement_ = placement_;
1901 
1902     return position;
1903 }
1904 
GetAdjustPosition(std::vector<Placement> & currentPlacementStates,size_t step,const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1905 OffsetF MenuLayoutAlgorithm::GetAdjustPosition(std::vector<Placement>& currentPlacementStates, size_t step,
1906     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1907 {
1908     OffsetF childPosition;
1909     OffsetF position;
1910     for (size_t i = 0, len = currentPlacementStates.size(); i < len;) {
1911         placement_ = currentPlacementStates[i];
1912         if (placement_ == Placement::NONE) {
1913             break;
1914         }
1915         childPosition = GetPositionWithPlacement(childSize, topPosition, bottomPosition);
1916         position = AdjustPosition(childPosition, childSize.Width(), childSize.Height(), targetSecurity_);
1917         if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
1918             i += step;
1919             continue;
1920         }
1921         break;
1922     }
1923     return position;
1924 }
1925 
AdjustPosition(const OffsetF & position,float width,float height,float space)1926 OffsetF MenuLayoutAlgorithm::AdjustPosition(const OffsetF& position, float width, float height, float space)
1927 {
1928     float xMax = 0.0f;
1929     float yMax = 0.0f;
1930     float xMin = paddingStart_;
1931     float yMin = std::max(1.0f, static_cast<float>(wrapperRect_.Top()) + paddingTop_);
1932     float wrapperBottom = wrapperRect_.Bottom();
1933     switch (placement_) {
1934         case Placement::LEFT_TOP:
1935         case Placement::LEFT_BOTTOM:
1936         case Placement::LEFT: {
1937             xMax = std::min(targetOffset_.GetX() - width - space, wrapperSize_.Width() - paddingEnd_ - width);
1938             yMax = wrapperBottom - height - paddingBottom_;
1939             break;
1940         }
1941         case Placement::RIGHT_TOP:
1942         case Placement::RIGHT_BOTTOM:
1943         case Placement::RIGHT: {
1944             xMin = std::max(targetOffset_.GetX() + targetSize_.Width() + space, paddingStart_);
1945             xMax = wrapperSize_.Width() - width - paddingEnd_;
1946             yMax = wrapperBottom - height - paddingBottom_;
1947             break;
1948         }
1949         case Placement::TOP_LEFT:
1950         case Placement::TOP_RIGHT:
1951         case Placement::TOP: {
1952             xMax = wrapperSize_.Width() - width - paddingEnd_;
1953             yMax = std::min(targetOffset_.GetY() - height - space, wrapperBottom - paddingBottom_ - height);
1954             break;
1955         }
1956         case Placement::BOTTOM_LEFT:
1957         case Placement::BOTTOM_RIGHT:
1958         case Placement::BOTTOM: {
1959             xMax = wrapperSize_.Width() - width - paddingEnd_;
1960             yMin = std::max(targetOffset_.GetY() + targetSize_.Height() + space, yMin);
1961             yMax = wrapperBottom - height - paddingBottom_;
1962             break;
1963         }
1964         default:
1965             break;
1966     }
1967     if (xMax < xMin || yMax < yMin) {
1968         return OffsetF(0.0f, 0.0f);
1969     }
1970     auto x = std::clamp(position.GetX(), xMin, xMax);
1971     auto y = std::clamp(position.GetY(), yMin, yMax);
1972     return OffsetF(x, y);
1973 }
1974 
AddTargetSpace(const OffsetF & position)1975 OffsetF MenuLayoutAlgorithm::AddTargetSpace(const OffsetF& position)
1976 {
1977     auto x = position.GetX();
1978     auto y = position.GetY();
1979     switch (placement_) {
1980         case Placement::BOTTOM_LEFT:
1981         case Placement::BOTTOM_RIGHT:
1982         case Placement::BOTTOM: {
1983             y += targetSecurity_;
1984             break;
1985         }
1986         case Placement::TOP_LEFT:
1987         case Placement::TOP_RIGHT:
1988         case Placement::TOP: {
1989             y -= targetSecurity_;
1990             break;
1991         }
1992         case Placement::RIGHT_TOP:
1993         case Placement::RIGHT_BOTTOM:
1994         case Placement::RIGHT: {
1995             x += targetSecurity_;
1996             break;
1997         }
1998         case Placement::LEFT_TOP:
1999         case Placement::LEFT_BOTTOM:
2000         case Placement::LEFT: {
2001             x -= targetSecurity_;
2002             break;
2003         }
2004         default: {
2005             y += targetSecurity_;
2006             break;
2007         }
2008     }
2009     return OffsetF(x, y);
2010 }
2011 
AddOffset(const OffsetF & position)2012 OffsetF MenuLayoutAlgorithm::AddOffset(const OffsetF& position)
2013 {
2014     auto x = position.GetX();
2015     auto y = position.GetY();
2016     switch (placement_) {
2017         case Placement::BOTTOM_LEFT:
2018         case Placement::BOTTOM_RIGHT:
2019         case Placement::BOTTOM: {
2020             x += positionOffset_.GetX();
2021             y += positionOffset_.GetY();
2022             break;
2023         }
2024         case Placement::TOP_LEFT:
2025         case Placement::TOP_RIGHT:
2026         case Placement::TOP: {
2027             x += positionOffset_.GetX();
2028             y -= positionOffset_.GetY();
2029             break;
2030         }
2031         case Placement::RIGHT_TOP:
2032         case Placement::RIGHT_BOTTOM:
2033         case Placement::RIGHT: {
2034             x += positionOffset_.GetX();
2035             y += positionOffset_.GetY();
2036             break;
2037         }
2038         case Placement::LEFT_TOP:
2039         case Placement::LEFT_BOTTOM:
2040         case Placement::LEFT: {
2041             x -= positionOffset_.GetX();
2042             y += positionOffset_.GetY();
2043             break;
2044         }
2045         default: {
2046             x += positionOffset_.GetX();
2047             y += positionOffset_.GetY();
2048             break;
2049         }
2050     }
2051     return OffsetF(x, y);
2052 }
2053 
CheckPositionInPlacementRect(const Rect & rect,const OffsetF & position,const SizeF & childSize)2054 bool MenuLayoutAlgorithm::CheckPositionInPlacementRect(
2055     const Rect& rect, const OffsetF& position, const SizeF& childSize)
2056 {
2057     auto x = position.GetX();
2058     auto y = position.GetY();
2059     if (x < rect.Left() || (x + childSize.Width()) > rect.Right() || y < rect.Top() ||
2060         (y + childSize.Height()) > rect.Bottom()) {
2061         return false;
2062     }
2063     return true;
2064 }
2065 
CheckPosition(const OffsetF & position,const SizeF & childSize)2066 bool MenuLayoutAlgorithm::CheckPosition(const OffsetF& position, const SizeF& childSize)
2067 {
2068     float targetOffsetX = targetOffset_.GetX();
2069     float targetOffsetY = targetOffset_.GetY();
2070     float yAvoid = wrapperRect_.Top() + paddingTop_;
2071     Rect rect;
2072     switch (placement_) {
2073         case Placement::BOTTOM_LEFT:
2074         case Placement::BOTTOM_RIGHT:
2075         case Placement::BOTTOM: {
2076             auto y = std::max(targetOffsetY + targetSize_.Height(), yAvoid);
2077             auto height = std::min(
2078                 static_cast<float>(wrapperRect_.Bottom()) - paddingBottom_ - targetOffsetY - targetSize_.Height(),
2079                 wrapperSize_.Height() - paddingBottom_ - paddingTop_);
2080             rect.SetRect(
2081                 paddingStart_, y, wrapperSize_.Width() - paddingEnd_ - paddingStart_, height);
2082             break;
2083         }
2084         case Placement::TOP_LEFT:
2085         case Placement::TOP_RIGHT:
2086         case Placement::TOP: {
2087             auto height = std::min(targetOffsetY - yAvoid, wrapperSize_.Height() - paddingTop_ - paddingBottom_);
2088             rect.SetRect(paddingStart_, yAvoid, wrapperSize_.Width() - paddingEnd_ - paddingStart_, height);
2089             break;
2090         }
2091         case Placement::RIGHT_TOP:
2092         case Placement::RIGHT_BOTTOM:
2093         case Placement::RIGHT: {
2094             auto x = std::max(targetOffsetX + targetSize_.Width(), paddingStart_);
2095             auto width = std::min(wrapperSize_.Width() - targetOffsetX - targetSize_.Width() - paddingEnd_,
2096                 wrapperSize_.Width() - paddingStart_ - paddingEnd_);
2097             rect.SetRect(x, yAvoid, width, wrapperSize_.Height() - paddingBottom_ - paddingTop_);
2098             break;
2099         }
2100         case Placement::LEFT_TOP:
2101         case Placement::LEFT_BOTTOM:
2102         case Placement::LEFT: {
2103             auto width = std::min(
2104                 targetOffsetX - paddingStart_, wrapperSize_.Width() - paddingEnd_ - paddingStart_);
2105             rect.SetRect(paddingStart_, yAvoid, width, wrapperSize_.Height() - paddingBottom_ - paddingTop_);
2106             break;
2107         }
2108         default:
2109             return false;
2110     }
2111     return CheckPositionInPlacementRect(rect, position, childSize);
2112 }
2113 
GetPositionWithPlacementTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2114 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementTop(
2115     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2116 {
2117     return topPosition;
2118 }
2119 
GetPositionWithPlacementTopLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2120 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementTopLeft(
2121     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2122 {
2123     OffsetF childPosition;
2124     float marginRight = 0.0f;
2125     float marginBottom = 0.0f;
2126     childPosition = OffsetF(targetOffset_.GetX() - marginRight,
2127         targetOffset_.GetY() - childSize.Height() - marginBottom - targetSpace_);
2128     return childPosition;
2129 }
2130 
GetPositionWithPlacementTopRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2131 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementTopRight(
2132     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2133 {
2134     OffsetF childPosition;
2135     float marginBottom = 0.0f;
2136     float marginLeft = 0.0f;
2137     childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
2138         targetOffset_.GetY() - childSize.Height() - targetSpace_ - marginBottom);
2139     return childPosition;
2140 }
2141 
GetPositionWithPlacementBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2142 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementBottom(
2143     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2144 {
2145     return bottomPosition;
2146 }
2147 
GetPositionWithPlacementBottomLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2148 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementBottomLeft(
2149     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2150 {
2151     OffsetF childPosition;
2152     float marginRight = 0.0f;
2153     float marginTop = 0.0f;
2154     childPosition = OffsetF(targetOffset_.GetX() - marginRight,
2155         targetOffset_.GetY() + targetSize_.Height() + targetSpace_ + marginTop);
2156     return childPosition;
2157 }
2158 
GetPositionWithPlacementBottomRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2159 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementBottomRight(
2160     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2161 {
2162     OffsetF childPosition;
2163     float marginTop = 0.0f;
2164     float marginLeft = 0.0f;
2165     childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
2166         targetOffset_.GetY() + targetSize_.Height() + targetSpace_ + marginTop);
2167     return childPosition;
2168 }
2169 
GetPositionWithPlacementLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2170 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementLeft(
2171     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2172 {
2173     OffsetF childPosition;
2174     float marginRight = 0.0f;
2175     childPosition =
2176         OffsetF(targetOffset_.GetX() - targetSpace_ - childSize.Width() - marginRight,
2177             targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
2178     return childPosition;
2179 }
2180 
GetPositionWithPlacementLeftTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2181 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementLeftTop(
2182     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2183 {
2184     OffsetF childPosition;
2185     float marginRight = 0.0f;
2186     float marginBottom = 0.0f;
2187     childPosition =
2188         OffsetF(targetOffset_.GetX() - targetSpace_ - childSize.Width() - marginRight,
2189             targetOffset_.GetY() - marginBottom);
2190     return childPosition;
2191 }
2192 
GetPositionWithPlacementLeftBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2193 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementLeftBottom(
2194     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2195 {
2196     OffsetF childPosition;
2197     float marginRight = 0.0f;
2198     float marginTop = 0.0f;
2199     childPosition =
2200         OffsetF(targetOffset_.GetX() - targetSpace_ - childSize.Width() - marginRight,
2201             targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
2202     return childPosition;
2203 }
2204 
GetPositionWithPlacementRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2205 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementRight(
2206     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2207 {
2208     OffsetF childPosition;
2209     float marginLeft = 0.0f;
2210     childPosition =
2211         OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_ + marginLeft,
2212             targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
2213     return childPosition;
2214 }
2215 
GetPositionWithPlacementRightTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2216 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementRightTop(
2217     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2218 {
2219     OffsetF childPosition;
2220     float marginBottom = 0.0f;
2221     float marginLeft = 0.0f;
2222     childPosition =
2223         OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_ + marginLeft,
2224             targetOffset_.GetY() - marginBottom);
2225     return childPosition;
2226 }
2227 
GetPositionWithPlacementRightBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2228 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementRightBottom(
2229     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2230 {
2231     OffsetF childPosition;
2232     float marginTop = 0.0f;
2233     float marginLeft = 0.0f;
2234     childPosition =
2235         OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_ + marginLeft,
2236             targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
2237     return childPosition;
2238 }
2239 
InitHierarchicalParameters(bool isShowInSubWindow)2240 void MenuLayoutAlgorithm::InitHierarchicalParameters(bool isShowInSubWindow)
2241 {
2242     auto pipeline = PipelineBase::GetCurrentContext();
2243     CHECK_NULL_VOID(pipeline);
2244     auto theme = pipeline->GetTheme<SelectTheme>();
2245     CHECK_NULL_VOID(theme);
2246     auto expandDisplay = theme->GetExpandDisplay();
2247     if (expandDisplay && !isShowInSubWindow) {
2248         hierarchicalParameters_ = false;
2249         return;
2250     }
2251     hierarchicalParameters_ = expandDisplay;
2252 }
2253 
2254 } // namespace OHOS::Ace::NG
2255