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