• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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/sub_menu_layout_algorithm.h"
17 
18 #include "core/components/container_modal/container_modal_constants.h"
19 #include "core/components_ng/pattern/menu/menu_item/menu_item_pattern.h"
20 namespace OHOS::Ace::NG {
21 constexpr double MOUNT_MENU_FINAL_SCALE = 0.95f;
Layout(LayoutWrapper * layoutWrapper)22 void SubMenuLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
23 {
24     CHECK_NULL_VOID(layoutWrapper);
25     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
26     auto menuNode = layoutWrapper->GetHostNode();
27     CHECK_NULL_VOID(menuNode);
28     auto menuPattern = menuNode->GetPattern<MenuPattern>();
29     CHECK_NULL_VOID(menuPattern);
30     auto props = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
31     CHECK_NULL_VOID(props);
32     auto parentMenuItem = menuPattern->GetParentMenuItem();
33     CHECK_NULL_VOID(parentMenuItem);
34     InitCanExpandCurrentWindow(props->GetShowInSubWindowValue(false), layoutWrapper);
35     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
36         ModifySubMenuWrapper(layoutWrapper);
37     }
38     CheckMenuPadding(layoutWrapper);
39     const auto& geometryNode = layoutWrapper->GetGeometryNode();
40     CHECK_NULL_VOID(geometryNode);
41     auto parentItemPattern = parentMenuItem->GetPattern<MenuItemPattern>();
42     CHECK_NULL_VOID(parentItemPattern);
43     auto expandingMode = parentItemPattern->GetExpandingMode();
44     OffsetF position = GetSubMenuLayoutOffset(layoutWrapper, parentMenuItem, size, expandingMode);
45     geometryNode->SetMarginFrameOffset(position);
46     if (parentMenuItem) {
47         UpdateHoverRegion(parentMenuItem, position, size);
48     }
49     auto child = layoutWrapper->GetOrCreateChildByIndex(0);
50     CHECK_NULL_VOID(child);
51     child->Layout();
52     ClipMenuPath(layoutWrapper);
53 }
54 
UpdateHoverRegion(RefPtr<FrameNode> & parentMenuItem,const OffsetF & position,const SizeF & size)55 void SubMenuLayoutAlgorithm::UpdateHoverRegion(
56     RefPtr<FrameNode>& parentMenuItem, const OffsetF& position, const SizeF& size)
57 {
58     auto parentPattern = parentMenuItem->GetPattern<MenuItemPattern>();
59     CHECK_NULL_VOID(parentPattern);
60     auto bottomRightPoint = position + OffsetF(size.Width(), size.Height());
61     auto pipelineContext = parentMenuItem->GetContextWithCheck();
62     CHECK_NULL_VOID(pipelineContext);
63     auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL;
64     OffsetF wrapperOffset;
65     if ((!canExpandCurrentWindow_) && isContainerModal) {
66         auto newOffsetX = static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx());
67         auto newOffsetY = static_cast<float>(pipelineContext->GetCustomTitleHeight().ConvertToPx()) +
68                           static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx());
69         wrapperOffset = OffsetF(newOffsetX, newOffsetY);
70     }
71     parentPattern->AddHoverRegions(position + wrapperOffset, bottomRightPoint + wrapperOffset);
72 }
73 
GetSubMenuLayoutOffset(LayoutWrapper * layoutWrapper,const RefPtr<FrameNode> & parentMenuItem,const SizeF & size,SubMenuExpandingMode expandingMode)74 OffsetF SubMenuLayoutAlgorithm::GetSubMenuLayoutOffset(LayoutWrapper* layoutWrapper,
75     const RefPtr<FrameNode>& parentMenuItem, const SizeF& size, SubMenuExpandingMode expandingMode)
76 {
77     OffsetF position;
78     auto layoutDirection = layoutWrapper->GetLayoutProperty()->GetNonAutoLayoutDirection();
79     position = MenuLayoutAvoidAlgorithm(parentMenuItem, size, expandingMode, layoutWrapper);
80     if (layoutDirection == TextDirection::RTL) {
81         position.SetX(wrapperSize_.Width() - position.GetX() - size.Width());
82     }
83     return position;
84 }
85 
MenuLayoutAvoidAlgorithm(const RefPtr<FrameNode> & parentMenuItem,const SizeF & size,SubMenuExpandingMode expandingMode,LayoutWrapper * layoutWrapper)86 OffsetF SubMenuLayoutAlgorithm::MenuLayoutAvoidAlgorithm(const RefPtr<FrameNode>& parentMenuItem, const SizeF& size,
87     SubMenuExpandingMode expandingMode, LayoutWrapper* layoutWrapper)
88 {
89     auto pipelineContext = PipelineContext::GetMainPipelineContext();
90     CHECK_NULL_RETURN(pipelineContext, NG::OffsetF(0.0f, 0.0f));
91     auto menuItemSize = parentMenuItem->GetGeometryNode()->GetFrameSize();
92     position_ = GetSubMenuPosition(parentMenuItem, expandingMode);
93     bool stacked = expandingMode == SubMenuExpandingMode::STACK;
94     if (layoutWrapper != nullptr) {
95         auto menuLayoutProperty = layoutWrapper->GetLayoutProperty();
96         CHECK_NULL_RETURN(menuLayoutProperty, NG::OffsetF(0.0f, 0.0f));
97         auto layoutDirection = menuLayoutProperty->GetNonAutoLayoutDirection();
98         if (layoutDirection == TextDirection::RTL) {
99             float leftSpace = position_.GetX() - menuItemSize.Width();
100             position_ = OffsetF(wrapperSize_.Width() - leftSpace, position_.GetY());
101         }
102     }
103     float x = HorizontalLayoutSubMenu(size, position_.GetX(), menuItemSize);
104     x = std::clamp(x, paddingStart_, wrapperSize_.Width() - size.Width() - paddingEnd_);
105     float y = 0.0f;
106     if (canExpandCurrentWindow_ || !Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
107         y = VerticalLayoutSubMenu(size, position_.GetY(), menuItemSize, parentMenuItem, stacked, layoutWrapper);
108     } else {
109         y = VerticalLayoutSubMenuHalfScreen(size, position_.GetY(), menuItemSize, parentMenuItem, stacked,
110             layoutWrapper);
111     }
112     float yMinAvoid = wrapperRect_.Top() + paddingTop_;
113     float yMaxAvoid = wrapperRect_.Bottom() - paddingBottom_ - size.Height();
114     if (stacked) {
115         yMinAvoid = wrapperRect_.Top() + param_.topSecurity;
116         yMaxAvoid = wrapperRect_.Bottom() - param_.bottomSecurity - size.Height();
117     }
118     y = std::clamp(y, yMinAvoid, yMaxAvoid);
119     return NG::OffsetF(x, y);
120 }
121 
GetSubMenuPosition(const RefPtr<FrameNode> & parentMenuItem,SubMenuExpandingMode expandingMode)122 OffsetF SubMenuLayoutAlgorithm::GetSubMenuPosition(
123     const RefPtr<FrameNode>& parentMenuItem, SubMenuExpandingMode expandingMode)
124 {
125     auto parentItemFrameSize = parentMenuItem->GetGeometryNode()->GetMarginFrameSize();
126     OffsetF position;
127     if (expandingMode == SubMenuExpandingMode::STACK) {
128         auto parentItemPattern = parentMenuItem->GetPattern<MenuItemPattern>();
129         if (parentItemPattern != nullptr) {
130             auto parentMenu = parentItemPattern->GetMenu();
131             position = parentMenu == nullptr
132                 ? parentMenuItem->GetPaintRectOffset(false, true) + OffsetF(parentItemFrameSize.Width(), 0.0)
133                 : OffsetF(parentMenu->GetPaintRectOffset(false, true).GetX(),
134                     parentMenuItem->GetPaintRectOffset(false, true).GetY() +
135                     parentItemFrameSize.Height()); // * 0.95
136         }
137     } else if (expandingMode == SubMenuExpandingMode::SIDE) {
138         auto pipeline = parentMenuItem->GetContext();
139         auto selectTheme = pipeline ? pipeline->GetTheme<SelectTheme>() : nullptr;
140         float contentPadding = selectTheme ? -static_cast<float>(selectTheme->GetMenuPadding().ConvertToPx()) : 0.0f;
141         position =
142             parentMenuItem->GetPaintRectOffset(false, true) + OffsetF(parentItemFrameSize.Width(), contentPadding);
143     } else {
144         position = parentMenuItem->GetPaintRectOffset(false, true) + OffsetF(parentItemFrameSize.Width(), 0.0);
145     }
146 
147     auto pipelineContext = parentMenuItem->GetContextWithCheck();
148     CHECK_NULL_RETURN(pipelineContext, OffsetF());
149     auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL;
150     if ((!canExpandCurrentWindow_) && isContainerModal) {
151         auto newOffsetX = static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx());
152         auto newOffsetY = static_cast<float>(pipelineContext->GetCustomTitleHeight().ConvertToPx()) +
153                             static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx());
154         position -= OffsetF(newOffsetX, newOffsetY);
155     }
156     auto parentMenu = AceType::DynamicCast<FrameNode>(parentMenuItem->GetParent());
157     CHECK_NULL_RETURN(parentMenu, position);
158     auto scroll = AceType::DynamicCast<FrameNode>(parentMenu->GetParent());
159     CHECK_NULL_RETURN(scroll, position);
160     while (scroll && (scroll->GetTag() != V2::SCROLL_ETS_TAG)) {
161         scroll = AceType::DynamicCast<FrameNode>(scroll->GetParent());
162     }
163     CHECK_NULL_RETURN(scroll, position);
164     auto scrollGeometryNode = scroll->GetGeometryNode();
165     CHECK_NULL_RETURN(scrollGeometryNode, position);
166     auto scrollTop = scroll->GetPaintRectOffset(false, true).GetY();
167     auto scrollHeight = scrollGeometryNode->GetFrameSize().Height();
168     auto bottomOffset = scrollTop + scrollHeight;
169     if (parentMenuItem->GetPaintRectOffset(false, true).GetY() > bottomOffset) {
170         return scroll->GetPaintRectOffset(false, true) + OffsetF(parentItemFrameSize.Width(), 0.0);
171     }
172     return position;
173 }
174 
CalcStackSubMenuPositionYHalfScreenWithPreview(const SizeF & size,const RefPtr<FrameNode> & parentMenu,LayoutWrapper * layoutWrapper)175 float SubMenuLayoutAlgorithm::CalcStackSubMenuPositionYHalfScreenWithPreview(
176     const SizeF& size,
177     const RefPtr<FrameNode>& parentMenu,
178     LayoutWrapper* layoutWrapper
179 )
180 {
181     CHECK_NULL_RETURN(layoutWrapper, 0.0f);
182     CHECK_NULL_RETURN(parentMenu, 0.0f);
183     auto parentMenuPattern = parentMenu->GetPattern<MenuPattern>();
184     CHECK_NULL_RETURN(parentMenuPattern, 0.0f);
185     auto parentPlacement = parentMenuPattern->GetLastPlacement().value_or(Placement::NONE);
186     auto firstItemBottomPositionY = GetFirstItemBottomPositionY(parentMenu);
187     float parentMenuBottomY = GetMenuBottomPositionY(parentMenu);
188     auto lastItemPositionY = GetLastItemTopPositionY(parentMenu);
189     auto containerModalOffsetY = GetContainerModalOffsetY(parentMenu);
190     //correct position when window modal is containerModal
191     if (isContainerModal(parentMenu)) {
192         firstItemBottomPositionY -= containerModalOffsetY;
193         parentMenuBottomY -= containerModalOffsetY;
194         lastItemPositionY -= containerModalOffsetY;
195     }
196     if (parentPlacement == Placement::TOP_LEFT || parentPlacement == Placement::TOP ||
197         parentPlacement == Placement::TOP_RIGHT) {
198         auto bottomSpace = parentMenuBottomY - position_.GetY();
199         if (bottomSpace >= size.Height()) {
200             return position_.GetY();
201         }
202         bottomSpace = lastItemPositionY - wrapperRect_.Top() - param_.topSecurity;
203         if (size.Height() <= bottomSpace) {
204             return lastItemPositionY - size.Height();
205         }
206         auto subMenuNode = layoutWrapper->GetHostNode();
207         CHECK_NULL_RETURN(subMenuNode, 0.0f);
208         auto subMenuPattern = subMenuNode->GetPattern<MenuPattern>();
209         CHECK_NULL_RETURN(subMenuPattern, 0.0f);
210         auto diffY = bottomSpace - size.Height();
211         subMenuPattern->SetTranslateYForStack(diffY);
212         return wrapperRect_.Top() + param_.topSecurity;
213     } else {
214         auto bottomSpace = wrapperRect_.Bottom() - param_.bottomSecurity - position_.GetY();
215         if (bottomSpace >= size.Height()) {
216             return position_.GetY();
217         }
218         bottomSpace = wrapperRect_.Bottom() - param_.bottomSecurity - firstItemBottomPositionY;
219         if (size.Height() <= bottomSpace) {
220             return wrapperRect_.Bottom() - param_.bottomSecurity - size.Height();
221         }
222         auto subMenuNode = layoutWrapper->GetHostNode();
223         CHECK_NULL_RETURN(subMenuNode, 0.0f);
224         auto subMenuPattern = subMenuNode->GetPattern<MenuPattern>();
225         CHECK_NULL_RETURN(subMenuPattern, 0.0f);
226         auto diffY = size.Height() - bottomSpace;
227         subMenuPattern->SetTranslateYForStack(diffY);
228         return wrapperRect_.Bottom() - param_.bottomSecurity - size.Height();
229     }
230 }
231 
NormalizePositionY(const RefPtr<FrameNode> & frameNode,float menuTopPositionY,float positionY)232 float SubMenuLayoutAlgorithm::NormalizePositionY(const RefPtr<FrameNode>& frameNode, float menuTopPositionY,
233     float positionY)
234 {
235     CHECK_NULL_RETURN(frameNode, positionY);
236     auto pipeline = frameNode->GetContext();
237     CHECK_NULL_RETURN(pipeline, positionY);
238     auto theme = pipeline->GetTheme<SelectTheme>();
239     CHECK_NULL_RETURN(theme, positionY);
240     // only 2in1 device need fix position
241     auto expandDisplay = theme->GetExpandDisplay();
242     CHECK_NULL_RETURN(expandDisplay, positionY);
243     return menuTopPositionY + (positionY - menuTopPositionY) * MOUNT_MENU_FINAL_SCALE;
244 }
245 
CalcStackSubMenuPositionYHalfScreen(const SizeF & size,const RefPtr<FrameNode> & parentMenu,const RefPtr<FrameNode> & parentMenuItem)246 float SubMenuLayoutAlgorithm::CalcStackSubMenuPositionYHalfScreen(
247     const SizeF& size, const RefPtr<FrameNode>& parentMenu, const RefPtr<FrameNode>& parentMenuItem
248 )
249 {
250     auto parentMenuPattern = parentMenu->GetPattern<MenuPattern>();
251     auto parentPlacement = parentMenuPattern->GetLastPlacement().value_or(Placement::NONE);
252     auto firstItemBottomPositionY = GetFirstItemBottomPositionY(parentMenu);
253     float parentMenuBottomY = GetMenuBottomPositionY(parentMenu);
254     float lastMenuItemPositionY = GetLastItemTopPositionY(parentMenu);
255     auto containerModalOffsetY = GetContainerModalOffsetY(parentMenu);
256     auto parentMenuPositionY = parentMenu->GetPaintRectOffset(false, true).GetY();
257     firstItemBottomPositionY = NormalizePositionY(parentMenu, parentMenuPositionY, firstItemBottomPositionY);
258     parentMenuBottomY = NormalizePositionY(parentMenu, parentMenuPositionY, parentMenuBottomY);
259     lastMenuItemPositionY = NormalizePositionY(parentMenu, parentMenuPositionY, lastMenuItemPositionY);
260     //correct position when window modal is containerModal
261     if (isContainerModal(parentMenu)) {
262         firstItemBottomPositionY -= containerModalOffsetY;
263         parentMenuBottomY -= containerModalOffsetY;
264         lastMenuItemPositionY -= containerModalOffsetY;
265         parentMenuPositionY -= containerModalOffsetY;
266     }
267     float bottomSpace = 0.0f;
268     if (parentPlacement == Placement::TOP_LEFT || parentPlacement == Placement::TOP ||
269             parentPlacement == Placement::TOP_RIGHT) {
270         bottomSpace = parentMenuBottomY - position_.GetY();
271         if (bottomSpace >= size.Height()) {
272             return NormalizePositionY(parentMenu, parentMenuPositionY, position_.GetY());
273         }
274         bottomSpace = lastMenuItemPositionY - wrapperRect_.Top() - param_.topSecurity;
275         if (bottomSpace >= size.Height()) {
276             return lastMenuItemPositionY - size.Height();
277         }
278         return wrapperRect_.Top() + param_.topSecurity;
279     }
280     bottomSpace = wrapperRect_.Bottom() - param_.bottomSecurity - position_.GetY();
281     if (bottomSpace >= size.Height()) {
282         return NormalizePositionY(parentMenu, parentMenuPositionY, position_.GetY());
283     }
284     if (size.Height() < wrapperRect_.Height()) {
285         bottomSpace = wrapperRect_.Bottom() - param_.bottomSecurity - firstItemBottomPositionY;
286         if (size.Height() <= bottomSpace) {
287             return wrapperRect_.Bottom() - param_.bottomSecurity - size.Height();
288         }
289         if (bottomSpace < parentMenuItem->GetGeometryNode()->GetFrameSize().Height()) {
290             return parentMenuPositionY;
291         }
292         return firstItemBottomPositionY;
293     }
294     // can't fit in screen, line up with top of the screen
295     return 0.0f;
296 }
297 
VerticalLayoutSubMenuHalfScreen(const SizeF & size,float position,const SizeF & menuItemSize,const RefPtr<FrameNode> & parentMenuItem,bool stacked,LayoutWrapper * layoutWrapper)298 float SubMenuLayoutAlgorithm::VerticalLayoutSubMenuHalfScreen(
299     const SizeF& size, float position, const SizeF& menuItemSize,
300     const RefPtr<FrameNode>& parentMenuItem, bool stacked, LayoutWrapper* layoutWrapper)
301 {
302     auto pipelineContext = PipelineContext::GetMainPipelineContext();
303     CHECK_NULL_RETURN(pipelineContext, 0.0f);
304     auto safeAreaManager = pipelineContext->GetSafeAreaManager();
305     CHECK_NULL_RETURN(safeAreaManager, 0.0f);
306     float wrapperHeight = wrapperSize_.Height();
307     float bottomSpace = 0.0f;
308     if (!stacked) {
309         bottomSpace = wrapperSize_.Height() - (position_.GetY() - param_.windowsOffsetY) - margin_ * 2.0f;
310         // line up top of subMenu with top of the menuItem
311         if (bottomSpace >= size.Height()) {
312             return position;
313         }
314         // line up bottom of menu with bottom of the screen
315         if (size.Height() < wrapperHeight) {
316             return wrapperHeight - size.Height();
317         }
318         // can't fit in screen, line up with top of the screen
319         return 0.0f;
320     }
321     CHECK_NULL_RETURN(parentMenuItem, bottomSpace);
322     auto parentItemPattern = parentMenuItem->GetPattern<MenuItemPattern>();
323     CHECK_NULL_RETURN(parentItemPattern, bottomSpace);
324     auto parentMenu = parentItemPattern->GetMenu(true);
325     CHECK_NULL_RETURN(parentMenu, bottomSpace);
326     auto parentMenuPattern = parentMenu->GetPattern<MenuPattern>();
327     CHECK_NULL_RETURN(parentMenuPattern, bottomSpace);
328     if (parentMenuPattern->GetPreviewMode() != MenuPreviewMode::NONE) {
329         CHECK_NULL_RETURN(layoutWrapper, bottomSpace);
330         return CalcStackSubMenuPositionYHalfScreenWithPreview(size, parentMenu, layoutWrapper);
331     } else {
332         return CalcStackSubMenuPositionYHalfScreen(size, parentMenu, parentMenuItem);
333     }
334 }
335 
336 // return submenu vertical offset
VerticalLayoutSubMenu(const SizeF & size,float position,const SizeF & menuItemSize,const RefPtr<FrameNode> & parentMenuItem,bool stacked,LayoutWrapper * layoutWrapper)337 float SubMenuLayoutAlgorithm::VerticalLayoutSubMenu(const SizeF& size, float position, const SizeF& menuItemSize,
338     const RefPtr<FrameNode>& parentMenuItem, bool stacked, LayoutWrapper* layoutWrapper)
339 {
340     float bottomSpace = 0.0f;
341     if (!stacked) {
342         bottomSpace = wrapperRect_.Bottom() - position - paddingBottom_;
343         // line up top of subMenu with top of the menuItem
344         if (bottomSpace >= size.Height()) {
345             return position;
346         }
347         // line up bottom of menu with bottom of the screen
348         if (size.Height() < wrapperRect_.Height()) {
349             return wrapperRect_.Bottom() - size.Height() - paddingBottom_;
350         }
351         // can't fit in screen, line up with top of the screen
352         return wrapperRect_.Top() + paddingTop_;
353     }
354     CHECK_NULL_RETURN(parentMenuItem, bottomSpace);
355     auto parentItemPattern = parentMenuItem->GetPattern<MenuItemPattern>();
356     CHECK_NULL_RETURN(parentItemPattern, bottomSpace);
357     auto parentMenu = parentItemPattern->GetMenu(true);
358     CHECK_NULL_RETURN(parentMenu, bottomSpace);
359     auto parentMenuPattern = parentMenu->GetPattern<MenuPattern>();
360     CHECK_NULL_RETURN(parentMenuPattern, bottomSpace);
361     if (parentMenuPattern->GetPreviewMode() != MenuPreviewMode::NONE) {
362         CHECK_NULL_RETURN(layoutWrapper, bottomSpace);
363         return CalcStackSubMenuPositionYHalfScreenWithPreview(size, parentMenu, layoutWrapper);
364     } else {
365         return CalcStackSubMenuPositionYHalfScreen(size, parentMenu, parentMenuItem);
366     }
367 }
368 
369 // returns submenu horizontal offset
HorizontalLayoutSubMenu(const SizeF & size,float position,const SizeF & menuItemSize,LayoutWrapper * layoutWrapper)370 float SubMenuLayoutAlgorithm::HorizontalLayoutSubMenu(
371     const SizeF& size, float position, const SizeF& menuItemSize, LayoutWrapper* layoutWrapper)
372 {
373     float wrapperWidth = wrapperSize_.Width();
374     float rightSpace = wrapperWidth - position - paddingEnd_;
375     float leftSpace = position - menuItemSize.Width();
376     if (layoutWrapper != nullptr) {
377         auto menuLayoutProperty = layoutWrapper->GetLayoutProperty();
378         CHECK_NULL_RETURN(menuLayoutProperty, 0.0f);
379         auto layoutDirection = menuLayoutProperty->GetNonAutoLayoutDirection();
380         if (layoutDirection == TextDirection::RTL) {
381             rightSpace = position - menuItemSize.Width();
382             leftSpace = wrapperWidth - position;
383         }
384     }
385     // can fit subMenu on the right side of menuItem
386     if (rightSpace >= size.Width()) {
387         return position;
388     }
389     // fit subMenu on the left side of menuItem
390     if (leftSpace >= size.Width()) {
391         return position - size.Width() - menuItemSize.Width();
392     }
393     // line up right side of menu with right boundary of the screen
394     if (size.Width() < wrapperWidth) {
395         return wrapperWidth - size.Width() - paddingEnd_;
396     }
397     // can't fit in screen, line up with left side of the screen
398     return 0.0f;
399 }
400 
ModifySubMenuWrapper(LayoutWrapper * layoutWrapper)401 void SubMenuLayoutAlgorithm::ModifySubMenuWrapper(LayoutWrapper* layoutWrapper)
402 {
403     CHECK_NULL_VOID(layoutWrapper);
404     auto pipelineContext = PipelineContext::GetMainPipelineContext();
405     CHECK_NULL_VOID(pipelineContext);
406     auto safeAreaManager = pipelineContext->GetSafeAreaManager();
407     CHECK_NULL_VOID(safeAreaManager);
408     auto bottom = safeAreaManager->GetSystemSafeArea().bottom_.Length();
409     if (!canExpandCurrentWindow_) {
410         wrapperSize_ = SizeF(param_.menuWindowRect.Width(), param_.menuWindowRect.Height() - bottom);
411     } else {
412         wrapperSize_ = SizeF(wrapperSize_.Width(), wrapperSize_.Height());
413     }
414 }
415 
InitializePadding(LayoutWrapper * layoutWrapper)416 void SubMenuLayoutAlgorithm::InitializePadding(LayoutWrapper* layoutWrapper)
417 {
418     auto menuPattern = layoutWrapper->GetHostNode()->GetPattern<MenuPattern>();
419     CHECK_NULL_VOID(menuPattern);
420     auto host = menuPattern->GetHost();
421     CHECK_NULL_VOID(host);
422     auto pipeline = host->GetContextWithCheck();
423     CHECK_NULL_VOID(pipeline);
424     auto theme = pipeline->GetTheme<SelectTheme>();
425     CHECK_NULL_VOID(theme);
426     if (!menuPattern->IsSelectOverlayExtensionMenu()) {
427         margin_ = static_cast<float>(theme->GetMenuPadding().ConvertToPx());
428         paddingStart_ = static_cast<float>(theme->GetDefaultPaddingStart().ConvertToPx());
429         paddingEnd_ = static_cast<float>(theme->GetDefaultPaddingEnd().ConvertToPx());
430         if (!AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
431             paddingTop_ = static_cast<float>(theme->GetDefaultPaddingTop().ConvertToPx());
432             paddingBottom_ = static_cast<float>(theme->GetDefaultPaddingBottomFixed().ConvertToPx());
433         }
434     }
435 }
436 
InitializePaddingAPI12(LayoutWrapper * layoutWrapper)437 void SubMenuLayoutAlgorithm::InitializePaddingAPI12(LayoutWrapper* layoutWrapper)
438 {
439     auto menuNode = layoutWrapper->GetHostNode();
440     CHECK_NULL_VOID(menuNode);
441     auto menuPattern = menuNode->GetPattern<MenuPattern>();
442     CHECK_NULL_VOID(menuPattern);
443     auto pipeline = PipelineContext::GetMainPipelineContext();
444     CHECK_NULL_VOID(pipeline);
445     auto theme = pipeline->GetTheme<SelectTheme>();
446     CHECK_NULL_VOID(theme);
447     if (!menuPattern->IsSelectOverlayExtensionMenu()) {
448         margin_ = static_cast<float>(theme->GetMenuPadding().ConvertToPx());
449         if (!canExpandCurrentWindow_) {
450             paddingStart_ = static_cast<float>(theme->GetMenuLargeMargin().ConvertToPx());
451             paddingEnd_ = static_cast<float>(theme->GetMenuLargeMargin().ConvertToPx());
452         } else {
453             paddingStart_ = static_cast<float>(theme->GetMenuMediumMargin().ConvertToPx());
454             paddingEnd_ = static_cast<float>(theme->GetMenuMediumMargin().ConvertToPx());
455         }
456     }
457 }
458 
CheckMenuPadding(LayoutWrapper * layoutWrapper)459 void SubMenuLayoutAlgorithm::CheckMenuPadding(LayoutWrapper* layoutWrapper)
460 {
461     CHECK_NULL_VOID(layoutWrapper);
462     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TWELVE)) {
463         InitializePaddingAPI12(layoutWrapper);
464     } else {
465         InitializePadding(layoutWrapper);
466     }
467 }
468 
469 } // namespace OHOS::Ace::NG
470