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