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