• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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/wrapper/menu_wrapper_pattern.h"
17 
18 #include "base/utils/utils.h"
19 #include "core/components/common/properties/shadow_config.h"
20 #include "core/components/select/select_theme.h"
21 #include "core/components_ng/event/click_event.h"
22 #include "core/event/touch_event.h"
23 #include "core/pipeline_ng/pipeline_context.h"
24 
25 namespace OHOS::Ace::NG {
HideMenu(const RefPtr<FrameNode> & menu)26 void MenuWrapperPattern::HideMenu(const RefPtr<FrameNode>& menu)
27 {
28     if (GetHost()->GetTag() == V2::SELECT_OVERLAY_ETS_TAG) {
29         return;
30     }
31     isHided_ = true;
32 
33     auto menuPattern = menu->GetPattern<MenuPattern>();
34     CHECK_NULL_VOID(menuPattern);
35     LOGI("MenuWrapperPattern closing menu %{public}d", targetId_);
36     menuPattern->HideMenu();
37 }
38 
OnAttachToFrameNode()39 void MenuWrapperPattern::OnAttachToFrameNode()
40 {
41     RegisterOnTouch();
42 }
43 
OnModifyDone()44 void MenuWrapperPattern::OnModifyDone()
45 {
46     Pattern::OnModifyDone();
47 
48     isHided_ = false;
49 }
50 
51 // close subMenu when mouse move outside
HandleMouseEvent(const MouseInfo & info,RefPtr<MenuItemPattern> & menuItemPattern)52 void MenuWrapperPattern::HandleMouseEvent(const MouseInfo& info, RefPtr<MenuItemPattern>& menuItemPattern)
53 {
54     auto host = GetHost();
55     CHECK_NULL_VOID(host);
56     auto subMenu = host->GetChildren().back();
57     if (host->GetChildren().size() <= 1) {
58         return;
59     }
60     auto subMenuNode = DynamicCast<FrameNode>(subMenu);
61     CHECK_NULL_VOID(subMenuNode);
62     auto subMenuPattern = subMenuNode->GetPattern<MenuPattern>();
63     CHECK_NULL_VOID(subMenuPattern);
64     auto currentHoverMenuItem = subMenuPattern->GetParentMenuItem();
65     CHECK_NULL_VOID(currentHoverMenuItem);
66 
67     auto menuItemNode = menuItemPattern->GetHost();
68     CHECK_NULL_VOID(menuItemNode);
69     if (currentHoverMenuItem->GetId() != menuItemNode->GetId()) {
70         return;
71     }
72     const auto& mousePosition = info.GetGlobalLocation();
73     if (!menuItemPattern->IsInHoverRegions(mousePosition.GetX(), mousePosition.GetY()) &&
74         menuItemPattern->IsSubMenuShowed()) {
75         LOGI("MenuWrapperPattern Hide SubMenu");
76         HideSubMenu();
77         menuItemPattern->SetIsSubMenuShowed(false);
78         menuItemPattern->ClearHoverRegions();
79         menuItemPattern->ResetWrapperMouseEvent();
80     }
81 }
82 
HideMenu()83 void MenuWrapperPattern::HideMenu()
84 {
85     auto host = GetHost();
86     CHECK_NULL_VOID(host);
87     auto menuNode = DynamicCast<FrameNode>(host->GetChildAtIndex(0));
88     CHECK_NULL_VOID(menuNode);
89     HideMenu(menuNode);
90 }
91 
HideSubMenu()92 void MenuWrapperPattern::HideSubMenu()
93 {
94     auto host = GetHost();
95     CHECK_NULL_VOID(host);
96     if (host->GetChildren().size() <= 1) {
97         // sub menu not show
98         return;
99     }
100     auto subMenu = host->GetChildren().back();
101     host->RemoveChild(subMenu);
102     auto menuPattern = DynamicCast<FrameNode>(subMenu)->GetPattern<MenuPattern>();
103     if (menuPattern) {
104         menuPattern->RemoveParentHoverStyle();
105         auto frameNode = FrameNode::GetFrameNode(menuPattern->GetTargetTag(), menuPattern->GetTargetId());
106         CHECK_NULL_VOID(frameNode);
107         auto menuItem = frameNode->GetPattern<MenuItemPattern>();
108         if (menuItem) {
109             menuItem->SetIsSubMenuShowed(false);
110         }
111     }
112     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
113 }
114 
RegisterOnTouch()115 void MenuWrapperPattern::RegisterOnTouch()
116 {
117     // if already initialized touch event
118     CHECK_NULL_VOID_NOLOG(!onTouch_);
119     auto host = GetHost();
120     CHECK_NULL_VOID(host);
121     auto gesture = host->GetOrCreateGestureEventHub();
122     CHECK_NULL_VOID(gesture);
123     // hide menu when touched outside the menu region
124     auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
125         auto pattern = weak.Upgrade();
126         CHECK_NULL_VOID_NOLOG(pattern);
127         pattern->OnTouchEvent(info);
128     };
129     onTouch_ = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
130     gesture->AddTouchEvent(onTouch_);
131 }
132 
OnTouchEvent(const TouchEventInfo & info)133 void MenuWrapperPattern::OnTouchEvent(const TouchEventInfo& info)
134 {
135     CHECK_NULL_VOID_NOLOG(!info.GetTouches().empty());
136     auto touch = info.GetTouches().front();
137     // filter out other touch types
138     if (touch.GetTouchType() != TouchType::DOWN) {
139         return;
140     }
141     if (IsHided()) {
142         return;
143     }
144     auto host = GetHost();
145     CHECK_NULL_VOID(host);
146 
147     auto position = OffsetF(
148         static_cast<float>(touch.GetGlobalLocation().GetX()), static_cast<float>(touch.GetGlobalLocation().GetY()));
149     position -= host->GetPaintRectOffset();
150     auto children = host->GetChildren();
151     for (auto child = children.rbegin(); child != children.rend(); ++child) {
152         // get menu frame node (child of menu wrapper)
153         auto menuNode = DynamicCast<FrameNode>(*child);
154         CHECK_NULL_VOID(menuNode);
155         // get menuNode's touch region
156         auto menuZone = menuNode->GetGeometryNode()->GetFrameRect();
157         if (menuZone.IsInRegion(PointF(position.GetX(), position.GetY()))) {
158             return;
159         }
160         // if DOWN-touched outside the menu region, then hide menu
161         auto menuPattern = menuNode->GetPattern<MenuPattern>();
162         CHECK_NULL_VOID(menuPattern);
163         if (menuPattern->IsSubMenu() || menuPattern->IsSelectOverlaySubMenu()) {
164             HideSubMenu();
165         } else {
166             HideMenu(menuNode);
167         }
168     }
169 }
170 
CheckAndShowAnimation()171 void MenuWrapperPattern::CheckAndShowAnimation()
172 {
173     if (isFirstShow_) {
174         // only start animation when menu wrapper mount on.
175         StartShowAnimation();
176         isFirstShow_ = false;
177     }
178 }
179 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)180 bool MenuWrapperPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
181 {
182     if (IsContextMenu()) {
183         SetHotAreas(dirty);
184     }
185     CheckAndShowAnimation();
186     return false;
187 }
188 
SetHotAreas(const RefPtr<LayoutWrapper> & layoutWrapper)189 void MenuWrapperPattern::SetHotAreas(const RefPtr<LayoutWrapper>& layoutWrapper)
190 {
191     if (layoutWrapper->GetAllChildrenWithBuild().empty() || !IsContextMenu()) {
192         return;
193     }
194     auto layoutProps = layoutWrapper->GetLayoutProperty();
195     CHECK_NULL_VOID(layoutProps);
196     float safeAreaInsetsLeft = 0.0f;
197     float safeAreaInsetsTop = 0.0f;
198     auto&& safeAreaInsets = layoutProps->GetSafeAreaInsets();
199     if (safeAreaInsets) {
200         safeAreaInsetsLeft = static_cast<float>(safeAreaInsets->left_.end);
201         safeAreaInsetsTop = static_cast<float>(safeAreaInsets->top_.end);
202     }
203     std::vector<Rect> rects;
204     for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
205         auto frameRect = child->GetGeometryNode()->GetFrameRect();
206         // rect is relative to window
207         auto rect = Rect(frameRect.GetX() + safeAreaInsetsLeft, frameRect.GetY() + safeAreaInsetsTop, frameRect.Width(),
208             frameRect.Height());
209 
210         rects.emplace_back(rect);
211     }
212     SubwindowManager::GetInstance()->SetHotAreas(rects, GetHost()->GetId(), GetContainerId());
213 }
214 
StartShowAnimation()215 void MenuWrapperPattern::StartShowAnimation()
216 {
217     auto host = GetHost();
218     CHECK_NULL_VOID(host);
219     auto context = host->GetRenderContext();
220     CHECK_NULL_VOID(context);
221     context->UpdateOpacity(0.0);
222     context->UpdateOffset(GetAnimationOffset());
223 
224     AnimationUtils::Animate(
225         animationOption_,
226         [context]() {
227             if (context) {
228                 context->UpdateOpacity(1.0);
229                 context->UpdateOffset(OffsetT<Dimension>());
230             }
231         },
232         animationOption_.GetOnFinishEvent());
233 }
234 
SetAniamtinOption(const AnimationOption & animationOption)235 void MenuWrapperPattern::SetAniamtinOption(const AnimationOption& animationOption)
236 {
237     animationOption_.SetCurve(animationOption.GetCurve());
238     animationOption_.SetDuration(animationOption.GetDuration());
239     animationOption_.SetFillMode(animationOption.GetFillMode());
240     animationOption_.SetOnFinishEvent(animationOption.GetOnFinishEvent());
241 }
242 
GetAnimationOffset()243 OffsetT<Dimension> MenuWrapperPattern::GetAnimationOffset()
244 {
245     OffsetT<Dimension> offset;
246 
247     auto pipeline = PipelineBase::GetCurrentContext();
248     CHECK_NULL_RETURN(pipeline, offset);
249     auto theme = pipeline->GetTheme<SelectTheme>();
250     CHECK_NULL_RETURN(theme, offset);
251     auto animationOffset = theme->GetMenuAnimationOffset();
252 
253     switch (menuPlacement_) {
254         case Placement::LEFT:
255         case Placement::LEFT_TOP:
256         case Placement::LEFT_BOTTOM:
257             offset.SetX(animationOffset);
258             break;
259         case Placement::RIGHT:
260         case Placement::RIGHT_TOP:
261         case Placement::RIGHT_BOTTOM:
262             offset.SetX(-animationOffset);
263             break;
264         case Placement::TOP:
265         case Placement::TOP_LEFT:
266         case Placement::TOP_RIGHT:
267             offset.SetY(animationOffset);
268             break;
269         default:
270             offset.SetY(-animationOffset);
271             break;
272     }
273     return offset;
274 }
275 } // namespace OHOS::Ace::NG
276