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 "base/geometry/ng/offset_t.h"
19 #include "core/components/container_modal/container_modal_constants.h"
20 #include "core/components_ng/pattern/menu/menu_item/menu_item_pattern.h"
21 #include "core/components_ng/pattern/menu/menu_pattern.h"
22 #include "core/pipeline_ng/pipeline_context.h"
23
24 namespace OHOS::Ace::NG {
25
Layout(LayoutWrapper * layoutWrapper)26 void SubMenuLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
27 {
28 CHECK_NULL_VOID(layoutWrapper);
29 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
30 auto menuNode = layoutWrapper->GetHostNode();
31 CHECK_NULL_VOID(menuNode);
32 auto menuPattern = menuNode->GetPattern<MenuPattern>();
33 CHECK_NULL_VOID(menuPattern);
34 auto parentMenuItem = menuPattern->GetParentMenuItem();
35 CHECK_NULL_VOID(parentMenuItem);
36 auto menuItemSize = parentMenuItem->GetGeometryNode()->GetFrameSize();
37 auto parentFrameSize = parentMenuItem->GetGeometryNode()->GetMarginFrameSize();
38 position_ = parentMenuItem->GetPaintRectOffset() + OffsetF(parentFrameSize.Width(), 0.0);
39
40 float x = HorizontalLayoutSubMenu(size, position_.GetX(), menuItemSize);
41 float y = VerticalLayoutSubMenu(size, position_.GetY(), menuItemSize);
42
43 const auto& geometryNode = layoutWrapper->GetGeometryNode();
44 CHECK_NULL_VOID(geometryNode);
45 geometryNode->SetMarginFrameOffset(NG::OffsetF(x, y));
46
47 if (parentMenuItem) {
48 auto parentPattern = parentMenuItem->GetPattern<MenuItemPattern>();
49 CHECK_NULL_VOID(parentPattern);
50 auto topLeftPoint = OffsetF(x, y);
51 auto bottomRightPoint = OffsetF(x + size.Width(), y + size.Height());
52
53 auto pipelineContext = PipelineContext::GetCurrentContext();
54 CHECK_NULL_VOID(pipelineContext);
55 auto windowManager = pipelineContext->GetWindowManager();
56 auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL && windowManager &&
57 windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING;
58 OffsetF wrapperOffset;
59 if (isContainerModal) {
60 auto newOffsetX = static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()) +
61 static_cast<float>(CONTENT_PADDING.ConvertToPx());
62 auto newOffsetY = static_cast<float>(CONTAINER_TITLE_HEIGHT.ConvertToPx()) +
63 static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx());
64 wrapperOffset = OffsetF(newOffsetX, newOffsetY);
65 }
66 parentPattern->AddHoverRegions(topLeftPoint + wrapperOffset, bottomRightPoint + wrapperOffset);
67 }
68
69 auto child = layoutWrapper->GetOrCreateChildByIndex(0);
70 CHECK_NULL_VOID(child);
71 child->Layout();
72 }
73
74 // return submenu vertical offset
VerticalLayoutSubMenu(const SizeF & size,float position,const SizeF & menuItemSize)75 float SubMenuLayoutAlgorithm::VerticalLayoutSubMenu(const SizeF& size, float position, const SizeF& menuItemSize)
76 {
77 float wrapperHeight = wrapperSize_.Height();
78 float bottomSpace = wrapperHeight - position;
79 // line up top of subMenu with top of the menuItem
80 if (bottomSpace >= size.Height()) {
81 return position;
82 }
83 // line up bottom of menu with bottom of the screen
84 if (size.Height() < wrapperHeight) {
85 return wrapperHeight - size.Height();
86 }
87 // can't fit in screen, line up with top of the screen
88 return 0.0f;
89 }
90
91 // returns submenu horizontal offset
HorizontalLayoutSubMenu(const SizeF & size,float position,const SizeF & menuItemSize)92 float SubMenuLayoutAlgorithm::HorizontalLayoutSubMenu(const SizeF& size, float position, const SizeF& menuItemSize)
93 {
94 float wrapperWidth = wrapperSize_.Width();
95 float rightSpace = wrapperWidth - position;
96 float leftSpace = position - menuItemSize.Width();
97 // can fit subMenu on the right side of menuItem
98 if (rightSpace >= size.Width()) {
99 return position;
100 }
101 // fit subMenu on the left side of menuItem
102 if (leftSpace >= size.Width()) {
103 return position - size.Width() - menuItemSize.Width();
104 }
105 // line up right side of menu with right boundary of the screen
106 if (size.Width() < wrapperWidth) {
107 return wrapperWidth - size.Width();
108 }
109 // can't fit in screen, line up with left side of the screen
110 return 0.0f;
111 }
112 } // namespace OHOS::Ace::NG
113