• 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/log/dump_log.h"
19 #include "base/utils/utils.h"
20 #include "core/common/container.h"
21 #include "core/components/common/properties/shadow_config.h"
22 #include "core/components/select/select_theme.h"
23 #include "core/components_ng/event/click_event.h"
24 #include "core/components_ng/pattern/menu/menu_item/menu_item_pattern.h"
25 #include "core/components_ng/pattern/menu/preview/menu_preview_pattern.h"
26 #include "core/event/touch_event.h"
27 #include "core/pipeline_ng/pipeline_context.h"
28 
29 namespace OHOS::Ace::NG {
30 constexpr int32_t FOCUS_MENU_NUM = 2;
HideMenu(const RefPtr<FrameNode> & menu)31 void MenuWrapperPattern::HideMenu(const RefPtr<FrameNode>& menu)
32 {
33     if (GetHost()->GetTag() == V2::SELECT_OVERLAY_ETS_TAG) {
34         return;
35     }
36     SetIsStopHoverImageAnimation(true);
37     auto menuPattern = menu->GetPattern<MenuPattern>();
38     CHECK_NULL_VOID(menuPattern);
39     menuPattern->HideMenu();
40     CallMenuStateChangeCallback("false");
41 }
42 
OnModifyDone()43 void MenuWrapperPattern::OnModifyDone()
44 {
45     InitFocusEvent();
46 }
47 
InitFocusEvent()48 void MenuWrapperPattern::InitFocusEvent()
49 {
50     auto host = GetHost();
51     CHECK_NULL_VOID(host);
52     auto focusHub = host->GetOrCreateFocusHub();
53     CHECK_NULL_VOID(focusHub);
54     auto blurTask = [weak = WeakClaim(this)]() {
55         auto pattern = weak.Upgrade();
56         CHECK_NULL_VOID(pattern);
57         pattern->HideMenu();
58     };
59     focusHub->SetOnBlurInternal(std::move(blurTask));
60 }
61 
GetShowedSubMenu()62 RefPtr<FrameNode> MenuWrapperPattern::GetShowedSubMenu()
63 {
64     auto host = GetHost();
65     CHECK_NULL_RETURN(host, nullptr);
66     return DynamicCast<FrameNode>(host->GetLastChild());
67 }
68 
GetInnerMenu(RefPtr<UINode> & innerMenuNode,const PointF & position)69 bool MenuWrapperPattern::GetInnerMenu(RefPtr<UINode>& innerMenuNode, const PointF& position)
70 {
71     auto host = GetHost();
72     CHECK_NULL_RETURN(host, false);
73     auto children = host->GetChildren();
74     CHECK_NULL_RETURN(!children.empty(), false);
75     for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
76         auto& child = *iter;
77         if (!child || child->GetTag() != V2::MENU_ETS_TAG) {
78             continue;
79         }
80         auto frameNode = AceType::DynamicCast<FrameNode>(child);
81         CHECK_NULL_RETURN(frameNode, false);
82         auto menuPattern = frameNode->GetPattern<MenuPattern>();
83         CHECK_NULL_RETURN(menuPattern, false);
84         auto subMenuNode = menuPattern->GetShowedSubMenu();
85         innerMenuNode = subMenuNode ? subMenuNode : menuPattern->GetFirstInnerMenu();
86         if (!innerMenuNode) {
87             innerMenuNode = child;
88         }
89         auto geometryNode = frameNode->GetGeometryNode();
90         CHECK_NULL_RETURN(geometryNode, false);
91         return CheckPointInMenuZone(frameNode, position);
92     }
93     return false;
94 }
95 
FindTouchedMenuItem(const RefPtr<UINode> & menuNode,const PointF & position)96 RefPtr<FrameNode> MenuWrapperPattern::FindTouchedMenuItem(const RefPtr<UINode>& menuNode, const PointF& position)
97 {
98     CHECK_NULL_RETURN(menuNode, nullptr);
99     RefPtr<FrameNode> menuItem = nullptr;
100     const auto& children = menuNode->GetChildren();
101     for (auto child : children) {
102         if (child->GetTag() == V2::MENU_ITEM_ETS_TAG) {
103             auto frameNode = AceType::DynamicCast<FrameNode>(child);
104             auto pattern = frameNode ? frameNode->GetPattern<MenuItemPattern>() : nullptr;
105             menuItem = pattern ? pattern->FindTouchedEmbeddedMenuItem(position) : nullptr;
106         } else {
107             menuItem = FindTouchedMenuItem(child, position);
108         }
109         if (menuItem) {
110             if (CheckPointInMenuZone(menuItem, position)) {
111                 break;
112             } else {
113                 menuItem = nullptr;
114             }
115         }
116     }
117     return menuItem;
118 }
119 
HandleInteraction(const TouchEventInfo & info)120 void MenuWrapperPattern::HandleInteraction(const TouchEventInfo& info)
121 {
122     CHECK_NULL_VOID(!info.GetChangedTouches().empty());
123     auto touch = info.GetChangedTouches().front();
124     auto host = GetHost();
125     CHECK_NULL_VOID(host);
126     auto position = PointF(
127         static_cast<float>(touch.GetGlobalLocation().GetX()), static_cast<float>(touch.GetGlobalLocation().GetY()));
128     RefPtr<UINode> innerMenuNode = nullptr;
129     auto isInRegion = GetInnerMenu(innerMenuNode, position);
130     CHECK_NULL_VOID(innerMenuNode);
131 
132     ClearLastMenuItem();
133     // get menuNode's touch region
134     if (isInRegion) {
135         currentTouchItem_ = FindTouchedMenuItem(innerMenuNode, position);
136         ChangeCurMenuItemBgColor();
137         lastTouchItem_ = currentTouchItem_;
138     }
139     innerMenuNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
140 }
141 
ChangeCurMenuItemBgColor()142 void MenuWrapperPattern::ChangeCurMenuItemBgColor()
143 {
144     if (!currentTouchItem_) {
145         return;
146     }
147     auto pipeline = PipelineBase::GetCurrentContext();
148     CHECK_NULL_VOID(pipeline);
149     auto theme = pipeline->GetTheme<SelectTheme>();
150     CHECK_NULL_VOID(theme);
151     auto curMenuItemPattern = currentTouchItem_->GetPattern<MenuItemPattern>();
152     CHECK_NULL_VOID(curMenuItemPattern);
153     if (curMenuItemPattern->IsDisabled() || curMenuItemPattern->IsStackSubmenuHeader()) {
154         return;
155     }
156     curMenuItemPattern->NotifyPressStatus(true);
157 }
158 
ClearLastMenuItem()159 void MenuWrapperPattern::ClearLastMenuItem()
160 {
161     if (lastTouchItem_) {
162         auto lastMenuItemPattern = lastTouchItem_->GetPattern<MenuItemPattern>();
163         CHECK_NULL_VOID(lastMenuItemPattern);
164         lastMenuItemPattern->NotifyPressStatus(false);
165         lastTouchItem_ = nullptr;
166     }
167 }
168 
OnAttachToFrameNode()169 void MenuWrapperPattern::OnAttachToFrameNode()
170 {
171     RegisterOnTouch();
172 }
173 
174 // close subMenu when mouse move outside
HandleMouseEvent(const MouseInfo & info,RefPtr<MenuItemPattern> & menuItemPattern)175 void MenuWrapperPattern::HandleMouseEvent(const MouseInfo& info, RefPtr<MenuItemPattern>& menuItemPattern)
176 {
177     auto host = GetHost();
178     CHECK_NULL_VOID(host);
179     auto subMenu = host->GetChildren().back();
180     if (host->GetChildren().size() <= 1) {
181         return;
182     }
183     auto subMenuNode = DynamicCast<FrameNode>(subMenu);
184     CHECK_NULL_VOID(subMenuNode);
185     auto subMenuPattern = subMenuNode->GetPattern<MenuPattern>();
186     CHECK_NULL_VOID(subMenuPattern);
187     auto currentHoverMenuItem = subMenuPattern->GetParentMenuItem();
188     CHECK_NULL_VOID(currentHoverMenuItem);
189 
190     auto menuItemNode = menuItemPattern->GetHost();
191     CHECK_NULL_VOID(menuItemNode);
192     if (currentHoverMenuItem->GetId() != menuItemNode->GetId()) {
193         return;
194     }
195     const auto& mousePosition = info.GetGlobalLocation();
196     if (!menuItemPattern->IsInHoverRegions(mousePosition.GetX(), mousePosition.GetY()) &&
197         menuItemPattern->IsSubMenuShowed()) {
198         HideSubMenu();
199         menuItemPattern->SetIsSubMenuShowed(false);
200         menuItemPattern->ClearHoverRegions();
201         menuItemPattern->ResetWrapperMouseEvent();
202     }
203 }
204 
HideMenu()205 void MenuWrapperPattern::HideMenu()
206 {
207     auto host = GetHost();
208     CHECK_NULL_VOID(host);
209     auto menuNode = DynamicCast<FrameNode>(host->GetChildAtIndex(0));
210     CHECK_NULL_VOID(menuNode);
211     HideMenu(menuNode);
212 }
213 
GetExpandingMode(const RefPtr<UINode> & subMenu,SubMenuExpandingMode & expandingMode,bool & hasAnimation)214 void MenuWrapperPattern::GetExpandingMode(const RefPtr<UINode>& subMenu, SubMenuExpandingMode& expandingMode,
215     bool& hasAnimation)
216 {
217     CHECK_NULL_VOID(subMenu);
218     auto subMenuNode = DynamicCast<FrameNode>(subMenu);
219     CHECK_NULL_VOID(subMenuNode);
220     auto subMenuPattern = subMenuNode->GetPattern<MenuPattern>();
221     CHECK_NULL_VOID(subMenuPattern);
222     hasAnimation = subMenuPattern->GetDisappearAnimation();
223     auto menuItem = FrameNode::GetFrameNode(subMenuPattern->GetTargetTag(), subMenuPattern->GetTargetId());
224     CHECK_NULL_VOID(menuItem);
225     auto menuItemPattern = menuItem->GetPattern<MenuItemPattern>();
226     CHECK_NULL_VOID(menuItemPattern);
227     auto menuNode = menuItemPattern->GetMenu();
228     CHECK_NULL_VOID(menuNode);
229     auto menuProperty = menuNode->GetLayoutProperty<MenuLayoutProperty>();
230     CHECK_NULL_VOID(menuProperty);
231     expandingMode = menuProperty->GetExpandingMode().value_or(SubMenuExpandingMode::SIDE);
232     menuItemPattern->SetIsSubMenuShowed (false);
233 }
234 
HideSubMenu()235 void MenuWrapperPattern::HideSubMenu()
236 {
237     auto host = GetHost();
238     CHECK_NULL_VOID(host);
239     if (host->GetChildren().size() <= 1) {
240         // sub menu not show
241         return;
242     }
243     auto menu = GetMenu();
244     CHECK_NULL_VOID(menu);
245     auto menuPattern = menu->GetPattern<MenuPattern>();
246     CHECK_NULL_VOID(menuPattern);
247     auto subMenu = host->GetChildren().back();
248     auto focusMenu = MenuFocusViewShow();
249     CHECK_NULL_VOID(focusMenu);
250     menuPattern->SetShowedSubMenu(nullptr);
251     auto innerMenu = GetMenuChild(focusMenu);
252     if (!innerMenu) {
253         UpdateMenuAnimation(host);
254         SendToAccessibility(subMenu, false);
255         host->RemoveChild(subMenu);
256         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
257         return;
258     }
259     auto innerMenuPattern = innerMenu->GetPattern<MenuPattern>();
260     CHECK_NULL_VOID(innerMenuPattern);
261     auto layoutProps = innerMenuPattern->GetLayoutProperty<MenuLayoutProperty>();
262     CHECK_NULL_VOID(layoutProps);
263     auto expandingMode = layoutProps->GetExpandingMode().value_or(SubMenuExpandingMode::SIDE);
264     auto outterMenuPattern = focusMenu->GetPattern<MenuPattern>();
265     CHECK_NULL_VOID(outterMenuPattern);
266     bool hasAnimation = outterMenuPattern->GetDisappearAnimation();
267     GetExpandingMode(subMenu, expandingMode, hasAnimation);
268     if (expandingMode == SubMenuExpandingMode::STACK && hasAnimation) {
269         HideStackExpandMenu(subMenu);
270     } else {
271         UpdateMenuAnimation(host);
272         SendToAccessibility(subMenu, false);
273         host->RemoveChild(subMenu);
274         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
275     }
276 }
277 
SendToAccessibility(const RefPtr<UINode> & subMenu,bool isShow)278 void MenuWrapperPattern::SendToAccessibility(const RefPtr<UINode>& subMenu, bool isShow)
279 {
280     auto subMenuNode = AceType::DynamicCast<FrameNode>(subMenu);
281     CHECK_NULL_VOID(subMenuNode);
282     auto accessibilityProperty = subMenuNode->GetAccessibilityProperty<MenuAccessibilityProperty>();
283     CHECK_NULL_VOID(accessibilityProperty);
284     accessibilityProperty->SetAccessibilityIsShow(isShow);
285     subMenuNode->OnAccessibilityEvent(AccessibilityEventType::PAGE_CLOSE);
286 }
287 
HasStackSubMenu()288 bool MenuWrapperPattern::HasStackSubMenu()
289 {
290     auto outterMenu = GetMenu();
291     CHECK_NULL_RETURN(outterMenu, false);
292     auto innerMenu = GetMenuChild(outterMenu);
293     CHECK_NULL_RETURN(innerMenu, false);
294     auto innerMenuPattern = innerMenu->GetPattern<MenuPattern>();
295     CHECK_NULL_RETURN(innerMenuPattern, false);
296     auto layoutProps = innerMenuPattern->GetLayoutProperty<MenuLayoutProperty>();
297     CHECK_NULL_RETURN(layoutProps, false);
298     auto expandingMode = layoutProps->GetExpandingMode().value_or(SubMenuExpandingMode::SIDE);
299     if (expandingMode != SubMenuExpandingMode::STACK) {
300         return false;
301     }
302     auto host = GetHost();
303     CHECK_NULL_RETURN(host, false);
304     return host->GetChildren().size() > 1;
305 }
306 
HasEmbeddedSubMenu()307 bool MenuWrapperPattern::HasEmbeddedSubMenu()
308 {
309     auto outterMenu = GetMenu();
310     CHECK_NULL_RETURN(outterMenu, false);
311     auto innerMenu = GetMenuChild(outterMenu);
312     CHECK_NULL_RETURN(innerMenu, false);
313     auto innerMenuPattern = innerMenu->GetPattern<MenuPattern>();
314     CHECK_NULL_RETURN(innerMenuPattern, false);
315     auto layoutProps = innerMenuPattern->GetLayoutProperty<MenuLayoutProperty>();
316     CHECK_NULL_RETURN(layoutProps, false);
317     auto expandingMode = layoutProps->GetExpandingMode().value_or(SubMenuExpandingMode::SIDE);
318     return expandingMode == SubMenuExpandingMode::EMBEDDED;
319 }
320 
MenuFocusViewShow()321 RefPtr<FrameNode> MenuWrapperPattern::MenuFocusViewShow()
322 {
323     auto host = GetHost();
324     CHECK_NULL_RETURN(host, nullptr);
325     auto iter = host->GetChildren().begin();
326     int32_t focusNodeId = static_cast<int32_t>(host->GetChildren().size()) - FOCUS_MENU_NUM;
327     CHECK_NULL_RETURN(focusNodeId >= 0, nullptr);
328     std::advance(iter, focusNodeId);
329     auto focusMenu = DynamicCast<FrameNode>(*iter);
330     CHECK_NULL_RETURN(focusMenu, nullptr);
331     if (GetPreviewMode() != MenuPreviewMode::NONE && focusNodeId == 1) {
332         focusMenu = DynamicCast<FrameNode>(host->GetChildAtIndex(0));
333         CHECK_NULL_RETURN(focusMenu, nullptr);
334     }
335     // SelectOverlay's custom menu does not need to be focused.
336     auto isCustomMenu = IsSelectOverlayCustomMenu(focusMenu);
337     if (!isCustomMenu) {
338         auto menuPattern = focusMenu->GetPattern<MenuPattern>();
339         CHECK_NULL_RETURN(menuPattern, nullptr);
340         menuPattern->FocusViewShow();
341     }
342     return DynamicCast<FrameNode>(focusMenu);
343 }
344 
HideStackExpandMenu(const RefPtr<UINode> & subMenu)345 void MenuWrapperPattern::HideStackExpandMenu(const RefPtr<UINode>& subMenu)
346 {
347     auto host = GetHost();
348     CHECK_NULL_VOID(host);
349     auto menuNode = host->GetFirstChild();
350     CHECK_NULL_VOID(menuNode);
351     AnimationOption option;
352     option.SetOnFinishEvent(
353         [weak = WeakClaim(RawPtr(host)), subMenuWk = WeakClaim(RawPtr(subMenu))] {
354             auto pipeline = PipelineBase::GetCurrentContext();
355             CHECK_NULL_VOID(pipeline);
356             auto taskExecutor = pipeline->GetTaskExecutor();
357             CHECK_NULL_VOID(taskExecutor);
358             taskExecutor->PostTask(
359                 [weak, subMenuWk]() {
360                     auto subMenuNode = subMenuWk.Upgrade();
361                     CHECK_NULL_VOID(subMenuNode);
362                     auto menuWrapper = weak.Upgrade();
363                     CHECK_NULL_VOID(menuWrapper);
364                     menuWrapper->RemoveChild(subMenuNode);
365                     menuWrapper->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
366                 },
367                 TaskExecutor::TaskType::UI, "HideStackExpandMenu");
368     });
369     auto menuNodePattern = DynamicCast<FrameNode>(menuNode)->GetPattern<MenuPattern>();
370     CHECK_NULL_VOID(menuNodePattern);
371     menuNodePattern->ShowStackExpandDisappearAnimation(DynamicCast<FrameNode>(menuNode),
372         DynamicCast<FrameNode>(subMenu), option);
373     menuNodePattern->SetDisappearAnimation(true);
374 }
375 
RegisterOnTouch()376 void MenuWrapperPattern::RegisterOnTouch()
377 {
378     // if already initialized touch event
379     CHECK_NULL_VOID(!onTouch_);
380     auto host = GetHost();
381     CHECK_NULL_VOID(host);
382     auto gesture = host->GetOrCreateGestureEventHub();
383     CHECK_NULL_VOID(gesture);
384     // hide menu when touched outside the menu region
385     auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
386         auto pattern = weak.Upgrade();
387         CHECK_NULL_VOID(pattern);
388         pattern->OnTouchEvent(info);
389     };
390     onTouch_ = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
391     gesture->AddTouchEvent(onTouch_);
392 }
393 
OnTouchEvent(const TouchEventInfo & info)394 void MenuWrapperPattern::OnTouchEvent(const TouchEventInfo& info)
395 {
396     CHECK_NULL_VOID(!info.GetChangedTouches().empty());
397     auto touch = info.GetChangedTouches().front();
398     // filter out other touch types
399     if (touch.GetTouchType() != TouchType::DOWN &&
400         Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
401         return;
402     }
403     if (IsHide()) {
404         return;
405     }
406     auto host = GetHost();
407     CHECK_NULL_VOID(host);
408 
409     auto position = PointF(
410         static_cast<float>(touch.GetGlobalLocation().GetX()), static_cast<float>(touch.GetGlobalLocation().GetY()));
411     auto children = host->GetChildren();
412     if (touch.GetTouchType() == TouchType::DOWN) {
413         // Record the latest touch finger ID. If other fingers are pressed, the latest one prevails
414         if (fingerId_ != -1) {
415             ClearLastMenuItem();
416         }
417         fingerId_ = touch.GetFingerId();
418         TAG_LOGD(AceLogTag::ACE_MENU, "record newest finger ID %{public}d", fingerId_);
419         for (auto child = children.rbegin(); child != children.rend(); ++child) {
420             // get child frame node of menu wrapper
421             auto menuWrapperChildNode = DynamicCast<FrameNode>(*child);
422             CHECK_NULL_VOID(menuWrapperChildNode);
423             // get menuWrapperChildNode's touch region
424             if (CheckPointInMenuZone(menuWrapperChildNode, position)) {
425                 currentTouchItem_ = FindTouchedMenuItem(menuWrapperChildNode, position);
426                 ChangeCurMenuItemBgColor();
427                 return;
428             }
429             // if DOWN-touched outside the menu region, then hide menu
430             auto menuPattern = menuWrapperChildNode->GetPattern<MenuPattern>();
431             if (!menuPattern) {
432                 continue;
433             }
434             HideMenu(menuPattern, menuWrapperChildNode, position);
435         }
436         return;
437     }
438     // When the Move or Up event is not the recorded finger ID, this event is not responded
439     if (fingerId_ != touch.GetFingerId()) {
440         return;
441     }
442     ChangeTouchItem(info, touch.GetTouchType());
443 }
444 
ChangeTouchItem(const TouchEventInfo & info,TouchType touchType)445 void MenuWrapperPattern::ChangeTouchItem(const TouchEventInfo& info, TouchType touchType)
446 {
447     auto host = GetHost();
448     CHECK_NULL_VOID(host);
449     if (touchType == TouchType::MOVE) {
450         auto menuNode = DynamicCast<FrameNode>(host->GetChildAtIndex(0));
451         CHECK_NULL_VOID(menuNode);
452         if (GetPreviewMode() != MenuPreviewMode::NONE || IsSelectOverlayCustomMenu(menuNode)) {
453             return;
454         }
455         HandleInteraction(info);
456     } else if (touchType == TouchType::UP || touchType == TouchType::CANCEL) {
457         if (currentTouchItem_) {
458             auto currentTouchItemPattern = currentTouchItem_->GetPattern<MenuItemPattern>();
459             CHECK_NULL_VOID(currentTouchItemPattern);
460             currentTouchItemPattern->NotifyPressStatus(false);
461             currentTouchItem_ = nullptr;
462         }
463         // Reset finger ID when touch Up or Cancel
464         TAG_LOGD(AceLogTag::ACE_MENU, "reset finger ID %{public}d", fingerId_);
465         fingerId_ = -1;
466     }
467 }
468 
HideMenu(const RefPtr<MenuPattern> & menuPattern,const RefPtr<FrameNode> & menu,const PointF & position)469 void MenuWrapperPattern::HideMenu(const RefPtr<MenuPattern>& menuPattern, const RefPtr<FrameNode>& menu,
470     const PointF& position)
471 {
472     auto host = GetHost();
473     CHECK_NULL_VOID(host);
474     auto mainMenu = DynamicCast<FrameNode>(host->GetFirstChild());
475     CHECK_NULL_VOID(mainMenu);
476     bool isFindTargetId = false;
477     if (CheckPointInMenuZone(mainMenu, position)) {
478         isFindTargetId = true;
479     }
480     if (menuPattern->IsSubMenu() || menuPattern->IsSelectOverlaySubMenu()) {
481         if (HasStackSubMenu() && !isFindTargetId) {
482             UpdateMenuAnimation(host);
483         }
484         HideSubMenu();
485     } else {
486         if (HasEmbeddedSubMenu() && embeddedSubMenuCount_ > 0 && !isFindTargetId) {
487             UpdateMenuAnimation(host);
488         }
489         HideMenu(menu);
490     }
491 }
492 
UpdateMenuAnimation(const RefPtr<FrameNode> & host)493 void MenuWrapperPattern::UpdateMenuAnimation(const RefPtr<FrameNode>& host)
494 {
495     // update Menu disappear animation direction
496     // change to LEFT_BOTTOM -> RIGHT_TOP by calling SetExitAnimation
497     // or keep BOTTOM -> TOP by default
498     CHECK_NULL_VOID(host);
499     auto outterMenu = host->GetFirstChild();
500     CHECK_NULL_VOID(outterMenu);
501     auto innerMenu = GetMenuChild(outterMenu);
502     if (!innerMenu && host->GetChildren().size() > 1) {
503         SetExitAnimation(host);
504         return;
505     }
506     CHECK_NULL_VOID(innerMenu);
507     auto innerMenuPattern = innerMenu->GetPattern<MenuPattern>();
508     CHECK_NULL_VOID(innerMenuPattern);
509     auto layoutProps = innerMenuPattern->GetLayoutProperty<MenuLayoutProperty>();
510     CHECK_NULL_VOID(layoutProps);
511     auto expandingMode = layoutProps->GetExpandingMode().value_or(SubMenuExpandingMode::SIDE);
512     if (host->GetChildren().size() > 1) {
513         SetExitAnimation(host);
514     }
515     if (expandingMode == SubMenuExpandingMode::EMBEDDED && embeddedSubMenuCount_ > 0) {
516         SetExitAnimation(host);
517     }
518 }
519 
SetExitAnimation(const RefPtr<FrameNode> & host)520 void MenuWrapperPattern::SetExitAnimation(const RefPtr<FrameNode>& host)
521 {
522     CHECK_NULL_VOID(host);
523     auto outterMenu = AceType::DynamicCast<FrameNode>(host->GetFirstChild());
524     CHECK_NULL_VOID(outterMenu);
525     auto outterMenuPattern = outterMenu->GetPattern<MenuPattern>();
526     CHECK_NULL_VOID(outterMenuPattern);
527     outterMenuPattern->SetDisappearAnimation(false);
528 }
529 
CheckAndShowAnimation()530 void MenuWrapperPattern::CheckAndShowAnimation()
531 {
532     if (isFirstShow_) {
533         // only start animation when menu wrapper mount on.
534         StartShowAnimation();
535         isFirstShow_ = false;
536     }
537 }
538 
MarkWholeSubTreeNoDraggable(const RefPtr<FrameNode> & frameNode)539 void MenuWrapperPattern::MarkWholeSubTreeNoDraggable(const RefPtr<FrameNode>& frameNode)
540 {
541     CHECK_NULL_VOID(frameNode);
542     auto eventHub = frameNode->GetEventHub<EventHub>();
543     CHECK_NULL_VOID(eventHub);
544     auto gestureEventHub = eventHub->GetGestureEventHub();
545     CHECK_NULL_VOID(gestureEventHub);
546     gestureEventHub->SetDragForbiddenForcely(true);
547 }
548 
MarkAllMenuNoDraggable()549 void MenuWrapperPattern::MarkAllMenuNoDraggable()
550 {
551     auto host = GetHost();
552     CHECK_NULL_VOID(host);
553     for (const auto& child : host->GetChildren()) {
554         auto node = DynamicCast<FrameNode>(child);
555         if (node && node->GetTag() == V2::MENU_ETS_TAG) {
556             MarkWholeSubTreeNoDraggable(node);
557         }
558     }
559 }
560 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)561 bool MenuWrapperPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
562 {
563     auto pipeline = PipelineBase::GetCurrentContext();
564     CHECK_NULL_RETURN(pipeline, false);
565     auto theme = pipeline->GetTheme<SelectTheme>();
566     CHECK_NULL_RETURN(theme, false);
567     auto expandDisplay = theme->GetExpandDisplay();
568     auto host = GetHost();
569     CHECK_NULL_RETURN(host, false);
570     auto menuNode = DynamicCast<FrameNode>(host->GetChildAtIndex(0));
571     CHECK_NULL_RETURN(menuNode, false);
572     auto menuPattern = AceType::DynamicCast<MenuPattern>(menuNode->GetPattern());
573     CHECK_NULL_RETURN(menuPattern, false);
574     // copy menu pattern properties to rootMenu
575     auto layoutProperty = menuPattern->GetLayoutProperty<MenuLayoutProperty>();
576     CHECK_NULL_RETURN(layoutProperty, false);
577     isShowInSubWindow_ = layoutProperty->GetShowInSubWindowValue(true);
578     if ((IsContextMenu() && !IsHide()) || ((expandDisplay && isShowInSubWindow_) && !IsHide())) {
579         SetHotAreas(dirty);
580     }
581     MarkAllMenuNoDraggable();
582     MarkWholeSubTreeNoDraggable(GetPreview());
583     CheckAndShowAnimation();
584     return false;
585 }
586 
SetHotAreas(const RefPtr<LayoutWrapper> & layoutWrapper)587 void MenuWrapperPattern::SetHotAreas(const RefPtr<LayoutWrapper>& layoutWrapper)
588 {
589     auto pipeline = PipelineBase::GetCurrentContext();
590     CHECK_NULL_VOID(pipeline);
591     auto theme = pipeline->GetTheme<SelectTheme>();
592     CHECK_NULL_VOID(theme);
593     auto expandDisplay = theme->GetExpandDisplay();
594     if ((layoutWrapper->GetAllChildrenWithBuild().empty() || !IsContextMenu()) &&
595         !(expandDisplay && isShowInSubWindow_)) {
596         return;
597     }
598     auto layoutProps = layoutWrapper->GetLayoutProperty();
599     CHECK_NULL_VOID(layoutProps);
600     float safeAreaInsetsLeft = 0.0f;
601     float safeAreaInsetsTop = 0.0f;
602     auto&& safeAreaInsets = layoutProps->GetSafeAreaInsets();
603     if (safeAreaInsets) {
604         safeAreaInsetsLeft = static_cast<float>(safeAreaInsets->left_.end);
605         safeAreaInsetsTop = static_cast<float>(safeAreaInsets->top_.end);
606     }
607     std::vector<Rect> rects;
608     for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
609         auto frameRect = child->GetGeometryNode()->GetFrameRect();
610         // rect is relative to window
611         auto rect = Rect(frameRect.GetX() + safeAreaInsetsLeft, frameRect.GetY() + safeAreaInsetsTop, frameRect.Width(),
612             frameRect.Height());
613 
614         rects.emplace_back(rect);
615     }
616     // If container is UIExtensionWindow, set hotArea size equals to subwindow's filterColumnNode's size
617     if (IsContextMenu() && GetPreviewMode() != MenuPreviewMode::NONE) {
618         auto filterNode = GetFilterColumnNode();
619         if (filterNode) {
620             auto frameRect = filterNode->GetGeometryNode()->GetFrameRect();
621             auto rect = Rect(frameRect.GetX(), frameRect.GetY(), frameRect.Width(), frameRect.Height());
622             rects.emplace_back(rect);
623         }
624     }
625     SubwindowManager::GetInstance()->SetHotAreas(rects, GetHost()->GetId(), GetContainerId());
626 }
627 
StartShowAnimation()628 void MenuWrapperPattern::StartShowAnimation()
629 {
630     auto host = GetHost();
631     CHECK_NULL_VOID(host);
632     auto context = host->GetRenderContext();
633     CHECK_NULL_VOID(context);
634     if (GetPreviewMode() == MenuPreviewMode::NONE) {
635         context->UpdateOffset(GetAnimationOffset());
636         context->UpdateOpacity(0.0);
637     }
638 
639     AnimationUtils::Animate(
640         animationOption_,
641         [context, weak = WeakClaim(this)]() {
642             if (context) {
643                 auto pattern = weak.Upgrade();
644                 CHECK_NULL_VOID(pattern);
645                 if (pattern->GetPreviewMode() == MenuPreviewMode::NONE) {
646                     context->UpdateOffset(OffsetT<Dimension>());
647                     context->UpdateOpacity(1.0);
648                 }
649             }
650         },
651         animationOption_.GetOnFinishEvent());
652 }
653 
SetAniamtinOption(const AnimationOption & animationOption)654 void MenuWrapperPattern::SetAniamtinOption(const AnimationOption& animationOption)
655 {
656     animationOption_.SetCurve(animationOption.GetCurve());
657     animationOption_.SetDuration(animationOption.GetDuration());
658     animationOption_.SetFillMode(animationOption.GetFillMode());
659     animationOption_.SetOnFinishEvent(animationOption.GetOnFinishEvent());
660 }
661 
GetAnimationOffset()662 OffsetT<Dimension> MenuWrapperPattern::GetAnimationOffset()
663 {
664     OffsetT<Dimension> offset;
665 
666     auto pipeline = PipelineBase::GetCurrentContext();
667     CHECK_NULL_RETURN(pipeline, offset);
668     auto theme = pipeline->GetTheme<SelectTheme>();
669     CHECK_NULL_RETURN(theme, offset);
670     auto animationOffset = theme->GetMenuAnimationOffset();
671 
672     switch (menuPlacement_) {
673         case Placement::LEFT:
674         case Placement::LEFT_TOP:
675         case Placement::LEFT_BOTTOM:
676             offset.SetX(animationOffset);
677             break;
678         case Placement::RIGHT:
679         case Placement::RIGHT_TOP:
680         case Placement::RIGHT_BOTTOM:
681             offset.SetX(-animationOffset);
682             break;
683         case Placement::TOP:
684         case Placement::TOP_LEFT:
685         case Placement::TOP_RIGHT:
686             offset.SetY(animationOffset);
687             break;
688         default:
689             offset.SetY(-animationOffset);
690             break;
691     }
692     return offset;
693 }
694 
IsSelectOverlayCustomMenu(const RefPtr<FrameNode> & menu) const695 bool MenuWrapperPattern::IsSelectOverlayCustomMenu(const RefPtr<FrameNode>& menu) const
696 {
697     auto menuPattern = menu->GetPattern<MenuPattern>();
698     CHECK_NULL_RETURN(menuPattern, false);
699     return menuPattern->IsSelectOverlayCustomMenu();
700 }
701 
RegisterMenuCallback(const RefPtr<FrameNode> & menuWrapperNode,const MenuParam & menuParam)702 void MenuWrapperPattern::RegisterMenuCallback(const RefPtr<FrameNode>& menuWrapperNode, const MenuParam& menuParam)
703 {
704     TAG_LOGD(AceLogTag::ACE_DIALOG, "register menu enter");
705     CHECK_NULL_VOID(menuWrapperNode);
706     auto pattern = menuWrapperNode->GetPattern<MenuWrapperPattern>();
707     CHECK_NULL_VOID(pattern);
708     pattern->RegisterMenuAppearCallback(menuParam.onAppear);
709     pattern->RegisterMenuDisappearCallback(menuParam.onDisappear);
710     pattern->RegisterMenuAboutToAppearCallback(menuParam.aboutToAppear);
711     pattern->RegisterMenuAboutToDisappearCallback(menuParam.aboutToDisappear);
712     pattern->RegisterMenuStateChangeCallback(menuParam.onStateChange);
713 }
714 
SetMenuTransitionEffect(const RefPtr<FrameNode> & menuWrapperNode,const MenuParam & menuParam)715 void MenuWrapperPattern::SetMenuTransitionEffect(const RefPtr<FrameNode>& menuWrapperNode, const MenuParam& menuParam)
716 {
717     TAG_LOGD(AceLogTag::ACE_DIALOG, "set menu transition effect");
718     CHECK_NULL_VOID(menuWrapperNode);
719     auto pattern = menuWrapperNode->GetPattern<MenuWrapperPattern>();
720     CHECK_NULL_VOID(pattern);
721     pattern->SetHasTransitionEffect(menuParam.hasTransitionEffect);
722     if (menuParam.hasTransitionEffect) {
723         auto renderContext = menuWrapperNode->GetRenderContext();
724         CHECK_NULL_VOID(renderContext);
725         CHECK_NULL_VOID(menuParam.transition);
726         renderContext->UpdateChainedTransition(menuParam.transition);
727     }
728     pattern->SetHasPreviewTransitionEffect(menuParam.hasPreviewTransitionEffect);
729     if (menuParam.hasPreviewTransitionEffect) {
730         auto previewChild = pattern->GetPreview();
731         CHECK_NULL_VOID(previewChild);
732         auto renderContext = previewChild->GetRenderContext();
733         CHECK_NULL_VOID(renderContext);
734         CHECK_NULL_VOID(menuParam.previewTransition);
735         renderContext->UpdateChainedTransition(menuParam.previewTransition);
736     }
737 }
738 
GetMenuChild(const RefPtr<UINode> & node)739 RefPtr<FrameNode> MenuWrapperPattern::GetMenuChild(const RefPtr<UINode>& node)
740 {
741     CHECK_NULL_RETURN(node, nullptr);
742     RefPtr<FrameNode> menuChild;
743     auto child = node->GetChildAtIndex(0);
744     while (child) {
745         if (child->GetTag() == V2::JS_VIEW_ETS_TAG) {
746             auto customNode = DynamicCast<CustomNode>(child);
747             CHECK_NULL_RETURN(customNode, nullptr);
748             customNode->Render();
749         } else if (child->GetTag() == V2::MENU_ETS_TAG) {
750             menuChild = DynamicCast<FrameNode>(child);
751             break;
752         }
753         child = child->GetChildAtIndex(0);
754     }
755     return menuChild;
756 }
757 
ClearAllSubMenu()758 void MenuWrapperPattern::ClearAllSubMenu()
759 {
760     auto host = GetHost();
761     CHECK_NULL_VOID(host);
762     auto children = host->GetChildren();
763     for (auto child = children.rbegin(); child != children.rend(); ++child) {
764         auto frameNode = DynamicCast<FrameNode>(*child);
765         if (!frameNode) {
766             continue;
767         }
768         auto pattern = frameNode->GetPattern<MenuPattern>();
769         if (pattern && pattern->IsSubMenu()) {
770             host->RemoveChild(frameNode);
771             host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
772         }
773     }
774 }
775 
StopHoverImageToPreviewAnimation()776 void MenuWrapperPattern::StopHoverImageToPreviewAnimation()
777 {
778     auto menuWrapperNode = GetHost();
779     CHECK_NULL_VOID(menuWrapperNode);
780     auto menuWrapperPattern = menuWrapperNode->GetPattern<MenuWrapperPattern>();
781     CHECK_NULL_VOID(menuWrapperPattern);
782 
783     auto flexNode = menuWrapperPattern->GetHoverImageFlexNode();
784     CHECK_NULL_VOID(flexNode);
785     auto flexContext = flexNode->GetRenderContext();
786     CHECK_NULL_VOID(flexContext);
787 
788     auto stackNode = menuWrapperPattern->GetHoverImageStackNode();
789     CHECK_NULL_VOID(stackNode);
790     auto stackContext = stackNode->GetRenderContext();
791     CHECK_NULL_VOID(stackContext);
792 
793     auto menuChild = menuWrapperPattern->GetMenu();
794     CHECK_NULL_VOID(menuChild);
795     auto menuPattern = menuChild->GetPattern<MenuPattern>();
796     CHECK_NULL_VOID(menuPattern);
797     auto originPosition = menuPattern->GetPreviewOriginOffset();
798 
799     auto geometryNode = flexNode->GetGeometryNode();
800     CHECK_NULL_VOID(geometryNode);
801     auto position = geometryNode->GetFrameOffset();
802 
803     auto flexPosition = originPosition;
804     if (Positive(hoverImageToPreviewRate_)) {
805         flexPosition += (position - originPosition) * hoverImageToPreviewRate_;
806     }
807 
808     AnimationUtils::Animate(AnimationOption(Curves::LINEAR, 0),
809         [stackContext, flexContext, flexPosition, scale = hoverImageToPreviewScale_]() {
810             if (flexContext) {
811                 flexContext->UpdatePosition(
812                     OffsetT<Dimension>(Dimension(flexPosition.GetX()), Dimension(flexPosition.GetY())));
813             }
814 
815             CHECK_NULL_VOID(stackContext && Positive(scale));
816             stackContext->UpdateTransformScale(VectorF(scale, scale));
817         });
818 }
819 
DumpInfo()820 void MenuWrapperPattern::DumpInfo()
821 {
822     DumpLog::GetInstance().AddDesc("MenuPreviewMode: " + std::to_string(dumpInfo_.menuPreviewMode));
823     DumpLog::GetInstance().AddDesc("MenuType: " + std::to_string(dumpInfo_.menuType));
824     DumpLog::GetInstance().AddDesc("EnableArrow: " + std::to_string(dumpInfo_.enableArrow));
825     DumpLog::GetInstance().AddDesc("Offset: " + dumpInfo_.offset.ToString());
826     DumpLog::GetInstance().AddDesc("TargetNode: " + dumpInfo_.targetNode);
827     DumpLog::GetInstance().AddDesc("TargetOffset: " + dumpInfo_.targetOffset.ToString());
828     DumpLog::GetInstance().AddDesc("TargetSize: " + dumpInfo_.targetSize.ToString());
829     DumpLog::GetInstance().AddDesc("MenuWindowRect: " + dumpInfo_.menuWindowRect.ToString());
830     DumpLog::GetInstance().AddDesc("WrapperRect: " + dumpInfo_.wrapperRect.ToString());
831     DumpLog::GetInstance().AddDesc("PreviewBeginScale: " + std::to_string(dumpInfo_.previewBeginScale));
832     DumpLog::GetInstance().AddDesc("PreviewEndScale: " + std::to_string(dumpInfo_.previewEndScale));
833     DumpLog::GetInstance().AddDesc("Top: " + std::to_string(dumpInfo_.top));
834     DumpLog::GetInstance().AddDesc("Bottom: " + std::to_string(dumpInfo_.bottom));
835     DumpLog::GetInstance().AddDesc("GlobalLocation: " + dumpInfo_.globalLocation.ToString());
836     DumpLog::GetInstance().AddDesc("OriginPlacement: " + dumpInfo_.originPlacement);
837     DumpLog::GetInstance().AddDesc("DefaultPlacement: " + dumpInfo_.defaultPlacement);
838     DumpLog::GetInstance().AddDesc("FinalPosition: " + dumpInfo_.finalPosition.ToString());
839     DumpLog::GetInstance().AddDesc("FinalPlacement: " + dumpInfo_.finalPlacement);
840 }
841 
CheckPointInMenuZone(const RefPtr<FrameNode> & node,const PointF & point)842 bool MenuWrapperPattern::CheckPointInMenuZone(const RefPtr<FrameNode>& node, const PointF& point)
843 {
844     CHECK_NULL_RETURN(node, false);
845     auto childOffset = node->GetPaintRectOffset(false, true);
846     auto childSize = node->GetPaintRectWithTransform();
847     auto menuZone = RectF(childOffset.GetX(), childOffset.GetY(), childSize.Width(), childSize.Height());
848     return menuZone.IsInRegion(point);
849 }
850 } // namespace OHOS::Ace::NG
851