• 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/memory/ace_type.h"
24 #include "base/memory/referenced.h"
25 #include "base/subwindow/subwindow_manager.h"
26 #include "base/utils/utils.h"
27 #include "core/common/ace_engine.h"
28 #include "core/components/common/layout/grid_system_manager.h"
29 #include "core/components/common/properties/placement.h"
30 #include "core/components/container_modal/container_modal_constants.h"
31 #include "core/components_ng/pattern/menu/menu_layout_property.h"
32 #include "core/components_ng/pattern/menu/menu_pattern.h"
33 #include "core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.h"
34 #include "core/components_ng/property/layout_constraint.h"
35 #include "core/components_ng/property/measure_property.h"
36 #include "core/pipeline/pipeline_base.h"
37 #include "core/pipeline_ng/pipeline_context.h"
38 namespace OHOS::Ace::NG {
39 
40 namespace {
41 constexpr uint32_t MIN_GRID_COUNTS = 2;
42 constexpr uint32_t GRID_COUNTS_4 = 4;
43 constexpr uint32_t GRID_COUNTS_6 = 6;
44 constexpr uint32_t GRID_COUNTS_8 = 8;
45 constexpr uint32_t GRID_COUNTS_12 = 12;
46 constexpr int32_t PLATFORM_VERSION_TEN = 10;
47 constexpr size_t ALIGNMENT_STEP_OFFSET = 2;
48 
49 const std::map<Placement, std::vector<Placement>> PLACEMENT_STATES = {
50     { Placement::BOTTOM_LEFT,
51         {
52             Placement::BOTTOM_LEFT,
53             Placement::BOTTOM_RIGHT,
54             Placement::TOP_LEFT,
55             Placement::TOP_RIGHT,
56             Placement::RIGHT_TOP,
57             Placement::RIGHT_BOTTOM,
58             Placement::LEFT_TOP,
59             Placement::LEFT_BOTTOM,
60             Placement::NONE,
61         } },
62     { Placement::BOTTOM,
63         {
64             Placement::BOTTOM,
65             Placement::BOTTOM_LEFT,
66             Placement::BOTTOM_RIGHT,
67             Placement::TOP,
68             Placement::TOP_LEFT,
69             Placement::TOP_RIGHT,
70             Placement::RIGHT,
71             Placement::RIGHT_TOP,
72             Placement::RIGHT_BOTTOM,
73             Placement::LEFT,
74             Placement::LEFT_TOP,
75             Placement::LEFT_BOTTOM,
76             Placement::NONE,
77         } },
78     { Placement::BOTTOM_RIGHT,
79         {
80             Placement::BOTTOM_RIGHT,
81             Placement::BOTTOM_LEFT,
82             Placement::TOP_RIGHT,
83             Placement::TOP_LEFT,
84             Placement::RIGHT_BOTTOM,
85             Placement::RIGHT_TOP,
86             Placement::LEFT_BOTTOM,
87             Placement::LEFT_TOP,
88             Placement::NONE,
89         } },
90     { Placement::TOP_LEFT,
91         {
92             Placement::TOP_LEFT,
93             Placement::TOP_RIGHT,
94             Placement::BOTTOM_LEFT,
95             Placement::BOTTOM_RIGHT,
96             Placement::RIGHT_TOP,
97             Placement::RIGHT_BOTTOM,
98             Placement::LEFT_TOP,
99             Placement::LEFT_BOTTOM,
100             Placement::NONE,
101         } },
102     { Placement::TOP,
103         {
104             Placement::TOP,
105             Placement::TOP_LEFT,
106             Placement::TOP_RIGHT,
107             Placement::BOTTOM,
108             Placement::BOTTOM_LEFT,
109             Placement::BOTTOM_RIGHT,
110             Placement::RIGHT,
111             Placement::RIGHT_TOP,
112             Placement::RIGHT_BOTTOM,
113             Placement::LEFT,
114             Placement::LEFT_TOP,
115             Placement::LEFT_BOTTOM,
116             Placement::NONE,
117         } },
118     { Placement::TOP_RIGHT,
119         {
120             Placement::TOP_RIGHT,
121             Placement::TOP_LEFT,
122             Placement::BOTTOM_RIGHT,
123             Placement::BOTTOM_LEFT,
124             Placement::RIGHT_BOTTOM,
125             Placement::RIGHT_TOP,
126             Placement::LEFT_BOTTOM,
127             Placement::LEFT_TOP,
128             Placement::NONE,
129         } },
130     { Placement::LEFT_TOP,
131         {
132             Placement::LEFT_TOP,
133             Placement::LEFT_BOTTOM,
134             Placement::RIGHT_TOP,
135             Placement::RIGHT_BOTTOM,
136             Placement::BOTTOM_LEFT,
137             Placement::BOTTOM_RIGHT,
138             Placement::TOP_LEFT,
139             Placement::TOP_RIGHT,
140             Placement::NONE,
141         } },
142     { Placement::LEFT,
143         {
144             Placement::LEFT,
145             Placement::LEFT_TOP,
146             Placement::LEFT_BOTTOM,
147             Placement::RIGHT,
148             Placement::RIGHT_TOP,
149             Placement::RIGHT_BOTTOM,
150             Placement::BOTTOM,
151             Placement::BOTTOM_LEFT,
152             Placement::BOTTOM_RIGHT,
153             Placement::TOP,
154             Placement::TOP_LEFT,
155             Placement::TOP_RIGHT,
156             Placement::NONE,
157         } },
158     { Placement::LEFT_BOTTOM,
159         {
160             Placement::LEFT_BOTTOM,
161             Placement::LEFT_TOP,
162             Placement::RIGHT_BOTTOM,
163             Placement::RIGHT_TOP,
164             Placement::BOTTOM_RIGHT,
165             Placement::BOTTOM_LEFT,
166             Placement::TOP_RIGHT,
167             Placement::TOP_LEFT,
168             Placement::NONE,
169         } },
170     { Placement::RIGHT_TOP,
171         {
172             Placement::RIGHT_TOP,
173             Placement::RIGHT_BOTTOM,
174             Placement::LEFT_TOP,
175             Placement::LEFT_BOTTOM,
176             Placement::BOTTOM_LEFT,
177             Placement::BOTTOM_RIGHT,
178             Placement::TOP_LEFT,
179             Placement::TOP_RIGHT,
180             Placement::NONE,
181         } },
182     { Placement::RIGHT,
183         {
184             Placement::RIGHT,
185             Placement::RIGHT_TOP,
186             Placement::RIGHT_BOTTOM,
187             Placement::LEFT,
188             Placement::LEFT_TOP,
189             Placement::LEFT_BOTTOM,
190             Placement::BOTTOM,
191             Placement::BOTTOM_LEFT,
192             Placement::BOTTOM_RIGHT,
193             Placement::TOP,
194             Placement::TOP_LEFT,
195             Placement::TOP_RIGHT,
196             Placement::NONE,
197         } },
198     { Placement::RIGHT_BOTTOM,
199         {
200             Placement::RIGHT_BOTTOM,
201             Placement::RIGHT_TOP,
202             Placement::LEFT_BOTTOM,
203             Placement::LEFT_TOP,
204             Placement::BOTTOM_RIGHT,
205             Placement::BOTTOM_LEFT,
206             Placement::TOP_RIGHT,
207             Placement::TOP_LEFT,
208             Placement::NONE,
209         } },
210 };
211 
GetMaxGridCounts(const RefPtr<GridColumnInfo> & columnInfo)212 uint32_t GetMaxGridCounts(const RefPtr<GridColumnInfo>& columnInfo)
213 {
214     CHECK_NULL_RETURN(columnInfo, GRID_COUNTS_8);
215     auto currentColumns = columnInfo->GetParent()->GetColumns();
216     auto maxGridCounts = GRID_COUNTS_8;
217     switch (currentColumns) {
218         case GRID_COUNTS_4:
219             maxGridCounts = GRID_COUNTS_4;
220             break;
221         case GRID_COUNTS_8:
222             maxGridCounts = GRID_COUNTS_6;
223             break;
224         case GRID_COUNTS_12:
225             maxGridCounts = GRID_COUNTS_8;
226             break;
227         default:
228             break;
229     }
230     return maxGridCounts;
231 }
232 } // namespace
233 
MenuLayoutAlgorithm(int32_t id,const std::string & tag)234 MenuLayoutAlgorithm::MenuLayoutAlgorithm(int32_t id, const std::string& tag) : targetNodeId_(id), targetTag_(tag)
235 {
236     placementFuncMap_[Placement::TOP] = &MenuLayoutAlgorithm::GetPositionWithPlacementTop;
237     placementFuncMap_[Placement::TOP_LEFT] = &MenuLayoutAlgorithm::GetPositionWithPlacementTopLeft;
238     placementFuncMap_[Placement::TOP_RIGHT] = &MenuLayoutAlgorithm::GetPositionWithPlacementTopRight;
239     placementFuncMap_[Placement::BOTTOM] = &MenuLayoutAlgorithm::GetPositionWithPlacementBottom;
240     placementFuncMap_[Placement::BOTTOM_LEFT] = &MenuLayoutAlgorithm::GetPositionWithPlacementBottomLeft;
241     placementFuncMap_[Placement::BOTTOM_RIGHT] = &MenuLayoutAlgorithm::GetPositionWithPlacementBottomRight;
242     placementFuncMap_[Placement::LEFT] = &MenuLayoutAlgorithm::GetPositionWithPlacementLeft;
243     placementFuncMap_[Placement::LEFT_TOP] = &MenuLayoutAlgorithm::GetPositionWithPlacementLeftTop;
244     placementFuncMap_[Placement::LEFT_BOTTOM] = &MenuLayoutAlgorithm::GetPositionWithPlacementLeftBottom;
245     placementFuncMap_[Placement::RIGHT] = &MenuLayoutAlgorithm::GetPositionWithPlacementRight;
246     placementFuncMap_[Placement::RIGHT_TOP] = &MenuLayoutAlgorithm::GetPositionWithPlacementRightTop;
247     placementFuncMap_[Placement::RIGHT_BOTTOM] = &MenuLayoutAlgorithm::GetPositionWithPlacementRightBottom;
248 
249     setHorizontal_ = { Placement::LEFT, Placement::LEFT_BOTTOM, Placement::LEFT_TOP,
250         Placement::RIGHT, Placement::RIGHT_BOTTOM, Placement::RIGHT_TOP };
251     setVertical_ = { Placement::TOP, Placement::TOP_LEFT, Placement::TOP_RIGHT,
252         Placement::BOTTOM, Placement::BOTTOM_LEFT, Placement::BOTTOM_RIGHT };
253 }
254 
~MenuLayoutAlgorithm()255 MenuLayoutAlgorithm::~MenuLayoutAlgorithm()
256 {
257     placementFuncMap_.clear();
258     setHorizontal_.clear();
259     setVertical_.clear();
260 }
261 
Initialize(LayoutWrapper * layoutWrapper)262 void MenuLayoutAlgorithm::Initialize(LayoutWrapper* layoutWrapper)
263 {
264     CHECK_NULL_VOID(layoutWrapper);
265     // currently using click point as menu position
266     auto props = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
267     CHECK_NULL_VOID(props);
268     auto menuPattern = layoutWrapper->GetHostNode()->GetPattern<MenuPattern>();
269     auto targetSize = props->GetTargetSizeValue(SizeF());
270     position_ = props->GetMenuOffset().value_or(OffsetF());
271     positionOffset_ = props->GetPositionOffset().value_or(OffsetF());
272     LOGD("menu position_ = %{public}s, targetSize = %{public}s", position_.ToString().c_str(),
273         targetSize.ToString().c_str());
274     InitializePadding(layoutWrapper);
275     auto constraint = props->GetLayoutConstraint();
276     auto wrapperIdealSize =
277         CreateIdealSize(constraint.value(), Axis::FREE, props->GetMeasureType(MeasureType::MATCH_PARENT), true);
278     wrapperSize_ = wrapperIdealSize;
279 
280     ModifyPositionToWrapper(layoutWrapper, position_);
281     if (menuPattern->IsSelectOverlayExtensionMenu()) {
282         topSpace_ = 0.0f;
283         bottomSpace_ = constraint->maxSize.Height() - position_.GetY();
284         leftSpace_ = Infinity<float>();
285     } else {
286         if (props->GetMenuPlacement().has_value()) {
287             auto targetSecurity = static_cast<float>(TARGET_SECURITY.ConvertToPx());
288             topSpace_ = std::max(0.0f, targetOffset_.GetY() - targetSecurity - paddingTop_);
289             bottomSpace_ = std::max(0.0f,
290                 wrapperSize_.Height() - targetOffset_.GetY() - targetSize_.Height() - targetSecurity - paddingBottom_);
291             leftSpace_ = std::max(0.0f, targetOffset_.GetX() - paddingStart_ - targetSecurity);
292             rightSpace_ = std::max(
293                 0.0f, wrapperSize_.Width() - targetSize_.Width() - targetSecurity - paddingStart_ - paddingEnd_);
294         } else {
295             topSpace_ = position_.GetY() - targetSize.Height() - margin_ * 2.0f;
296             bottomSpace_ = wrapperSize_.Height() - position_.GetY() - margin_ * 2.0f;
297             leftSpace_ = position_.GetX();
298             rightSpace_ = wrapperSize_.Width() - leftSpace_;
299         }
300     }
301 
302     placement_ = props->GetMenuPlacement().value_or(Placement::BOTTOM_LEFT);
303 }
304 
InitializePadding(LayoutWrapper * layoutWrapper)305 void MenuLayoutAlgorithm::InitializePadding(LayoutWrapper* layoutWrapper)
306 {
307     auto menuPattern = layoutWrapper->GetHostNode()->GetPattern<MenuPattern>();
308     CHECK_NULL_VOID(menuPattern);
309     auto pipeline = PipelineBase::GetCurrentContext();
310     CHECK_NULL_VOID(pipeline);
311     auto theme = pipeline->GetTheme<SelectTheme>();
312     CHECK_NULL_VOID(theme);
313     if (!menuPattern->IsSelectOverlayExtensionMenu()) {
314         margin_ = static_cast<float>(theme->GetOutPadding().ConvertToPx());
315         optionPadding_ = margin_;
316         paddingStart_ = static_cast<float>(theme->GetDefaultPaddingStart().ConvertToPx());
317         paddingEnd_ = static_cast<float>(theme->GetDefaultPaddingEnd().ConvertToPx());
318         paddingTop_ = static_cast<float>(theme->GetDefaultPaddingTop().ConvertToPx());
319         paddingBottom_ = static_cast<float>(theme->GetDefaultPaddingBottomFixed().ConvertToPx());
320     } else {
321         optionPadding_ = static_cast<float>(theme->GetOutPadding().ConvertToPx());
322     }
323 }
324 
ModifyPositionToWrapper(LayoutWrapper * layoutWrapper,OffsetF & position)325 void MenuLayoutAlgorithm::ModifyPositionToWrapper(LayoutWrapper* layoutWrapper, OffsetF& position)
326 {
327     auto menu = layoutWrapper->GetHostNode();
328     auto wrapper = AceType::DynamicCast<FrameNode>(menu->GetParent());
329     CHECK_NULL_VOID(wrapper);
330 
331     OffsetF wrapperOffset;
332     // minus wrapper offset in LayoutFullScreen
333     auto wrapperLayoutProps = wrapper->GetLayoutProperty();
334     CHECK_NULL_VOID(wrapperLayoutProps);
335     auto&& safeAreaInsets = wrapperLayoutProps->GetSafeAreaInsets();
336     if (safeAreaInsets) {
337         wrapperOffset +=
338             OffsetF(static_cast<float>(safeAreaInsets->left_.end), static_cast<float>(safeAreaInsets->top_.end));
339         position -= wrapperOffset;
340     }
341 
342     auto menuPattern = menu->GetPattern<MenuPattern>();
343     CHECK_NULL_VOID(menuPattern);
344     bool isSubMenu = menuPattern->IsSubMenu() || menuPattern->IsSelectOverlaySubMenu();
345     if (menuPattern->IsContextMenu() || (isSubMenu && Container::CurrentId() >= MIN_SUBCONTAINER_ID)) {
346         // no need to modify for context menu, because context menu wrapper is full screen.
347         return;
348     }
349     // minus wrapper offset in floating window
350     auto pipelineContext = GetCurrentPipelineContext();
351     CHECK_NULL_VOID(pipelineContext);
352     auto windowManager = pipelineContext->GetWindowManager();
353     auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL && windowManager &&
354                             windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING;
355     if (isContainerModal) {
356         wrapperOffset = OffsetF(static_cast<float>((CONTAINER_BORDER_WIDTH + CONTENT_PADDING).ConvertToPx()),
357             static_cast<float>((CONTAINER_TITLE_HEIGHT + CONTAINER_BORDER_WIDTH).ConvertToPx()));
358         position -= wrapperOffset;
359     }
360 }
361 
362 // Called to perform layout render node and child.
Measure(LayoutWrapper * layoutWrapper)363 void MenuLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
364 {
365     // initialize screen size and menu position
366     CHECK_NULL_VOID(layoutWrapper);
367     auto menuNode = layoutWrapper->GetHostNode();
368     CHECK_NULL_VOID(menuNode);
369     auto menuPattern = menuNode->GetPattern<MenuPattern>();
370     CHECK_NULL_VOID(menuPattern);
371     if (!targetTag_.empty()) {
372         InitTargetSizeAndPosition(layoutWrapper, menuPattern->IsContextMenu());
373     }
374     Initialize(layoutWrapper);
375 
376     auto menuLayoutProperty = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
377     CHECK_NULL_VOID(menuLayoutProperty);
378     const auto& constraint = menuLayoutProperty->GetLayoutConstraint();
379     if (!constraint) {
380         LOGE("fail to measure menu due to layoutConstraint is nullptr");
381         return;
382     }
383     auto idealSize = CreateIdealSize(
384         constraint.value(), Axis::VERTICAL, menuLayoutProperty->GetMeasureType(MeasureType::MATCH_CONTENT), true);
385     const auto& padding = menuLayoutProperty->CreatePaddingAndBorder();
386     MinusPaddingToSize(padding, idealSize);
387 
388     // calculate menu main size
389     auto childConstraint = CreateChildConstraint(layoutWrapper);
390     float idealHeight = 0.0f;
391     float idealWidth = 0.0f;
392     for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
393         child->Measure(childConstraint);
394         auto childSize = child->GetGeometryNode()->GetMarginFrameSize();
395         LOGD("child finish measure, child %{public}s size = %{public}s", child->GetHostTag().c_str(),
396             child->GetGeometryNode()->GetMarginFrameSize().ToString().c_str());
397         idealHeight += childSize.Height();
398         idealWidth = std::max(idealWidth, childSize.Width());
399     }
400     idealSize.SetHeight(idealHeight);
401     idealSize.SetWidth(idealWidth);
402     AddPaddingToSize(padding, idealSize);
403 
404     auto geometryNode = layoutWrapper->GetGeometryNode();
405     CHECK_NULL_VOID(geometryNode);
406     LOGD("finish measure, menu size = %{public}f x %{public}f", idealSize.Width(), idealSize.Height());
407     geometryNode->SetFrameSize(idealSize);
408 }
409 
Layout(LayoutWrapper * layoutWrapper)410 void MenuLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
411 {
412     CHECK_NULL_VOID(layoutWrapper);
413     auto menuProp = DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
414     CHECK_NULL_VOID(menuProp);
415     auto menuNode = layoutWrapper->GetHostNode();
416     CHECK_NULL_VOID(menuNode);
417     auto menuPattern = menuNode->GetPattern<MenuPattern>();
418     CHECK_NULL_VOID(menuPattern);
419 
420     if (!menuPattern->IsSelectOverlayExtensionMenu()) {
421         auto geometryNode = layoutWrapper->GetGeometryNode();
422         CHECK_NULL_VOID(geometryNode);
423         auto size = geometryNode->GetMarginFrameSize();
424         bool didNeedArrow = GetIfNeedArrow(layoutWrapper, size);
425         if (menuPattern->IsSelectMenu()) {
426             ComputeMenuPositionByAlignType(menuProp, size);
427             auto offset = ComputeMenuPositionByOffset(menuProp, geometryNode);
428             position_ += offset;
429         }
430         auto menuPosition = MenuLayoutAvoidAlgorithm(menuProp, menuPattern, size, didNeedArrow);
431         SetMenuPlacementForAnimation(layoutWrapper);
432         arrowPosition_ = GetArrowPositionWithPlacement(size);
433         if (didNeedArrow && arrowPlacement_ != Placement::NONE) {
434             LayoutArrow(layoutWrapper);
435         }
436         LOGD("Menu layout, offset = %{public}s", menuPosition.ToString().c_str());
437         geometryNode->SetFrameOffset(menuPosition);
438     }
439 
440     // translate each option by the height of previous options
441     OffsetF translate;
442     for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
443         child->GetGeometryNode()->SetMarginFrameOffset(translate);
444         child->Layout();
445         translate += OffsetF(0, child->GetGeometryNode()->GetFrameSize().Height());
446     }
447 }
448 
SetMenuPlacementForAnimation(LayoutWrapper * layoutWrapper)449 void MenuLayoutAlgorithm::SetMenuPlacementForAnimation(LayoutWrapper* layoutWrapper)
450 {
451     auto menu = layoutWrapper->GetHostNode();
452     CHECK_NULL_VOID(menu);
453     auto menuPattern = menu->GetPattern<MenuPattern>();
454     CHECK_NULL_VOID(menuPattern);
455     auto menuWrapper = menuPattern->GetMenuWrapper();
456     CHECK_NULL_VOID(menuWrapper);
457     auto wrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
458     CHECK_NULL_VOID(wrapperPattern);
459     wrapperPattern->SetMenuPlacementAfterLayout(placement_);
460 }
461 
LayoutArrow(const LayoutWrapper * layoutWrapper)462 void MenuLayoutAlgorithm::LayoutArrow(const LayoutWrapper* layoutWrapper)
463 {
464     auto paintProperty = GetPaintProperty(layoutWrapper);
465     CHECK_NULL_VOID(paintProperty);
466     paintProperty->UpdateArrowPosition(arrowPosition_);
467     paintProperty->UpdateArrowPlacement(arrowPlacement_);
468 }
469 
GetPaintProperty(const LayoutWrapper * layoutWrapper)470 RefPtr<MenuPaintProperty> MenuLayoutAlgorithm::GetPaintProperty(const LayoutWrapper* layoutWrapper)
471 {
472     auto menuNode = layoutWrapper->GetHostNode();
473     CHECK_NULL_RETURN(menuNode, nullptr);
474     auto paintProperty = menuNode->GetPaintProperty<MenuPaintProperty>();
475     CHECK_NULL_RETURN(paintProperty, nullptr);
476     return paintProperty;
477 }
478 
GetIfNeedArrow(const LayoutWrapper * layoutWrapper,const SizeF & menuSize)479 bool MenuLayoutAlgorithm::GetIfNeedArrow(const LayoutWrapper* layoutWrapper, const SizeF& menuSize)
480 {
481     CHECK_NULL_RETURN(layoutWrapper, false);
482     auto menuNode = layoutWrapper->GetHostNode();
483     CHECK_NULL_RETURN(menuNode, false);
484     auto menuPattern = menuNode->GetPattern<MenuPattern>();
485     CHECK_NULL_RETURN(menuPattern, false);
486     auto menuProp = DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
487     CHECK_NULL_RETURN(menuProp, false);
488     auto paintProperty = GetPaintProperty(layoutWrapper);
489     CHECK_NULL_RETURN(paintProperty, false);
490     propNeedArrow_ = paintProperty->GetEnableArrow().value_or(false);
491 
492     auto pipeline = PipelineBase::GetCurrentContext();
493     CHECK_NULL_RETURN(pipeline, false);
494     auto selectThemePtr = pipeline->GetTheme<SelectTheme>();
495     CHECK_NULL_RETURN(selectThemePtr, false);
496     if (!propNeedArrow_ || !menuProp->GetMenuPlacement().has_value()) {
497         return false;
498     }
499 
500     propArrowOffset_ = paintProperty->GetArrowOffset().value_or(Dimension(0));
501     menuRadius_ = selectThemePtr->GetMenuBorderRadius().ConvertToPx();
502     arrowMinLimit_ = menuRadius_ + ARROW_WIDTH.ConvertToPx() / 2.0;
503     arrowWidth_ = ARROW_WIDTH.ConvertToPx();
504     auto targetSpaceReal = TARGET_SPACE.ConvertToPx();
505 
506     if (setHorizontal_.find(placement_) != setHorizontal_.end()) {
507         if (menuSize.Height() >= menuRadius_ * 2 + arrowWidth_) {
508             arrowInMenu_ = true;
509             targetSpace_ = targetSpaceReal;
510         }
511     }
512 
513     if (setVertical_.find(placement_) != setVertical_.end()) {
514         if (menuSize.Width() >= menuRadius_ * 2 + arrowWidth_) {
515             arrowInMenu_ = true;
516             targetSpace_ = targetSpaceReal;
517         }
518     }
519 
520     return menuPattern->IsContextMenu() && !targetTag_.empty() && arrowInMenu_;
521 }
522 
UpdatePropArrowOffset()523 void MenuLayoutAlgorithm::UpdatePropArrowOffset()
524 {
525     if (propArrowOffset_.IsValid()) {
526         if (propArrowOffset_.Unit() == DimensionUnit::PERCENT) {
527             propArrowOffset_.SetValue(std::clamp(propArrowOffset_.Value(), 0.0, 1.0));
528         }
529         return;
530     }
531     switch (arrowPlacement_) {
532         case Placement::LEFT:
533         case Placement::RIGHT:
534         case Placement::TOP:
535         case Placement::BOTTOM:
536             propArrowOffset_ = ARROW_HALF_PERCENT_VALUE;
537             break;
538         case Placement::TOP_LEFT:
539         case Placement::BOTTOM_LEFT:
540         case Placement::LEFT_TOP:
541         case Placement::RIGHT_TOP:
542             propArrowOffset_ = ARROW_ZERO_PERCENT_VALUE;
543             break;
544         case Placement::TOP_RIGHT:
545         case Placement::BOTTOM_RIGHT:
546         case Placement::LEFT_BOTTOM:
547         case Placement::RIGHT_BOTTOM:
548             propArrowOffset_ = ARROW_ONE_HUNDRED_PERCENT_VALUE;
549             break;
550         default:
551             break;
552     }
553 }
554 
UpdateArrowOffsetWithMenuLimit(const SizeF & menuSize)555 void MenuLayoutAlgorithm::UpdateArrowOffsetWithMenuLimit(const SizeF& menuSize)
556 {
557     UpdatePropArrowOffset();
558 
559     if (setHorizontal_.find(arrowPlacement_) != setHorizontal_.end()) {
560         if (menuSize.Height() >= menuRadius_ * 2 + arrowWidth_) {
561             float range = menuSize.Height() - menuRadius_ * 2  - arrowWidth_;
562             float tempOffset = propArrowOffset_.Unit() == DimensionUnit::PERCENT ? propArrowOffset_.Value() * range :
563                 propArrowOffset_.ConvertToPx();
564             arrowOffset_ = std::clamp(tempOffset, 0.0f, range);
565         }
566     }
567 
568     if (setVertical_.find(arrowPlacement_) != setVertical_.end()) {
569         if (menuSize.Width() >= menuRadius_ * 2 + arrowWidth_) {
570             arrowInMenu_ = true;
571             float range = menuSize.Width() - menuRadius_ * 2  - arrowWidth_;
572             float tempOffset = propArrowOffset_.Unit() == DimensionUnit::PERCENT ? propArrowOffset_.Value() * range :
573                 propArrowOffset_.ConvertToPx();
574             arrowOffset_ = std::clamp(tempOffset, 0.0f, range);
575         }
576     }
577 }
578 
ComputeMenuPositionByAlignType(const RefPtr<MenuLayoutProperty> & menuProp,const SizeF & menuSize)579 void MenuLayoutAlgorithm::ComputeMenuPositionByAlignType(
580     const RefPtr<MenuLayoutProperty>& menuProp, const SizeF& menuSize)
581 {
582     auto alignType = menuProp->GetAlignType().value_or(MenuAlignType::START);
583     auto targetSize = menuProp->GetTargetSizeValue(SizeF());
584     switch (alignType) {
585         case MenuAlignType::CENTER: {
586             position_.AddX(targetSize.Width() / 2.0f - menuSize.Width() / 2.0f);
587             break;
588         }
589         case MenuAlignType::END: {
590             position_.AddX(targetSize.Width() - menuSize.Width());
591             break;
592         }
593         default:
594             break;
595     }
596 }
597 
ComputeMenuPositionByOffset(const RefPtr<MenuLayoutProperty> & menuProp,const RefPtr<GeometryNode> & geometryNode)598 OffsetF MenuLayoutAlgorithm::ComputeMenuPositionByOffset(
599     const RefPtr<MenuLayoutProperty>& menuProp, const RefPtr<GeometryNode>& geometryNode)
600 {
601     CHECK_NULL_RETURN(menuProp, OffsetF(0, 0));
602     CHECK_NULL_RETURN(geometryNode, OffsetF(0, 0));
603 
604     const auto& layoutConstraint = menuProp->GetLayoutConstraint();
605     CHECK_NULL_RETURN(layoutConstraint, OffsetF(0, 0));
606     auto menuAlignOffset = menuProp->GetOffset().value_or(
607         DimensionOffset(Dimension(0, DimensionUnit::VP), Dimension(0, DimensionUnit::VP)));
608 
609     auto menuSize = geometryNode->GetFrameSize();
610     auto menuTrimOffsetX =
611         ConvertToPx(CalcLength(menuAlignOffset.GetX()), layoutConstraint->scaleProperty, menuSize.Width());
612     auto menuTrimOffsetY =
613         ConvertToPx(CalcLength(menuAlignOffset.GetY()), layoutConstraint->scaleProperty, menuSize.Height());
614     OffsetF menuTrimOffset = OffsetF(menuTrimOffsetX.value_or(0.0), menuTrimOffsetY.value_or(0.0));
615     return menuTrimOffset;
616 }
617 
GetCurrentPipelineContext()618 RefPtr<PipelineContext> MenuLayoutAlgorithm::GetCurrentPipelineContext()
619 {
620     auto containerId = Container::CurrentId();
621     RefPtr<PipelineContext> context;
622     if (containerId >= MIN_SUBCONTAINER_ID) {
623         auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(containerId);
624         auto parentContainer = AceEngine::Get().GetContainer(parentContainerId);
625         CHECK_NULL_RETURN(parentContainer, nullptr);
626         context = DynamicCast<PipelineContext>(parentContainer->GetPipelineContext());
627     } else {
628         context = PipelineContext::GetCurrentContext();
629     }
630     return context;
631 }
632 
MenuLayoutAvoidAlgorithm(const RefPtr<MenuLayoutProperty> & menuProp,const RefPtr<MenuPattern> & menuPattern,const SizeF & size,bool didNeedArrow)633 OffsetF MenuLayoutAlgorithm::MenuLayoutAvoidAlgorithm(const RefPtr<MenuLayoutProperty>& menuProp,
634     const RefPtr<MenuPattern>& menuPattern, const SizeF& size, bool didNeedArrow)
635 {
636     CHECK_NULL_RETURN(menuProp, OffsetF(0, 0));
637     CHECK_NULL_RETURN(menuPattern, OffsetF(0, 0));
638     float x = 0.0f;
639     float y = 0.0f;
640     if (menuProp->GetMenuPlacement().has_value() && (targetSize_.Width() > 0.0 || targetSize_.Height() > 0.0)) {
641         placement_ = menuProp->GetMenuPlacement().value();
642         auto childOffset = GetChildPosition(size, didNeedArrow);
643         x = childOffset.GetX();
644         y = childOffset.GetY();
645     } else {
646         x = HorizontalLayout(size, position_.GetX(), menuPattern->IsSelectMenu()) + positionOffset_.GetX();
647         y = VerticalLayout(size, position_.GetY()) + positionOffset_.GetY();
648         x = std::clamp(x, margin_, wrapperSize_.Width() - size.Width() - margin_);
649         y = std::clamp(y, margin_, wrapperSize_.Height() - size.Height() - margin_);
650     }
651 
652     return { x, y };
653 }
654 
UpdateConstraintWidth(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)655 void MenuLayoutAlgorithm::UpdateConstraintWidth(LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
656 {
657     RefPtr<GridColumnInfo> columnInfo;
658     columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
659     auto menuPattern = layoutWrapper->GetHostNode()->GetPattern<MenuPattern>();
660     if (menuPattern && menuPattern->IsSelectOverlayExtensionMenu()) {
661         columnInfo->GetParent()->BuildColumnWidth();
662     } else {
663         columnInfo->GetParent()->BuildColumnWidth(wrapperSize_.Width());
664     }
665     auto menuLayoutProperty = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
666     CHECK_NULL_VOID(menuLayoutProperty);
667     // set max width
668     const auto& padding = menuLayoutProperty->CreatePaddingAndBorder();
669     auto maxHorizontalSpace = std::max(leftSpace_, rightSpace_) - 2.0f * padding.Width();
670     auto maxWidth = static_cast<float>(columnInfo->GetWidth(GetMaxGridCounts(columnInfo)));
671     if (PipelineBase::GetCurrentContext() &&
672         PipelineBase::GetCurrentContext()->GetMinPlatformVersion() < PLATFORM_VERSION_TEN) {
673         maxWidth = std::min(maxHorizontalSpace, maxWidth);
674     }
675     maxWidth = std::min(constraint.maxSize.Width(), maxWidth);
676     constraint.maxSize.SetWidth(maxWidth);
677 }
678 
UpdateConstraintHeight(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)679 void MenuLayoutAlgorithm::UpdateConstraintHeight(LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
680 {
681     auto maxSpaceHeight = std::max(topSpace_, bottomSpace_);
682     constraint.maxSize.SetHeight(maxSpaceHeight);
683 }
684 
CreateChildConstraint(LayoutWrapper * layoutWrapper)685 LayoutConstraintF MenuLayoutAlgorithm::CreateChildConstraint(LayoutWrapper* layoutWrapper)
686 {
687     auto menuLayoutProperty = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
688     CHECK_NULL_RETURN(menuLayoutProperty, LayoutConstraintF());
689 
690     auto childConstraint = menuLayoutProperty->CreateChildConstraint();
691     UpdateConstraintWidth(layoutWrapper, childConstraint);
692     UpdateConstraintHeight(layoutWrapper, childConstraint);
693     UpdateConstraintBaseOnOptions(layoutWrapper, childConstraint);
694     return childConstraint;
695 }
696 
UpdateConstraintBaseOnOptions(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)697 void MenuLayoutAlgorithm::UpdateConstraintBaseOnOptions(LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
698 {
699     auto menuPattern = layoutWrapper->GetHostNode()->GetPattern<MenuPattern>();
700     CHECK_NULL_VOID(menuPattern);
701     auto options = menuPattern->GetOptions();
702     if (options.empty()) {
703         LOGD("options is empty, no need to update constraint.");
704         return;
705     }
706     auto optionConstraint = constraint;
707     RefPtr<GridColumnInfo> columnInfo;
708     columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
709     columnInfo->GetParent()->BuildColumnWidth(wrapperSize_.Width());
710     auto minWidth = static_cast<float>(columnInfo->GetWidth(MIN_GRID_COUNTS));
711     optionConstraint.maxSize.MinusWidth(optionPadding_ * 2.0f);
712     optionConstraint.minSize.SetWidth(minWidth - optionPadding_ * 2.0f);
713     auto maxChildrenWidth = optionConstraint.minSize.Width();
714     auto optionsLayoutWrapper = GetOptionsLayoutWrappper(layoutWrapper);
715     for (const auto& optionWrapper : optionsLayoutWrapper) {
716         optionWrapper->GetLayoutProperty()->ResetCalcMinSize();
717         optionWrapper->Measure(optionConstraint);
718         auto childSize = optionWrapper->GetGeometryNode()->GetMarginFrameSize();
719         maxChildrenWidth = std::max(maxChildrenWidth, childSize.Width());
720     }
721     if (menuPattern->IsSelectOverlayExtensionMenu()) {
722         maxChildrenWidth = std::min(maxChildrenWidth, optionConstraint.maxSize.Width());
723         UpdateOptionConstraint(optionsLayoutWrapper, maxChildrenWidth);
724         constraint.minSize.SetWidth(maxChildrenWidth);
725         return;
726     }
727     UpdateOptionConstraint(optionsLayoutWrapper, maxChildrenWidth);
728     constraint.minSize.SetWidth(maxChildrenWidth + optionPadding_ * 2.0f);
729 }
730 
GetOptionsLayoutWrappper(LayoutWrapper * layoutWrapper)731 std::list<RefPtr<LayoutWrapper>> MenuLayoutAlgorithm::GetOptionsLayoutWrappper(LayoutWrapper* layoutWrapper)
732 {
733     std::list<RefPtr<LayoutWrapper>> optionsWrapper;
734     auto scrollWrapper = layoutWrapper->GetOrCreateChildByIndex(0);
735     CHECK_NULL_RETURN(scrollWrapper, optionsWrapper);
736     auto columnWrapper = scrollWrapper->GetOrCreateChildByIndex(0);
737     CHECK_NULL_RETURN(columnWrapper, optionsWrapper);
738     optionsWrapper = columnWrapper->GetAllChildrenWithBuild();
739     return optionsWrapper;
740 }
741 
UpdateOptionConstraint(std::list<RefPtr<LayoutWrapper>> & options,float width)742 void MenuLayoutAlgorithm::UpdateOptionConstraint(std::list<RefPtr<LayoutWrapper>>& options, float width)
743 {
744     for (const auto& option : options) {
745         auto optionLayoutProps = option->GetLayoutProperty();
746         CHECK_NULL_VOID(optionLayoutProps);
747         optionLayoutProps->UpdateCalcMinSize(CalcSize(CalcLength(width), std::nullopt));
748     }
749 }
750 
751 // return vertical offset
VerticalLayout(const SizeF & size,float position)752 float MenuLayoutAlgorithm::VerticalLayout(const SizeF& size, float position)
753 {
754     float wrapperHeight = wrapperSize_.Height();
755     placement_ = Placement::BOTTOM;
756     // can put menu below click point
757     if (bottomSpace_ >= size.Height()) {
758         return position + margin_;
759     }
760 
761     // put menu above click point
762     if (topSpace_ >= size.Height()) {
763         // menu show on top
764         placement_ = Placement::TOP;
765         return topSpace_ - size.Height() + margin_;
766     }
767 
768     // line up bottom of menu with bottom of the screen
769     if (size.Height() < wrapperHeight) {
770         return wrapperHeight - size.Height();
771     }
772     // can't fit in screen, line up with top of the screen
773     return 0.0f;
774 }
775 
776 // returns horizontal offset
HorizontalLayout(const SizeF & size,float position,bool isSelectMenu)777 float MenuLayoutAlgorithm::HorizontalLayout(const SizeF& size, float position, bool isSelectMenu)
778 {
779     float wrapperWidth = wrapperSize_.Width();
780     // can fit menu on the right side of position
781     if (rightSpace_ >= size.Width()) {
782         return position + margin_;
783     }
784 
785     // fit menu on the left side
786     if (!isSelectMenu && leftSpace_ >= size.Width()) {
787         return position - size.Width();
788     }
789 
790     // line up right side of menu with right boundary of the screen
791     if (size.Width() < wrapperWidth) {
792         return wrapperWidth - size.Width();
793     }
794 
795     // can't fit in screen, line up with left side of the screen
796     return 0.0f;
797 }
798 
GetPositionWithPlacement(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)799 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacement(
800     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
801 {
802     OffsetF childPosition;
803 
804     auto func = placementFuncMap_.find(placement_);
805     if (func != placementFuncMap_.end()) {
806         auto placementFunc = func->second;
807         if (placementFunc != nullptr) {
808             childPosition = (this->*placementFunc)(childSize, topPosition, bottomPosition);
809         } else {
810             LOGE("Invalid Placement of menu layout.");
811         }
812     } else {
813         LOGE("Invalid Placement of menu layout.");
814     }
815 
816     return childPosition;
817 }
818 
GetArrowPositionWithPlacement(const SizeF & menuSize)819 OffsetF MenuLayoutAlgorithm::GetArrowPositionWithPlacement(const SizeF& menuSize)
820 {
821     UpdateArrowOffsetWithMenuLimit(menuSize);
822     auto addArrowOffsetToArrowMin = arrowOffset_ + arrowMinLimit_;
823     auto space_ = ARROW_HIGHT.ConvertToPx();
824     OffsetF childPosition;
825     switch (arrowPlacement_) {
826         case Placement::TOP:
827         case Placement::TOP_LEFT:
828         case Placement::TOP_RIGHT:
829             childPosition = OffsetF(addArrowOffsetToArrowMin, menuSize.Height() + space_);
830             break;
831         case Placement::BOTTOM:
832         case Placement::BOTTOM_LEFT:
833         case Placement::BOTTOM_RIGHT:
834             childPosition = OffsetF(addArrowOffsetToArrowMin, -space_);
835             break;
836         case Placement::LEFT:
837         case Placement::LEFT_TOP:
838         case Placement::LEFT_BOTTOM:
839             childPosition = OffsetF(menuSize.Width() + space_, addArrowOffsetToArrowMin);
840             break;
841         case Placement::RIGHT:
842         case Placement::RIGHT_TOP:
843         case Placement::RIGHT_BOTTOM:
844             childPosition = OffsetF(-space_, addArrowOffsetToArrowMin);
845             break;
846         default:
847             break;
848     }
849     return childPosition;
850 }
851 
GetMenuWrapperOffset(const LayoutWrapper * layoutWrapper)852 OffsetF MenuLayoutAlgorithm::GetMenuWrapperOffset(const LayoutWrapper* layoutWrapper)
853 {
854     CHECK_NULL_RETURN(layoutWrapper, OffsetF());
855     auto menuNode = layoutWrapper->GetHostNode();
856     CHECK_NULL_RETURN(menuNode, OffsetF());
857     return menuNode->GetParentGlobalOffsetDuringLayout();
858 }
859 
InitTargetSizeAndPosition(const LayoutWrapper * layoutWrapper,bool isContextMenu)860 void MenuLayoutAlgorithm::InitTargetSizeAndPosition(const LayoutWrapper* layoutWrapper, bool isContextMenu)
861 {
862     auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
863     CHECK_NULL_VOID(targetNode);
864     auto geometryNode = targetNode->GetGeometryNode();
865     CHECK_NULL_VOID(geometryNode);
866     targetSize_ = geometryNode->GetFrameSize();
867     auto pipelineContext = GetCurrentPipelineContext();
868     CHECK_NULL_VOID(pipelineContext);
869 
870     targetOffset_ = targetNode->GetPaintRectOffset();
871     if (isContextMenu) {
872         auto windowGlobalRect = pipelineContext->GetDisplayWindowRectInfo();
873         float windowsOffsetX = static_cast<float>(windowGlobalRect.GetOffset().GetX());
874         float windowsOffsetY = static_cast<float>(windowGlobalRect.GetOffset().GetY());
875         targetOffset_ += OffsetF(windowsOffsetX, windowsOffsetY);
876 
877         OffsetF offset = GetMenuWrapperOffset(layoutWrapper);
878         targetOffset_ -= offset;
879         return;
880     }
881 
882     auto windowManager = pipelineContext->GetWindowManager();
883     auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL && windowManager &&
884                             windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING;
885     if (isContainerModal) {
886         auto newOffsetX = static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()) +
887                           static_cast<float>(CONTENT_PADDING.ConvertToPx());
888         auto newOffsetY = static_cast<float>(CONTAINER_TITLE_HEIGHT.ConvertToPx()) +
889                           static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx());
890         targetOffset_ -= OffsetF(newOffsetX, newOffsetY);
891     } else {
892         OffsetF offset = GetMenuWrapperOffset(layoutWrapper);
893         targetOffset_ -= offset;
894     }
895 }
896 
FitToScreen(const OffsetF & position,const SizeF & childSize,bool didNeedArrow)897 OffsetF MenuLayoutAlgorithm::FitToScreen(const OffsetF& position, const SizeF& childSize, bool didNeedArrow)
898 {
899     OffsetF afterOffsetPosition;
900     auto originPosition = position;
901 
902     if (NearEqual(positionOffset_, OffsetF(0.0f, 0.0f)) && (!didNeedArrow || arrowPlacement_ == Placement::NONE)) {
903         afterOffsetPosition = AddTargetSpace(originPosition);
904     } else {
905         afterOffsetPosition = AddOffset(originPosition);
906     }
907 
908     if (!CheckPosition(afterOffsetPosition, childSize)) {
909         return OffsetF(0.0f, 0.0f);
910     }
911 
912     return afterOffsetPosition;
913 }
914 
GetChildPosition(const SizeF & childSize,bool didNeedArrow)915 OffsetF MenuLayoutAlgorithm::GetChildPosition(const SizeF& childSize, bool didNeedArrow)
916 {
917     OffsetF bottomPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
918         targetOffset_.GetY() + targetSize_.Height() + targetSpace_);
919     OffsetF topPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
920         targetOffset_.GetY() - childSize.Height() - targetSpace_);
921     OffsetF defaultPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
922         targetOffset_.GetY() + (targetSize_.Height() - childSize.Height()) / 2.0);
923 
924     OffsetF childPosition;
925     OffsetF position = defaultPosition;
926     auto positionOffset = positionOffset_;
927     std::vector<Placement> currentPlacementStates = PLACEMENT_STATES.find(Placement::BOTTOM_LEFT)->second;
928     if (PLACEMENT_STATES.find(placement_) != PLACEMENT_STATES.end()) {
929         currentPlacementStates = PLACEMENT_STATES.find(placement_)->second;
930     }
931     size_t step = ALIGNMENT_STEP_OFFSET;
932     if (placement_ <= Placement::BOTTOM) {
933         step += 1;
934     }
935     for (size_t i = 0, len = currentPlacementStates.size(); i < len; i++) {
936         placement_ = currentPlacementStates[i];
937         if (placement_ == Placement::NONE) {
938             break;
939         }
940         if (i >= step) {
941             positionOffset_ = OffsetF(0.0f, 0.0f);
942         }
943         childPosition = GetPositionWithPlacement(childSize, topPosition, bottomPosition);
944         position = FitToScreen(childPosition, childSize, didNeedArrow);
945         if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
946             continue;
947         }
948         break;
949     }
950     if (placement_ == Placement::NONE) {
951         position = GetAdjustPosition(currentPlacementStates, step, childSize, topPosition, bottomPosition);
952         if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
953             position = defaultPosition;
954         }
955     }
956     positionOffset_ = positionOffset;
957     arrowPlacement_ = placement_;
958 
959     return position;
960 }
961 
GetAdjustPosition(std::vector<Placement> & currentPlacementStates,size_t step,const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)962 OffsetF MenuLayoutAlgorithm::GetAdjustPosition(std::vector<Placement>& currentPlacementStates, size_t step,
963     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
964 {
965     OffsetF childPosition;
966     OffsetF position;
967     for (size_t i = 0, len = currentPlacementStates.size(); i < len;) {
968         placement_ = currentPlacementStates[i];
969         if (placement_ == Placement::NONE) {
970             break;
971         }
972         childPosition = GetPositionWithPlacement(childSize, topPosition, bottomPosition);
973         position = AdjustPosition(
974             childPosition, childSize.Width(), childSize.Height(), static_cast<float>(TARGET_SECURITY.ConvertToPx()));
975         if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
976             i += step;
977             continue;
978         }
979         break;
980     }
981     return position;
982 }
983 
AdjustPosition(const OffsetF & position,float width,float height,float space)984 OffsetF MenuLayoutAlgorithm::AdjustPosition(const OffsetF& position, float width, float height, float space)
985 {
986     float xMax = 0.0f;
987     float yMax = 0.0f;
988     float xMin = 1.0f;
989     float yMin = 1.0f;
990     switch (placement_) {
991         case Placement::LEFT_TOP:
992         case Placement::LEFT_BOTTOM:
993         case Placement::LEFT: {
994             xMin = paddingStart_;
995             xMax = std::min(targetOffset_.GetX() - width - space, wrapperSize_.Width() - paddingEnd_ - width);
996             yMin = paddingTop_;
997             yMax = wrapperSize_.Height() - height - paddingBottom_;
998             break;
999         }
1000         case Placement::RIGHT_TOP:
1001         case Placement::RIGHT_BOTTOM:
1002         case Placement::RIGHT: {
1003             xMin = std::max(targetOffset_.GetX() + targetSize_.Width() + space, paddingStart_);
1004             xMax = wrapperSize_.Width() - width - paddingEnd_;
1005             yMin = paddingTop_;
1006             yMax = wrapperSize_.Height() - height - paddingBottom_;
1007             break;
1008         }
1009         case Placement::TOP_LEFT:
1010         case Placement::TOP_RIGHT:
1011         case Placement::TOP: {
1012             xMin = paddingStart_;
1013             xMax = wrapperSize_.Width() - width - paddingEnd_;
1014             yMin = paddingTop_;
1015             yMax = std::min(targetOffset_.GetY() - height - space, wrapperSize_.Height() - paddingBottom_ - height);
1016             break;
1017         }
1018         case Placement::BOTTOM_LEFT:
1019         case Placement::BOTTOM_RIGHT:
1020         case Placement::BOTTOM: {
1021             xMin = paddingStart_;
1022             xMax = wrapperSize_.Width() - width - paddingEnd_;
1023             yMin = std::max(targetOffset_.GetY() + targetSize_.Height() + space, paddingTop_);
1024             yMax = wrapperSize_.Height() - height - paddingBottom_;
1025             break;
1026         }
1027         default:
1028             break;
1029     }
1030     if (xMax < xMin || yMax < yMin) {
1031         return OffsetF(0.0f, 0.0f);
1032     }
1033     auto x = std::clamp(position.GetX(), xMin, xMax);
1034     auto y = std::clamp(position.GetY(), yMin, yMax);
1035     return OffsetF(x, y);
1036 }
1037 
AddTargetSpace(const OffsetF & position)1038 OffsetF MenuLayoutAlgorithm::AddTargetSpace(const OffsetF& position)
1039 {
1040     auto x = position.GetX();
1041     auto y = position.GetY();
1042     switch (placement_) {
1043         case Placement::BOTTOM_LEFT:
1044         case Placement::BOTTOM_RIGHT:
1045         case Placement::BOTTOM: {
1046             y += TARGET_SECURITY.ConvertToPx();
1047             break;
1048         }
1049         case Placement::TOP_LEFT:
1050         case Placement::TOP_RIGHT:
1051         case Placement::TOP: {
1052             y -= TARGET_SECURITY.ConvertToPx();
1053             break;
1054         }
1055         case Placement::RIGHT_TOP:
1056         case Placement::RIGHT_BOTTOM:
1057         case Placement::RIGHT: {
1058             x += TARGET_SECURITY.ConvertToPx();
1059             break;
1060         }
1061         case Placement::LEFT_TOP:
1062         case Placement::LEFT_BOTTOM:
1063         case Placement::LEFT: {
1064             x -= TARGET_SECURITY.ConvertToPx();
1065             break;
1066         }
1067         default: {
1068             y += TARGET_SECURITY.ConvertToPx();
1069             break;
1070         }
1071     }
1072     return OffsetF(x, y);
1073 }
1074 
AddOffset(const OffsetF & position)1075 OffsetF MenuLayoutAlgorithm::AddOffset(const OffsetF& position)
1076 {
1077     auto x = position.GetX();
1078     auto y = position.GetY();
1079     switch (placement_) {
1080         case Placement::BOTTOM_LEFT:
1081         case Placement::BOTTOM_RIGHT:
1082         case Placement::BOTTOM: {
1083             x += positionOffset_.GetX();
1084             y += positionOffset_.GetY();
1085             break;
1086         }
1087         case Placement::TOP_LEFT:
1088         case Placement::TOP_RIGHT:
1089         case Placement::TOP: {
1090             x += positionOffset_.GetX();
1091             y -= positionOffset_.GetY();
1092             break;
1093         }
1094         case Placement::RIGHT_TOP:
1095         case Placement::RIGHT_BOTTOM:
1096         case Placement::RIGHT: {
1097             x += positionOffset_.GetX();
1098             y += positionOffset_.GetY();
1099             break;
1100         }
1101         case Placement::LEFT_TOP:
1102         case Placement::LEFT_BOTTOM:
1103         case Placement::LEFT: {
1104             x -= positionOffset_.GetX();
1105             y += positionOffset_.GetY();
1106             break;
1107         }
1108         default: {
1109             x += positionOffset_.GetX();
1110             y += positionOffset_.GetY();
1111             break;
1112         }
1113     }
1114     return OffsetF(x, y);
1115 }
1116 
CheckPositionInPlacementRect(const Rect & rect,const OffsetF & position,const SizeF & childSize)1117 bool MenuLayoutAlgorithm::CheckPositionInPlacementRect(
1118     const Rect& rect, const OffsetF& position, const SizeF& childSize)
1119 {
1120     auto x = position.GetX();
1121     auto y = position.GetY();
1122     if (x < rect.Left() || (x + childSize.Width()) > rect.Right() || y < rect.Top() ||
1123         (y + childSize.Height()) > rect.Bottom()) {
1124         return false;
1125     }
1126     return true;
1127 }
1128 
CheckPosition(const OffsetF & position,const SizeF & childSize)1129 bool MenuLayoutAlgorithm::CheckPosition(const OffsetF& position, const SizeF& childSize)
1130 {
1131     float targetOffsetX = targetOffset_.GetX();
1132     float targetOffsetY = targetOffset_.GetY();
1133 
1134     Rect rect;
1135     switch (placement_) {
1136         case Placement::BOTTOM_LEFT:
1137         case Placement::BOTTOM_RIGHT:
1138         case Placement::BOTTOM: {
1139             auto y = std::max(targetOffsetY + targetSize_.Height(), paddingTop_);
1140             auto height =
1141                 std::min(wrapperSize_.Height() - paddingBottom_ - targetOffsetY - targetSize_.Height(),
1142                     wrapperSize_.Height() - paddingBottom_ - paddingTop_);
1143             rect.SetRect(
1144                 paddingStart_, y, wrapperSize_.Width() - paddingEnd_ - paddingStart_, height);
1145             break;
1146         }
1147         case Placement::TOP_LEFT:
1148         case Placement::TOP_RIGHT:
1149         case Placement::TOP: {
1150             auto height = std::min(targetOffsetY - paddingTop_,
1151                 wrapperSize_.Height() - paddingTop_ - paddingBottom_);
1152             rect.SetRect(paddingStart_, paddingTop_,
1153                 wrapperSize_.Width() - paddingEnd_ - paddingStart_, height);
1154             break;
1155         }
1156         case Placement::RIGHT_TOP:
1157         case Placement::RIGHT_BOTTOM:
1158         case Placement::RIGHT: {
1159             auto x = std::max(targetOffsetX + targetSize_.Width(), paddingStart_);
1160             auto width = std::min(wrapperSize_.Width() - targetOffsetX - targetSize_.Width() - paddingEnd_,
1161                 wrapperSize_.Width() - paddingStart_ - paddingEnd_);
1162             rect.SetRect(
1163                 x, paddingTop_, width, wrapperSize_.Height() - paddingBottom_ - paddingTop_);
1164             break;
1165         }
1166         case Placement::LEFT_TOP:
1167         case Placement::LEFT_BOTTOM:
1168         case Placement::LEFT: {
1169             auto width = std::min(
1170                 targetOffsetX - paddingStart_, wrapperSize_.Width() - paddingEnd_ - paddingStart_);
1171             rect.SetRect(paddingStart_, paddingTop_, width,
1172                 wrapperSize_.Height() - paddingBottom_ - paddingTop_);
1173             break;
1174         }
1175         default:
1176             return false;
1177     }
1178     return CheckPositionInPlacementRect(rect, position, childSize);
1179 }
1180 
GetPositionWithPlacementTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1181 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementTop(
1182     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1183 {
1184     return topPosition;
1185 }
1186 
GetPositionWithPlacementTopLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1187 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementTopLeft(
1188     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1189 {
1190     OffsetF childPosition;
1191     float marginRight = 0.0f;
1192     float marginBottom = 0.0f;
1193     childPosition = OffsetF(targetOffset_.GetX() - marginRight,
1194         targetOffset_.GetY() - childSize.Height() - marginBottom - targetSpace_);
1195     return childPosition;
1196 }
1197 
GetPositionWithPlacementTopRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1198 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementTopRight(
1199     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1200 {
1201     OffsetF childPosition;
1202     float marginBottom = 0.0f;
1203     float marginLeft = 0.0f;
1204     childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
1205         targetOffset_.GetY() - childSize.Height() - targetSpace_ - marginBottom);
1206     return childPosition;
1207 }
1208 
GetPositionWithPlacementBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1209 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementBottom(
1210     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1211 {
1212     return bottomPosition;
1213 }
1214 
GetPositionWithPlacementBottomLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1215 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementBottomLeft(
1216     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1217 {
1218     OffsetF childPosition;
1219     float marginRight = 0.0f;
1220     float marginTop = 0.0f;
1221     childPosition = OffsetF(targetOffset_.GetX() - marginRight,
1222         targetOffset_.GetY() + targetSize_.Height() + targetSpace_ + marginTop);
1223     return childPosition;
1224 }
1225 
GetPositionWithPlacementBottomRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1226 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementBottomRight(
1227     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1228 {
1229     OffsetF childPosition;
1230     float marginTop = 0.0f;
1231     float marginLeft = 0.0f;
1232     childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
1233         targetOffset_.GetY() + targetSize_.Height() + targetSpace_ + marginTop);
1234     return childPosition;
1235 }
1236 
GetPositionWithPlacementLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1237 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementLeft(
1238     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1239 {
1240     OffsetF childPosition;
1241     float marginRight = 0.0f;
1242     childPosition =
1243         OffsetF(targetOffset_.GetX() - targetSpace_ - childSize.Width() - marginRight,
1244             targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
1245     return childPosition;
1246 }
1247 
GetPositionWithPlacementLeftTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1248 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementLeftTop(
1249     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1250 {
1251     OffsetF childPosition;
1252     float marginRight = 0.0f;
1253     float marginBottom = 0.0f;
1254     childPosition =
1255         OffsetF(targetOffset_.GetX() - targetSpace_ - childSize.Width() - marginRight,
1256             targetOffset_.GetY() - marginBottom);
1257     return childPosition;
1258 }
1259 
GetPositionWithPlacementLeftBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1260 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementLeftBottom(
1261     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1262 {
1263     OffsetF childPosition;
1264     float marginRight = 0.0f;
1265     float marginTop = 0.0f;
1266     childPosition =
1267         OffsetF(targetOffset_.GetX() - targetSpace_ - childSize.Width() - marginRight,
1268             targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
1269     return childPosition;
1270 }
1271 
GetPositionWithPlacementRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1272 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementRight(
1273     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1274 {
1275     OffsetF childPosition;
1276     float marginLeft = 0.0f;
1277     childPosition =
1278         OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_ + marginLeft,
1279             targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
1280     return childPosition;
1281 }
1282 
GetPositionWithPlacementRightTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1283 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementRightTop(
1284     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1285 {
1286     OffsetF childPosition;
1287     float marginBottom = 0.0f;
1288     float marginLeft = 0.0f;
1289     childPosition =
1290         OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_ + marginLeft,
1291             targetOffset_.GetY() - marginBottom);
1292     return childPosition;
1293 }
1294 
GetPositionWithPlacementRightBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1295 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementRightBottom(
1296     const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1297 {
1298     OffsetF childPosition;
1299     float marginTop = 0.0f;
1300     float marginLeft = 0.0f;
1301     childPosition =
1302         OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_ + marginLeft,
1303             targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
1304     return childPosition;
1305 }
1306 
1307 } // namespace OHOS::Ace::NG
1308