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