• 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/subwindow/subwindow_manager.h"
20 #include "core/common/ace_engine.h"
21 #include "core/components_ng/pattern/menu/preview/menu_preview_pattern.h"
22 
23 namespace OHOS::Ace::NG {
24 namespace {
ConvertModalModeToString(std::optional<ModalMode> modalModeOpt)25 std::string ConvertModalModeToString(std::optional<ModalMode> modalModeOpt)
26 {
27     if (!modalModeOpt.has_value()) {
28         return "undefined";
29     }
30     auto modalMode = modalModeOpt.value();
31     switch (modalMode) {
32         case ModalMode::AUTO:
33             return "ModalMode.AUTO";
34         case ModalMode::NONE:
35             return "ModalMode.NONE";
36         case ModalMode::TARGET_WINDOW:
37             return "ModalMode.TARGET_WINDOW";
38         default:
39             return "ModalMode.AUTO";
40     }
41 }
42 } // namespace
HideMenu(const RefPtr<FrameNode> & menu,const HideMenuType & reason)43 void MenuWrapperPattern::HideMenu(const RefPtr<FrameNode>& menu, const HideMenuType& reason)
44 {
45     CHECK_NULL_VOID(menu);
46     auto host = GetHost();
47     CHECK_NULL_VOID(host);
48     if (host->GetTag() == V2::SELECT_OVERLAY_ETS_TAG) {
49         return;
50     }
51     SetIsStopHoverImageAnimation(true);
52     auto menuPattern = menu->GetPattern<MenuPattern>();
53     CHECK_NULL_VOID(menuPattern);
54     menuPattern->HideMenu(reason);
55 }
56 
OnModifyDone()57 void MenuWrapperPattern::OnModifyDone()
58 {
59     InitFocusEvent();
60 }
61 
InitFocusEvent()62 void MenuWrapperPattern::InitFocusEvent()
63 {
64     auto host = GetHost();
65     CHECK_NULL_VOID(host);
66     auto focusHub = host->GetOrCreateFocusHub();
67     CHECK_NULL_VOID(focusHub);
68     auto blurTask = [weak = WeakClaim(this)]() {
69         auto pattern = weak.Upgrade();
70         CHECK_NULL_VOID(pattern);
71         TAG_LOGI(AceLogTag::ACE_MENU, "will hide menu due to lost focus");
72         pattern->HideMenu(HideMenuType::WRAPPER_LOSE_FOCUS);
73     };
74     focusHub->SetOnBlurInternal(std::move(blurTask));
75 }
76 
GetShowedSubMenu()77 RefPtr<FrameNode> MenuWrapperPattern::GetShowedSubMenu()
78 {
79     auto host = GetHost();
80     CHECK_NULL_RETURN(host, nullptr);
81     return DynamicCast<FrameNode>(host->GetLastChild());
82 }
83 
GetInnerMenu(RefPtr<UINode> & innerMenuNode,const PointF & position)84 bool MenuWrapperPattern::GetInnerMenu(RefPtr<UINode>& innerMenuNode, const PointF& position)
85 {
86     auto host = GetHost();
87     CHECK_NULL_RETURN(host, false);
88     auto children = host->GetChildren();
89     CHECK_NULL_RETURN(!children.empty(), false);
90     for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
91         auto& child = *iter;
92         if (!child || child->GetTag() != V2::MENU_ETS_TAG) {
93             continue;
94         }
95         auto frameNode = AceType::DynamicCast<FrameNode>(child);
96         CHECK_NULL_RETURN(frameNode, false);
97         auto menuPattern = frameNode->GetPattern<MenuPattern>();
98         CHECK_NULL_RETURN(menuPattern, false);
99         auto subMenuNode = menuPattern->GetShowedSubMenu();
100         innerMenuNode = subMenuNode ? subMenuNode : menuPattern->GetFirstInnerMenu();
101         if (!innerMenuNode) {
102             innerMenuNode = child;
103         }
104         auto geometryNode = frameNode->GetGeometryNode();
105         CHECK_NULL_RETURN(geometryNode, false);
106         return CheckPointInMenuZone(frameNode, position);
107     }
108     return false;
109 }
110 
FindTouchedMenuItem(const RefPtr<UINode> & menuNode,const PointF & position)111 RefPtr<FrameNode> MenuWrapperPattern::FindTouchedMenuItem(const RefPtr<UINode>& menuNode, const PointF& position)
112 {
113     CHECK_NULL_RETURN(menuNode, nullptr);
114     RefPtr<FrameNode> menuItem = nullptr;
115     const auto& children = menuNode->GetChildren();
116     for (auto child : children) {
117         if (!child) {
118             continue;
119         }
120         if (child->GetTag() == V2::MENU_ITEM_ETS_TAG) {
121             auto frameNode = AceType::DynamicCast<FrameNode>(child);
122             auto pattern = frameNode ? frameNode->GetPattern<MenuItemPattern>() : nullptr;
123             menuItem = pattern ? pattern->FindTouchedEmbeddedMenuItem(position) : nullptr;
124         } else {
125             menuItem = FindTouchedMenuItem(child, position);
126         }
127         if (menuItem) {
128             if (CheckPointInMenuZone(menuItem, position)) {
129                 break;
130             } else {
131                 menuItem = nullptr;
132             }
133         }
134     }
135     return menuItem;
136 }
137 
HandleInteraction(const TouchEventInfo & info)138 void MenuWrapperPattern::HandleInteraction(const TouchEventInfo& info)
139 {
140     CHECK_NULL_VOID(!info.GetChangedTouches().empty());
141     auto touch = info.GetChangedTouches().front();
142     auto host = GetHost();
143     CHECK_NULL_VOID(host);
144     auto position = PointF(
145         static_cast<float>(touch.GetGlobalLocation().GetX()), static_cast<float>(touch.GetGlobalLocation().GetY()));
146     RefPtr<UINode> innerMenuNode = nullptr;
147     auto isInRegion = GetInnerMenu(innerMenuNode, position);
148     CHECK_NULL_VOID(innerMenuNode);
149 
150     if (isClearLastMenuItem_ || isInRegion) {
151         ClearLastMenuItem();
152     }
153     // get menuNode's touch region
154     if (isInRegion) {
155         isClearLastMenuItem_ = true;
156         currentTouchItem_ = FindTouchedMenuItem(innerMenuNode, position);
157         ChangeCurMenuItemBgColor();
158         lastTouchItem_ = currentTouchItem_;
159     }
160     innerMenuNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
161 }
162 
ChangeCurMenuItemBgColor()163 void MenuWrapperPattern::ChangeCurMenuItemBgColor()
164 {
165     if (!currentTouchItem_) {
166         return;
167     }
168     auto host = GetHost();
169     CHECK_NULL_VOID(host);
170     auto pipeline = host->GetContext();
171     CHECK_NULL_VOID(pipeline);
172     auto theme = pipeline->GetTheme<SelectTheme>();
173     CHECK_NULL_VOID(theme);
174     auto curMenuItemPattern = currentTouchItem_->GetPattern<MenuItemPattern>();
175     CHECK_NULL_VOID(curMenuItemPattern);
176     if (curMenuItemPattern->IsDisabled() || curMenuItemPattern->IsStackSubmenuHeader()) {
177         return;
178     }
179     curMenuItemPattern->NotifyPressStatus(true);
180 }
181 
ClearLastMenuItem()182 void MenuWrapperPattern::ClearLastMenuItem()
183 {
184     if (lastTouchItem_) {
185         auto lastMenuItemPattern = lastTouchItem_->GetPattern<MenuItemPattern>();
186         CHECK_NULL_VOID(lastMenuItemPattern);
187         lastMenuItemPattern->NotifyPressStatus(false);
188         lastTouchItem_ = nullptr;
189     }
190 }
191 
OnAttachToFrameNode()192 void MenuWrapperPattern::OnAttachToFrameNode()
193 {
194     RegisterOnTouch();
195 }
196 
OnDetachFromMainTree()197 void MenuWrapperPattern::OnDetachFromMainTree()
198 {
199     CHECK_NULL_VOID(filterColumnNode_);
200     auto pipeline = filterColumnNode_->GetContext();
201     CHECK_NULL_VOID(pipeline);
202     auto overlay = pipeline->GetOverlayManager();
203     CHECK_NULL_VOID(overlay);
204     overlay->RemoveMenuFilter(GetHost());
205 }
206 
207 // close subMenu when mouse move outside
HandleMouseEvent(const MouseInfo & info,RefPtr<MenuItemPattern> & menuItemPattern)208 void MenuWrapperPattern::HandleMouseEvent(const MouseInfo& info, RefPtr<MenuItemPattern>& menuItemPattern)
209 {
210     CHECK_NULL_VOID(menuItemPattern);
211     auto host = GetHost();
212     CHECK_NULL_VOID(host);
213     auto subMenu = host->GetChildren().back();
214     if (host->GetChildren().size() <= 1) {
215         return;
216     }
217     auto subMenuNode = DynamicCast<FrameNode>(subMenu);
218     CHECK_NULL_VOID(subMenuNode);
219     auto subMenuPattern = subMenuNode->GetPattern<MenuPattern>();
220     CHECK_NULL_VOID(subMenuPattern);
221     auto currentHoverMenuItem = subMenuPattern->GetParentMenuItem();
222     CHECK_NULL_VOID(currentHoverMenuItem);
223     auto geometryNode = subMenuNode->GetGeometryNode();
224     CHECK_NULL_VOID(geometryNode);
225     auto offset = subMenuNode->GetPaintRectOffset(false, true);
226     auto frameSize = geometryNode->GetFrameSize();
227     auto menuZone = RectF(offset.GetX(), offset.GetY(), frameSize.Width(), frameSize.Height());
228     auto menuItemNode = menuItemPattern->GetHost();
229     CHECK_NULL_VOID(menuItemNode);
230     if (currentHoverMenuItem->GetId() != menuItemNode->GetId()) {
231         return;
232     }
233     const auto& mousePosition = info.GetGlobalLocation();
234     if (!menuItemPattern->IsInHoverRegions(mousePosition.GetX(), mousePosition.GetY()) &&
235         menuItemPattern->IsSubMenuShowed()) {
236         menuItemPattern->CheckHideSubMenu(
237             [weak = WeakClaim(this), weakSubMenu = WeakClaim(RawPtr(subMenuNode))] {
238                 auto pattern = weak.Upgrade();
239                 CHECK_NULL_VOID(pattern);
240                 auto subMenu = weakSubMenu.Upgrade();
241                 CHECK_NULL_VOID(subMenu);
242                 if (subMenu == pattern->GetShowedSubMenu()) {
243                     pattern->HideSubMenu();
244                 }
245             },
246             PointF(mousePosition.GetX(), mousePosition.GetY()), menuZone);
247     } else {
248         menuItemPattern->CancelHideSubMenuTask(PointF(mousePosition.GetX(), mousePosition.GetY()));
249     }
250 }
251 
HideMenu(const HideMenuType & reason)252 void MenuWrapperPattern::HideMenu(const HideMenuType& reason)
253 {
254     auto host = GetHost();
255     CHECK_NULL_VOID(host);
256     auto menuNode = DynamicCast<FrameNode>(host->GetChildAtIndex(0));
257     CHECK_NULL_VOID(menuNode);
258     HideMenu(menuNode, reason);
259 }
260 
GetExpandingMode(const RefPtr<UINode> & subMenu,SubMenuExpandingMode & expandingMode,bool & hasAnimation)261 void MenuWrapperPattern::GetExpandingMode(const RefPtr<UINode>& subMenu, SubMenuExpandingMode& expandingMode,
262     bool& hasAnimation)
263 {
264     CHECK_NULL_VOID(subMenu);
265     auto subMenuNode = DynamicCast<FrameNode>(subMenu);
266     CHECK_NULL_VOID(subMenuNode);
267     auto subMenuPattern = subMenuNode->GetPattern<MenuPattern>();
268     CHECK_NULL_VOID(subMenuPattern);
269     hasAnimation = subMenuPattern->GetDisappearAnimation();
270     auto menuItem = FrameNode::GetFrameNode(subMenuPattern->GetTargetTag(), subMenuPattern->GetTargetId());
271     CHECK_NULL_VOID(menuItem);
272     auto menuItemPattern = menuItem->GetPattern<MenuItemPattern>();
273     CHECK_NULL_VOID(menuItemPattern);
274     auto menuNode = menuItemPattern->GetMenu();
275     CHECK_NULL_VOID(menuNode);
276     auto menuProperty = menuNode->GetLayoutProperty<MenuLayoutProperty>();
277     CHECK_NULL_VOID(menuProperty);
278     expandingMode = menuProperty->GetExpandingMode().value_or(SubMenuExpandingMode::SIDE);
279     menuItemPattern->SetIsSubMenuShowed(false);
280 }
281 
HideSubMenuByDepth(const RefPtr<FrameNode> & menuItem)282 void MenuWrapperPattern::HideSubMenuByDepth(const RefPtr<FrameNode>& menuItem)
283 {
284     CHECK_NULL_VOID(menuItem);
285     auto host = GetHost();
286     CHECK_NULL_VOID(host);
287     if (host->GetChildren().size() <= 1) {
288         // sub menu not show
289         return;
290     }
291     auto menuItemPattern = menuItem->GetPattern<MenuItemPattern>();
292     CHECK_NULL_VOID(menuItemPattern);
293     auto menuNode = menuItemPattern->GetMenu(true);
294     CHECK_NULL_VOID(menuNode);
295     auto menuPattern = menuNode->GetPattern<MenuPattern>();
296     CHECK_NULL_VOID(menuPattern);
297     auto curDepth = menuPattern->GetSubMenuDepth();
298     auto children = host->GetChildren();
299     bool clearShowedSubMenu = true;
300     for (auto child = children.rbegin(); child != children.rend(); ++child) {
301         auto childNode = DynamicCast<FrameNode>(*child);
302         CHECK_NULL_VOID(childNode);
303         if (childNode->GetTag() != V2::MENU_ETS_TAG) {
304             continue;
305         }
306         auto subMenuPattern = childNode->GetPattern<MenuPattern>();
307         CHECK_NULL_VOID(subMenuPattern);
308         if (subMenuPattern->GetSubMenuDepth() <= curDepth) {
309             break;
310         }
311         auto parentMenuItem = subMenuPattern->GetParentMenuItem();
312         CHECK_NULL_VOID(parentMenuItem);
313         auto parentItemPattern = parentMenuItem->GetPattern<MenuItemPattern>();
314         CHECK_NULL_VOID(parentItemPattern);
315         if (parentItemPattern->HasHideTask()) {
316             clearShowedSubMenu = false;
317             continue;
318         }
319         if (parentMenuItem->GetId() == menuItem->GetId()) {
320             clearShowedSubMenu = false;
321             break;
322         }
323         subMenuPattern->RemoveParentHoverStyle();
324         UpdateMenuAnimation(host);
325         SendToAccessibility(childNode, false);
326         host->RemoveChild(childNode);
327     }
328     if (clearShowedSubMenu) {
329         menuPattern->SetShowedSubMenu(nullptr);
330     }
331     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
332 }
333 
HideSubMenu()334 void MenuWrapperPattern::HideSubMenu()
335 {
336     auto host = GetHost();
337     CHECK_NULL_VOID(host);
338     if (host->GetChildren().size() <= 1) {
339         // sub menu not show
340         return;
341     }
342     auto subMenu = host->GetChildren().back();
343     CHECK_NULL_VOID(subMenu);
344     auto subMenuFrameNode = DynamicCast<FrameNode>(subMenu);
345     CHECK_NULL_VOID(subMenuFrameNode);
346     auto subMenuPattern = subMenuFrameNode->GetPattern<MenuPattern>();
347     CHECK_NULL_VOID(subMenuPattern);
348     subMenuPattern->RemoveParentHoverStyle();
349     auto menuNode = GetParentMenu(subMenu);
350     CHECK_NULL_VOID(menuNode);
351     auto menuPattern = menuNode->GetPattern<MenuPattern>();
352     CHECK_NULL_VOID(menuPattern);
353     MenuFocusViewShow(menuNode);
354     menuPattern->SetShowedSubMenu(nullptr);
355     auto innerMenu = GetMenuChild(menuNode);
356     if (!innerMenu) {
357         UpdateMenuAnimation(host);
358         SendToAccessibility(subMenu, false);
359         host->RemoveChild(subMenu);
360         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
361         return;
362     }
363     auto innerMenuPattern = innerMenu->GetPattern<MenuPattern>();
364     CHECK_NULL_VOID(innerMenuPattern);
365     auto layoutProps = innerMenuPattern->GetLayoutProperty<MenuLayoutProperty>();
366     CHECK_NULL_VOID(layoutProps);
367     auto expandingMode = layoutProps->GetExpandingMode().value_or(SubMenuExpandingMode::SIDE);
368     bool hasAnimation = menuPattern->GetDisappearAnimation();
369     GetExpandingMode(subMenu, expandingMode, hasAnimation);
370     if (expandingMode == SubMenuExpandingMode::STACK && hasAnimation) {
371         HideStackExpandMenu(subMenu);
372     } else {
373         UpdateMenuAnimation(host);
374         ShowSubMenuDisappearAnimation(host, subMenu);
375     }
376 }
377 
ShowSubMenuDisappearAnimation(const RefPtr<FrameNode> & host,const RefPtr<UINode> & subMenu)378 void MenuWrapperPattern::ShowSubMenuDisappearAnimation(const RefPtr<FrameNode>& host, const RefPtr<UINode>& subMenu)
379 {
380     CHECK_NULL_VOID(host);
381     CHECK_NULL_VOID(subMenu);
382     auto pipeline = host->GetContext();
383     CHECK_NULL_VOID(pipeline);
384     auto theme = pipeline->GetTheme<SelectTheme>();
385     CHECK_NULL_VOID(theme);
386     if (theme->GetMenuAnimationDuration()) {
387         auto animationOption = AnimationOption();
388         animationOption.SetDuration(theme->GetMenuAnimationDuration());
389         animationOption.SetCurve(theme->GetMenuAnimationCurve());
390         double scale = theme->GetMenuAnimationScale();
391         AnimationUtils::Animate(
392             animationOption,
393             [weak = WeakClaim(RawPtr(subMenu)), scale]() {
394                 auto subMenuNode = weak.Upgrade();
395                 CHECK_NULL_VOID(subMenuNode);
396                 auto subMenu = AceType::DynamicCast<FrameNode>(subMenuNode);
397                 CHECK_NULL_VOID(subMenu);
398                 auto renderContext = subMenu->GetRenderContext();
399                 CHECK_NULL_VOID(renderContext);
400                 renderContext->UpdateTransformScale(VectorF(scale, scale));
401                 renderContext->UpdateOpacity(MENU_ANIMATION_MIN_OPACITY);
402             },
403             [hostWeak = WeakClaim(RawPtr(host)), subMenuWeak = WeakClaim(RawPtr(subMenu))]() {
404                 auto host = hostWeak.Upgrade();
405                 CHECK_NULL_VOID(host);
406                 auto subMenu = subMenuWeak.Upgrade();
407                 CHECK_NULL_VOID(subMenu);
408                 auto wrapperPattern = host->GetPattern<MenuWrapperPattern>();
409                 if (wrapperPattern) {
410                     wrapperPattern->SendToAccessibility(subMenu, false);
411                 }
412                 host->RemoveChild(subMenu);
413                 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
414             }, nullptr, host->GetContextRefPtr());
415     } else {
416         SendToAccessibility(subMenu, false);
417         host->RemoveChild(subMenu);
418         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
419     }
420 }
421 
GetParentMenu(const RefPtr<UINode> & subMenu)422 RefPtr<FrameNode> MenuWrapperPattern::GetParentMenu(const RefPtr<UINode>& subMenu)
423 {
424     auto subMenuNode = AceType::DynamicCast<FrameNode>(subMenu);
425     CHECK_NULL_RETURN(subMenuNode, nullptr);
426     auto subMenuPattern = subMenuNode->GetPattern<MenuPattern>();
427     CHECK_NULL_RETURN(subMenuPattern, nullptr);
428     auto menuItem = subMenuPattern->GetParentMenuItem();
429     CHECK_NULL_RETURN(menuItem, nullptr);
430     auto itemPattern = menuItem->GetPattern<MenuItemPattern>();
431     CHECK_NULL_RETURN(itemPattern, nullptr);
432     return itemPattern->GetMenu(true);
433 }
434 
SendToAccessibility(const RefPtr<UINode> & subMenu,bool isShow)435 void MenuWrapperPattern::SendToAccessibility(const RefPtr<UINode>& subMenu, bool isShow)
436 {
437     auto subMenuNode = AceType::DynamicCast<FrameNode>(subMenu);
438     CHECK_NULL_VOID(subMenuNode);
439     auto accessibilityProperty = subMenuNode->GetAccessibilityProperty<MenuAccessibilityProperty>();
440     CHECK_NULL_VOID(accessibilityProperty);
441     accessibilityProperty->SetAccessibilityIsShow(isShow);
442     subMenuNode->OnAccessibilityEvent(AccessibilityEventType::PAGE_CLOSE);
443 }
444 
HasStackSubMenu()445 bool MenuWrapperPattern::HasStackSubMenu()
446 {
447     auto outterMenu = GetMenu();
448     CHECK_NULL_RETURN(outterMenu, false);
449     auto innerMenu = GetMenuChild(outterMenu);
450     CHECK_NULL_RETURN(innerMenu, false);
451     auto innerMenuPattern = innerMenu->GetPattern<MenuPattern>();
452     CHECK_NULL_RETURN(innerMenuPattern, false);
453     auto layoutProps = innerMenuPattern->GetLayoutProperty<MenuLayoutProperty>();
454     CHECK_NULL_RETURN(layoutProps, false);
455     auto expandingMode = layoutProps->GetExpandingMode().value_or(SubMenuExpandingMode::SIDE);
456     if (expandingMode != SubMenuExpandingMode::STACK) {
457         return false;
458     }
459     auto host = GetHost();
460     CHECK_NULL_RETURN(host, false);
461     auto menuCount = 0;
462     for (const auto& child : host->GetChildren()) {
463         if (child && child->GetTag() == V2::MENU_ETS_TAG) {
464             menuCount++;
465         }
466     }
467     return menuCount > 1;
468 }
469 
HasEmbeddedSubMenu()470 bool MenuWrapperPattern::HasEmbeddedSubMenu()
471 {
472     auto outterMenu = GetMenu();
473     CHECK_NULL_RETURN(outterMenu, false);
474     auto innerMenu = GetMenuChild(outterMenu);
475     CHECK_NULL_RETURN(innerMenu, false);
476     auto innerMenuPattern = innerMenu->GetPattern<MenuPattern>();
477     CHECK_NULL_RETURN(innerMenuPattern, false);
478     auto layoutProps = innerMenuPattern->GetLayoutProperty<MenuLayoutProperty>();
479     CHECK_NULL_RETURN(layoutProps, false);
480     auto expandingMode = layoutProps->GetExpandingMode().value_or(SubMenuExpandingMode::SIDE);
481     return expandingMode == SubMenuExpandingMode::EMBEDDED;
482 }
483 
HasSideSubMenu()484 bool MenuWrapperPattern::HasSideSubMenu()
485 {
486     auto outterMenu = GetMenu();
487     CHECK_NULL_RETURN(outterMenu, false);
488     auto innerMenu = GetMenuChild(outterMenu);
489     CHECK_NULL_RETURN(innerMenu, false);
490     auto innerMenuPattern = innerMenu->GetPattern<MenuPattern>();
491     CHECK_NULL_RETURN(innerMenuPattern, false);
492     auto layoutProps = innerMenuPattern->GetLayoutProperty<MenuLayoutProperty>();
493     CHECK_NULL_RETURN(layoutProps, false);
494     auto expandingMode = layoutProps->GetExpandingMode().value_or(SubMenuExpandingMode::SIDE);
495     return expandingMode == SubMenuExpandingMode::SIDE;
496 }
497 
MenuFocusViewShow(const RefPtr<FrameNode> & menuNode)498 void MenuWrapperPattern::MenuFocusViewShow(const RefPtr<FrameNode>& menuNode)
499 {
500     CHECK_NULL_VOID(menuNode);
501     // SelectOverlay's custom menu does not need to be focused.
502     auto isCustomMenu = IsSelectOverlayCustomMenu(menuNode);
503     auto isRightClickMenu = IsSelectOverlayRightClickMenu(menuNode);
504     if (!isCustomMenu && !isRightClickMenu) {
505         auto menuPattern = menuNode->GetPattern<MenuPattern>();
506         CHECK_NULL_VOID(menuPattern);
507         menuPattern->FocusViewShow();
508     }
509 }
510 
HideStackExpandMenu(const RefPtr<UINode> & subMenu)511 void MenuWrapperPattern::HideStackExpandMenu(const RefPtr<UINode>& subMenu)
512 {
513     auto host = GetHost();
514     CHECK_NULL_VOID(host);
515     auto menuNode = host->GetFirstChild();
516     CHECK_NULL_VOID(menuNode);
517     AnimationOption option;
518     option.SetOnFinishEvent(
519         [weak = WeakClaim(RawPtr(host)), subMenuWk = WeakClaim(RawPtr(subMenu))] {
520             auto host = weak.Upgrade();
521             CHECK_NULL_VOID(host);
522             auto pipeline = host->GetContext();
523             CHECK_NULL_VOID(pipeline);
524             auto taskExecutor = pipeline->GetTaskExecutor();
525             CHECK_NULL_VOID(taskExecutor);
526             taskExecutor->PostTask(
527                 [weak, subMenuWk]() {
528                     auto subMenuNode = subMenuWk.Upgrade();
529                     CHECK_NULL_VOID(subMenuNode);
530                     auto menuWrapper = weak.Upgrade();
531                     CHECK_NULL_VOID(menuWrapper);
532 
533                     auto subMenuFrameNode = DynamicCast<FrameNode>(subMenuNode);
534                     CHECK_NULL_VOID(subMenuFrameNode);
535                     auto accessibilityProperty =
536                         subMenuFrameNode->GetAccessibilityProperty<MenuAccessibilityProperty>();
537                     CHECK_NULL_VOID(accessibilityProperty);
538                     accessibilityProperty->SetAccessibilityIsShow(false);
539                     subMenuFrameNode->OnAccessibilityEvent(AccessibilityEventType::PAGE_CLOSE,
540                         WindowsContentChangeTypes::CONTENT_CHANGE_TYPE_SUBTREE);
541                     TAG_LOGI(AceLogTag::ACE_MENU, "Send event to %{public}d",
542                         static_cast<int32_t>(AccessibilityEventType::PAGE_CLOSE));
543 
544                     menuWrapper->RemoveChild(subMenuNode);
545                     menuWrapper->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
546                 },
547                 TaskExecutor::TaskType::UI, "HideStackExpandMenu");
548     });
549     auto menuFrameNode = DynamicCast<FrameNode>(menuNode);
550     CHECK_NULL_VOID(menuFrameNode);
551     auto menuNodePattern = menuFrameNode->GetPattern<MenuPattern>();
552     CHECK_NULL_VOID(menuNodePattern);
553     auto subMenuFrameNode = DynamicCast<FrameNode>(subMenu);
554     CHECK_NULL_VOID(subMenuFrameNode);
555     menuNodePattern->ShowStackMenuDisappearAnimation(menuFrameNode,
556         subMenuFrameNode, option);
557     menuNodePattern->SetDisappearAnimation(true);
558     auto subMenuPattern = subMenuFrameNode->GetPattern<MenuPattern>();
559     CHECK_NULL_VOID(subMenuPattern);
560     subMenuPattern->SetSubMenuShow(false);
561 }
562 
RegisterOnTouch()563 void MenuWrapperPattern::RegisterOnTouch()
564 {
565     // if already initialized touch event
566     CHECK_NULL_VOID(!onTouch_);
567     auto host = GetHost();
568     CHECK_NULL_VOID(host);
569     auto gesture = host->GetOrCreateGestureEventHub();
570     CHECK_NULL_VOID(gesture);
571     // hide menu when touched outside the menu region
572     auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
573         auto pattern = weak.Upgrade();
574         CHECK_NULL_VOID(pattern);
575         pattern->OnTouchEvent(info);
576     };
577     onTouch_ = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
578     gesture->AddTouchEvent(onTouch_);
579 }
580 
OnTouchEvent(const TouchEventInfo & info)581 void MenuWrapperPattern::OnTouchEvent(const TouchEventInfo& info)
582 {
583     CHECK_NULL_VOID(!info.GetChangedTouches().empty());
584     auto touch = info.GetChangedTouches().front();
585     // filter out other touch types
586     if (touch.GetTouchType() != TouchType::DOWN &&
587         Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
588         return;
589     }
590     CHECK_EQUAL_VOID(IsHide(), true);
591     auto host = GetHost();
592     CHECK_NULL_VOID(host);
593 
594     auto position = PointF(
595         static_cast<float>(touch.GetGlobalLocation().GetX()), static_cast<float>(touch.GetGlobalLocation().GetY()));
596     auto children = host->GetChildren();
597     if (touch.GetTouchType() == TouchType::DOWN) {
598         // Record the latest touch finger ID. If other fingers are pressed, the latest one prevails
599         fingerId_ = touch.GetFingerId();
600         TAG_LOGD(AceLogTag::ACE_MENU, "record newest finger ID %{public}d", fingerId_);
601         std::vector<RefPtr<FrameNode>> toHideMenus;
602         for (auto child = children.rbegin(); child != children.rend(); ++child) {
603             // get child frame node of menu wrapper
604             auto menuWrapperChildNode = DynamicCast<FrameNode>(*child);
605             CHECK_NULL_VOID(menuWrapperChildNode);
606             // get menuWrapperChildNode's touch region
607             if (CheckPointInMenuZone(menuWrapperChildNode, position)) {
608                 HandleInteraction(info);
609                 break;
610             }
611             // if DOWN-touched outside the menu region, then hide menu
612             auto menuPattern = menuWrapperChildNode->GetPattern<MenuPattern>();
613             CHECK_NULL_CONTINUE(menuPattern);
614             isClearLastMenuItem_ = true;
615             if (menuPattern->IsSubMenu() && HasSideSubMenu() &&
616                 IsTouchWithinParentMenuItemZone(child, children, position)) {
617                 continue;
618             }
619             TAG_LOGI(AceLogTag::ACE_MENU, "will hide menu due to touch down");
620             toHideMenus.push_back(menuWrapperChildNode);
621         }
622         if (!toHideMenus.empty()) {
623             for (auto node : toHideMenus) {
624                 auto pattern = node->GetPattern<MenuPattern>();
625                 CHECK_NULL_CONTINUE(pattern);
626                 HideMenu(pattern, node, position, HideMenuType::WRAPPER_TOUCH_DOWN);
627             }
628             toHideMenus.clear();
629         }
630         return;
631     }
632     // When the Move or Up event is not the recorded finger ID, this event is not responded
633     if (fingerId_ != touch.GetFingerId()) {
634         return;
635     }
636     ChangeTouchItem(info, touch.GetTouchType());
637 }
638 
IsTouchWithinParentMenuItemZone(std::list<RefPtr<UINode>>::reverse_iterator & child,const std::list<RefPtr<UINode>> & children,const PointF & position)639 bool MenuWrapperPattern::IsTouchWithinParentMenuItemZone(std::list<RefPtr<UINode>>::reverse_iterator& child,
640     const std::list<RefPtr<UINode>>& children, const PointF& position)
641 {
642     auto currentMenuNode = DynamicCast<FrameNode>(*child);
643     CHECK_NULL_RETURN(currentMenuNode, false);
644     auto menuIterator = child;
645     menuIterator++;
646     while (menuIterator != children.rend()) {
647         auto parentMenuNode = DynamicCast<FrameNode>(*menuIterator);
648         CHECK_NULL_RETURN(parentMenuNode, false);
649         if (parentMenuNode->GetTag() != V2::MENU_ETS_TAG) {
650             menuIterator++;
651             continue;
652         }
653         if (CheckPointInMenuZone(parentMenuNode, position)) {
654             auto menuPattern = parentMenuNode->GetPattern<MenuPattern>();
655             CHECK_NULL_RETURN(menuPattern, false);
656             auto innerMenuNode = menuPattern->GetFirstInnerMenu();
657             if (!innerMenuNode) {
658                 innerMenuNode = parentMenuNode;
659             }
660             auto currentTouchItem = FindTouchedMenuItem(innerMenuNode, position);
661             auto currentMenuPattern = currentMenuNode->GetPattern<MenuPattern>();
662             CHECK_NULL_RETURN(currentMenuPattern, false);
663             if (currentTouchItem == currentMenuPattern->GetParentMenuItem()) {
664                 ClearLastMenuItem();
665                 currentTouchItem_ = currentTouchItem;
666                 ChangeCurMenuItemBgColor();
667                 lastTouchItem_ = currentTouchItem_;
668                 isClearLastMenuItem_ = false;
669                 return true;
670             }
671         }
672         return false;
673     }
674     return false;
675 }
676 
ChangeTouchItem(const TouchEventInfo & info,TouchType touchType)677 void MenuWrapperPattern::ChangeTouchItem(const TouchEventInfo& info, TouchType touchType)
678 {
679     auto host = GetHost();
680     CHECK_NULL_VOID(host);
681     if (touchType == TouchType::MOVE) {
682         auto menuNode = DynamicCast<FrameNode>(host->GetChildAtIndex(0));
683         CHECK_NULL_VOID(menuNode);
684         if (GetPreviewMode() != MenuPreviewMode::NONE || IsSelectOverlayCustomMenu(menuNode)) {
685             return;
686         }
687         HandleInteraction(info);
688     } else if (touchType == TouchType::UP || touchType == TouchType::CANCEL) {
689         if (currentTouchItem_) {
690             auto currentTouchItemPattern = currentTouchItem_->GetPattern<MenuItemPattern>();
691             CHECK_NULL_VOID(currentTouchItemPattern);
692             currentTouchItemPattern->NotifyPressStatus(false);
693             currentTouchItem_ = nullptr;
694         }
695         // Reset finger ID when touch Up or Cancel
696         TAG_LOGD(AceLogTag::ACE_MENU, "reset finger ID %{public}d", fingerId_);
697         fingerId_ = -1;
698     }
699 }
700 
HideMenu(const RefPtr<MenuPattern> & menuPattern,const RefPtr<FrameNode> & menu,const PointF & position,const HideMenuType & reason)701 void MenuWrapperPattern::HideMenu(const RefPtr<MenuPattern>& menuPattern, const RefPtr<FrameNode>& menu,
702     const PointF& position, const HideMenuType& reason)
703 {
704     CHECK_NULL_VOID(menuPattern);
705     auto host = GetHost();
706     CHECK_NULL_VOID(host);
707     auto mainMenu = DynamicCast<FrameNode>(host->GetFirstChild());
708     CHECK_NULL_VOID(mainMenu);
709     bool isFindTargetId = false;
710     if (CheckPointInMenuZone(mainMenu, position)) {
711         isFindTargetId = true;
712     }
713     if (menuPattern->IsSubMenu() || menuPattern->IsSelectOverlaySubMenu()) {
714         if (HasStackSubMenu() && !isFindTargetId) {
715             UpdateMenuAnimation(host);
716         }
717         HideSubMenu();
718     } else {
719         if (HasEmbeddedSubMenu() && embeddedSubMenuCount_ > 0 && !isFindTargetId) {
720             UpdateMenuAnimation(host);
721         }
722         HideMenu(menu, reason);
723     }
724 }
725 
UpdateMenuAnimation(const RefPtr<FrameNode> & host)726 void MenuWrapperPattern::UpdateMenuAnimation(const RefPtr<FrameNode>& host)
727 {
728     // update Menu disappear animation direction
729     // change to LEFT_BOTTOM -> RIGHT_TOP by calling SetExitAnimation
730     // or keep BOTTOM -> TOP by default
731     CHECK_NULL_VOID(host);
732     auto outterMenu = host->GetFirstChild();
733     CHECK_NULL_VOID(outterMenu);
734     auto innerMenu = GetMenuChild(outterMenu);
735     if (!innerMenu && host->GetChildren().size() > 1) {
736         SetExitAnimation(host);
737         return;
738     }
739     CHECK_NULL_VOID(innerMenu);
740     auto innerMenuPattern = innerMenu->GetPattern<MenuPattern>();
741     CHECK_NULL_VOID(innerMenuPattern);
742     auto layoutProps = innerMenuPattern->GetLayoutProperty<MenuLayoutProperty>();
743     CHECK_NULL_VOID(layoutProps);
744     auto expandingMode = layoutProps->GetExpandingMode().value_or(SubMenuExpandingMode::SIDE);
745     if (host->GetChildren().size() > 1) {
746         SetExitAnimation(host);
747     }
748     if (expandingMode == SubMenuExpandingMode::EMBEDDED && embeddedSubMenuCount_ > 0) {
749         SetExitAnimation(host);
750     }
751 }
752 
SetExitAnimation(const RefPtr<FrameNode> & host)753 void MenuWrapperPattern::SetExitAnimation(const RefPtr<FrameNode>& host)
754 {
755     CHECK_NULL_VOID(host);
756     auto outterMenu = AceType::DynamicCast<FrameNode>(host->GetFirstChild());
757     CHECK_NULL_VOID(outterMenu);
758     auto outterMenuPattern = outterMenu->GetPattern<MenuPattern>();
759     CHECK_NULL_VOID(outterMenuPattern);
760     outterMenuPattern->SetDisappearAnimation(false);
761 }
762 
CheckAndShowAnimation()763 void MenuWrapperPattern::CheckAndShowAnimation()
764 {
765     if (isFirstShow_) {
766         // only start animation when menu wrapper mount on.
767         StartShowAnimation();
768         isFirstShow_ = false;
769     }
770 }
771 
MarkWholeSubTreeNoDraggable(const RefPtr<FrameNode> & frameNode)772 void MenuWrapperPattern::MarkWholeSubTreeNoDraggable(const RefPtr<FrameNode>& frameNode)
773 {
774     CHECK_NULL_VOID(frameNode);
775     auto eventHub = frameNode->GetOrCreateEventHub<EventHub>();
776     CHECK_NULL_VOID(eventHub);
777     auto gestureEventHub = eventHub->GetOrCreateGestureEventHub();
778     CHECK_NULL_VOID(gestureEventHub);
779     gestureEventHub->SetDragForbiddenForcely(true);
780 }
781 
MarkAllMenuNoDraggable()782 void MenuWrapperPattern::MarkAllMenuNoDraggable()
783 {
784     auto host = GetHost();
785     CHECK_NULL_VOID(host);
786     for (const auto& child : host->GetChildren()) {
787         auto node = DynamicCast<FrameNode>(child);
788         if (node && node->GetTag() == V2::MENU_ETS_TAG) {
789             MarkWholeSubTreeNoDraggable(node);
790         }
791     }
792 }
793 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)794 bool MenuWrapperPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
795 {
796     forceUpdateEmbeddedMenu_ = false;
797     auto host = GetHost();
798     CHECK_NULL_RETURN(host, false);
799     auto pipeline = host->GetContext();
800     CHECK_NULL_RETURN(pipeline, false);
801     auto theme = pipeline->GetTheme<SelectTheme>();
802     CHECK_NULL_RETURN(theme, false);
803     auto expandDisplay = theme->GetExpandDisplay();
804     auto menuNode = DynamicCast<FrameNode>(host->GetChildAtIndex(0));
805     CHECK_NULL_RETURN(menuNode, false);
806     auto menuPattern = AceType::DynamicCast<MenuPattern>(menuNode->GetPattern());
807     CHECK_NULL_RETURN(menuPattern, false);
808     // copy menu pattern properties to rootMenu
809     auto layoutProperty = menuPattern->GetLayoutProperty<MenuLayoutProperty>();
810     CHECK_NULL_RETURN(layoutProperty, false);
811     isShowInSubWindow_ = layoutProperty->GetShowInSubWindowValue(true);
812     if (host->IsOnMainTree() &&
813         ((IsContextMenu() && !IsHide()) || (((expandDisplay || isOpenMenu_) && isShowInSubWindow_) && !IsHide()) ||
814             GetIsSelectOverlaySubWindowWrapper())) {
815         SetHotAreas(dirty);
816     }
817     MarkAllMenuNoDraggable();
818     MarkWholeSubTreeNoDraggable(GetPreview());
819     if (!GetHoverScaleInterruption()) {
820         CheckAndShowAnimation();
821     }
822     return false;
823 }
824 
IsNeedSetHotAreas(const RefPtr<LayoutWrapper> & layoutWrapper)825 bool MenuWrapperPattern::IsNeedSetHotAreas(const RefPtr<LayoutWrapper>& layoutWrapper)
826 {
827     CHECK_NULL_RETURN(layoutWrapper, false);
828     auto host = GetHost();
829     CHECK_NULL_RETURN(host, false);
830     auto pipeline = host->GetContext();
831     CHECK_NULL_RETURN(pipeline, false);
832     auto theme = pipeline->GetTheme<SelectTheme>();
833     CHECK_NULL_RETURN(theme, false);
834     bool menuNotNeedsHotAreas = (layoutWrapper->GetAllChildrenWithBuild().empty() || !IsContextMenu()) &&
835                                 !((theme->GetExpandDisplay() || isOpenMenu_) && isShowInSubWindow_);
836     if (menuNotNeedsHotAreas && !GetIsSelectOverlaySubWindowWrapper()) {
837         return false;
838     }
839 
840     return true;
841 }
842 
AddTargetWindowHotArea(std::vector<Rect> & rects)843 void MenuWrapperPattern::AddTargetWindowHotArea(std::vector<Rect>& rects)
844 {
845     if (menuParam_.modalMode.value_or(ModalMode::AUTO) != ModalMode::TARGET_WINDOW) {
846         return;
847     }
848     auto targetNode = FrameNode::GetFrameNode(targetTag_, targetId_);
849     CHECK_NULL_VOID(targetNode);
850     auto context = targetNode->GetContext();
851     CHECK_NULL_VOID(context);
852     auto container = AceEngine::Get().GetContainer(context->GetInstanceId());
853     CHECK_NULL_VOID(container);
854     if (container->IsSubContainer()) {
855         return;
856     }
857     if (container->IsUIExtensionWindow()) {
858         auto windowRect = container->GetUIExtensionHostWindowRect();
859         rects.emplace_back(windowRect);
860     } else {
861         auto windowRect = context->GetDisplayWindowRectInfo();
862         auto titleHeight = context->GetCustomTitleHeight().ConvertToPx();
863         rects.emplace_back(Rect(
864             windowRect.Left(), windowRect.Top() + titleHeight, windowRect.Width(), windowRect.Height() - titleHeight));
865     }
866 }
867 
AddWrapperChildHotArea(std::vector<Rect> & rects,const RefPtr<LayoutWrapper> & layoutWrapper)868 void MenuWrapperPattern::AddWrapperChildHotArea(std::vector<Rect>& rects, const RefPtr<LayoutWrapper>& layoutWrapper)
869 {
870     auto layoutProps = layoutWrapper->GetLayoutProperty();
871     CHECK_NULL_VOID(layoutProps);
872     float safeAreaInsetsLeft = 0.0f;
873     float safeAreaInsetsTop = 0.0f;
874     auto&& safeAreaInsets = layoutProps->GetSafeAreaInsets();
875     if (safeAreaInsets) {
876         safeAreaInsetsLeft = static_cast<float>(safeAreaInsets->left_.end);
877         safeAreaInsetsTop = static_cast<float>(safeAreaInsets->top_.end);
878     }
879     for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
880         CHECK_NULL_VOID(child);
881         auto childGeometryNode = child->GetGeometryNode();
882         CHECK_NULL_VOID(childGeometryNode);
883         auto frameRect = childGeometryNode->GetFrameRect();
884         // rect is relative to window
885         auto childNode = child->GetHostNode();
886         if (childNode &&
887             (childNode->GetTag() == V2::MENU_PREVIEW_ETS_TAG || childNode->GetTag() == V2::IMAGE_ETS_TAG)) {
888             frameRect = childNode->GetPaintRectWithTransform(); // get preview area with scale transform
889         }
890         auto rect = Rect(frameRect.GetX() + safeAreaInsetsLeft, frameRect.GetY() + safeAreaInsetsTop, frameRect.Width(),
891             frameRect.Height());
892 
893         rects.emplace_back(rect);
894     }
895 }
896 
AddFilterHotArea(std::vector<Rect> & rects)897 void MenuWrapperPattern::AddFilterHotArea(std::vector<Rect>& rects)
898 {
899     if (!IsContextMenu() || GetPreviewMode() == MenuPreviewMode::NONE) {
900         return;
901     }
902     auto filterNode = GetFilterColumnNode();
903     if (filterNode && GetIsFilterInSubwindow()) {
904         auto filterNodeGeometryNode = filterNode->GetGeometryNode();
905         CHECK_NULL_VOID(filterNodeGeometryNode);
906         auto frameRect = filterNodeGeometryNode->GetFrameRect();
907         auto rect = Rect(frameRect.GetX(), frameRect.GetY(), frameRect.Width(), frameRect.Height());
908         rects.emplace_back(rect);
909     }
910 }
911 
SetHotAreas(const RefPtr<LayoutWrapper> & layoutWrapper)912 void MenuWrapperPattern::SetHotAreas(const RefPtr<LayoutWrapper>& layoutWrapper)
913 {
914     CHECK_NULL_VOID(layoutWrapper);
915     if (!IsNeedSetHotAreas(layoutWrapper)) {
916         return;
917     }
918 
919     std::vector<Rect> rects;
920     AddTargetWindowHotArea(rects);
921     AddWrapperChildHotArea(rects, layoutWrapper);
922     // If container is UIExtensionWindow, set hotArea size equals to subwindow's filterColumnNode's size
923     AddFilterHotArea(rects);
924     auto subwindowManager = SubwindowManager::GetInstance();
925     CHECK_NULL_VOID(subwindowManager);
926     auto host = GetHost();
927     CHECK_NULL_VOID(host);
928     if (GetIsSelectOverlaySubWindowWrapper()) {
929         subwindowManager->SetSelectOverlayHotAreas(rects, host->GetId(), GetContainerId());
930     } else {
931         subwindowManager->SetHotAreas(rects, SubwindowType::TYPE_MENU, host->GetId(), GetContainerId());
932     }
933 }
934 
StartShowAnimation()935 void MenuWrapperPattern::StartShowAnimation()
936 {
937     TAG_LOGI(AceLogTag::ACE_MENU, "start show menu animation");
938     auto host = GetHost();
939     CHECK_NULL_VOID(host);
940     auto context = host->GetRenderContext();
941     CHECK_NULL_VOID(context);
942     auto pipeline = host->GetContext();
943     CHECK_NULL_VOID(pipeline);
944     auto theme = pipeline->GetTheme<SelectTheme>();
945     CHECK_NULL_VOID(theme);
946     if (GetPreviewMode() == MenuPreviewMode::NONE) {
947         context->UpdateOffset(GetAnimationOffset());
948         context->UpdateOpacity(0.0);
949     }
950     if (theme->GetMenuAnimationDuration() && GetPreviewMode() == MenuPreviewMode::NONE) {
951         auto menu = GetMenu();
952         auto menuGeometryNode = menu ? menu->GetGeometryNode() : nullptr;
953         if (menuGeometryNode) {
954             auto offset = menuGeometryNode->GetFrameOffset();
955             context->UpdateTransformCenter(DimensionOffset(Dimension(offset.GetX()), Dimension(offset.GetY())));
956         }
957         context->UpdateTransformScale(VectorF(theme->GetMenuAnimationScale(), theme->GetMenuAnimationScale()));
958         context->UpdateOpacity(MENU_ANIMATION_MIN_OPACITY);
959     }
960     CallMenuOnWillAppearCallback();
961     AnimationUtils::Animate(
962         animationOption_,
963         [context, weak = WeakClaim(this), theme]() {
964             if (context) {
965                 auto pattern = weak.Upgrade();
966                 CHECK_NULL_VOID(pattern);
967                 if (pattern->GetPreviewMode() == MenuPreviewMode::NONE) {
968                     context->UpdateOffset(OffsetT<Dimension>());
969                     context->UpdateOpacity(1.0);
970                 }
971                 CHECK_NULL_VOID(theme);
972                 if (theme->GetMenuAnimationDuration() && pattern->GetPreviewMode() == MenuPreviewMode::NONE) {
973                     context->UpdateTransformScale(VectorF(MENU_ANIMATION_MAX_SCALE, MENU_ANIMATION_MAX_SCALE));
974                 }
975             }
976         },
977         animationOption_.GetOnFinishEvent(), nullptr, host->GetContextRefPtr());
978 }
979 
SetAniamtinOption(const AnimationOption & animationOption)980 void MenuWrapperPattern::SetAniamtinOption(const AnimationOption& animationOption)
981 {
982     animationOption_.SetCurve(animationOption.GetCurve());
983     animationOption_.SetDuration(animationOption.GetDuration());
984     animationOption_.SetFillMode(animationOption.GetFillMode());
985     animationOption_.SetOnFinishEvent(animationOption.GetOnFinishEvent());
986 }
987 
GetAnimationOffset()988 OffsetT<Dimension> MenuWrapperPattern::GetAnimationOffset()
989 {
990     OffsetT<Dimension> offset;
991 
992     auto host = GetHost();
993     CHECK_NULL_RETURN(host, offset);
994     auto pipeline = host->GetContext();
995     CHECK_NULL_RETURN(pipeline, offset);
996     auto theme = pipeline->GetTheme<SelectTheme>();
997     CHECK_NULL_RETURN(theme, offset);
998     auto animationOffset = theme->GetMenuAnimationOffset();
999 
1000     switch (menuPlacement_) {
1001         case Placement::LEFT:
1002         case Placement::LEFT_TOP:
1003         case Placement::LEFT_BOTTOM:
1004             offset.SetX(animationOffset);
1005             break;
1006         case Placement::RIGHT:
1007         case Placement::RIGHT_TOP:
1008         case Placement::RIGHT_BOTTOM:
1009             offset.SetX(-animationOffset);
1010             break;
1011         case Placement::TOP:
1012         case Placement::TOP_LEFT:
1013         case Placement::TOP_RIGHT:
1014             offset.SetY(animationOffset);
1015             break;
1016         default:
1017             offset.SetY(-animationOffset);
1018             break;
1019     }
1020     return offset;
1021 }
1022 
IsSelectOverlayCustomMenu(const RefPtr<FrameNode> & menu) const1023 bool MenuWrapperPattern::IsSelectOverlayCustomMenu(const RefPtr<FrameNode>& menu) const
1024 {
1025     CHECK_NULL_RETURN(menu, false);
1026     auto menuPattern = menu->GetPattern<MenuPattern>();
1027     CHECK_NULL_RETURN(menuPattern, false);
1028     return menuPattern->IsSelectOverlayCustomMenu();
1029 }
1030 
IsSelectOverlayRightClickMenu(const RefPtr<FrameNode> & menu) const1031 bool MenuWrapperPattern::IsSelectOverlayRightClickMenu(const RefPtr<FrameNode>& menu) const
1032 {
1033     auto menuPattern = menu->GetPattern<MenuPattern>();
1034     CHECK_NULL_RETURN(menuPattern, false);
1035     return menuPattern->IsSelectOverlayRightClickMenu();
1036 }
1037 
RegisterMenuCallback(const RefPtr<FrameNode> & menuWrapperNode,const MenuParam & menuParam)1038 void MenuWrapperPattern::RegisterMenuCallback(const RefPtr<FrameNode>& menuWrapperNode, const MenuParam& menuParam)
1039 {
1040     TAG_LOGD(AceLogTag::ACE_MENU, "register menu enter");
1041     CHECK_NULL_VOID(menuWrapperNode);
1042     auto pattern = menuWrapperNode->GetPattern<MenuWrapperPattern>();
1043     CHECK_NULL_VOID(pattern);
1044     pattern->RegisterMenuAppearCallback(menuParam.onAppear);
1045     pattern->RegisterMenuDisappearCallback(menuParam.onDisappear);
1046     pattern->RegisterMenuAboutToAppearCallback(menuParam.aboutToAppear);
1047     pattern->RegisterMenuAboutToDisappearCallback(menuParam.aboutToDisappear);
1048     pattern->RegisterMenuStateChangeCallback(menuParam.onStateChange);
1049     pattern->RegisterMenuOnWillAppearCallback(menuParam.onWillAppear);
1050     pattern->RegisterMenuOnDidAppearCallback(menuParam.onDidAppear);
1051     pattern->RegisterMenuOnWillDisappearCallback(menuParam.onWillDisappear);
1052     pattern->RegisterMenuOnDidDisappearCallback(menuParam.onDidDisappear);
1053 }
1054 
SetMenuTransitionEffect(const RefPtr<FrameNode> & menuWrapperNode,const MenuParam & menuParam)1055 void MenuWrapperPattern::SetMenuTransitionEffect(const RefPtr<FrameNode>& menuWrapperNode, const MenuParam& menuParam)
1056 {
1057     TAG_LOGD(AceLogTag::ACE_MENU, "set menu transition effect");
1058     CHECK_NULL_VOID(menuWrapperNode);
1059     auto pattern = menuWrapperNode->GetPattern<MenuWrapperPattern>();
1060     CHECK_NULL_VOID(pattern);
1061     pattern->SetHasTransitionEffect(menuParam.hasTransitionEffect);
1062     if (menuParam.hasTransitionEffect) {
1063         auto renderContext = menuWrapperNode->GetRenderContext();
1064         CHECK_NULL_VOID(renderContext);
1065         CHECK_NULL_VOID(menuParam.transition);
1066         renderContext->UpdateChainedTransition(menuParam.transition);
1067     }
1068     pattern->SetHasPreviewTransitionEffect(menuParam.hasPreviewTransitionEffect);
1069     if (menuParam.hasPreviewTransitionEffect) {
1070         auto previewChild = pattern->GetPreview();
1071         CHECK_NULL_VOID(previewChild);
1072         auto renderContext = previewChild->GetRenderContext();
1073         CHECK_NULL_VOID(renderContext);
1074         CHECK_NULL_VOID(menuParam.previewTransition);
1075         renderContext->UpdateChainedTransition(menuParam.previewTransition);
1076     }
1077 }
1078 
GetMenuChild(const RefPtr<UINode> & node)1079 RefPtr<FrameNode> MenuWrapperPattern::GetMenuChild(const RefPtr<UINode>& node)
1080 {
1081     CHECK_NULL_RETURN(node, nullptr);
1082     RefPtr<FrameNode> menuChild;
1083     auto child = node->GetChildAtIndex(0);
1084     while (child) {
1085         if (child->GetTag() == V2::JS_VIEW_ETS_TAG) {
1086             auto customNode = DynamicCast<CustomNode>(child);
1087             CHECK_NULL_RETURN(customNode, nullptr);
1088             customNode->Render();
1089         } else if (child->GetTag() == V2::MENU_ETS_TAG) {
1090             menuChild = DynamicCast<FrameNode>(child);
1091             break;
1092         }
1093         child = child->GetChildAtIndex(0);
1094     }
1095     return menuChild;
1096 }
1097 
ClearAllSubMenu()1098 void MenuWrapperPattern::ClearAllSubMenu()
1099 {
1100     auto host = GetHost();
1101     CHECK_NULL_VOID(host);
1102     auto children = host->GetChildren();
1103     for (auto child = children.rbegin(); child != children.rend(); ++child) {
1104         auto frameNode = DynamicCast<FrameNode>(*child);
1105         if (!frameNode) {
1106             continue;
1107         }
1108         auto pattern = frameNode->GetPattern<MenuPattern>();
1109         if (pattern && pattern->IsSubMenu()) {
1110             host->RemoveChild(frameNode);
1111             host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
1112         }
1113     }
1114 }
1115 
1116 
RequestPathRender()1117 void MenuWrapperPattern::RequestPathRender()
1118 {
1119     auto host = GetHost();
1120     CHECK_NULL_VOID(host);
1121     auto paintProperty = host->GetPaintProperty<MenuWrapperPaintProperty>();
1122     CHECK_NULL_VOID(paintProperty);
1123     auto flag = paintProperty->GetRenderFlagValue(0);
1124     flag++;
1125     paintProperty->UpdateRenderFlag(flag);
1126     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1127 }
1128 
IsMenuPreviewNode(const RefPtr<FrameNode> & frameNode) const1129 bool MenuWrapperPattern::IsMenuPreviewNode(const RefPtr<FrameNode>& frameNode) const
1130 {
1131     if (GetPreviewMode() == MenuPreviewMode::NONE) {
1132         return false;
1133     }
1134 
1135     CHECK_NULL_RETURN(frameNode, false);
1136     auto tag = frameNode->GetTag();
1137     auto isPreviewTag = tag == V2::IMAGE_ETS_TAG || tag == V2::MENU_PREVIEW_ETS_TAG || tag == V2::FLEX_ETS_TAG;
1138     CHECK_NULL_RETURN(isPreviewTag, false);
1139 
1140     auto host = GetHost();
1141     CHECK_NULL_RETURN(host, false);
1142     auto preview = host->GetChildAtIndex(1);
1143     CHECK_NULL_RETURN(preview, false);
1144     return preview->GetId() == frameNode->GetId();
1145 }
1146 
DumpInfo()1147 void MenuWrapperPattern::DumpInfo()
1148 {
1149     DumpLog::GetInstance().AddDesc("MenuPreviewMode: " + std::to_string(dumpInfo_.menuPreviewMode));
1150     DumpLog::GetInstance().AddDesc("MenuType: " + std::to_string(dumpInfo_.menuType));
1151     DumpLog::GetInstance().AddDesc("EnableArrow: " + std::to_string(dumpInfo_.enableArrow));
1152     DumpLog::GetInstance().AddDesc("Offset: " + dumpInfo_.offset.ToString());
1153     DumpLog::GetInstance().AddDesc("TargetNode: " + dumpInfo_.targetNode);
1154     DumpLog::GetInstance().AddDesc("TargetOffset: " + dumpInfo_.targetOffset.ToString());
1155     DumpLog::GetInstance().AddDesc("TargetSize: " + dumpInfo_.targetSize.ToString());
1156     DumpLog::GetInstance().AddDesc("MenuWindowRect: " + dumpInfo_.menuWindowRect.ToString());
1157     DumpLog::GetInstance().AddDesc("WrapperRect: " + dumpInfo_.wrapperRect.ToString());
1158     DumpLog::GetInstance().AddDesc("PreviewBeginScale: " + std::to_string(dumpInfo_.previewBeginScale));
1159     DumpLog::GetInstance().AddDesc("PreviewEndScale: " + std::to_string(dumpInfo_.previewEndScale));
1160     DumpLog::GetInstance().AddDesc("Top: " + std::to_string(dumpInfo_.top));
1161     DumpLog::GetInstance().AddDesc("Bottom: " + std::to_string(dumpInfo_.bottom));
1162     DumpLog::GetInstance().AddDesc("Left: " + std::to_string(dumpInfo_.left));
1163     DumpLog::GetInstance().AddDesc("Right: " + std::to_string(dumpInfo_.right));
1164     DumpLog::GetInstance().AddDesc("GlobalLocation: " + dumpInfo_.globalLocation.ToString());
1165     DumpLog::GetInstance().AddDesc("OriginPlacement: " + dumpInfo_.originPlacement);
1166     DumpLog::GetInstance().AddDesc("DefaultPlacement: " + dumpInfo_.defaultPlacement);
1167     DumpLog::GetInstance().AddDesc("FinalPosition: " + dumpInfo_.finalPosition.ToString());
1168     DumpLog::GetInstance().AddDesc("FinalPlacement: " + dumpInfo_.finalPlacement);
1169     DumpLog::GetInstance().AddDesc("AnchorPosition: " + dumpInfo_.anchorPosition.ToString());
1170     auto modalMode = ConvertModalModeToString(menuParam_.modalMode);
1171     DumpLog::GetInstance().AddDesc("ModalMode: " + modalMode);
1172 }
1173 
DumpInfo(std::unique_ptr<JsonValue> & json)1174 void MenuWrapperPattern::DumpInfo(std::unique_ptr<JsonValue>& json)
1175 {
1176     json->Put("MenuPreviewMode", std::to_string(dumpInfo_.menuPreviewMode).c_str());
1177     json->Put("MenuType", std::to_string(dumpInfo_.menuType).c_str());
1178     json->Put("EnableArrow", std::to_string(dumpInfo_.enableArrow).c_str());
1179     json->Put("Offset", dumpInfo_.offset.ToString().c_str());
1180     json->Put("TargetNode", dumpInfo_.targetNode.c_str());
1181     json->Put("TargetOffset", dumpInfo_.targetOffset.ToString().c_str());
1182     json->Put("TargetSize", dumpInfo_.targetSize.ToString().c_str());
1183     json->Put("MenuWindowRect", dumpInfo_.menuWindowRect.ToString().c_str());
1184     json->Put("WrapperRect", dumpInfo_.wrapperRect.ToString().c_str());
1185     json->Put("PreviewBeginScale", std::to_string(dumpInfo_.previewBeginScale).c_str());
1186     json->Put("PreviewEndScale", std::to_string(dumpInfo_.previewEndScale).c_str());
1187     json->Put("Top", std::to_string(dumpInfo_.top).c_str());
1188     json->Put("Bottom", std::to_string(dumpInfo_.bottom).c_str());
1189     json->Put("Left", std::to_string(dumpInfo_.left).c_str());
1190     json->Put("Right", std::to_string(dumpInfo_.right).c_str());
1191     json->Put("GlobalLocation", dumpInfo_.globalLocation.ToString().c_str());
1192     json->Put("OriginPlacement", dumpInfo_.originPlacement.c_str());
1193     json->Put("DefaultPlacement", dumpInfo_.defaultPlacement.c_str());
1194     json->Put("FinalPosition", dumpInfo_.finalPosition.ToString().c_str());
1195     json->Put("FinalPlacement", dumpInfo_.finalPlacement.c_str());
1196     json->Put("AnchorPosition", dumpInfo_.anchorPosition.ToString().c_str());
1197     auto modalMode = ConvertModalModeToString(menuParam_.modalMode);
1198     json->Put("ModalMode", modalMode.c_str());
1199 }
1200 
CheckPointInMenuZone(const RefPtr<FrameNode> & node,const PointF & point)1201 bool MenuWrapperPattern::CheckPointInMenuZone(const RefPtr<FrameNode>& node, const PointF& point)
1202 {
1203     CHECK_NULL_RETURN(node, false);
1204     auto childOffset = node->GetPaintRectOffset(false, true);
1205     auto childSize = node->GetPaintRectWithTransform();
1206     auto menuZone = RectF(childOffset.GetX(), childOffset.GetY(), childSize.Width(), childSize.Height());
1207     return menuZone.IsInRegion(point);
1208 }
1209 
GetMenuMaskEnable() const1210 bool MenuWrapperPattern::GetMenuMaskEnable() const
1211 {
1212     return menuParam_.maskEnable.value_or(false);
1213 }
1214 
GetMenuMaskColor() const1215 Color MenuWrapperPattern::GetMenuMaskColor() const
1216 {
1217     if (menuParam_.maskType.has_value() && menuParam_.maskType->maskColor.has_value()) {
1218         return menuParam_.maskType->maskColor.value();
1219     }
1220 
1221     Color Color(NG::MENU_MASK_COLOR);
1222     auto node = GetHost();
1223     CHECK_NULL_RETURN(node, Color);
1224     auto pipelineContext = node->GetContext();
1225     CHECK_NULL_RETURN(pipelineContext, Color);
1226     auto menuTheme = pipelineContext->GetTheme<NG::MenuTheme>();
1227     CHECK_NULL_RETURN(menuTheme, Color);
1228     return menuTheme->GetPreviewMenuMaskColor();
1229 }
1230 
GetMenuMaskBlurStyle() const1231 BlurStyle MenuWrapperPattern::GetMenuMaskBlurStyle() const
1232 {
1233     if (menuParam_.maskType.has_value() && menuParam_.maskType->maskBackGroundBlurStyle.has_value()) {
1234         return menuParam_.maskType->maskBackGroundBlurStyle.value();
1235     }
1236     return BlurStyle::BACKGROUND_THIN;
1237 }
1238 
UpdateFilterMaskType()1239 void MenuWrapperPattern::UpdateFilterMaskType()
1240 {
1241     auto filterNode = GetFilterColumnNode();
1242     if (filterNode) {
1243         BlurStyleOption styleOption;
1244         styleOption.blurStyle = GetMenuMaskBlurStyle();
1245         auto filterRenderContext = filterNode->GetRenderContext();
1246         CHECK_NULL_VOID(filterRenderContext);
1247         filterRenderContext->UpdateBackBlurStyle(styleOption);
1248         filterRenderContext->UpdateBackgroundColor(GetMenuMaskColor());
1249     }
1250 }
1251 } // namespace OHOS::Ace::NG
1252