• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/menu/menu_layout_algorithm.h"
17 
18 #include <optional>
19 #include <vector>
20 
21 #include "base/geometry/dimension.h"
22 #include "base/geometry/ng/offset_t.h"
23 #include "base/geometry/ng/size_t.h"
24 #include "base/geometry/size.h"
25 #include "base/memory/ace_type.h"
26 #include "base/memory/referenced.h"
27 #include "base/subwindow/subwindow_manager.h"
28 #include "base/utils/utils.h"
29 #include "core/common/ace_engine.h"
30 #include "core/components/common/layout/grid_system_manager.h"
31 #include "core/components/common/properties/placement.h"
32 #include "core/components/container_modal/container_modal_constants.h"
33 #include "core/components_ng/pattern/linear_layout/linear_layout_property.h"
34 #include "core/components_ng/pattern/menu/menu_layout_property.h"
35 #include "core/components_ng/pattern/menu/menu_pattern.h"
36 #include "core/components_ng/pattern/menu/menu_theme.h"
37 #include "core/components_ng/pattern/menu/preview/menu_preview_pattern.h"
38 #include "core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.h"
39 #include "core/components_ng/pattern/scroll/scroll_layout_property.h"
40 #include "core/components_ng/property/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     UpdateSelectFocus(layoutWrapper, childConstraint);
677     CalculateIdealSize(layoutWrapper, childConstraint, padding, idealSize, parentItem);
678 }
679 
CheckChildConstraintCondition(const RefPtr<MenuPattern> & menuPattern)680 bool MenuLayoutAlgorithm::CheckChildConstraintCondition(const RefPtr<MenuPattern>& menuPattern)
681 {
682     CHECK_NULL_RETURN(menuPattern, false);
683     CHECK_NULL_RETURN(Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_THIRTEEN), false);
684     if (menuPattern->IsSubMenu()) {
685         auto parentItem = menuPattern->GetParentMenuItem();
686         CHECK_NULL_RETURN(parentItem, false);
687         auto parentPattern = parentItem->GetPattern<MenuItemPattern>();
688         CHECK_NULL_RETURN(parentPattern, false);
689         auto expandingMode = parentPattern->GetExpandingMode();
690         if (expandingMode == SubMenuExpandingMode::SIDE) {
691             return true;
692         }
693         return false;
694     }
695 
696     if (menuPattern->IsMenu() || menuPattern->IsContextMenu()) {
697         return true;
698     }
699     return false;
700 }
701 
UpdateChildConstraintByDevice(const RefPtr<MenuPattern> & menuPattern,LayoutConstraintF & childConstraint,const LayoutConstraintF & layoutConstraint)702 void MenuLayoutAlgorithm::UpdateChildConstraintByDevice(const RefPtr<MenuPattern>& menuPattern,
703     LayoutConstraintF& childConstraint, const LayoutConstraintF& layoutConstraint)
704 {
705     CHECK_NULL_VOID(menuPattern);
706 
707     // only 2in1 device has restrictions on the menu width in API13
708     if (!CheckChildConstraintCondition(menuPattern)) {
709         return;
710     }
711     auto pipeline = PipelineBase::GetCurrentContext();
712     CHECK_NULL_VOID(pipeline);
713     auto theme = pipeline->GetTheme<SelectTheme>();
714     CHECK_NULL_VOID(theme);
715 
716     auto expandDisplay = theme->GetExpandDisplay();
717     CHECK_NULL_VOID(expandDisplay);
718 
719     auto menuMinWidth = theme->GetMenuMinWidth().ConvertToPx();
720     auto menuDefaultWidth = theme->GetMenuDefaultWidth().ConvertToPx();
721     auto menuMaxWidth = theme->GetMenuMaxWidthRatio() * SystemProperties::GetDeviceWidth();
722     double minWidth = 0.0f;
723     double maxWidth = 0.0f;
724 
725     auto firstMenu = menuPattern->GetFirstInnerMenu();
726     CHECK_NULL_VOID(firstMenu);
727     auto layoutProperty = firstMenu->GetLayoutProperty<MenuLayoutProperty>();
728     CHECK_NULL_VOID(layoutProperty);
729     if (layoutProperty->HasMenuWidth()) {
730         auto menuWidth = layoutProperty->GetMenuWidthValue();
731         auto menuWidthPX = (menuWidth.Unit() == DimensionUnit::PERCENT) ?
732             menuWidth.Value() * layoutConstraint.percentReference.Width() : menuWidth.ConvertToPx();
733         if (LessNotEqual(menuWidthPX, menuMinWidth) || GreatNotEqual(menuWidthPX, menuMaxWidth)) {
734             minWidth = menuDefaultWidth;
735             maxWidth = menuMaxWidth;
736         } else {
737             minWidth = menuWidthPX;
738             maxWidth = menuWidthPX;
739         }
740     } else {
741         minWidth = menuDefaultWidth;
742         maxWidth = menuMaxWidth;
743     }
744     childConstraint.minSize.SetWidth(minWidth);
745     childConstraint.maxSize.SetWidth(maxWidth);
746 }
747 
UpdateSelectFocus(LayoutWrapper * layoutWrapper,LayoutConstraintF & childConstraint)748 void MenuLayoutAlgorithm::UpdateSelectFocus(LayoutWrapper* layoutWrapper, LayoutConstraintF& childConstraint)
749 {
750     auto host = layoutWrapper->GetHostNode();
751     CHECK_NULL_VOID(host);
752     auto pattern = host->GetPattern<MenuPattern>();
753     CHECK_NULL_VOID(pattern);
754     if (!pattern->IsSelectMenu()) {
755         return;
756     }
757     for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
758         auto childHost = child->GetHostNode();
759         CHECK_NULL_VOID(childHost);
760         // when use contentModifier the origin scroll node should be hide.
761         auto needHide = pattern->UseContentModifier() && childHost->GetId() != pattern->GetBuilderId();
762         auto layoutProperty = childHost->GetLayoutProperty();
763         CHECK_NULL_VOID(layoutProperty);
764         layoutProperty->UpdateVisibility(needHide ? VisibleType::GONE : VisibleType::VISIBLE);
765         auto focusHub = childHost->GetOrCreateFocusHub();
766         CHECK_NULL_VOID(focusHub);
767         focusHub->SetShow(!needHide);
768         // when use contentModifier the default focus should change to the first custom node.
769         if (childHost->GetChildren().empty()) {
770             return;
771         }
772         auto columnChild = childHost->GetChildAtIndex(0);
773         CHECK_NULL_VOID(columnChild);
774         if (columnChild->GetChildren().empty()) {
775             return;
776         }
777         auto optionChild = columnChild->GetChildAtIndex(0);
778         CHECK_NULL_VOID(optionChild);
779         auto optionNode = AceType::DynamicCast<FrameNode>(optionChild);
780         CHECK_NULL_VOID(optionNode);
781         auto optionFocusHub = optionNode->GetOrCreateFocusHub();
782         CHECK_NULL_VOID(optionFocusHub);
783         optionFocusHub->SetIsDefaultFocus(!needHide);
784     }
785 }
786 
CalculateIdealSize(LayoutWrapper * layoutWrapper,LayoutConstraintF & childConstraint,PaddingPropertyF padding,SizeF & idealSize,RefPtr<FrameNode> parentItem)787 void MenuLayoutAlgorithm::CalculateIdealSize(LayoutWrapper* layoutWrapper,
788     LayoutConstraintF& childConstraint, PaddingPropertyF padding, SizeF& idealSize,
789     RefPtr<FrameNode> parentItem)
790 {
791     if (parentItem != nullptr) {
792         auto parentPattern = parentItem->GetPattern<MenuItemPattern>();
793         CHECK_NULL_VOID(parentPattern);
794         auto expandingMode = parentPattern->GetExpandingMode();
795         if (expandingMode == SubMenuExpandingMode::STACK) {
796             auto parentPattern = parentItem->GetPattern<MenuItemPattern>();
797             CHECK_NULL_VOID(parentPattern);
798             auto parentMenu = parentPattern->GetMenu();
799             auto parentWidth = parentMenu->GetGeometryNode()->GetFrameSize().Width();
800             childConstraint.minSize.SetWidth(parentWidth);
801             childConstraint.maxSize.SetWidth(parentWidth);
802             childConstraint.selfIdealSize.SetWidth(parentWidth);
803         }
804     }
805 
806     float idealHeight = 0.0f;
807     float idealWidth = 0.0f;
808     auto host = layoutWrapper->GetHostNode();
809     CHECK_NULL_VOID(host);
810     auto pattern = host->GetPattern<MenuPattern>();
811     CHECK_NULL_VOID(pattern);
812     std::list<RefPtr<LayoutWrapper>> builderChildList;
813     for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
814         if (pattern->UseContentModifier()) {
815             if (child->GetHostNode()->GetId() != pattern->GetBuilderId()) {
816                 child->GetGeometryNode()->Reset();
817                 child->GetGeometryNode()->SetContentSize(SizeF());
818             } else {
819                 child->Measure(childConstraint);
820                 builderChildList.push_back(child);
821             }
822             BoxLayoutAlgorithm::PerformMeasureSelfWithChildList(layoutWrapper, builderChildList);
823         } else {
824             child->Measure(childConstraint);
825         }
826         auto childSize = child->GetGeometryNode()->GetMarginFrameSize();
827         idealHeight += childSize.Height();
828         idealWidth = std::max(idealWidth, childSize.Width());
829     }
830     idealSize.SetHeight(idealHeight);
831     idealSize.SetWidth(idealWidth);
832     AddPaddingToSize(padding, idealSize);
833 
834     auto geometryNode = layoutWrapper->GetGeometryNode();
835     CHECK_NULL_VOID(geometryNode);
836     geometryNode->SetFrameSize(idealSize);
837 }
838 
CheckPreviewConstraint(const RefPtr<FrameNode> & frameNode,const Rect & menuWindowRect)839 void MenuLayoutAlgorithm::CheckPreviewConstraint(const RefPtr<FrameNode>& frameNode, const Rect& menuWindowRect)
840 {
841     CHECK_NULL_VOID(frameNode &&
842         (frameNode->GetTag() == V2::MENU_PREVIEW_ETS_TAG || frameNode->GetTag() == V2::FLEX_ETS_TAG));
843     auto geometryNode = frameNode->GetGeometryNode();
844     CHECK_NULL_VOID(geometryNode);
845 
846     auto maxWidth = wrapperSize_.Width();
847     if (layoutRegionMargin_.left.has_value() || layoutRegionMargin_.right.has_value()) {
848         maxWidth = std::max(0.0f, wrapperSize_.Width() - paddingStart_ - paddingEnd_) / previewScale_;
849     } else {
850         RefPtr<GridColumnInfo> columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
851         CHECK_NULL_VOID(columnInfo);
852         auto parent = columnInfo->GetParent();
853         CHECK_NULL_VOID(parent);
854         parent->BuildColumnWidth(std::min(menuWindowRect.Width(), menuWindowRect.Height()));
855         maxWidth = static_cast<float>(columnInfo->GetWidth(GRID_COUNTS_4)) / previewScale_;
856     }
857 
858     auto frameSize = geometryNode->GetMarginFrameSize();
859     if (!isPreviewContainScale_) {
860         static SizeF previewSize;
861         static int32_t hostId = -1;
862         if (previewSize == SizeF(0.0f, 0.0f) || hostId != frameNode->GetId()) {
863             previewSize = frameSize;
864             hostId = frameNode->GetId();
865         } else {
866             frameSize = previewSize;
867         }
868     }
869 
870     if (LessOrEqual(frameSize.Width(), maxWidth)) {
871         geometryNode->SetFrameSize(SizeF(frameSize.Width(), frameSize.Height()));
872     } else if (isPreviewContainScale_) {
873         geometryNode->SetFrameSize(SizeF(maxWidth, frameSize.Height() * (maxWidth / frameSize.Width())));
874     } else {
875         geometryNode->SetFrameSize(SizeF(maxWidth, frameSize.Height()));
876     }
877 }
878 
CheckPreviewSize(const RefPtr<LayoutWrapper> & previewLayoutWrapper,const RefPtr<MenuPattern> & menuPattern)879 void MenuLayoutAlgorithm::CheckPreviewSize(
880     const RefPtr<LayoutWrapper>& previewLayoutWrapper, const RefPtr<MenuPattern>& menuPattern)
881 {
882     CHECK_NULL_VOID(previewLayoutWrapper && menuPattern);
883     auto previewNode = previewLayoutWrapper->GetHostNode();
884     CHECK_NULL_VOID(previewNode);
885     auto tag = previewNode->GetTag();
886     auto isPreview = tag == V2::IMAGE_ETS_TAG || tag == V2::MENU_PREVIEW_ETS_TAG || tag == V2::FLEX_ETS_TAG;
887     CHECK_NULL_VOID(isPreview);
888 
889     auto previewGeometryNode = previewNode->GetGeometryNode();
890     CHECK_NULL_VOID(previewGeometryNode);
891     auto previewSize = previewGeometryNode->GetMarginFrameSize();
892 
893     if (menuPattern->GetIsFirstShow()) {
894         menuPattern->SetPreviewIdealSize(previewSize);
895         return;
896     }
897 
898     if (previewSize != menuPattern->GetPreviewIdealSize()) {
899         auto menuWrapper = menuPattern->GetMenuWrapper();
900         CHECK_NULL_VOID(menuWrapper);
901         auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
902         CHECK_NULL_VOID(menuWrapperPattern);
903         auto constraint = menuWrapperPattern->GetChildLayoutConstraint();
904         CHECK_NULL_VOID(constraint.maxSize.IsPositive() && constraint.percentReference.IsPositive());
905         auto layoutProperty = previewLayoutWrapper->GetLayoutProperty();
906         CHECK_NULL_VOID(layoutProperty);
907         layoutProperty->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE);
908         previewLayoutWrapper->Measure(constraint);
909         menuPattern->SetPreviewIdealSize(previewGeometryNode->GetMarginFrameSize());
910     }
911 }
912 
GetPreviewNodeTotalSize(const RefPtr<LayoutWrapper> & child,const Rect & menuWindowRect,RefPtr<LayoutWrapper> & previewLayoutWrapper,SizeF & size,const RefPtr<LayoutWrapper> & menuLayoutWrapper)913 void MenuLayoutAlgorithm::GetPreviewNodeTotalSize(const RefPtr<LayoutWrapper>& child, const Rect& menuWindowRect,
914     RefPtr<LayoutWrapper>& previewLayoutWrapper, SizeF& size, const RefPtr<LayoutWrapper>& menuLayoutWrapper)
915 {
916     CHECK_NULL_VOID(child);
917     auto hostNode = child->GetHostNode();
918     auto geometryNode = child->GetGeometryNode();
919     if (!hostNode || !geometryNode) {
920         return;
921     }
922 
923     bool isImageNode = hostNode->GetTag() == V2::IMAGE_ETS_TAG;
924     bool isPreviewNode = hostNode->GetTag() == V2::MENU_PREVIEW_ETS_TAG;
925     bool isFlexNode = hostNode->GetTag() == V2::FLEX_ETS_TAG;
926     if (!isPreviewNode && !isImageNode && !isFlexNode) {
927         return;
928     }
929 
930     CHECK_NULL_VOID(menuLayoutWrapper);
931     auto menuNode = menuLayoutWrapper->GetHostNode();
932     CHECK_NULL_VOID(menuNode);
933     auto menuPattern = menuNode->GetPattern<MenuPattern>();
934     CHECK_NULL_VOID(menuPattern);
935     CheckPreviewSize(child, menuPattern);
936 
937     if (isImageNode && menuPattern->GetIsShowHoverImage()) {
938         return;
939     }
940 
941     auto frameSize = geometryNode->GetMarginFrameSize();
942     if (isPreviewNode || isFlexNode) {
943         CheckPreviewConstraint(hostNode, menuWindowRect);
944     } else {
945         geometryNode->SetFrameSize(frameSize);
946     }
947     frameSize = geometryNode->GetMarginFrameSize() * previewScale_;
948     auto widthLeftSpace = menuWindowRect.Width() - paddingStart_ - paddingEnd_;
949     if (GreatNotEqual(frameSize.Width(), widthLeftSpace)) {
950         auto unitSpace = widthLeftSpace / frameSize.Width() / previewScale_;
951         geometryNode->SetFrameSize(SizeF(widthLeftSpace / previewScale_, unitSpace * frameSize.Height()));
952         frameSize = geometryNode->GetMarginFrameSize() * previewScale_;
953     }
954     previewLayoutWrapper = child;
955     size += frameSize;
956 }
957 
GetPreviewNodeAndMenuNodeTotalSize(const RefPtr<FrameNode> & frameNode,RefPtr<LayoutWrapper> & previewLayoutWrapper,RefPtr<LayoutWrapper> & menuLayoutWrapper)958 SizeF MenuLayoutAlgorithm::GetPreviewNodeAndMenuNodeTotalSize(const RefPtr<FrameNode>& frameNode,
959     RefPtr<LayoutWrapper>& previewLayoutWrapper, RefPtr<LayoutWrapper>& menuLayoutWrapper)
960 {
961     SizeF size;
962     CHECK_NULL_RETURN(frameNode, size);
963     auto pipelineContext = GetCurrentPipelineContext();
964     CHECK_NULL_RETURN(pipelineContext, size);
965     bool isShowHoverImage = false;
966     for (auto& child : frameNode->GetAllChildrenWithBuild()) {
967         auto hostNode = child->GetHostNode();
968         auto geometryNode = child->GetGeometryNode();
969         if (!hostNode || !geometryNode) {
970             continue;
971         }
972         GetPreviewNodeTotalSize(child, param_.menuWindowRect, previewLayoutWrapper, size, menuLayoutWrapper);
973         auto menuPattern = hostNode->GetPattern<MenuPattern>();
974         if (hostNode->GetTag() == V2::MENU_ETS_TAG && menuPattern && !menuPattern->IsSubMenu()) {
975             menuLayoutWrapper = child;
976             size += geometryNode->GetMarginFrameSize();
977             isShowHoverImage = menuPattern->GetIsShowHoverImage();
978         }
979     }
980     return size;
981 }
982 
LayoutNormalTopPreviewBottomMenuLessThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize)983 void MenuLayoutAlgorithm::LayoutNormalTopPreviewBottomMenuLessThan(
984     const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize)
985 {
986     CHECK_NULL_VOID(previewGeometryNode);
987     CHECK_NULL_VOID(menuGeometryNode);
988 
989     OffsetF center(
990         targetOffset_.GetX() + targetSize_.Width() / HALF, targetOffset_.GetY() + targetSize_.Height() / HALF);
991     targetCenterOffset_ = center;
992     auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
993     OffsetF offset(center.GetX() - previewSize.Width() / HALF,
994         std::min<float>(center.GetY() - previewSize.Height() / HALF, param_.windowGlobalSizeF.Height() -
995                                                                          param_.bottomSecurity - param_.bottom -
996                                                                          totalSize.Height() - param_.previewMenuGap));
997     auto x = std::clamp(offset.GetX(), static_cast<float>(wrapperRect_.Left()) + paddingStart_,
998         static_cast<float>(wrapperRect_.Right()) - paddingEnd_ - previewSize.Width());
999     auto y = std::clamp(offset.GetY(), static_cast<float>(wrapperRect_.Top()) + param_.topSecurity,
1000         static_cast<float>(wrapperRect_.Bottom()) - param_.bottomSecurity - previewSize.Height());
1001     x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / HALF;
1002     y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / HALF;
1003     previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1004 }
1005 
LayoutNormalTopPreviewBottomMenuGreateThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize)1006 void MenuLayoutAlgorithm::LayoutNormalTopPreviewBottomMenuGreateThan(
1007     const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize)
1008 {
1009     CHECK_NULL_VOID(previewGeometryNode);
1010     CHECK_NULL_VOID(menuGeometryNode);
1011 
1012     OffsetF center(targetOffset_.GetX() + targetSize_.Width() / 2, targetOffset_.GetY() + targetSize_.Height() / 2);
1013     targetCenterOffset_ = center;
1014     auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1015     auto menuHeight = totalSize.Height() - previewSize.Height();
1016     auto previewHalfHeight = previewSize.Height() / 2;
1017     if (LessNotEqual(menuHeight, previewHalfHeight)) {
1018         auto menuSize = menuGeometryNode->GetMarginFrameSize();
1019         if (GreatNotEqual(param_.menuItemTotalHeight, previewHalfHeight)) {
1020             menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), previewHalfHeight));
1021             totalSize = SizeF(totalSize.Width(), previewHalfHeight + previewSize.Height());
1022         } else {
1023             menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), param_.menuItemTotalHeight));
1024             totalSize = SizeF(totalSize.Width(), param_.menuItemTotalHeight + previewSize.Height());
1025         }
1026     }
1027 
1028     auto heightLeftSpace = wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity - param_.previewMenuGap;
1029     auto delta = totalSize.Height() - heightLeftSpace;
1030     if (GreatNotEqual(delta, 0.0f)) {
1031         menuHeight = totalSize.Height() - previewSize.Height();
1032         float unitSpace = 0.0f;
1033         if (LessNotEqual(menuHeight, previewHalfHeight)) {
1034             unitSpace = delta / previewSize.Height();
1035         } else {
1036             unitSpace = delta / totalSize.Height();
1037             auto menuSize = menuGeometryNode->GetMarginFrameSize();
1038             menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), (1 - unitSpace) * menuSize.Height()));
1039         }
1040         previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
1041             (1 - unitSpace) * previewSize.Height() / previewScale_));
1042         totalSize = totalSize - SizeF(0.0f, delta);
1043     }
1044     previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1045     OffsetF offset(center.GetX() - previewSize.Width() / 2, 0.0f);
1046     auto x = std::clamp(offset.GetX(), static_cast<float>(wrapperRect_.Left()) + paddingStart_,
1047         static_cast<float>(wrapperRect_.Right()) - paddingEnd_ - previewSize.Width());
1048     auto y = std::clamp(offset.GetY(), static_cast<float>(wrapperRect_.Top()) + param_.topSecurity,
1049         static_cast<float>(wrapperRect_.Bottom()) - param_.bottomSecurity - previewSize.Height());
1050     x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / 2;
1051     y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / 2;
1052     previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1053 }
1054 
LayoutNormalTopPreviewBottomMenu(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize,float menuItemTotalHeight)1055 void MenuLayoutAlgorithm::LayoutNormalTopPreviewBottomMenu(const RefPtr<GeometryNode>& previewGeometryNode,
1056     const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize, float menuItemTotalHeight)
1057 {
1058     CHECK_NULL_VOID(previewGeometryNode);
1059     CHECK_NULL_VOID(menuGeometryNode);
1060     param_.menuItemTotalHeight = menuItemTotalHeight;
1061     auto pipelineContext = GetCurrentPipelineContext();
1062     CHECK_NULL_VOID(pipelineContext);
1063     if (LessNotEqual(totalSize.Height() + targetSecurity_,
1064         wrapperRect_.Height() - paddingTop_ - paddingBottom_ - param_.topSecurity - param_.bottomSecurity)) {
1065         LayoutNormalTopPreviewBottomMenuLessThan(previewGeometryNode, menuGeometryNode, totalSize);
1066     } else {
1067         LayoutNormalTopPreviewBottomMenuGreateThan(previewGeometryNode, menuGeometryNode, totalSize);
1068     }
1069     auto previewSize = previewGeometryNode->GetMarginFrameSize();
1070     auto securityHeight = wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity;
1071     if (GreatNotEqual(param_.windowGlobalSizeF.Height(), param_.menuWindowRect.Height()) &&
1072         GreatNotEqual(previewSize.Height(), securityHeight)) {
1073         previewGeometryNode->SetFrameSize(SizeF(previewSize.Width(), securityHeight));
1074     }
1075 }
1076 
LayoutNormalBottomPreviewTopMenuLessThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize)1077 void MenuLayoutAlgorithm::LayoutNormalBottomPreviewTopMenuLessThan(
1078     const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize)
1079 {
1080     CHECK_NULL_VOID(previewGeometryNode);
1081     CHECK_NULL_VOID(menuGeometryNode);
1082 
1083     OffsetF center(targetOffset_.GetX() + targetSize_.Width() / 2, targetOffset_.GetY() + targetSize_.Height() / 2);
1084     targetCenterOffset_ = center;
1085     auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1086     OffsetF offset(center.GetX() - previewSize.Width() / 2,
1087         std::max<float>(center.GetY() - previewSize.Height() / 2,
1088             param_.top + param_.topSecurity + totalSize.Height() - previewSize.Height() + param_.previewMenuGap));
1089     auto x = std::clamp(offset.GetX(), static_cast<float>(wrapperRect_.Left()) + paddingStart_,
1090         static_cast<float>(wrapperRect_.Right()) - previewSize.Width() - paddingEnd_);
1091     auto y = std::clamp(offset.GetY(), static_cast<float>(wrapperRect_.Top()) + param_.topSecurity,
1092         static_cast<float>(wrapperRect_.Bottom()) - param_.bottomSecurity - previewSize.Height());
1093     x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / 2;
1094     y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / 2;
1095     previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1096 }
1097 
LayoutNormalBottomPreviewTopMenuGreateThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize)1098 void MenuLayoutAlgorithm::LayoutNormalBottomPreviewTopMenuGreateThan(
1099     const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize)
1100 {
1101     CHECK_NULL_VOID(previewGeometryNode);
1102     CHECK_NULL_VOID(menuGeometryNode);
1103 
1104     OffsetF center(targetOffset_.GetX() + targetSize_.Width() / 2, targetOffset_.GetY() + targetSize_.Height() / 2);
1105     targetCenterOffset_ = center;
1106     auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1107     auto menuHeight = totalSize.Height() - previewSize.Height();
1108     auto previewHalfHeight = previewSize.Height() / 2;
1109     if (LessNotEqual(menuHeight, previewHalfHeight)) {
1110         auto menuSize = menuGeometryNode->GetMarginFrameSize();
1111         if (GreatNotEqual(param_.menuItemTotalHeight, previewHalfHeight)) {
1112             menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), previewHalfHeight));
1113             totalSize = SizeF(totalSize.Width(), previewHalfHeight + previewSize.Height());
1114         } else {
1115             menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), param_.menuItemTotalHeight));
1116             totalSize = SizeF(totalSize.Width(), param_.menuItemTotalHeight + previewSize.Height());
1117         }
1118     }
1119 
1120     auto heightLeftSpace = param_.windowGlobalSizeF.Height() - param_.top - param_.topSecurity - param_.bottom -
1121                            param_.bottomSecurity - param_.previewMenuGap;
1122     auto delta = totalSize.Height() - heightLeftSpace;
1123     if (GreatNotEqual(delta, 0.0f)) {
1124         menuHeight = totalSize.Height() - previewSize.Height();
1125         float unitSpace = 0.0f;
1126         if (LessNotEqual(menuHeight, previewHalfHeight)) {
1127             unitSpace = delta / previewSize.Height();
1128         } else {
1129             unitSpace = delta / totalSize.Height();
1130             auto menuSize = menuGeometryNode->GetMarginFrameSize();
1131             menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), (1 - unitSpace) * menuSize.Height()));
1132         }
1133         previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
1134             (1 - unitSpace) * previewSize.Height() / previewScale_));
1135         totalSize = totalSize - SizeF(0.0f, delta);
1136     }
1137     previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1138     OffsetF offset(center.GetX() - previewSize.Width() / 2,
1139         param_.windowGlobalSizeF.Height() - param_.bottomSecurity - param_.bottom - previewSize.Height());
1140 
1141     auto x = std::clamp(offset.GetX(), static_cast<float>(wrapperRect_.Left()) + paddingStart_,
1142         static_cast<float>(wrapperRect_.Right()) - previewSize.Width() - paddingEnd_);
1143     auto y = std::clamp(offset.GetY(), static_cast<float>(wrapperRect_.Top()) + param_.topSecurity,
1144         static_cast<float>(wrapperRect_.Bottom()) - param_.bottomSecurity - previewSize.Height());
1145     x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / 2;
1146     y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / 2;
1147     previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1148 }
1149 
LayoutNormalBottomPreviewTopMenu(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize,float menuItemTotalHeight)1150 void MenuLayoutAlgorithm::LayoutNormalBottomPreviewTopMenu(const RefPtr<GeometryNode>& previewGeometryNode,
1151     const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize, float menuItemTotalHeight)
1152 {
1153     CHECK_NULL_VOID(previewGeometryNode);
1154     CHECK_NULL_VOID(menuGeometryNode);
1155     param_.menuItemTotalHeight = menuItemTotalHeight;
1156     auto pipelineContext = GetCurrentPipelineContext();
1157     CHECK_NULL_VOID(pipelineContext);
1158     if (LessNotEqual(totalSize.Height() + targetSecurity_, param_.windowGlobalSizeF.Height() - param_.topSecurity -
1159                                                                param_.bottomSecurity - param_.top - param_.bottom)) {
1160         LayoutNormalBottomPreviewTopMenuLessThan(previewGeometryNode, menuGeometryNode, totalSize);
1161     } else {
1162         LayoutNormalBottomPreviewTopMenuGreateThan(previewGeometryNode, menuGeometryNode, totalSize);
1163     }
1164     auto previewSize = previewGeometryNode->GetMarginFrameSize();
1165     auto securityHeight =
1166         param_.menuWindowRect.Height() - param_.topSecurity - param_.top - param_.bottomSecurity - param_.bottom;
1167     if (GreatNotEqual(param_.windowGlobalSizeF.Height(), param_.menuWindowRect.Height()) &&
1168         GreatNotEqual(previewSize.Height(), securityHeight)) {
1169         previewGeometryNode->SetFrameSize(SizeF(previewSize.Width(), securityHeight));
1170     }
1171 }
1172 
UpdateScrollAndColumnLayoutConstraint(const RefPtr<LayoutWrapper> & previewLayoutWrapper,const RefPtr<LayoutWrapper> & menuLayoutWrapper)1173 void MenuLayoutAlgorithm::UpdateScrollAndColumnLayoutConstraint(
1174     const RefPtr<LayoutWrapper>& previewLayoutWrapper, const RefPtr<LayoutWrapper>& menuLayoutWrapper)
1175 {
1176     CHECK_NULL_VOID(menuLayoutWrapper);
1177     CHECK_NULL_VOID(previewLayoutWrapper);
1178     RefPtr<GeometryNode> menuGeometryNode = menuLayoutWrapper->GetGeometryNode();
1179     RefPtr<GeometryNode> previewGeometryNode = previewLayoutWrapper->GetGeometryNode();
1180     CHECK_NULL_VOID(menuGeometryNode);
1181     CHECK_NULL_VOID(previewGeometryNode);
1182 
1183     for (auto& child : menuLayoutWrapper->GetAllChildrenWithBuild()) {
1184         auto geometryNode = child->GetGeometryNode();
1185         if (!geometryNode) {
1186             continue;
1187         }
1188         auto frameSize = menuGeometryNode->GetMarginFrameSize();
1189         geometryNode->SetFrameSize(SizeF(frameSize.Width(), frameSize.Height()));
1190         auto layoutProperty = child->GetLayoutProperty();
1191         CHECK_NULL_VOID(layoutProperty);
1192         auto constraint = layoutProperty->GetLayoutConstraint();
1193         if (constraint.has_value()) {
1194             constraint.value().maxSize.SetWidth(frameSize.Width());
1195             constraint.value().maxSize.SetHeight(frameSize.Height());
1196             constraint.value().selfIdealSize.UpdateSizeWithCheck(SizeF(frameSize.Width(), frameSize.Height()));
1197             layoutProperty->UpdateLayoutConstraint(constraint.value());
1198             child->Measure(constraint);
1199         }
1200     }
1201 
1202     for (auto& child : previewLayoutWrapper->GetAllChildrenWithBuild()) {
1203         auto hostNode = child->GetHostNode();
1204         auto geometryNode = child->GetGeometryNode();
1205         if (!hostNode || !geometryNode) {
1206             continue;
1207         }
1208         auto frameSize = previewGeometryNode->GetMarginFrameSize();
1209         geometryNode->SetFrameSize(SizeF(frameSize.Width(), frameSize.Height()));
1210         auto layoutProperty = child->GetLayoutProperty();
1211         CHECK_NULL_VOID(layoutProperty);
1212         auto constraint = layoutProperty->GetLayoutConstraint();
1213         if (constraint.has_value()) {
1214             constraint.value().maxSize.SetWidth(frameSize.Width());
1215             constraint.value().maxSize.SetHeight(frameSize.Height());
1216             constraint.value().selfIdealSize.UpdateSizeWithCheck(SizeF(frameSize.Width(), frameSize.Height()));
1217             layoutProperty->UpdateLayoutConstraint(constraint.value());
1218             hostNode->GetRenderContext()->SetClipToBounds(true);
1219             child->Measure(constraint);
1220         }
1221     }
1222 }
1223 
GetMenuItemTotalHeight(const RefPtr<LayoutWrapper> & menuLayoutWrapper)1224 float MenuLayoutAlgorithm::GetMenuItemTotalHeight(const RefPtr<LayoutWrapper>& menuLayoutWrapper)
1225 {
1226     CHECK_NULL_RETURN(menuLayoutWrapper, 0.0f);
1227     RefPtr<GeometryNode> menuGeometryNode = menuLayoutWrapper->GetGeometryNode();
1228     CHECK_NULL_RETURN(menuGeometryNode, 0.0f);
1229     float height = 0.0f;
1230 
1231     for (auto& child : menuLayoutWrapper->GetAllChildrenWithBuild()) {
1232         auto geometryNode = child->GetGeometryNode();
1233         if (!geometryNode) {
1234             continue;
1235         }
1236         for (auto& menuItem : child->GetAllChildrenWithBuild()) {
1237             auto itemHostnode = menuItem->GetHostNode();
1238             auto itemGeometryNode = menuItem->GetGeometryNode();
1239             if (!itemHostnode || !itemGeometryNode) {
1240                 continue;
1241             }
1242             height += itemGeometryNode->GetMarginFrameSize().Height();
1243         }
1244     }
1245     auto menuHeight = menuGeometryNode->GetMarginFrameSize().Height();
1246     if (LessNotEqual(height, menuHeight)) {
1247         height = menuHeight;
1248     }
1249     return height;
1250 }
1251 
CheckHorizontalLayoutPreviewOffsetX(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,float offsetX)1252 float MenuLayoutAlgorithm::CheckHorizontalLayoutPreviewOffsetX(
1253     const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, float offsetX)
1254 {
1255     if (SystemProperties::GetDeviceOrientation() != DeviceOrientation::LANDSCAPE) {
1256         return offsetX;
1257     }
1258     CHECK_NULL_RETURN(previewGeometryNode, offsetX);
1259     auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1260     CHECK_NULL_RETURN(menuGeometryNode, offsetX);
1261     auto menuSize = menuGeometryNode->GetMarginFrameSize();
1262     // left menu right preview
1263     auto x_min = wrapperRect_.Left() + paddingStart_ + menuSize.Width() + targetSecurity_;
1264     // left preview right menu
1265     auto x_max = wrapperRect_.Right() - paddingEnd_ - menuSize.Width() - previewSize.Width() - targetSecurity_;
1266     auto needAvoid = GreatNotEqual(offsetX, x_max) && LessNotEqual(offsetX, x_min);
1267     return needAvoid ? x_max : offsetX;
1268 }
1269 
LayoutOtherDeviceLeftPreviewRightMenuLessThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize)1270 void MenuLayoutAlgorithm::LayoutOtherDeviceLeftPreviewRightMenuLessThan(
1271     const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize)
1272 {
1273     CHECK_NULL_VOID(previewGeometryNode);
1274     CHECK_NULL_VOID(menuGeometryNode);
1275 
1276     OffsetF targetCenterOffset(
1277         targetOffset_.GetX() + targetSize_.Width() / 2, targetOffset_.GetY() + targetSize_.Height() / 2);
1278     targetCenterOffset_ = targetCenterOffset;
1279     auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1280     auto heightLeftSpace =
1281         param_.windowGlobalSizeF.Height() - param_.top - param_.topSecurity - param_.bottomSecurity - param_.bottom;
1282     auto delta = previewSize.Height() - heightLeftSpace;
1283     if (GreatNotEqual(delta, 0.0f)) {
1284         auto unitSpace = delta / previewSize.Height();
1285         previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
1286             (1 - unitSpace) * previewSize.Height() / previewScale_));
1287         totalSize = totalSize - SizeF(unitSpace * previewSize.Width(), unitSpace * previewSize.Height());
1288     }
1289     previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1290     auto menuSize = menuGeometryNode->GetMarginFrameSize();
1291     menuGeometryNode->SetFrameSize(
1292         SizeF(menuSize.Width(), std::min<float>(param_.menuItemTotalHeight, heightLeftSpace)));
1293     menuSize = menuGeometryNode->GetMarginFrameSize();
1294     auto offsetX = targetCenterOffset.GetX() - previewSize.Width() / 2;
1295     auto offsetY = std::min<float>(targetCenterOffset.GetY() - previewSize.Height() / 2,
1296         param_.windowGlobalSizeF.Height() - param_.bottomSecurity - param_.bottom - menuSize.Height());
1297     offsetX = CheckHorizontalLayoutPreviewOffsetX(previewGeometryNode, menuGeometryNode, offsetX);
1298     auto x = std::clamp(offsetX, static_cast<float>(wrapperRect_.Left()) + paddingStart_,
1299         static_cast<float>(wrapperRect_.Right()) - previewSize.Width() - paddingEnd_);
1300     auto y = std::clamp(offsetY, static_cast<float>(wrapperRect_.Top()) + param_.topSecurity,
1301         static_cast<float>(wrapperRect_.Bottom()) - param_.bottomSecurity - previewSize.Height());
1302     x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / 2;
1303     y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / 2;
1304     previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1305 }
1306 
LayoutOtherDeviceLeftPreviewRightMenuGreateThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize)1307 void MenuLayoutAlgorithm::LayoutOtherDeviceLeftPreviewRightMenuGreateThan(
1308     const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize)
1309 {
1310     CHECK_NULL_VOID(previewGeometryNode);
1311     CHECK_NULL_VOID(menuGeometryNode);
1312 
1313     OffsetF targetCenterOffset(
1314         targetOffset_.GetX() + targetSize_.Width() / 2, targetOffset_.GetY() + targetSize_.Height() / 2);
1315     targetCenterOffset_ = targetCenterOffset;
1316     auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1317     auto widthLeftSpace = param_.windowGlobalSizeF.Width() - paddingStart_ - paddingEnd_ - param_.previewMenuGap -
1318                           param_.left - param_.right;
1319     auto delta = totalSize.Width() - widthLeftSpace;
1320     if (GreatNotEqual(delta, 0.0f)) {
1321         auto unitSpace = delta / previewSize.Width();
1322         previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
1323             (1 - unitSpace) * previewSize.Height() / previewScale_));
1324         totalSize = totalSize - SizeF(unitSpace * previewSize.Width(), unitSpace * previewSize.Height());
1325     }
1326     previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1327     auto heightLeftSpace =
1328         param_.windowGlobalSizeF.Height() - param_.topSecurity - param_.top - param_.bottomSecurity - param_.bottom;
1329     delta = previewSize.Height() - heightLeftSpace;
1330     if (GreatNotEqual(delta, 0.0f)) {
1331         auto unitSpace = delta / previewSize.Height();
1332         previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
1333             (1 - unitSpace) * previewSize.Height() / previewScale_));
1334         totalSize = totalSize - SizeF(unitSpace * previewSize.Width(), unitSpace * previewSize.Height());
1335     }
1336     previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1337     auto menuSize = menuGeometryNode->GetMarginFrameSize();
1338     menuGeometryNode->SetFrameSize(
1339         SizeF(menuSize.Width(), std::min<float>(param_.menuItemTotalHeight, heightLeftSpace)));
1340     menuSize = menuGeometryNode->GetMarginFrameSize();
1341     auto offsetX = 0.0f;
1342     auto offsetY = std::min<float>(targetCenterOffset.GetY() - previewSize.Height() / 2,
1343         param_.windowGlobalSizeF.Height() - param_.bottomSecurity - param_.bottom - menuSize.Height());
1344     auto x = std::clamp(offsetX, static_cast<float>(wrapperRect_.Left()) + paddingStart_,
1345         static_cast<float>(wrapperRect_.Right()) - previewSize.Width() - paddingEnd_);
1346     auto y = std::clamp(offsetY, static_cast<float>(wrapperRect_.Top()) + param_.topSecurity,
1347         static_cast<float>(wrapperRect_.Bottom()) - param_.bottomSecurity - previewSize.Height());
1348     x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / 2;
1349     y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / 2;
1350     previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1351 }
1352 
LayoutOtherDeviceLeftPreviewRightMenu(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize,float menuItemTotalHeight)1353 void MenuLayoutAlgorithm::LayoutOtherDeviceLeftPreviewRightMenu(const RefPtr<GeometryNode>& previewGeometryNode,
1354     const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize, float menuItemTotalHeight)
1355 {
1356     CHECK_NULL_VOID(previewGeometryNode);
1357     CHECK_NULL_VOID(menuGeometryNode);
1358     param_.menuItemTotalHeight = menuItemTotalHeight;
1359     auto safeAreaWidth = param_.left + param_.right;
1360     auto maxRectRight = param_.windowGlobalSizeF.Width() - paddingStart_ - paddingEnd_ - safeAreaWidth;
1361     if (LessNotEqual(totalSize.Width() + targetSecurity_, maxRectRight)) {
1362         LayoutOtherDeviceLeftPreviewRightMenuLessThan(previewGeometryNode, menuGeometryNode, totalSize);
1363     } else {
1364         LayoutOtherDeviceLeftPreviewRightMenuGreateThan(previewGeometryNode, menuGeometryNode, totalSize);
1365     }
1366     auto previewSize = previewGeometryNode->GetMarginFrameSize();
1367     auto securityHeight =
1368         param_.menuWindowRect.Height() - param_.topSecurity - param_.top - param_.bottomSecurity - param_.bottom;
1369     if (GreatNotEqual(param_.windowGlobalSizeF.Height(), param_.menuWindowRect.Height()) &&
1370         GreatNotEqual(previewSize.Height(), securityHeight)) {
1371         previewGeometryNode->SetFrameSize(SizeF(previewSize.Width(), securityHeight));
1372     }
1373 }
1374 
LayoutPreviewMenu(LayoutWrapper * layoutWrapper)1375 void MenuLayoutAlgorithm::LayoutPreviewMenu(LayoutWrapper* layoutWrapper)
1376 {
1377     CHECK_NULL_VOID(layoutWrapper);
1378     auto paintProperty = GetPaintProperty(layoutWrapper);
1379     CHECK_NULL_VOID(paintProperty);
1380     paintProperty->UpdateEnableArrow(false);
1381     auto menuNode = layoutWrapper->GetHostNode();
1382     CHECK_NULL_VOID(menuNode);
1383     auto parentNode = AceType::DynamicCast<FrameNode>(menuNode->GetParent());
1384     CHECK_NULL_VOID(parentNode);
1385     RefPtr<LayoutWrapper> menuLayoutWrapper;
1386     RefPtr<LayoutWrapper> previewLayoutWrapper;
1387     SizeF totalSize = GetPreviewNodeAndMenuNodeTotalSize(parentNode, previewLayoutWrapper, menuLayoutWrapper);
1388     CHECK_NULL_VOID(menuLayoutWrapper);
1389     CHECK_NULL_VOID(previewLayoutWrapper);
1390     RefPtr<GeometryNode> menuGeometryNode = menuLayoutWrapper->GetGeometryNode();
1391     RefPtr<GeometryNode> previewGeometryNode = previewLayoutWrapper->GetGeometryNode();
1392     CHECK_NULL_VOID(menuGeometryNode);
1393     CHECK_NULL_VOID(previewGeometryNode);
1394     auto menuItemTotalHeight = GetMenuItemTotalHeight(menuLayoutWrapper);
1395     if (placement_ == Placement::BOTTOM_LEFT || placement_ == Placement::BOTTOM ||
1396         placement_ == Placement::BOTTOM_RIGHT) {
1397         LayoutNormalTopPreviewBottomMenu(previewGeometryNode, menuGeometryNode, totalSize, menuItemTotalHeight);
1398     } else if (placement_ == Placement::TOP_LEFT || placement_ == Placement::TOP ||
1399                placement_ == Placement::TOP_RIGHT) {
1400         LayoutNormalBottomPreviewTopMenu(previewGeometryNode, menuGeometryNode, totalSize, menuItemTotalHeight);
1401     } else {
1402         LayoutOtherDeviceLeftPreviewRightMenu(previewGeometryNode, menuGeometryNode, totalSize, menuItemTotalHeight);
1403     }
1404     UpdateScrollAndColumnLayoutConstraint(previewLayoutWrapper, menuLayoutWrapper);
1405     UpdatePreviewPositionAndOffset(previewLayoutWrapper, menuLayoutWrapper);
1406 }
1407 
UpdatePreviewPositionAndOffset(RefPtr<LayoutWrapper> & previewLayoutWrapper,RefPtr<LayoutWrapper> & menuLayoutWrapper)1408 void MenuLayoutAlgorithm::UpdatePreviewPositionAndOffset(
1409     RefPtr<LayoutWrapper>& previewLayoutWrapper, RefPtr<LayoutWrapper>& menuLayoutWrapper)
1410 {
1411     CHECK_NULL_VOID(previewLayoutWrapper);
1412     CHECK_NULL_VOID(menuLayoutWrapper);
1413     RefPtr<GeometryNode> previewGeometryNode = previewLayoutWrapper->GetGeometryNode();
1414     RefPtr<GeometryNode> menuGeometryNode = menuLayoutWrapper->GetGeometryNode();
1415     CHECK_NULL_VOID(previewGeometryNode);
1416     CHECK_NULL_VOID(menuGeometryNode);
1417 
1418     auto previewSize = previewGeometryNode->GetMarginFrameSize();
1419     previewOffset_ = previewGeometryNode->GetFrameOffset();
1420     auto previewOffsetX = previewOffset_.GetX();
1421     auto previewOffsetY = previewOffset_.GetY();
1422     if (previewSize.IsPositive()) {
1423         targetSize_ = previewSize * previewScale_;
1424         targetOffset_ = OffsetF(previewOffsetX + (previewSize.Width() - targetSize_.Width()) / HALF,
1425             previewOffsetY + (previewSize.Height() - targetSize_.Height()) / HALF);
1426     }
1427     auto previewHostNode = previewLayoutWrapper->GetHostNode();
1428     CHECK_NULL_VOID(previewHostNode);
1429     auto renderContext = previewHostNode->GetRenderContext();
1430     CHECK_NULL_VOID(renderContext);
1431     renderContext->UpdatePosition(OffsetT<Dimension>(Dimension(previewOffsetX), Dimension(previewOffsetY)));
1432 
1433     auto menuHostNode = menuLayoutWrapper->GetHostNode();
1434     CHECK_NULL_VOID(menuHostNode);
1435     previewOriginOffset_ = targetCenterOffset_ - OffsetF(previewSize.Width() / HALF, previewSize.Height() / HALF);
1436     previewSize_ = previewSize;
1437     auto menuPattern = menuHostNode->GetPattern<MenuPattern>();
1438     CHECK_NULL_VOID(menuPattern);
1439     menuPattern->SetPreviewOriginOffset(previewOriginOffset_);
1440     menuPattern->SetPreviewRect(RectF(previewOffset_, previewSize_));
1441 }
1442 
FixMenuOriginOffset(float beforeAnimationScale,float afterAnimationScale)1443 OffsetF MenuLayoutAlgorithm::FixMenuOriginOffset(float beforeAnimationScale, float afterAnimationScale)
1444 {
1445     auto beforeRate = (1.0f - beforeAnimationScale) / 2;
1446     auto beforeScalePreviewOffset = OffsetF((previewSize_ * beforeRate).Width(), (previewSize_ * beforeRate).Height());
1447     auto afterScalePreviewOffset = OffsetF((previewSize_ * ((afterAnimationScale - 1.0f) / 2)).Width(),
1448         (previewSize_ * ((afterAnimationScale - 1.0f) / 2)).Height());
1449     auto scaleOffset = afterScalePreviewOffset + beforeScalePreviewOffset;
1450     float x = 0.0f;
1451     float y = 0.0f;
1452     switch (placement_) {
1453         case Placement::BOTTOM_LEFT:
1454         case Placement::LEFT_BOTTOM:
1455             x += scaleOffset.GetX();
1456             y -= scaleOffset.GetY();
1457             break;
1458         case Placement::TOP_RIGHT:
1459         case Placement::RIGHT_TOP:
1460             x -= scaleOffset.GetX();
1461             y += scaleOffset.GetY();
1462             break;
1463         case Placement::TOP_LEFT:
1464         case Placement::LEFT_TOP:
1465             x += scaleOffset.GetX();
1466             y += scaleOffset.GetY();
1467             break;
1468         case Placement::BOTTOM_RIGHT:
1469         case Placement::RIGHT_BOTTOM:
1470             x -= scaleOffset.GetX();
1471             y -= scaleOffset.GetY();
1472             break;
1473         case Placement::BOTTOM:
1474             y -= scaleOffset.GetY();
1475             break;
1476         case Placement::TOP:
1477             y += scaleOffset.GetY();
1478             break;
1479         case Placement::LEFT:
1480             x += scaleOffset.GetX();
1481             break;
1482         case Placement::RIGHT:
1483             x -= scaleOffset.GetX();
1484             break;
1485         default:
1486             x += scaleOffset.GetX();
1487             y -= scaleOffset.GetY();
1488             break;
1489     }
1490     return OffsetF(x, y);
1491 }
1492 
Layout(LayoutWrapper * layoutWrapper)1493 void MenuLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
1494 {
1495     CHECK_NULL_VOID(layoutWrapper);
1496     auto menuProp = DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
1497     CHECK_NULL_VOID(menuProp);
1498     auto menuNode = layoutWrapper->GetHostNode();
1499     CHECK_NULL_VOID(menuNode);
1500     auto menuPattern = menuNode->GetPattern<MenuPattern>();
1501     CHECK_NULL_VOID(menuPattern);
1502 
1503     if (menuPattern->GetPreviewMode() != MenuPreviewMode::NONE) {
1504         LayoutPreviewMenu(layoutWrapper);
1505     }
1506     if (!menuPattern->IsSelectOverlayCustomMenu()) {
1507         auto geometryNode = layoutWrapper->GetGeometryNode();
1508         CHECK_NULL_VOID(geometryNode);
1509         auto size = geometryNode->GetMarginFrameSize();
1510         bool didNeedArrow = GetIfNeedArrow(layoutWrapper, size);
1511         if (menuPattern->IsSelectMenu()) {
1512             ComputeMenuPositionByAlignType(menuProp, size);
1513             auto offset = ComputeMenuPositionByOffset(menuProp, geometryNode);
1514             position_ += offset;
1515         }
1516         auto menuPosition = lastPosition_.has_value() && CheckIsEmbeddedMode(layoutWrapper)
1517                                 ? lastPosition_.value()
1518                                 : MenuLayoutAvoidAlgorithm(menuProp, menuPattern, size, didNeedArrow, layoutWrapper);
1519         menuPattern->UpdateLastPosition(menuPosition);
1520         TAG_LOGI(AceLogTag::ACE_MENU, "update menu position : %{public}s", menuPosition.ToString().c_str());
1521         auto renderContext = menuNode->GetRenderContext();
1522         CHECK_NULL_VOID(renderContext);
1523         renderContext->UpdatePosition(
1524             OffsetT<Dimension>(Dimension(menuPosition.GetX()), Dimension(menuPosition.GetY())));
1525         dumpInfo_.finalPlacement = PlacementUtils::ConvertPlacementToString(placement_);
1526         dumpInfo_.finalPosition = menuPosition;
1527         SetMenuPlacementForAnimation(layoutWrapper);
1528         if (didNeedArrow && arrowPlacement_ != Placement::NONE) {
1529             arrowPosition_ = GetArrowPositionWithPlacement(size, layoutWrapper);
1530             LayoutArrow(layoutWrapper);
1531         }
1532         geometryNode->SetFrameOffset(menuPosition);
1533         auto pipeline = PipelineBase::GetCurrentContext();
1534         CHECK_NULL_VOID(pipeline);
1535         auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
1536         CHECK_NULL_VOID(menuTheme);
1537         auto beforeAnimationScale = menuTheme->GetPreviewBeforeAnimationScale();
1538         auto afterAnimationScale = menuTheme->GetPreviewAfterAnimationScale();
1539         auto menuOriginOffset = menuPosition - (previewOffset_ - previewOriginOffset_) +
1540                                 FixMenuOriginOffset(beforeAnimationScale, afterAnimationScale);
1541         menuPattern->SetOriginOffset(menuOriginOffset);
1542         auto previewScale = 1.0f;
1543         if (menuPattern->GetPreviewMode() == MenuPreviewMode::IMAGE &&
1544             !NearEqual(menuPattern->GetTargetSize().Width(), previewSize_.Width())) {
1545             previewScale = menuPattern->GetTargetSize().Width() / previewSize_.Width();
1546         }
1547         auto menuEndOffset = menuPosition - (previewOffset_ - previewOriginOffset_) +
1548                              FixMenuOriginOffset(previewScale, afterAnimationScale);
1549         menuPattern->SetEndOffset(menuEndOffset);
1550         menuPattern->SetHasLaid(true);
1551         dumpInfo_.menuPreviewMode = static_cast<uint32_t>(menuPattern->GetPreviewMode());
1552         dumpInfo_.menuType = static_cast<uint32_t>(menuPattern->GetMenuType());
1553         auto menuWrapper = menuPattern->GetMenuWrapper();
1554         CHECK_NULL_VOID(menuWrapper);
1555         auto wrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
1556         CHECK_NULL_VOID(wrapperPattern);
1557         wrapperPattern->SetDumpInfo(dumpInfo_);
1558     }
1559 
1560     TranslateOptions(layoutWrapper);
1561 }
1562 
TranslateOptions(LayoutWrapper * layoutWrapper)1563 void MenuLayoutAlgorithm::TranslateOptions(LayoutWrapper* layoutWrapper)
1564 {
1565     // translate each option by the height of previous options
1566     OffsetF translate;
1567     for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
1568         child->GetGeometryNode()->SetMarginFrameOffset(translate);
1569         child->Layout();
1570         translate += OffsetF(0, child->GetGeometryNode()->GetFrameSize().Height());
1571     }
1572 }
1573 
SetMenuPlacementForAnimation(LayoutWrapper * layoutWrapper)1574 void MenuLayoutAlgorithm::SetMenuPlacementForAnimation(LayoutWrapper* layoutWrapper)
1575 {
1576     auto menu = layoutWrapper->GetHostNode();
1577     CHECK_NULL_VOID(menu);
1578     auto menuPattern = menu->GetPattern<MenuPattern>();
1579     CHECK_NULL_VOID(menuPattern);
1580     auto menuWrapper = menuPattern->GetMenuWrapper();
1581     CHECK_NULL_VOID(menuWrapper);
1582     auto wrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
1583     CHECK_NULL_VOID(wrapperPattern);
1584     wrapperPattern->SetMenuPlacementAfterLayout(placement_);
1585 }
1586 
LayoutArrow(const LayoutWrapper * layoutWrapper)1587 void MenuLayoutAlgorithm::LayoutArrow(const LayoutWrapper* layoutWrapper)
1588 {
1589     auto paintProperty = GetPaintProperty(layoutWrapper);
1590     CHECK_NULL_VOID(paintProperty);
1591     paintProperty->UpdateArrowPosition(arrowPosition_);
1592     paintProperty->UpdateArrowPlacement(arrowPlacement_);
1593     dumpInfo_.enableArrow = true;
1594 }
1595 
GetPaintProperty(const LayoutWrapper * layoutWrapper)1596 RefPtr<MenuPaintProperty> MenuLayoutAlgorithm::GetPaintProperty(const LayoutWrapper* layoutWrapper)
1597 {
1598     auto menuNode = layoutWrapper->GetHostNode();
1599     CHECK_NULL_RETURN(menuNode, nullptr);
1600     auto paintProperty = menuNode->GetPaintProperty<MenuPaintProperty>();
1601     CHECK_NULL_RETURN(paintProperty, nullptr);
1602     return paintProperty;
1603 }
1604 
GetMenuRadius(const LayoutWrapper * layoutWrapper,const SizeF & menuSize)1605 BorderRadiusProperty MenuLayoutAlgorithm::GetMenuRadius(const LayoutWrapper* layoutWrapper, const SizeF& menuSize)
1606 {
1607     Dimension defaultDimension(0);
1608     BorderRadiusProperty radius = { defaultDimension, defaultDimension, defaultDimension, defaultDimension };
1609     auto pipeline = PipelineBase::GetCurrentContext();
1610     CHECK_NULL_RETURN(pipeline, radius);
1611     auto theme = pipeline->GetTheme<SelectTheme>();
1612     CHECK_NULL_RETURN(theme, radius);
1613     auto defaultRadius = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)
1614                              ? theme->GetMenuDefaultRadius()
1615                              : theme->GetMenuBorderRadius();
1616     radius.SetRadius(defaultRadius);
1617     auto menuLayoutProp = DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
1618     CHECK_NULL_RETURN(menuLayoutProp, radius);
1619     if (menuLayoutProp->GetBorderRadius().has_value()) {
1620         auto menuNode = layoutWrapper->GetHostNode();
1621         CHECK_NULL_RETURN(menuNode, radius);
1622         auto menuPattern = menuNode->GetPattern<MenuPattern>();
1623         CHECK_NULL_RETURN(menuPattern, radius);
1624         radius = menuPattern->CalcIdealBorderRadius(menuLayoutProp->GetBorderRadiusValue(), menuSize);
1625     }
1626 
1627     return radius;
1628 }
1629 
GetIfNeedArrow(const LayoutWrapper * layoutWrapper,const SizeF & menuSize)1630 bool MenuLayoutAlgorithm::GetIfNeedArrow(const LayoutWrapper* layoutWrapper, const SizeF& menuSize)
1631 {
1632     CHECK_NULL_RETURN(layoutWrapper, false);
1633     auto menuNode = layoutWrapper->GetHostNode();
1634     CHECK_NULL_RETURN(menuNode, false);
1635     auto menuPattern = menuNode->GetPattern<MenuPattern>();
1636     CHECK_NULL_RETURN(menuPattern, false);
1637     auto menuProp = DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
1638     CHECK_NULL_RETURN(menuProp, false);
1639     auto paintProperty = GetPaintProperty(layoutWrapper);
1640     CHECK_NULL_RETURN(paintProperty, false);
1641     propNeedArrow_ = paintProperty->GetEnableArrow().value_or(false);
1642 
1643     auto pipeline = PipelineBase::GetCurrentContext();
1644     CHECK_NULL_RETURN(pipeline, false);
1645     auto selectThemePtr = pipeline->GetTheme<SelectTheme>();
1646     CHECK_NULL_RETURN(selectThemePtr, false);
1647     if (!propNeedArrow_ || !menuProp->GetMenuPlacement().has_value()) {
1648         return false;
1649     }
1650 
1651     propArrowOffset_ = paintProperty->GetArrowOffset().value_or(Dimension(0));
1652     ProcessArrowParams(layoutWrapper, menuSize);
1653 
1654     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1655         return (menuPattern->IsContextMenu() || menuPattern->IsMenu()) && !targetTag_.empty() && arrowInMenu_;
1656     }
1657 
1658     return menuPattern->IsContextMenu() && !targetTag_.empty() && arrowInMenu_;
1659 }
1660 
ProcessArrowParams(const LayoutWrapper * layoutWrapper,const SizeF & menuSize)1661 void MenuLayoutAlgorithm::ProcessArrowParams(const LayoutWrapper* layoutWrapper, const SizeF& menuSize)
1662 {
1663     BorderRadiusProperty menuBorderRadius = GetMenuRadius(layoutWrapper, menuSize);
1664     auto radiusTopLeft = menuBorderRadius.radiusTopLeft.value_or(Dimension()).ConvertToPx();
1665     auto radiusTopRight = menuBorderRadius.radiusTopRight.value_or(Dimension()).ConvertToPx();
1666     auto radiusBottomLeft = menuBorderRadius.radiusBottomLeft.value_or(Dimension()).ConvertToPx();
1667     auto radiusBottomRight = menuBorderRadius.radiusBottomRight.value_or(Dimension()).ConvertToPx();
1668     arrowWidth_ = ARROW_WIDTH.ConvertToPx();
1669 
1670     switch (placement_) {
1671         case Placement::LEFT:
1672         case Placement::LEFT_TOP:
1673         case Placement::LEFT_BOTTOM:
1674             if (menuSize.Height() >= radiusTopRight + radiusBottomRight + arrowWidth_) {
1675                 arrowInMenu_ = true;
1676             }
1677             break;
1678         case Placement::RIGHT:
1679         case Placement::RIGHT_TOP:
1680         case Placement::RIGHT_BOTTOM:
1681             if (menuSize.Height() >= radiusTopLeft + radiusBottomLeft + arrowWidth_) {
1682                 arrowInMenu_ = true;
1683             }
1684             break;
1685         case Placement::TOP:
1686         case Placement::TOP_LEFT:
1687         case Placement::TOP_RIGHT:
1688             if (menuSize.Width() >= radiusBottomLeft + radiusBottomRight + arrowWidth_) {
1689                 arrowInMenu_ = true;
1690             }
1691             break;
1692         case Placement::BOTTOM:
1693         case Placement::BOTTOM_LEFT:
1694         case Placement::BOTTOM_RIGHT:
1695             if (menuSize.Width() >= radiusTopLeft + radiusTopRight + arrowWidth_) {
1696                 arrowInMenu_ = true;
1697             }
1698             break;
1699         default:
1700             break;
1701     }
1702 
1703     if (arrowInMenu_) {
1704         targetSpace_ = TARGET_SPACE.ConvertToPx();
1705     }
1706 }
1707 
UpdatePropArrowOffset()1708 void MenuLayoutAlgorithm::UpdatePropArrowOffset()
1709 {
1710     if (propArrowOffset_.IsValid()) {
1711         if (propArrowOffset_.Unit() == DimensionUnit::PERCENT) {
1712             propArrowOffset_.SetValue(std::clamp(propArrowOffset_.Value(), 0.0, 1.0));
1713         }
1714         return;
1715     }
1716     switch (arrowPlacement_) {
1717         case Placement::LEFT:
1718         case Placement::RIGHT:
1719         case Placement::TOP:
1720         case Placement::BOTTOM:
1721             propArrowOffset_ = ARROW_HALF_PERCENT_VALUE;
1722             break;
1723         case Placement::TOP_LEFT:
1724         case Placement::BOTTOM_LEFT:
1725         case Placement::LEFT_TOP:
1726         case Placement::RIGHT_TOP:
1727             propArrowOffset_ = ARROW_ZERO_PERCENT_VALUE;
1728             break;
1729         case Placement::TOP_RIGHT:
1730         case Placement::BOTTOM_RIGHT:
1731         case Placement::LEFT_BOTTOM:
1732         case Placement::RIGHT_BOTTOM:
1733             propArrowOffset_ = ARROW_ONE_HUNDRED_PERCENT_VALUE;
1734             break;
1735         default:
1736             break;
1737     }
1738 }
1739 
UpdateArrowOffsetWithMenuLimit(const SizeF & menuSize,const LayoutWrapper * layoutWrapper)1740 void MenuLayoutAlgorithm::UpdateArrowOffsetWithMenuLimit(const SizeF& menuSize, const LayoutWrapper* layoutWrapper)
1741 {
1742     UpdatePropArrowOffset();
1743     BorderRadiusProperty menuBorderRadius = GetMenuRadius(layoutWrapper, menuSize);
1744     auto radiusTopLeft = menuBorderRadius.radiusTopLeft.value_or(Dimension()).ConvertToPx();
1745     auto radiusTopRight = menuBorderRadius.radiusTopRight.value_or(Dimension()).ConvertToPx();
1746     auto radiusBottomLeft = menuBorderRadius.radiusBottomLeft.value_or(Dimension()).ConvertToPx();
1747     auto radiusBottomRight = menuBorderRadius.radiusBottomRight.value_or(Dimension()).ConvertToPx();
1748     float range = -1.0f;
1749 
1750     switch (arrowPlacement_) {
1751         case Placement::LEFT:
1752         case Placement::LEFT_TOP:
1753         case Placement::LEFT_BOTTOM:
1754             range = menuSize.Height() - radiusTopRight - radiusBottomRight - arrowWidth_;
1755             arrowMinLimit_ = radiusTopRight + ARROW_WIDTH.ConvertToPx() / ARROW_WIDTH_FACTOR;
1756             break;
1757         case Placement::RIGHT:
1758         case Placement::RIGHT_TOP:
1759         case Placement::RIGHT_BOTTOM:
1760             range = menuSize.Height() - radiusTopLeft - radiusBottomLeft - arrowWidth_;
1761             arrowMinLimit_ = radiusTopLeft + ARROW_WIDTH.ConvertToPx() / ARROW_WIDTH_FACTOR;
1762             break;
1763         case Placement::TOP:
1764         case Placement::TOP_LEFT:
1765         case Placement::TOP_RIGHT:
1766             range = menuSize.Width() - radiusBottomLeft - radiusBottomRight - arrowWidth_;
1767             arrowMinLimit_ = radiusBottomLeft + ARROW_WIDTH.ConvertToPx() / ARROW_WIDTH_FACTOR;
1768             break;
1769         case Placement::BOTTOM:
1770         case Placement::BOTTOM_LEFT:
1771         case Placement::BOTTOM_RIGHT:
1772             range = menuSize.Width() - radiusTopLeft - radiusTopRight - arrowWidth_;
1773             arrowMinLimit_ = radiusTopLeft + ARROW_WIDTH.ConvertToPx() / ARROW_WIDTH_FACTOR;
1774             break;
1775         default:
1776             break;
1777     }
1778     if (range >= 0) {
1779         float tempOffset = propArrowOffset_.Unit() == DimensionUnit::PERCENT ? propArrowOffset_.Value() * range
1780                                                                              : propArrowOffset_.ConvertToPx();
1781         arrowOffset_ = std::clamp(tempOffset, 0.0f, range);
1782     }
1783 }
1784 
ComputeMenuPositionByAlignType(const RefPtr<MenuLayoutProperty> & menuProp,const SizeF & menuSize)1785 void MenuLayoutAlgorithm::ComputeMenuPositionByAlignType(
1786     const RefPtr<MenuLayoutProperty>& menuProp, const SizeF& menuSize)
1787 {
1788     auto alignType = menuProp->GetAlignType().value_or(MenuAlignType::START);
1789     auto direction = menuProp->GetNonAutoLayoutDirection();
1790     auto targetSize = menuProp->GetTargetSizeValue(SizeF());
1791     switch (alignType) {
1792         case MenuAlignType::CENTER: {
1793             position_.AddX(targetSize.Width() / 2.0f - menuSize.Width() / 2.0f);
1794             break;
1795         }
1796         case MenuAlignType::END: {
1797             if (direction == TextDirection::RTL) {
1798                 return;
1799             }
1800             position_.AddX(targetSize.Width() - menuSize.Width());
1801             break;
1802         }
1803         case MenuAlignType::START: {
1804             if (direction != TextDirection::RTL) {
1805                 return;
1806             }
1807             position_.AddX(targetSize.Width() - menuSize.Width());
1808             break;
1809         }
1810         default:
1811             break;
1812     }
1813 }
1814 
ComputeMenuPositionByOffset(const RefPtr<MenuLayoutProperty> & menuProp,const RefPtr<GeometryNode> & geometryNode)1815 OffsetF MenuLayoutAlgorithm::ComputeMenuPositionByOffset(
1816     const RefPtr<MenuLayoutProperty>& menuProp, const RefPtr<GeometryNode>& geometryNode)
1817 {
1818     CHECK_NULL_RETURN(menuProp, OffsetF(0, 0));
1819     CHECK_NULL_RETURN(geometryNode, OffsetF(0, 0));
1820 
1821     const auto& layoutConstraint = menuProp->GetLayoutConstraint();
1822     CHECK_NULL_RETURN(layoutConstraint, OffsetF(0, 0));
1823     auto menuAlignOffset = menuProp->GetOffset().value_or(
1824         DimensionOffset(Dimension(0, DimensionUnit::VP), Dimension(0, DimensionUnit::VP)));
1825 
1826     auto menuSize = geometryNode->GetFrameSize();
1827     auto menuTrimOffsetX =
1828         ConvertToPx(CalcLength(menuAlignOffset.GetX()), layoutConstraint->scaleProperty, menuSize.Width());
1829     auto menuTrimOffsetY =
1830         ConvertToPx(CalcLength(menuAlignOffset.GetY()), layoutConstraint->scaleProperty, menuSize.Height());
1831     OffsetF menuTrimOffset = OffsetF(menuTrimOffsetX.value_or(0.0), menuTrimOffsetY.value_or(0.0));
1832     return menuTrimOffset;
1833 }
1834 
GetCurrentPipelineContext()1835 RefPtr<PipelineContext> MenuLayoutAlgorithm::GetCurrentPipelineContext()
1836 {
1837     // Get pipelineContext of main window or host window for UIExtension
1838     auto containerId = Container::CurrentId();
1839     RefPtr<PipelineContext> context;
1840     if (containerId >= MIN_SUBCONTAINER_ID) {
1841         auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(containerId);
1842         auto parentContainer = AceEngine::Get().GetContainer(parentContainerId);
1843         CHECK_NULL_RETURN(parentContainer, nullptr);
1844         context = DynamicCast<PipelineContext>(parentContainer->GetPipelineContext());
1845     } else {
1846         context = PipelineContext::GetCurrentContext();
1847     }
1848     return context;
1849 }
1850 
MenuLayoutAvoidAlgorithm(const RefPtr<MenuLayoutProperty> & menuProp,const RefPtr<MenuPattern> & menuPattern,const SizeF & size,bool didNeedArrow,LayoutWrapper * layoutWrapper)1851 OffsetF MenuLayoutAlgorithm::MenuLayoutAvoidAlgorithm(const RefPtr<MenuLayoutProperty>& menuProp,
1852     const RefPtr<MenuPattern>& menuPattern, const SizeF& size, bool didNeedArrow, LayoutWrapper* layoutWrapper)
1853 {
1854     auto pipelineContext = GetCurrentPipelineContext();
1855     CHECK_NULL_RETURN(pipelineContext, OffsetF(0, 0));
1856     CHECK_NULL_RETURN(menuProp, OffsetF(0, 0));
1857     CHECK_NULL_RETURN(menuPattern, OffsetF(0, 0));
1858     float x = 0.0f;
1859     float y = 0.0f;
1860     if (menuProp->GetMenuPlacement().has_value() && (targetSize_.Width() > 0.0 || targetSize_.Height() > 0.0)) {
1861         placement_ = menuProp->GetMenuPlacement().value();
1862         if (layoutWrapper != nullptr) {
1863             PlacementRTL(layoutWrapper, placement_);
1864         }
1865         auto childOffset = GetChildPosition(size, didNeedArrow);
1866         x = childOffset.GetX();
1867         y = childOffset.GetY();
1868     } else {
1869         x = HorizontalLayout(size, position_.GetX(), menuPattern->IsSelectMenu()) + positionOffset_.GetX();
1870         y = VerticalLayout(size, position_.GetY(), menuPattern->IsContextMenu()) + positionOffset_.GetY();
1871     }
1872     x = std::clamp(static_cast<double>(x), static_cast<double>(paddingStart_),
1873         static_cast<double>(wrapperRect_.Right() - size.Width() - paddingEnd_));
1874     float yMinAvoid = wrapperRect_.Top() + paddingTop_;
1875     float yMaxAvoid = wrapperRect_.Bottom() - paddingBottom_ - size.Height();
1876     y = std::clamp(y, yMinAvoid, yMaxAvoid);
1877     return { x, y };
1878 }
1879 
PlacementRTL(LayoutWrapper * layoutWrapper,Placement & placement_)1880 void MenuLayoutAlgorithm::PlacementRTL(LayoutWrapper* layoutWrapper, Placement& placement_)
1881 {
1882     auto menuLayoutProperty = layoutWrapper->GetLayoutProperty();
1883     CHECK_NULL_VOID(menuLayoutProperty);
1884     auto layoutDirection = menuLayoutProperty->GetNonAutoLayoutDirection();
1885     if (layoutDirection == TextDirection::RTL) {
1886         switch (placement_) {
1887             case Placement::LEFT:
1888                 placement_ = Placement::RIGHT;
1889                 break;
1890             case Placement::RIGHT:
1891                 placement_ = Placement::LEFT;
1892                 break;
1893             case Placement::TOP_LEFT:
1894                 placement_ = Placement::TOP_RIGHT;
1895                 break;
1896             case Placement::TOP_RIGHT:
1897                 placement_ = Placement::TOP_LEFT;
1898                 break;
1899             case Placement::BOTTOM_LEFT:
1900                 placement_ = Placement::BOTTOM_RIGHT;
1901                 break;
1902             case Placement::BOTTOM_RIGHT:
1903                 placement_ = Placement::BOTTOM_LEFT;
1904                 break;
1905             case Placement::LEFT_TOP:
1906                 placement_ = Placement::RIGHT_TOP;
1907                 break;
1908             case Placement::RIGHT_TOP:
1909                 placement_ = Placement::LEFT_TOP;
1910                 break;
1911             case Placement::LEFT_BOTTOM:
1912                 placement_ = Placement::RIGHT_BOTTOM;
1913                 break;
1914             case Placement::RIGHT_BOTTOM:
1915                 placement_ = Placement::LEFT_BOTTOM;
1916                 break;
1917             default:
1918                 break;
1919         }
1920     }
1921 }
1922 
LimitContainerModalMenuRect(double & rectWidth,double & rectHeight)1923 void MenuLayoutAlgorithm::LimitContainerModalMenuRect(double& rectWidth, double& rectHeight)
1924 {
1925     auto containerOffsetX = static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()) +
1926                             static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()) +
1927                             static_cast<float>(CONTENT_PADDING.ConvertToPx());
1928     auto pipeline = NG::PipelineContext::GetCurrentContext();
1929     CHECK_NULL_VOID(pipeline);
1930     auto containerOffsetY = static_cast<float>(pipeline->GetCustomTitleHeight().ConvertToPx()) +
1931                             static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()) +
1932                             static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx());
1933     rectWidth -= containerOffsetX;
1934     rectHeight -= containerOffsetY;
1935 }
1936 
UpdateConstraintWidth(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)1937 void MenuLayoutAlgorithm::UpdateConstraintWidth(LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
1938 {
1939     RefPtr<GridColumnInfo> columnInfo;
1940     columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
1941     auto menuNode = layoutWrapper->GetHostNode();
1942     CHECK_NULL_VOID(menuNode);
1943     auto menuPattern = menuNode->GetPattern<MenuPattern>();
1944     CHECK_NULL_VOID(menuPattern);
1945     columnInfo->GetParent()->BuildColumnWidth(wrapperSize_.Width());
1946     auto menuLayoutProperty = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
1947     CHECK_NULL_VOID(menuLayoutProperty);
1948     // set max width
1949     const auto& padding = menuLayoutProperty->CreatePaddingAndBorder();
1950     auto maxHorizontalSpace = std::max(leftSpace_, rightSpace_) - 2.0f * padding.Width();
1951     auto maxWidth = static_cast<float>(columnInfo->GetWidth(GetMaxGridCounts(columnInfo)));
1952     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
1953         maxWidth = std::min(maxHorizontalSpace, maxWidth);
1954     }
1955     maxWidth = std::min(constraint.maxSize.Width(), maxWidth);
1956     constraint.maxSize.SetWidth(maxWidth);
1957 }
1958 
UpdateConstraintHeight(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)1959 void MenuLayoutAlgorithm::UpdateConstraintHeight(LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
1960 {
1961     auto pipelineContext = GetCurrentPipelineContext();
1962     CHECK_NULL_VOID(pipelineContext);
1963     auto menuPattern = layoutWrapper->GetHostNode()->GetPattern<MenuPattern>();
1964     CHECK_NULL_VOID(menuPattern);
1965 
1966     float maxAvailableHeight = wrapperRect_.Height();
1967     float maxSpaceHeight = maxAvailableHeight * HEIGHT_CONSTRAINT_FACTOR;
1968     if (lastPosition_.has_value() && CheckIsEmbeddedMode(layoutWrapper)) {
1969         auto spaceToBottom = static_cast<float>(wrapperRect_.Bottom()) - lastPosition_.value().GetY();
1970         maxSpaceHeight = std::min(maxSpaceHeight, spaceToBottom);
1971     }
1972     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
1973         if (menuPattern->IsHeightModifiedBySelect()) {
1974             auto menuLayoutProps = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
1975             auto selectModifiedHeight = menuLayoutProps->GetSelectModifiedHeight().value();
1976             if (selectModifiedHeight < maxSpaceHeight) {
1977                 maxSpaceHeight = selectModifiedHeight;
1978             }
1979         }
1980     }
1981     constraint.maxSize.SetHeight(maxSpaceHeight);
1982 }
1983 
CreateChildConstraint(LayoutWrapper * layoutWrapper)1984 LayoutConstraintF MenuLayoutAlgorithm::CreateChildConstraint(LayoutWrapper* layoutWrapper)
1985 {
1986     auto menuLayoutProperty = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
1987     CHECK_NULL_RETURN(menuLayoutProperty, LayoutConstraintF());
1988 
1989     auto childConstraint = menuLayoutProperty->CreateChildConstraint();
1990     UpdateConstraintWidth(layoutWrapper, childConstraint);
1991     UpdateConstraintHeight(layoutWrapper, childConstraint);
1992     UpdateConstraintBaseOnOptions(layoutWrapper, childConstraint);
1993     return childConstraint;
1994 }
1995 
UpdateConstraintBaseOnOptions(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)1996 void MenuLayoutAlgorithm::UpdateConstraintBaseOnOptions(LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
1997 {
1998     auto menuNode = layoutWrapper->GetHostNode();
1999     CHECK_NULL_VOID(menuNode);
2000     auto menuPattern = menuNode->GetPattern<MenuPattern>();
2001     CHECK_NULL_VOID(menuPattern);
2002     auto options = menuPattern->GetOptions();
2003     if (options.empty()) {
2004         return;
2005     }
2006     auto optionConstraint = constraint;
2007     RefPtr<GridColumnInfo> columnInfo;
2008     columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
2009     columnInfo->GetParent()->BuildColumnWidth(wrapperSize_.Width());
2010     auto minWidth = static_cast<float>(columnInfo->GetWidth(MIN_GRID_COUNTS));
2011     optionConstraint.maxSize.MinusWidth(optionPadding_ * 2.0f);
2012     optionConstraint.minSize.SetWidth(minWidth - optionPadding_ * 2.0f);
2013     auto maxChildrenWidth = optionConstraint.minSize.Width();
2014     auto optionsLayoutWrapper = GetOptionsLayoutWrappper(layoutWrapper);
2015     for (const auto& optionWrapper : optionsLayoutWrapper) {
2016         optionWrapper->GetLayoutProperty()->ResetCalcMinSize();
2017         optionWrapper->Measure(optionConstraint);
2018         auto childSize = optionWrapper->GetGeometryNode()->GetMarginFrameSize();
2019         maxChildrenWidth = std::max(maxChildrenWidth, childSize.Width());
2020     }
2021     UpdateOptionConstraint(optionsLayoutWrapper, maxChildrenWidth);
2022     constraint.minSize.SetWidth(maxChildrenWidth + optionPadding_ * 2.0f);
2023 }
2024 
GetOptionsLayoutWrappper(LayoutWrapper * layoutWrapper)2025 std::list<RefPtr<LayoutWrapper>> MenuLayoutAlgorithm::GetOptionsLayoutWrappper(LayoutWrapper* layoutWrapper)
2026 {
2027     std::list<RefPtr<LayoutWrapper>> optionsWrapper;
2028     auto scrollWrapper = layoutWrapper->GetOrCreateChildByIndex(0);
2029     CHECK_NULL_RETURN(scrollWrapper, optionsWrapper);
2030     auto columnWrapper = scrollWrapper->GetOrCreateChildByIndex(0);
2031     CHECK_NULL_RETURN(columnWrapper, optionsWrapper);
2032     optionsWrapper = columnWrapper->GetAllChildrenWithBuild();
2033     return optionsWrapper;
2034 }
2035 
UpdateOptionConstraint(std::list<RefPtr<LayoutWrapper>> & options,float width)2036 void MenuLayoutAlgorithm::UpdateOptionConstraint(std::list<RefPtr<LayoutWrapper>>& options, float width)
2037 {
2038     for (const auto& option : options) {
2039         auto optionLayoutProps = option->GetLayoutProperty();
2040         CHECK_NULL_VOID(optionLayoutProps);
2041         optionLayoutProps->UpdateCalcMinSize(CalcSize(CalcLength(width), std::nullopt));
2042     }
2043 }
2044 
2045 // return vertical offset
VerticalLayout(const SizeF & size,float position,bool isContextMenu)2046 float MenuLayoutAlgorithm::VerticalLayout(const SizeF& size, float position, bool isContextMenu)
2047 {
2048     placement_ = Placement::BOTTOM;
2049     // can put menu below click point
2050     if (GreatOrEqual(bottomSpace_, size.Height())) {
2051         return position + margin_;
2052     }
2053     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) && isContextMenu) {
2054         if (LessNotEqual(bottomSpace_, size.Height()) && LessNotEqual(size.Height(), wrapperRect_.Height())) {
2055             return wrapperRect_.Bottom() - size.Height() - paddingBottom_;
2056         }
2057         // can't fit in screen, line up with top of the screen
2058         return wrapperRect_.Top() + paddingTop_;
2059     } else {
2060         float wrapperHeight = wrapperSize_.Height();
2061         // put menu above click point
2062         if (GreatOrEqual(topSpace_, size.Height())) {
2063             // menu show on top
2064             placement_ = Placement::TOP;
2065             return topSpace_ - size.Height() + margin_;
2066         }
2067         // line up bottom of menu with bottom of the screen
2068         if (LessNotEqual(size.Height(), wrapperHeight)) {
2069             return wrapperHeight - size.Height();
2070         }
2071         // can't fit in screen, line up with top of the screen
2072         return 0.0f;
2073     }
2074 }
2075 
2076 // returns horizontal offset
HorizontalLayout(const SizeF & size,float position,bool isSelectMenu)2077 float MenuLayoutAlgorithm::HorizontalLayout(const SizeF& size, float position, bool isSelectMenu)
2078 {
2079     float wrapperWidth = wrapperSize_.Width();
2080     // can fit menu on the right side of position
2081     if (rightSpace_ >= size.Width()) {
2082         return position + margin_;
2083     }
2084 
2085     // fit menu on the left side
2086     if (!isSelectMenu && leftSpace_ >= size.Width()) {
2087         return position - size.Width();
2088     }
2089 
2090     // line up right side of menu with right boundary of the screen
2091     if (size.Width() < wrapperWidth) {
2092         if (isSelectMenu) {
2093             return position + margin_;
2094         }
2095         return wrapperWidth - size.Width();
2096     }
2097 
2098     // can't fit in screen, line up with left side of the screen
2099     return 0.0f;
2100 }
2101 
GetPositionWithPlacement(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2102 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacement(
2103     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2104 {
2105     OffsetF childPosition;
2106 
2107     auto func = placementFuncMap_.find(placement_);
2108     if (func != placementFuncMap_.end()) {
2109         auto placementFunc = func->second;
2110         if (placementFunc != nullptr) {
2111             childPosition = (this->*placementFunc)(childSize, topPosition, bottomPosition);
2112         }
2113     }
2114     return childPosition;
2115 }
2116 
GetArrowPositionWithPlacement(const SizeF & menuSize,const LayoutWrapper * layoutWrapper)2117 OffsetF MenuLayoutAlgorithm::GetArrowPositionWithPlacement(const SizeF& menuSize, const LayoutWrapper* layoutWrapper)
2118 {
2119     UpdateArrowOffsetWithMenuLimit(menuSize, layoutWrapper);
2120     auto addArrowOffsetToArrowMin = arrowOffset_ + arrowMinLimit_;
2121     auto space_ = ARROW_HIGHT.ConvertToPx();
2122     OffsetF childPosition;
2123     switch (arrowPlacement_) {
2124         case Placement::TOP:
2125         case Placement::TOP_LEFT:
2126         case Placement::TOP_RIGHT:
2127             childPosition = OffsetF(addArrowOffsetToArrowMin, menuSize.Height() + space_);
2128             break;
2129         case Placement::BOTTOM:
2130         case Placement::BOTTOM_LEFT:
2131         case Placement::BOTTOM_RIGHT:
2132             childPosition = OffsetF(addArrowOffsetToArrowMin, -space_);
2133             break;
2134         case Placement::LEFT:
2135         case Placement::LEFT_TOP:
2136         case Placement::LEFT_BOTTOM:
2137             childPosition = OffsetF(menuSize.Width() + space_, addArrowOffsetToArrowMin);
2138             break;
2139         case Placement::RIGHT:
2140         case Placement::RIGHT_TOP:
2141         case Placement::RIGHT_BOTTOM:
2142             childPosition = OffsetF(-space_, addArrowOffsetToArrowMin);
2143             break;
2144         default:
2145             break;
2146     }
2147     return childPosition;
2148 }
2149 
GetMenuWrapperOffset(const LayoutWrapper * layoutWrapper)2150 OffsetF MenuLayoutAlgorithm::GetMenuWrapperOffset(const LayoutWrapper* layoutWrapper)
2151 {
2152     CHECK_NULL_RETURN(layoutWrapper, OffsetF());
2153     auto menuNode = layoutWrapper->GetHostNode();
2154     CHECK_NULL_RETURN(menuNode, OffsetF());
2155     auto menuLayoutProperty = layoutWrapper->GetLayoutProperty();
2156     if (menuLayoutProperty && menuLayoutProperty->GetNonAutoLayoutDirection() == TextDirection::RTL) {
2157         return menuNode->GetPaintRectOffset(true, true);
2158     }
2159     return menuNode->GetParentGlobalOffsetDuringLayout();
2160 }
2161 
SkipUpdateTargetNodeSize(const RefPtr<FrameNode> & targetNode,const RefPtr<MenuPattern> & menuPattern)2162 bool MenuLayoutAlgorithm::SkipUpdateTargetNodeSize(
2163     const RefPtr<FrameNode>& targetNode, const RefPtr<MenuPattern>& menuPattern)
2164 {
2165     CHECK_NULL_RETURN(menuPattern, false);
2166     auto menuWrapper = menuPattern->GetMenuWrapper();
2167     CHECK_NULL_RETURN(menuWrapper, false);
2168     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
2169     CHECK_NULL_RETURN(menuWrapperPattern, false);
2170 
2171     auto isMenuHide = menuWrapperPattern->IsHide();
2172     auto isTargetEmpty = !targetNode && menuPattern->GetTargetSize().IsPositive();
2173     if (isMenuHide || isTargetEmpty) {
2174         TAG_LOGI(AceLogTag::ACE_MENU,
2175             "targetNode empty: %{public}d, menu hidden: %{public}d, update targetNode to last size and position",
2176             isMenuHide, isTargetEmpty);
2177         targetSize_ = menuPattern->GetTargetSize();
2178         targetOffset_ = menuPattern->GetTargetOffset();
2179         return true;
2180     }
2181     return false;
2182 }
2183 
InitTargetSizeAndPosition(const LayoutWrapper * layoutWrapper,bool isContextMenu,const RefPtr<MenuPattern> & menuPattern)2184 void MenuLayoutAlgorithm::InitTargetSizeAndPosition(
2185     const LayoutWrapper* layoutWrapper, bool isContextMenu, const RefPtr<MenuPattern>& menuPattern)
2186 {
2187     CHECK_NULL_VOID(layoutWrapper && menuPattern);
2188     auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
2189     if (!SkipUpdateTargetNodeSize(targetNode, menuPattern)) {
2190         CHECK_NULL_VOID(targetNode);
2191         dumpInfo_.targetNode = targetNode->GetTag();
2192         auto props = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
2193         CHECK_NULL_VOID(props);
2194         if (props->GetIsRectInTargetValue(false)) {
2195             targetSize_ = props->GetTargetSizeValue(SizeF());
2196             targetOffset_ = props->GetMenuOffsetValue(OffsetF());
2197         } else {
2198             targetSize_ = targetNode->GetPaintRectWithTransform().GetSize();
2199             targetOffset_ = targetNode->GetPaintRectOffset(false, true);
2200         }
2201     }
2202     dumpInfo_.targetSize = targetSize_;
2203     dumpInfo_.targetOffset = targetOffset_;
2204     menuPattern->SetTargetSize(targetSize_);
2205     menuPattern->SetTargetOffset(targetOffset_);
2206     TAG_LOGI(AceLogTag::ACE_MENU, "targetNode: %{public}s, targetSize: %{public}s, targetOffset: %{public}s",
2207         targetTag_.c_str(), targetSize_.ToString().c_str(), targetOffset_.ToString().c_str());
2208     auto pipelineContext = GetCurrentPipelineContext();
2209     CHECK_NULL_VOID(pipelineContext);
2210     bool expandDisplay = menuPattern->GetMenuExpandDisplay();
2211     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
2212         expandDisplay = true;
2213     }
2214     if (canExpandCurrentWindow_ && targetTag_ != V2::SELECT_ETS_TAG) {
2215         ModifyTargetOffset();
2216         OffsetF offset = GetMenuWrapperOffset(layoutWrapper);
2217         targetOffset_ -= offset;
2218         return;
2219     }
2220 
2221     auto windowManager = pipelineContext->GetWindowManager();
2222     auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL && windowManager &&
2223                             windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING;
2224     if (isContainerModal) {
2225         auto newOffsetX = static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()) +
2226                           static_cast<float>(CONTENT_PADDING.ConvertToPx());
2227         auto newOffsetY = static_cast<float>(pipelineContext->GetCustomTitleHeight().ConvertToPx()) +
2228                           static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx());
2229         targetOffset_ -= OffsetF(newOffsetX, newOffsetY);
2230     } else {
2231         OffsetF offset = GetMenuWrapperOffset(layoutWrapper);
2232         targetOffset_ -= offset;
2233     }
2234 }
2235 
FitToScreen(const OffsetF & position,const SizeF & childSize,bool didNeedArrow)2236 OffsetF MenuLayoutAlgorithm::FitToScreen(const OffsetF& position, const SizeF& childSize, bool didNeedArrow)
2237 {
2238     OffsetF afterOffsetPosition;
2239     auto originPosition = position;
2240 
2241     if (NearEqual(positionOffset_, OffsetF(0.0f, 0.0f))) {
2242         afterOffsetPosition = AddTargetSpace(originPosition);
2243     } else {
2244         afterOffsetPosition = AddOffset(originPosition);
2245     }
2246 
2247     if (!CheckPosition(afterOffsetPosition, childSize) || flag_) {
2248         flag_ = false;
2249         return OffsetF(0.0f, 0.0f);
2250     }
2251 
2252     return afterOffsetPosition;
2253 }
2254 
GetChildPosition(const SizeF & childSize,bool didNeedArrow)2255 OffsetF MenuLayoutAlgorithm::GetChildPosition(const SizeF& childSize, bool didNeedArrow)
2256 {
2257     OffsetF bottomPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
2258         targetOffset_.GetY() + targetSize_.Height() + targetSpace_);
2259     OffsetF topPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
2260         targetOffset_.GetY() - childSize.Height() - targetSpace_);
2261     OffsetF defaultPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
2262         targetOffset_.GetY() + (targetSize_.Height() - childSize.Height()) / 2.0);
2263 
2264     OffsetF childPosition;
2265     OffsetF position = defaultPosition;
2266     auto positionOffset = positionOffset_;
2267     std::vector<Placement> currentPlacementStates = PLACEMENT_STATES.find(Placement::BOTTOM_LEFT)->second;
2268     auto it = PLACEMENT_STATES.find(placement_);
2269     if (it != PLACEMENT_STATES.end()) {
2270         currentPlacementStates = it->second;
2271     }
2272     size_t step = ALIGNMENT_STEP_OFFSET;
2273     if (placement_ <= Placement::BOTTOM) {
2274         step += 1;
2275     }
2276     for (size_t i = 0, len = currentPlacementStates.size(); i < len; i++) {
2277         placement_ = currentPlacementStates[i];
2278         if (placement_ == Placement::NONE) {
2279             break;
2280         }
2281         if (i >= step) {
2282             positionOffset_ = OffsetF(0.0f, 0.0f);
2283         }
2284         childPosition = GetPositionWithPlacement(childSize, topPosition, bottomPosition);
2285         position = FitToScreen(childPosition, childSize, didNeedArrow);
2286         if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
2287             continue;
2288         }
2289         break;
2290     }
2291     if (placement_ == Placement::NONE) {
2292         position = GetAdjustPosition(currentPlacementStates, step, childSize, topPosition, bottomPosition);
2293         if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
2294             position = defaultPosition;
2295         }
2296     }
2297     positionOffset_ = positionOffset;
2298     arrowPlacement_ = placement_;
2299 
2300     return position;
2301 }
2302 
GetAdjustPosition(std::vector<Placement> & currentPlacementStates,size_t step,const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2303 OffsetF MenuLayoutAlgorithm::GetAdjustPosition(std::vector<Placement>& currentPlacementStates, size_t step,
2304     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2305 {
2306     OffsetF childPosition;
2307     OffsetF position;
2308     for (size_t i = 0, len = currentPlacementStates.size(); i < len;) {
2309         placement_ = currentPlacementStates[i];
2310         if (placement_ == Placement::NONE) {
2311             break;
2312         }
2313         childPosition = GetPositionWithPlacement(childSize, topPosition, bottomPosition);
2314         position = AdjustPosition(childPosition, childSize.Width(), childSize.Height(), targetSecurity_);
2315         if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
2316             i += step;
2317             continue;
2318         }
2319         break;
2320     }
2321     return position;
2322 }
2323 
AdjustPosition(const OffsetF & position,float width,float height,float space)2324 OffsetF MenuLayoutAlgorithm::AdjustPosition(const OffsetF& position, float width, float height, float space)
2325 {
2326     float xMax = 0.0f;
2327     float yMax = 0.0f;
2328     float xMin = paddingStart_;
2329     float yMin = std::max(1.0f, static_cast<float>(wrapperRect_.Top()) + paddingTop_);
2330     float wrapperBottom = wrapperRect_.Bottom();
2331     switch (placement_) {
2332         case Placement::LEFT_TOP:
2333         case Placement::LEFT_BOTTOM:
2334         case Placement::LEFT: {
2335             xMax = std::min(targetOffset_.GetX() - width - space, wrapperSize_.Width() - paddingEnd_ - width);
2336             yMax = wrapperBottom - height - paddingBottom_;
2337             break;
2338         }
2339         case Placement::RIGHT_TOP:
2340         case Placement::RIGHT_BOTTOM:
2341         case Placement::RIGHT: {
2342             xMin = std::max(targetOffset_.GetX() + targetSize_.Width() + space, paddingStart_);
2343             xMax = wrapperSize_.Width() - width - paddingEnd_;
2344             yMax = wrapperBottom - height - paddingBottom_;
2345             break;
2346         }
2347         case Placement::TOP_LEFT:
2348         case Placement::TOP_RIGHT:
2349         case Placement::TOP: {
2350             xMax = wrapperSize_.Width() - width - paddingEnd_;
2351             yMax = std::min(targetOffset_.GetY() - height - space, wrapperBottom - paddingBottom_ - height);
2352             break;
2353         }
2354         case Placement::BOTTOM_LEFT:
2355         case Placement::BOTTOM_RIGHT:
2356         case Placement::BOTTOM: {
2357             xMax = wrapperRect_.Right() - width - paddingEnd_;
2358             yMin = std::max(targetOffset_.GetY() + targetSize_.Height() + space, yMin);
2359             yMax = wrapperBottom - height - paddingBottom_;
2360             break;
2361         }
2362         default:
2363             break;
2364     }
2365     if (xMax < xMin || yMax < yMin) {
2366         return OffsetF(0.0f, 0.0f);
2367     }
2368     auto x = std::clamp(position.GetX(), xMin, xMax);
2369     auto y = std::clamp(position.GetY(), yMin, yMax);
2370     return OffsetF(x, y);
2371 }
2372 
AddTargetSpace(const OffsetF & position)2373 OffsetF MenuLayoutAlgorithm::AddTargetSpace(const OffsetF& position)
2374 {
2375     auto x = position.GetX();
2376     auto y = position.GetY();
2377     switch (placement_) {
2378         case Placement::BOTTOM_LEFT:
2379         case Placement::BOTTOM_RIGHT:
2380         case Placement::BOTTOM: {
2381             y += targetSecurity_;
2382             break;
2383         }
2384         case Placement::TOP_LEFT:
2385         case Placement::TOP_RIGHT:
2386         case Placement::TOP: {
2387             y -= targetSecurity_;
2388             break;
2389         }
2390         case Placement::RIGHT_TOP:
2391         case Placement::RIGHT_BOTTOM:
2392         case Placement::RIGHT: {
2393             x += targetSecurity_;
2394             break;
2395         }
2396         case Placement::LEFT_TOP:
2397         case Placement::LEFT_BOTTOM:
2398         case Placement::LEFT: {
2399             x -= targetSecurity_;
2400             break;
2401         }
2402         default: {
2403             y += targetSecurity_;
2404             break;
2405         }
2406     }
2407     return OffsetF(x, y);
2408 }
2409 
AddOffset(const OffsetF & position)2410 OffsetF MenuLayoutAlgorithm::AddOffset(const OffsetF& position)
2411 {
2412     auto x = position.GetX();
2413     auto y = position.GetY();
2414     switch (placement_) {
2415         case Placement::BOTTOM_LEFT:
2416         case Placement::BOTTOM_RIGHT:
2417         case Placement::BOTTOM: {
2418             x += positionOffset_.GetX();
2419             y += positionOffset_.GetY();
2420             break;
2421         }
2422         case Placement::TOP_LEFT:
2423         case Placement::TOP_RIGHT:
2424         case Placement::TOP: {
2425             x += positionOffset_.GetX();
2426             y -= positionOffset_.GetY();
2427             break;
2428         }
2429         case Placement::RIGHT_TOP:
2430         case Placement::RIGHT_BOTTOM:
2431         case Placement::RIGHT: {
2432             x += positionOffset_.GetX();
2433             y += positionOffset_.GetY();
2434             break;
2435         }
2436         case Placement::LEFT_TOP:
2437         case Placement::LEFT_BOTTOM:
2438         case Placement::LEFT: {
2439             x -= positionOffset_.GetX();
2440             y += positionOffset_.GetY();
2441             break;
2442         }
2443         default: {
2444             x += positionOffset_.GetX();
2445             y += positionOffset_.GetY();
2446             break;
2447         }
2448     }
2449     return OffsetF(x, y);
2450 }
2451 
CheckPositionInPlacementRect(const Rect & rect,const OffsetF & position,const SizeF & childSize)2452 bool MenuLayoutAlgorithm::CheckPositionInPlacementRect(
2453     const Rect& rect, const OffsetF& position, const SizeF& childSize)
2454 {
2455     auto x = position.GetX();
2456     auto y = position.GetY();
2457     OffsetF tempPos = position;
2458     if (state_ != prevState_) {
2459         if (prevState_ == -1) {
2460             prevState_ = state_;
2461             preOffset_ = position;
2462             preOffset_.SetX(x);
2463             preOffset_.SetY(y);
2464             preRect_.SetOffset(rect.GetOffset());
2465             preRect_.SetSize(rect.GetSize());
2466             auto outside = LessNotEqual(x, rect.Left()) || GreatNotEqual(x + childSize.Width(), rect.Right()) ||
2467                            LessNotEqual(y, rect.Top()) || GreatNotEqual(y + childSize.Height(), rect.Bottom());
2468             if (!outside) {
2469                 preOffset_ = position;
2470                 preOffset_.SetX(x);
2471                 preOffset_.SetY(y);
2472                 preRect_.SetOffset(rect.GetOffset());
2473                 preRect_.SetSize(rect.GetSize());
2474                 return true;
2475             }
2476             flag_ = true;
2477             positionOffset_ = { 0.0f, 0.0f };
2478             return false;
2479         }
2480         return CheckPlacement(childSize);
2481     }
2482     x = tempPos.GetX();
2483     y = tempPos.GetY();
2484     if (LessNotEqual(x, rect.Left()) || GreatNotEqual(x + childSize.Width(), rect.Right()) ||
2485         LessNotEqual(y, rect.Top()) || GreatNotEqual(y + childSize.Height(), rect.Bottom())) {
2486         preOffset_ = position;
2487         preOffset_.SetX(x);
2488         preOffset_.SetY(y);
2489         preRect_.SetOffset(rect.GetOffset());
2490         preRect_.SetSize(rect.GetSize());
2491         return false;
2492     }
2493     return true;
2494 }
2495 
CheckPlacement(const SizeF & childSize)2496 bool MenuLayoutAlgorithm::CheckPlacement(const SizeF& childSize)
2497 {
2498     auto x = preOffset_.GetX();
2499     auto y = preOffset_.GetY();
2500 
2501     switch (prevState_) {
2502         case static_cast<int>(DirectionState::Bottom_Direction):
2503         case static_cast<int>(DirectionState::Top_Direction): {
2504             if ((LessNotEqual(x, preRect_.Left()) || GreatNotEqual(x + childSize.Width(), preRect_.Right())) &&
2505                 !(LessNotEqual(y, preRect_.Top()) || GreatNotEqual(y + childSize.Height(), preRect_.Bottom()))) {
2506                 placement_ = Placement::NONE;
2507                 return true;
2508             }
2509             break;
2510         }
2511         case static_cast<int>(DirectionState::Right_Direction):
2512         case static_cast<int>(DirectionState::Left_Direction): {
2513             if ((LessNotEqual(y, preRect_.Top()) || GreatNotEqual(y + childSize.Height(), preRect_.Bottom())) &&
2514                 !(LessNotEqual(x, preRect_.Left()) || GreatNotEqual(x + childSize.Width(), preRect_.Right()))) {
2515                 placement_ = Placement::NONE;
2516                 return true;
2517             }
2518             break;
2519         }
2520         default:
2521             return false;
2522     }
2523 
2524     return false;
2525 }
2526 
CheckPosition(const OffsetF & position,const SizeF & childSize)2527 bool MenuLayoutAlgorithm::CheckPosition(const OffsetF& position, const SizeF& childSize)
2528 {
2529     float xAvoid = wrapperRect_.Left() + paddingStart_;
2530     float yAvoid = wrapperRect_.Top() + paddingTop_ + param_.topSecurity;
2531     float maxWidth = wrapperSize_.Width() - paddingEnd_ - paddingStart_;
2532     float maxHeight = wrapperSize_.Height() - paddingTop_ - param_.topSecurity - paddingBottom_ - param_.bottomSecurity;
2533     Rect rect;
2534     Rect targetRect = Rect(targetOffset_.GetX(), targetOffset_.GetY(), targetSize_.Width(), targetSize_.Height());
2535     switch (placement_) {
2536         case Placement::BOTTOM_LEFT:
2537         case Placement::BOTTOM_RIGHT:
2538         case Placement::BOTTOM: {
2539             state_ = static_cast<int>(DirectionState::Bottom_Direction);
2540             auto y = std::max<float>(targetRect.Bottom(), yAvoid);
2541             auto height = std::min<float>(
2542                 wrapperRect_.Bottom() - targetRect.Bottom() - paddingBottom_ - param_.bottomSecurity, maxHeight);
2543             rect.SetRect(xAvoid, y, maxWidth, height);
2544             break;
2545         }
2546         case Placement::TOP_LEFT:
2547         case Placement::TOP_RIGHT:
2548         case Placement::TOP: {
2549             state_ = static_cast<int>(DirectionState::Top_Direction);
2550             auto height = std::min<float>(targetRect.Top() - yAvoid, maxHeight);
2551             rect.SetRect(xAvoid, yAvoid, maxWidth, height);
2552             break;
2553         }
2554         case Placement::RIGHT_TOP:
2555         case Placement::RIGHT_BOTTOM:
2556         case Placement::RIGHT: {
2557             state_ = static_cast<int>(DirectionState::Right_Direction);
2558             auto x = std::max<float>(targetRect.Right(), xAvoid);
2559             auto width = std::min<float>(wrapperRect_.Right() - targetRect.Right() - paddingEnd_, maxWidth);
2560             rect.SetRect(x, yAvoid, width, maxHeight);
2561             break;
2562         }
2563         case Placement::LEFT_TOP:
2564         case Placement::LEFT_BOTTOM:
2565         case Placement::LEFT: {
2566             state_ = static_cast<int>(DirectionState::Left_Direction);
2567             auto width = std::min<float>(targetRect.Left() - xAvoid, maxWidth);
2568             rect.SetRect(xAvoid, yAvoid, width, maxHeight);
2569             break;
2570         }
2571         default:
2572             state_ = static_cast<int>(DirectionState::None_Direction);
2573             return false;
2574     }
2575     return CheckPositionInPlacementRect(rect, position, childSize);
2576 }
2577 
GetPositionWithPlacementTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2578 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementTop(
2579     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2580 {
2581     return topPosition;
2582 }
2583 
GetPositionWithPlacementTopLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2584 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementTopLeft(
2585     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2586 {
2587     OffsetF childPosition;
2588     float marginRight = 0.0f;
2589     float marginBottom = 0.0f;
2590     childPosition = OffsetF(
2591         targetOffset_.GetX() - marginRight, targetOffset_.GetY() - childSize.Height() - marginBottom - targetSpace_);
2592     return childPosition;
2593 }
2594 
GetPositionWithPlacementTopRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2595 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementTopRight(
2596     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2597 {
2598     OffsetF childPosition;
2599     float marginBottom = 0.0f;
2600     float marginLeft = 0.0f;
2601     childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
2602         targetOffset_.GetY() - childSize.Height() - targetSpace_ - marginBottom);
2603     return childPosition;
2604 }
2605 
GetPositionWithPlacementBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2606 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementBottom(
2607     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2608 {
2609     return bottomPosition;
2610 }
2611 
GetPositionWithPlacementBottomLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2612 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementBottomLeft(
2613     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2614 {
2615     OffsetF childPosition;
2616     float marginRight = 0.0f;
2617     float marginTop = 0.0f;
2618     childPosition = OffsetF(
2619         targetOffset_.GetX() - marginRight, targetOffset_.GetY() + targetSize_.Height() + targetSpace_ + marginTop);
2620     return childPosition;
2621 }
2622 
GetPositionWithPlacementBottomRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2623 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementBottomRight(
2624     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2625 {
2626     OffsetF childPosition;
2627     float marginTop = 0.0f;
2628     float marginLeft = 0.0f;
2629     childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
2630         targetOffset_.GetY() + targetSize_.Height() + targetSpace_ + marginTop);
2631     return childPosition;
2632 }
2633 
GetPositionWithPlacementLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2634 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementLeft(
2635     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2636 {
2637     OffsetF childPosition;
2638     float marginRight = 0.0f;
2639     childPosition = OffsetF(targetOffset_.GetX() - targetSpace_ - childSize.Width() - marginRight,
2640         targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
2641     return childPosition;
2642 }
2643 
GetPositionWithPlacementLeftTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2644 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementLeftTop(
2645     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2646 {
2647     OffsetF childPosition;
2648     float marginRight = 0.0f;
2649     float marginBottom = 0.0f;
2650     childPosition = OffsetF(
2651         targetOffset_.GetX() - targetSpace_ - childSize.Width() - marginRight, targetOffset_.GetY() - marginBottom);
2652     return childPosition;
2653 }
2654 
GetPositionWithPlacementLeftBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2655 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementLeftBottom(
2656     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2657 {
2658     OffsetF childPosition;
2659     float marginRight = 0.0f;
2660     float marginTop = 0.0f;
2661     childPosition = OffsetF(targetOffset_.GetX() - targetSpace_ - childSize.Width() - marginRight,
2662         targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
2663     return childPosition;
2664 }
2665 
GetPositionWithPlacementRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2666 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementRight(
2667     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2668 {
2669     OffsetF childPosition;
2670     float marginLeft = 0.0f;
2671     childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_ + marginLeft,
2672         targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
2673     return childPosition;
2674 }
2675 
GetPositionWithPlacementRightTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2676 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementRightTop(
2677     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2678 {
2679     OffsetF childPosition;
2680     float marginBottom = 0.0f;
2681     float marginLeft = 0.0f;
2682     childPosition = OffsetF(
2683         targetOffset_.GetX() + targetSize_.Width() + targetSpace_ + marginLeft, targetOffset_.GetY() - marginBottom);
2684     return childPosition;
2685 }
2686 
GetPositionWithPlacementRightBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2687 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementRightBottom(
2688     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2689 {
2690     OffsetF childPosition;
2691     float marginTop = 0.0f;
2692     float marginLeft = 0.0f;
2693     childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_ + marginLeft,
2694         targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
2695     return childPosition;
2696 }
2697 
InitCanExpandCurrentWindow(bool isShowInSubWindow)2698 void MenuLayoutAlgorithm::InitCanExpandCurrentWindow(bool isShowInSubWindow)
2699 {
2700     auto pipelineContext = GetCurrentPipelineContext();
2701     CHECK_NULL_VOID(pipelineContext);
2702     auto containerId = Container::CurrentId();
2703     auto container = AceEngine::Get().GetContainer(containerId);
2704     if (containerId >= MIN_SUBCONTAINER_ID) {
2705         auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(containerId);
2706         container = AceEngine::Get().GetContainer(parentContainerId);
2707     }
2708     CHECK_NULL_VOID(container);
2709     // Get FreeMultiWindow status of main window or host window
2710     isFreeMultiWindow_ = container->IsFreeMultiWindow();
2711     auto theme = pipelineContext->GetTheme<SelectTheme>();
2712     CHECK_NULL_VOID(theme);
2713     // false for phone devices
2714     isExpandDisplay_ = theme->GetExpandDisplay() || isFreeMultiWindow_;
2715     if (isExpandDisplay_ && !isShowInSubWindow && containerId >= MIN_SUBCONTAINER_ID) {
2716         canExpandCurrentWindow_ = true;
2717         return;
2718     }
2719     canExpandCurrentWindow_ = isExpandDisplay_ && isShowInSubWindow;
2720     if (containerId >= MIN_SUBCONTAINER_ID) {
2721         auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(containerId);
2722         container = AceEngine::Get().GetContainer(parentContainerId);
2723         CHECK_NULL_VOID(container);
2724         isUIExtensionSubWindow_ = container->IsUIExtensionWindow();
2725         if (isUIExtensionSubWindow_) {
2726             canExpandCurrentWindow_ = true;
2727             auto subwindow = SubwindowManager::GetInstance()->GetSubwindow(parentContainerId);
2728             CHECK_NULL_VOID(subwindow);
2729             auto rect = subwindow->GetUIExtensionHostWindowRect();
2730             UIExtensionHostWindowRect_ = RectF(rect.Left(), rect.Top(), rect.Width(), rect.Height());
2731             TAG_LOGI(AceLogTag::ACE_MENU, "GetUIExtensionHostWindowRect : %{public}s",
2732                 UIExtensionHostWindowRect_.ToString().c_str());
2733         }
2734     }
2735 }
2736 
GetMenuWindowRectInfo(const RefPtr<MenuPattern> & menuPattern)2737 Rect MenuLayoutAlgorithm::GetMenuWindowRectInfo(const RefPtr<MenuPattern>& menuPattern)
2738 {
2739     auto menuWindowRect = Rect();
2740     CHECK_NULL_RETURN(menuPattern, menuWindowRect);
2741     auto pipelineContext = GetCurrentPipelineContext();
2742     CHECK_NULL_RETURN(pipelineContext, menuWindowRect);
2743     auto rect = pipelineContext->GetDisplayWindowRectInfo();
2744     displayWindowRect_ = RectF(rect.Left(), rect.Top(), rect.Width(), rect.Height());
2745     TAG_LOGI(AceLogTag::ACE_MENU, "GetDisplayWindowRectInfo : %{public}s", displayWindowRect_.ToString().c_str());
2746     menuWindowRect = Rect(rect.Left(), rect.Top(), rect.Width(), rect.Height());
2747     auto availableRect = pipelineContext->GetDisplayAvailableRect();
2748     TAG_LOGI(AceLogTag::ACE_MENU, "GetDisplayAvailableRect : %{public}s", availableRect.ToString().c_str());
2749     if (canExpandCurrentWindow_ && isExpandDisplay_) {
2750         menuWindowRect = Rect(availableRect.Left(), availableRect.Top(), availableRect.Width(), availableRect.Height());
2751     } else if (isUIExtensionSubWindow_ && !isExpandDisplay_) {
2752         rect = Rect(UIExtensionHostWindowRect_.Left(), UIExtensionHostWindowRect_.Top(),
2753             UIExtensionHostWindowRect_.Width(), UIExtensionHostWindowRect_.Height());
2754         menuWindowRect = rect;
2755     }
2756     TAG_LOGI(AceLogTag::ACE_MENU, "GetMenuWindowRectInfo : %{public}s", menuWindowRect.ToString().c_str());
2757     dumpInfo_.menuWindowRect = menuWindowRect;
2758     menuPattern->SetMenuWindowRect(menuWindowRect);
2759     return menuWindowRect;
2760 }
2761 
ModifyTargetOffset()2762 void MenuLayoutAlgorithm::ModifyTargetOffset()
2763 {
2764     TAG_LOGI(AceLogTag::ACE_MENU, "original targetOffset is : %{public}s", targetOffset_.ToString().c_str());
2765     if (canExpandCurrentWindow_ && isExpandDisplay_) {
2766         targetOffset_ += displayWindowRect_.GetOffset();
2767         TAG_LOGI(AceLogTag::ACE_MENU, "ModifyTargetOffset for displayAvailableRect : %{public}s",
2768             targetOffset_.ToString().c_str());
2769     } else if (isUIExtensionSubWindow_ && !isExpandDisplay_) {
2770         targetOffset_ += displayWindowRect_.GetOffset() - UIExtensionHostWindowRect_.GetOffset();
2771         TAG_LOGI(AceLogTag::ACE_MENU, "ModifyTargetOffset for UIExtensionHostWindowRect : %{public}s",
2772             targetOffset_.ToString().c_str());
2773     }
2774 }
2775 
CheckIsEmbeddedMode(LayoutWrapper * layoutWrapper)2776 bool MenuLayoutAlgorithm::CheckIsEmbeddedMode(LayoutWrapper* layoutWrapper)
2777 {
2778     auto menuNode = layoutWrapper->GetHostNode();
2779     CHECK_NULL_RETURN(menuNode, false);
2780     auto menuNodePattern = AceType::DynamicCast<MenuPattern>(menuNode->GetPattern());
2781     CHECK_NULL_RETURN(menuNodePattern, false);
2782     auto menuWrapper = menuNodePattern->GetMenuWrapper();
2783     CHECK_NULL_RETURN(menuWrapper, false);
2784     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
2785     CHECK_NULL_RETURN(menuWrapperPattern, false);
2786     auto innerMenu = menuWrapperPattern->GetMenuChild(menuNode);
2787     CHECK_NULL_RETURN(innerMenu, false);
2788     auto innerMenuPattern = AceType::DynamicCast<MenuPattern>(innerMenu->GetPattern());
2789     CHECK_NULL_RETURN(innerMenuPattern, false);
2790     auto layoutProps = innerMenuPattern->GetLayoutProperty<MenuLayoutProperty>();
2791     CHECK_NULL_RETURN(layoutProps, false);
2792     return layoutProps->GetExpandingMode().value_or(SubMenuExpandingMode::SIDE) == SubMenuExpandingMode::EMBEDDED;
2793 }
2794 
2795 } // namespace OHOS::Ace::NG
2796