• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 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/menu_pattern.h"
17 
18 #include "base/geometry/dimension.h"
19 #include "base/log/dump_log.h"
20 #include "base/subwindow/subwindow_manager.h"
21 #include "core/components/common/layout/grid_system_manager.h"
22 #include "core/components/common/properties/shadow_config.h"
23 #include "core/components/container_modal/container_modal_constants.h"
24 #include "core/components/select/select_theme.h"
25 #include "core/components/theme/shadow_theme.h"
26 #include "core/components_ng/base/ui_node.h"
27 #include "core/components_ng/manager/drag_drop/utils/drag_animation_helper.h"
28 #include "core/components_ng/pattern/image/image_pattern.h"
29 #include "core/components_ng/pattern/menu/menu_divider/menu_divider_pattern.h"
30 #include "core/components_ng/pattern/menu/menu_item/menu_item_layout_property.h"
31 #include "core/components_ng/pattern/menu/menu_item/menu_item_pattern.h"
32 #include "core/components_ng/pattern/menu/menu_item_group/menu_item_group_pattern.h"
33 #include "core/components_ng/pattern/menu/menu_layout_property.h"
34 #include "core/components_ng/pattern/menu/menu_view.h"
35 #include "core/components_ng/pattern/menu/multi_menu_layout_algorithm.h"
36 #include "core/components_ng/pattern/menu/preview/menu_preview_pattern.h"
37 #include "core/components_ng/pattern/menu/sub_menu_layout_algorithm.h"
38 #include "core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.h"
39 #include "core/components_ng/pattern/scroll/scroll_pattern.h"
40 #include "core/components_ng/pattern/stack/stack_pattern.h"
41 #include "core/components_ng/pattern/text/text_layout_property.h"
42 #include "core/components_ng/property/border_property.h"
43 #include "core/components_v2/inspector/inspector_constants.h"
44 #include "core/event/touch_event.h"
45 #include "core/pipeline/pipeline_base.h"
46 #include "core/pipeline_ng/pipeline_context.h"
47 
48 namespace OHOS::Ace::NG {
49 namespace {
50 constexpr float PAN_MAX_VELOCITY = 2000.0f;
51 constexpr Dimension MIN_SELECT_MENU_WIDTH = 64.0_vp;
52 constexpr int32_t COLUMN_NUM = 2;
53 constexpr int32_t HALF_FOLD_HOVER_DURATION = 1000;
54 constexpr double MENU_ORIGINAL_SCALE = 0.6f;
55 constexpr double MAIN_MENU_OPACITY = 0.4f;
56 constexpr double STACK_MAIN_MENU_DISAPPEAR_DELAY = 150.0f;
57 constexpr double STACK_MAIN_MENU_APPEAR_DURATION = 150.0f;
58 constexpr double STACK_SUB_MENU_APPEAR_DELAY = 50.0f;
59 constexpr double STACK_MENU_APPEAR_DURATION = 400.0f;
60 constexpr double STACK_MENU_DISAPPEAR_DURATION = 400.0f;
61 const RefPtr<Curve> CUSTOM_PREVIEW_ANIMATION_CURVE =
62     AceType::MakeRefPtr<InterpolatingSpring>(0.0f, 1.0f, 380.0f, 34.0f);
63 const RefPtr<InterpolatingSpring> MAIN_MENU_ANIMATION_CURVE =
64     AceType::MakeRefPtr<InterpolatingSpring>(0.0f, 1.0f, 528.0f, 35.0f);
65 const RefPtr<InterpolatingSpring> STACK_SUB_MENU_ANIMATION_CURVE =
66     AceType::MakeRefPtr<InterpolatingSpring>(0.0f, 1.0f, 228.0f, 26.0f);
67 const float MINIMUM_AMPLITUDE_RATION = 0.08f;
68 
69 constexpr double MOUNT_MENU_FINAL_SCALE = 0.95f;
70 constexpr double SEMI_CIRCLE_ANGEL = 90.0f;
71 constexpr Dimension PADDING = 4.0_vp;
72 constexpr double HALF = 2.0;
73 constexpr Dimension OPTION_MARGIN = 8.0_vp;
74 
UpdateFontStyle(RefPtr<MenuLayoutProperty> & menuProperty,RefPtr<MenuItemLayoutProperty> & itemProperty,RefPtr<MenuItemPattern> & itemPattern,bool & contentChanged,bool & labelChanged)75 void UpdateFontStyle(RefPtr<MenuLayoutProperty>& menuProperty, RefPtr<MenuItemLayoutProperty>& itemProperty,
76     RefPtr<MenuItemPattern>& itemPattern, bool& contentChanged, bool& labelChanged)
77 {
78     auto contentNode = itemPattern->GetContentNode();
79     CHECK_NULL_VOID(contentNode);
80     auto textLayoutProperty = contentNode->GetLayoutProperty<TextLayoutProperty>();
81     CHECK_NULL_VOID(textLayoutProperty);
82     auto label = itemPattern->GetLabelNode();
83     RefPtr<TextLayoutProperty> labelProperty = label ? label->GetLayoutProperty<TextLayoutProperty>() : nullptr;
84     if (menuProperty->GetItalicFontStyle().has_value()) {
85         if (!itemProperty->GetItalicFontStyle().has_value()) {
86             textLayoutProperty->UpdateItalicFontStyle(menuProperty->GetItalicFontStyle().value());
87             contentChanged = true;
88         }
89         if (labelProperty && !itemProperty->GetLabelItalicFontStyle().has_value()) {
90             labelProperty->UpdateItalicFontStyle(menuProperty->GetItalicFontStyle().value());
91             labelChanged = true;
92         }
93     }
94     if (menuProperty->GetFontFamily().has_value()) {
95         if (!itemProperty->GetFontFamily().has_value()) {
96             textLayoutProperty->UpdateFontFamily(menuProperty->GetFontFamily().value());
97             contentChanged = true;
98         }
99         if (labelProperty && !itemProperty->GetLabelFontFamily().has_value()) {
100             labelProperty->UpdateFontFamily(menuProperty->GetFontFamily().value());
101             labelChanged = true;
102         }
103     }
104 }
105 
UpdateMenuItemTextNode(RefPtr<MenuLayoutProperty> & menuProperty,RefPtr<MenuItemLayoutProperty> & itemProperty,RefPtr<MenuItemPattern> & itemPattern)106 void UpdateMenuItemTextNode(RefPtr<MenuLayoutProperty>& menuProperty, RefPtr<MenuItemLayoutProperty>& itemProperty,
107     RefPtr<MenuItemPattern>& itemPattern)
108 {
109     auto contentNode = itemPattern->GetContentNode();
110     CHECK_NULL_VOID(contentNode);
111     auto textLayoutProperty = contentNode->GetLayoutProperty<TextLayoutProperty>();
112     CHECK_NULL_VOID(textLayoutProperty);
113     auto label = itemPattern->GetLabelNode();
114     RefPtr<TextLayoutProperty> labelProperty = label ? label->GetLayoutProperty<TextLayoutProperty>() : nullptr;
115     bool contentChanged = false;
116     bool labelChanged = false;
117     if (menuProperty->GetFontSize().has_value()) {
118         if (!itemProperty->GetFontSize().has_value()) {
119             textLayoutProperty->UpdateFontSize(menuProperty->GetFontSize().value());
120             contentChanged = true;
121         }
122         if (labelProperty && !itemProperty->GetLabelFontSize().has_value()) {
123             labelProperty->UpdateFontSize(menuProperty->GetFontSize().value());
124             labelChanged = true;
125         }
126     }
127     if (menuProperty->GetFontWeight().has_value()) {
128         if (!itemProperty->GetFontWeight().has_value()) {
129             textLayoutProperty->UpdateFontWeight(menuProperty->GetFontWeight().value());
130             contentChanged = true;
131         }
132         if (labelProperty && !itemProperty->GetLabelFontWeight().has_value()) {
133             labelProperty->UpdateFontWeight(menuProperty->GetFontWeight().value());
134             labelChanged = true;
135         }
136     }
137     if (menuProperty->GetFontColor().has_value()) {
138         if (!itemProperty->GetFontColor().has_value()) {
139             textLayoutProperty->UpdateTextColor(menuProperty->GetFontColor().value());
140             contentChanged = true;
141         }
142         if (labelProperty && !itemProperty->GetLabelFontColor().has_value()) {
143             labelProperty->UpdateTextColor(menuProperty->GetFontColor().value());
144             labelChanged = true;
145         }
146     }
147     UpdateFontStyle(menuProperty, itemProperty, itemPattern, contentChanged, labelChanged);
148     if (contentChanged) {
149         contentNode->MarkModifyDone();
150         contentNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
151     }
152     if (labelChanged) {
153         label->MarkModifyDone();
154         label->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
155     }
156 }
157 
ShowMenuOpacityAnimation(const RefPtr<MenuTheme> & menuTheme,const RefPtr<FrameNode> & host,int32_t delay)158 void ShowMenuOpacityAnimation(const RefPtr<MenuTheme>& menuTheme, const RefPtr<FrameNode>& host,
159     int32_t delay)
160 {
161     CHECK_NULL_VOID(host);
162     CHECK_NULL_VOID(menuTheme);
163     auto renderContext = host->GetRenderContext();
164     CHECK_NULL_VOID(renderContext);
165 
166     renderContext->UpdateOpacity(0.0);
167     AnimationOption option = AnimationOption();
168     option.SetCurve(Curves::FRICTION);
169     option.SetDuration(menuTheme->GetContextMenuAppearDuration());
170     option.SetDelay(delay);
171     AnimationUtils::Animate(option, [renderContext]() {
172         if (renderContext) {
173             renderContext->UpdateOpacity(1.0);
174         }
175     }, nullptr, nullptr, host->GetContextRefPtr());
176 }
177 } // namespace
178 
OnAttachToFrameNode()179 void MenuPattern::OnAttachToFrameNode()
180 {
181     RegisterOnTouch();
182     auto host = GetHost();
183     CHECK_NULL_VOID(host);
184     MenuView::RegisterAccessibilityChildActionNotify(host);
185     auto focusHub = host->GetOrCreateFocusHub();
186     CHECK_NULL_VOID(focusHub);
187     RegisterOnKeyEvent(focusHub);
188     DisableTabInMenu();
189     InitTheme(host);
190     auto pipelineContext = host->GetContextWithCheck();
191     CHECK_NULL_VOID(pipelineContext);
192     auto targetNode = FrameNode::GetFrameNode(targetTag_, targetId_);
193     CHECK_NULL_VOID(targetNode);
194     auto eventHub = targetNode->GetOrCreateEventHub<EventHub>();
195     CHECK_NULL_VOID(eventHub);
196     halfFoldHoverCallbackId_ = RegisterHalfFoldHover(targetNode);
197     OnAreaChangedFunc onAreaChangedFunc = [menuNodeWk = WeakPtr<FrameNode>(host)](const RectF& /* oldRect */,
198                                               const OffsetF& /* oldOrigin */, const RectF& /* rect */,
199                                               const OffsetF& /* origin */) {
200         auto menuNode = menuNodeWk.Upgrade();
201         CHECK_NULL_VOID(menuNode);
202         auto menuPattern = menuNode->GetPattern<MenuPattern>();
203         CHECK_NULL_VOID(menuPattern);
204         auto menuWarpper = menuPattern->GetMenuWrapper();
205         CHECK_NULL_VOID(menuWarpper);
206         auto warpperPattern = menuWarpper->GetPattern<MenuWrapperPattern>();
207         CHECK_NULL_VOID(warpperPattern);
208         auto isMenuHide = warpperPattern->IsHide();
209         TAG_LOGI(AceLogTag::ACE_MENU, "the area of target node is changed, isMenuHide: %{public}d", isMenuHide);
210         if (menuNode->IsOnMainTree() && !isMenuHide) {
211             menuNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
212         }
213     };
214     eventHub->AddInnerOnAreaChangedCallback(host->GetId(), std::move(onAreaChangedFunc));
215 
216     auto foldStatusChangeCallback = [weak = WeakClaim(this)](FoldStatus foldStatus) {
217         TAG_LOGI(AceLogTag::ACE_MENU, "foldStatus is changed: %{public}d", foldStatus);
218         auto menuPattern = weak.Upgrade();
219         CHECK_NULL_VOID(menuPattern);
220         auto menuWrapper = menuPattern->GetMenuWrapper();
221         CHECK_NULL_VOID(menuWrapper);
222         auto wrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
223         CHECK_NULL_VOID(wrapperPattern);
224         wrapperPattern->SetHasFoldModeChangedTransition(true);
225         SubwindowManager::GetInstance()->HideMenuNG(false);
226     };
227     foldStatusChangedCallbackId_ =
228         pipelineContext->RegisterFoldStatusChangedCallback(std::move(foldStatusChangeCallback));
229 }
230 
RegisterHalfFoldHover(const RefPtr<FrameNode> & menuNode)231 int32_t MenuPattern::RegisterHalfFoldHover(const RefPtr<FrameNode>& menuNode)
232 {
233     // register when hoverMode enabled
234     auto pipelineContext = menuNode->GetContext();
235     CHECK_NULL_RETURN(pipelineContext, 0);
236     int32_t callbackId = pipelineContext->RegisterHalfFoldHoverChangedCallback(
237         [weak = WeakClaim(this), pipelineContext](bool isHalfFoldHover) {
238         auto pattern = weak.Upgrade();
239         CHECK_NULL_VOID(pattern);
240         auto host = pattern->GetHost();
241         CHECK_NULL_VOID(host);
242         AnimationOption optionPosition;
243         auto motion = AceType::MakeRefPtr<ResponsiveSpringMotion>(0.35f, 1.0f, 0.0f);
244         optionPosition.SetDuration(HALF_FOLD_HOVER_DURATION);
245         optionPosition.SetCurve(motion);
246         auto menuWrapperNode = pattern->GetMenuWrapper();
247         CHECK_NULL_VOID(menuWrapperNode);
248         auto menuWrapperPattern = menuWrapperNode->GetPattern<MenuWrapperPattern>();
249         CHECK_NULL_VOID(menuWrapperPattern);
250         if (menuWrapperPattern->GetHoverMode().value_or(false) && pattern->IsSubMenu()) {
251             menuWrapperPattern->HideSubMenu();
252         }
253         pipelineContext->FlushUITasks();
254         pipelineContext->Animate(optionPosition, motion, [host, pipelineContext]() {
255             host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
256             pipelineContext->FlushUITasks();
257         });
258     });
259     return callbackId;
260 }
261 
OnDetachFromFrameNode(FrameNode * frameNode)262 void MenuPattern::OnDetachFromFrameNode(FrameNode* frameNode)
263 {
264     CHECK_NULL_VOID(frameNode);
265     auto targetNode = FrameNode::GetFrameNode(targetTag_, targetId_);
266     CHECK_NULL_VOID(targetNode);
267     auto eventHub = targetNode->GetOrCreateEventHub<EventHub>();
268     CHECK_NULL_VOID(eventHub);
269     eventHub->RemoveInnerOnAreaChangedCallback(frameNode->GetId());
270 
271     if (foldStatusChangedCallbackId_.has_value()) {
272         auto pipeline = frameNode->GetContext();
273         CHECK_NULL_VOID(pipeline);
274         pipeline->UnRegisterFoldStatusChangedCallback(foldStatusChangedCallbackId_.value_or(-1));
275     }
276 }
277 
OnModifyDone()278 void MenuPattern::OnModifyDone()
279 {
280     Pattern::OnModifyDone();
281     auto host = GetHost();
282     CHECK_NULL_VOID(host);
283     isNeedDivider_ = false;
284     auto uiNode = AceType::DynamicCast<UINode>(host);
285     RefPtr<UINode> previousNode = nullptr;
286     UpdateMenuItemChildren(uiNode, previousNode);
287     RemoveLastNodeDivider(previousNode);
288     ResetThemeByInnerMenuCount();
289     auto menuWrapperNode = GetMenuWrapper();
290     CHECK_NULL_VOID(menuWrapperNode);
291     auto menuWrapperPattern = menuWrapperNode->GetPattern<MenuWrapperPattern>();
292     CHECK_NULL_VOID(menuWrapperPattern);
293     if (!menuWrapperPattern->GetHasCustomRadius()) {
294         auto menuFirstNode = GetFirstInnerMenu();
295         if (menuFirstNode) {
296             CopyMenuAttr(menuFirstNode);
297         }
298     }
299 
300     auto menuLayoutProperty = GetLayoutProperty<MenuLayoutProperty>();
301     CHECK_NULL_VOID(menuLayoutProperty);
302     if (menuLayoutProperty->GetBorderRadius().has_value()) {
303         BorderRadiusProperty borderRadius = menuLayoutProperty->GetBorderRadiusValue();
304         UpdateBorderRadius(host, borderRadius);
305     }
306     auto pipelineContext = host->GetContextRefPtr();
307     CHECK_NULL_VOID(pipelineContext);
308     auto selecTheme = pipelineContext->GetTheme<SelectTheme>();
309     CHECK_NULL_VOID(selecTheme);
310     if (selecTheme->GetMenuItemNeedFocus()) {
311         UpdateMenuBorderAndBackgroundBlur();
312     }
313     SetAccessibilityAction();
314 
315     if (previewMode_ != MenuPreviewMode::NONE) {
316         auto node = host->GetChildren().front();
317         CHECK_NULL_VOID(node);
318         auto scroll = AceType::DynamicCast<FrameNode>(node);
319         CHECK_NULL_VOID(scroll);
320         auto hub = scroll->GetOrCreateEventHub<EventHub>();
321         CHECK_NULL_VOID(hub);
322         auto gestureHub = hub->GetOrCreateGestureEventHub();
323         CHECK_NULL_VOID(gestureHub);
324         InitPanEvent(gestureHub);
325     }
326 }
327 
UpdateMenuBorderAndBackgroundBlur()328 void MenuPattern::UpdateMenuBorderAndBackgroundBlur()
329 {
330     auto host = GetHost();
331     CHECK_NULL_VOID(host);
332     auto renderContext = host->GetRenderContext();
333     CHECK_NULL_VOID(renderContext);
334     auto context = host->GetContext();
335     CHECK_NULL_VOID(context);
336     auto theme = context->GetTheme<SelectTheme>();
337     CHECK_NULL_VOID(theme);
338     if (!renderContext->HasBorderColor()) {
339         BorderColorProperty borderColor;
340         borderColor.SetColor(theme->GetMenuNormalBorderColor());
341         renderContext->UpdateBorderColor(borderColor);
342     }
343     if (!renderContext->HasBorderWidth()) {
344         auto menuLayoutProperty = GetLayoutProperty<MenuLayoutProperty>();
345         auto menuBorderWidth = theme->GetMenuNormalBorderWidth();
346         BorderWidthProperty borderWidth;
347         borderWidth.SetBorderWidth(menuBorderWidth);
348         menuLayoutProperty->UpdateBorderWidth(borderWidth);
349         renderContext->UpdateBorderWidth(borderWidth);
350         auto scroll = DynamicCast<FrameNode>(host->GetFirstChild());
351         CHECK_NULL_VOID(scroll);
352         auto scrollRenderContext = scroll->GetRenderContext();
353         CHECK_NULL_VOID(scrollRenderContext);
354         scrollRenderContext->UpdateOffset(OffsetT<Dimension>(menuBorderWidth, menuBorderWidth));
355     }
356 }
357 
ResetThemeByInnerMenuCount()358 void MenuPattern::ResetThemeByInnerMenuCount()
359 {
360     auto host = GetHost();
361     CHECK_NULL_VOID(host);
362     auto innerMenuCount = GetInnerMenuCount();
363     if (innerMenuCount == 1) {
364         ResetTheme(host, false);
365     } else if (innerMenuCount > 1) {
366         // multiple inner menus, reset outer container's shadow for desktop UX
367         ResetTheme(host, true);
368     }
369 }
370 
UpdateMenuItemDivider()371 void MenuPattern::UpdateMenuItemDivider()
372 {
373     if (!isSelectMenu_) {
374         return;
375     }
376     if (options_.empty()) {
377         return;
378     }
379     auto property = GetLayoutProperty<MenuLayoutProperty>();
380     CHECK_NULL_VOID(property);
381     auto dividerMode = property->GetItemDividerModeValue(DividerMode::FLOATING_ABOVE_MENU);
382     RefPtr<FrameNode> previousNode = nullptr;
383     RefPtr<FrameNode> lastNode = options_.back();
384     for (auto&& option : options_) {
385         CHECK_NULL_VOID(option);
386         auto props = option->GetPaintProperty<MenuItemPaintProperty>();
387         CHECK_NULL_VOID(props);
388         auto optionPattern = option->GetPattern<MenuItemPattern>();
389         CHECK_NULL_VOID(optionPattern);
390         auto frameNode = optionPattern->GetBottomDivider();
391         if (!frameNode) {
392             continue;
393         }
394         if (previousNode) {
395             auto previousPattern = previousNode->GetPattern<MenuItemPattern>();
396             CHECK_NULL_VOID(previousPattern);
397             auto previousBottomDivider = previousPattern->GetBottomDivider();
398             if (previousBottomDivider) {
399                 optionPattern->SetTopDivider(previousBottomDivider);
400             }
401         }
402         if (dividerMode == DividerMode::FLOATING_ABOVE_MENU || lastNode == option) {
403             optionPattern->RemoveBottomDivider();
404         } else {
405             optionPattern->AttachBottomDivider();
406         }
407         auto dividerProperty = frameNode->GetPaintProperty<MenuDividerPaintProperty>();
408         CHECK_NULL_VOID(dividerProperty);
409         dividerProperty->UpdateHasIcon(props->GetHasIconValue(false));
410         previousNode = option;
411     }
412     auto host = GetHost();
413     CHECK_NULL_VOID(host);
414     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
415 }
416 
CreateMenuScroll(const RefPtr<UINode> & node)417 RefPtr<FrameNode> CreateMenuScroll(const RefPtr<UINode>& node)
418 {
419     auto scroll = FrameNode::CreateFrameNode(
420         V2::SCROLL_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ScrollPattern>());
421     CHECK_NULL_RETURN(scroll, nullptr);
422     auto props = scroll->GetLayoutProperty<ScrollLayoutProperty>();
423     props->UpdateAxis(Axis::VERTICAL);
424     props->UpdateAlignment(Alignment::CENTER_LEFT);
425     auto pipeline = scroll->GetContext();
426     CHECK_NULL_RETURN(pipeline, nullptr);
427     auto theme = pipeline->GetTheme<SelectTheme>();
428     CHECK_NULL_RETURN(theme, nullptr);
429     auto contentPadding = static_cast<float>(theme->GetMenuPadding().ConvertToPx());
430     PaddingProperty padding;
431     padding.left = padding.right = padding.top = padding.bottom = CalcLength(contentPadding);
432     props->UpdatePadding(padding);
433     if (node) {
434         node->MountToParent(scroll);
435     }
436     auto renderContext = scroll->GetRenderContext();
437     CHECK_NULL_RETURN(renderContext, nullptr);
438     BorderRadiusProperty borderRadius;
439     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
440         borderRadius.SetRadius(theme->GetMenuDefaultRadius());
441     } else {
442         borderRadius.SetRadius(theme->GetMenuBorderRadius());
443     }
444     renderContext->UpdateBorderRadius(borderRadius);
445     return scroll;
446 }
447 
FireBuilder()448 void MenuPattern::FireBuilder()
449 {
450     auto host = GetHost();
451     CHECK_NULL_VOID(host);
452     host->RemoveChild(builderNode_.Upgrade());
453     if (!makeFunc_.has_value()) {
454         return;
455     }
456     auto column = FrameNode::CreateFrameNode(V2::COLUMN_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
457         AceType::MakeRefPtr<LinearLayoutPattern>(true));
458     auto scroll = CreateMenuScroll(column);
459     CHECK_NULL_VOID(scroll);
460     builderNode_ = scroll;
461     for (size_t i = 0; i < selectProperties_.size(); i++) {
462         auto contentModifierNode = BuildContentModifierNode(i);
463         if (contentModifierNode) {
464             contentModifierNode->MarkModifyDone();
465             contentModifierNode->MountToParent(column);
466         }
467     }
468     auto scrollPattern = scroll->GetPattern<ScrollPattern>();
469     CHECK_NULL_VOID(scrollPattern);
470     scrollPattern->SetIsSelectScroll(true);
471     scroll->MountToParent(host);
472     scroll->MarkModifyDone();
473     host->MarkModifyDone();
474     SetIsSelectMenu(true);
475 }
476 
BuildContentModifierNode(int index)477 RefPtr<FrameNode> MenuPattern::BuildContentModifierNode(int index)
478 {
479     if (!makeFunc_.has_value()) {
480         return nullptr;
481     }
482     auto property = selectProperties_[index];
483     MenuItemConfiguration menuItemConfiguration(property.value, property.icon, property.symbolModifier,
484         index, property.selected, property.selectEnable);
485     return (makeFunc_.value())(menuItemConfiguration);
486 }
487 
UpdateSelectIndex(int32_t index)488 void MenuPattern::UpdateSelectIndex(int32_t index)
489 {
490     for (size_t i = 0; i < selectParams_.size(); i++) {
491         selectProperties_[i].selected = index == static_cast<int32_t>(i);
492     }
493     FireBuilder();
494 }
495 
BeforeCreateLayoutWrapper()496 void InnerMenuPattern::BeforeCreateLayoutWrapper()
497 {
498     // determine menu type based on sibling menu count
499     auto count = FindSiblingMenuCount();
500     if (count > 0) {
501         SetType(MenuType::DESKTOP_MENU);
502         ApplyDesktopMenuTheme();
503     } else {
504         SetType(MenuType::MULTI_MENU);
505         ApplyMultiMenuTheme();
506     }
507 }
508 
OnModifyDone()509 void InnerMenuPattern::OnModifyDone()
510 {
511     Pattern::OnModifyDone();
512     auto host = GetHost();
513     CHECK_NULL_VOID(host);
514     ResetNeedDivider();
515     auto uiNode = AceType::DynamicCast<UINode>(host);
516     RefPtr<UINode> previousNode = nullptr;
517     UpdateMenuItemChildren(uiNode, previousNode);
518     RemoveLastNodeDivider(previousNode);
519     SetAccessibilityAction();
520     auto pipelineContext = host->GetContextRefPtr();
521     CHECK_NULL_VOID(pipelineContext);
522     auto selecTheme = pipelineContext->GetTheme<SelectTheme>();
523     CHECK_NULL_VOID(selecTheme);
524     if (selecTheme->GetMenuItemNeedFocus()) {
525         InitDefaultBorder(host);
526     }
527 }
528 
InitDefaultBorder(const RefPtr<FrameNode> & host)529 void InnerMenuPattern::InitDefaultBorder(const RefPtr<FrameNode>& host)
530 {
531     CHECK_NULL_VOID(host);
532     auto context = host->GetContextRefPtr();
533     CHECK_NULL_VOID(context);
534     auto menuTheme = context->GetTheme<NG::MenuTheme>();
535     CHECK_NULL_VOID(menuTheme);
536     auto renderContext = host->GetRenderContext();
537     CHECK_NULL_VOID(renderContext);
538 
539     if (!renderContext->HasBorderColor()) {
540         BorderColorProperty borderColorProperty;
541         borderColorProperty.SetColor(menuTheme->GetBorderColor());
542         renderContext->UpdateBorderColor(borderColorProperty);
543     }
544 
545     if (!renderContext->HasBorderWidth()) {
546         auto layoutProperty = host->GetLayoutProperty<MenuLayoutProperty>();
547         BorderWidthProperty widthProp;
548         widthProp.SetBorderWidth(menuTheme->GetBorderWidth());
549         layoutProperty->UpdateBorderWidth(widthProp);
550         renderContext->UpdateBorderWidth(widthProp);
551     }
552 }
553 
RemoveLastNodeDivider(const RefPtr<UINode> & lastNode)554 void MenuPattern::RemoveLastNodeDivider(const RefPtr<UINode>& lastNode)
555 {
556     CHECK_NULL_VOID(lastNode);
557     auto lastFrameNode = AceType::DynamicCast<FrameNode>(lastNode);
558     CHECK_NULL_VOID(lastFrameNode);
559     auto lastPattern = lastFrameNode->GetPattern<MenuItemPattern>();
560     CHECK_NULL_VOID(lastPattern);
561     lastPattern->RemoveBottomDivider();
562 }
563 
564 // close menu on touch up
RegisterOnTouch()565 void MenuPattern::RegisterOnTouch()
566 {
567     CHECK_NULL_VOID(!onTouch_);
568     auto host = GetHost();
569     CHECK_NULL_VOID(host);
570     auto gesture = host->GetOrCreateGestureEventHub();
571     CHECK_NULL_VOID(gesture);
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 MenuPattern::OnTouchEvent(const TouchEventInfo& info)
582 {
583     if (GetInnerMenuCount() > 0 || IsMultiMenu() || IsDesktopMenu()|| IsSelectOverlayCustomMenu()) {
584         // not click hide menu for multi menu or select overlay custom menu
585         return;
586     }
587     if (!options_.empty()) {
588         // not click hide menu for select and bindMenu default option
589         return;
590     }
591     if (!needHideAfterTouch_) {
592         // not click hide menu if needn't hide after touch
593         return;
594     }
595     if (info.GetTouches().empty()) {
596         return;
597     }
598     auto touchType = info.GetTouches().front().GetTouchType();
599     if (touchType == TouchType::DOWN) {
600         lastTouchOffset_ = info.GetTouches().front().GetLocalLocation();
601     } else if (touchType == TouchType::UP) {
602         auto touchUpOffset = info.GetTouches().front().GetLocalLocation();
603         if (lastTouchOffset_.has_value() &&
604             (touchUpOffset - lastTouchOffset_.value()).GetDistance() <= DEFAULT_CLICK_DISTANCE) {
605             auto touchGlobalLocation = info.GetTouches().front().GetGlobalLocation();
606             auto position = OffsetF(static_cast<float>(touchGlobalLocation.GetX()),
607                 static_cast<float>(touchGlobalLocation.GetY()));
608             TAG_LOGI(AceLogTag::ACE_MENU, "will hide menu, position is %{public}s.", position.ToString().c_str());
609             HideMenu(true, position, HideMenuType::MENU_TOUCH_UP);
610         }
611         lastTouchOffset_.reset();
612     }
613 }
614 
RegisterOnKeyEvent(const RefPtr<FocusHub> & focusHub)615 void MenuPattern::RegisterOnKeyEvent(const RefPtr<FocusHub>& focusHub)
616 {
617     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
618         auto pattern = wp.Upgrade();
619         CHECK_NULL_RETURN(pattern, false);
620         return pattern->OnKeyEvent(event);
621     };
622     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
623 }
624 
OnKeyEvent(const KeyEvent & event)625 bool MenuPattern::OnKeyEvent(const KeyEvent& event)
626 {
627     if (event.action != KeyAction::DOWN || IsMultiMenu() || IsDesktopMenu()) {
628         return false;
629     }
630     if ((event.code == KeyCode::KEY_DPAD_LEFT || event.code == KeyCode::KEY_ESCAPE) &&
631         (IsSubMenu() || IsSelectOverlaySubMenu())) {
632         auto menuWrapper = GetMenuWrapper();
633         CHECK_NULL_RETURN(menuWrapper, true);
634         auto wrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
635         CHECK_NULL_RETURN(wrapperPattern, true);
636         RemoveParentHoverStyle();
637         wrapperPattern->HideSubMenu();
638         return true;
639     }
640     return false;
641 }
642 
RemoveParentHoverStyle()643 void MenuPattern::RemoveParentHoverStyle()
644 {
645     if (!IsSubMenu()) {
646         return;
647     }
648     auto menuItemParent = GetParentMenuItem();
649     CHECK_NULL_VOID(menuItemParent);
650     auto menuItemPattern = menuItemParent->GetPattern<MenuItemPattern>();
651     CHECK_NULL_VOID(menuItemPattern);
652     menuItemPattern->SetIsSubMenuShowed(false);
653     menuItemPattern->OnHover(false);
654 }
655 
UpdateMenuItemChildren(const RefPtr<UINode> & host,RefPtr<UINode> & previousNode)656 void MenuPattern::UpdateMenuItemChildren(const RefPtr<UINode>& host, RefPtr<UINode>& previousNode)
657 {
658     CHECK_NULL_VOID(host);
659     auto layoutProperty = GetLayoutProperty<MenuLayoutProperty>();
660     CHECK_NULL_VOID(layoutProperty);
661     const auto& children = host->GetChildren();
662     int32_t index = 0;
663     for (auto child : children) {
664         auto tag = child->GetTag();
665         if (tag == V2::MENU_ITEM_ETS_TAG) {
666             auto itemNode = AceType::DynamicCast<FrameNode>(child);
667             CHECK_NULL_VOID(itemNode);
668             auto itemProperty = itemNode->GetLayoutProperty<MenuItemLayoutProperty>();
669             CHECK_NULL_VOID(itemProperty);
670             auto itemPattern = itemNode->GetPattern<MenuItemPattern>();
671             CHECK_NULL_VOID(itemPattern);
672             auto expandingMode = layoutProperty->GetExpandingMode().value_or(SubMenuExpandingMode::SIDE);
673             if (expandingMode != itemPattern->GetExpandingMode() || IsEmbedded()) {
674                 auto expandNode = itemPattern->GetHost();
675                 CHECK_NULL_VOID(expandNode);
676                 expandNode->MarkModifyDone();
677                 expandNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
678             }
679             UpdateMenuItemTextNode(layoutProperty, itemProperty, itemPattern);
680             UpdateMenuDividerWithMode(previousNode, child, layoutProperty, index);
681             itemPattern->UpdateNeedDivider(isNeedDivider_);
682             isNeedDivider_ = true;
683             itemPattern->SetIndex(index);
684             previousNode = child;
685         } else if (tag == V2::MENU_ITEM_GROUP_ETS_TAG) {
686             auto childItemNode = AceType::DynamicCast<FrameNode>(child);
687             CHECK_NULL_VOID(childItemNode);
688             auto pattern = childItemNode->GetPattern<MenuItemGroupPattern>();
689             CHECK_NULL_VOID(pattern);
690             pattern->ModifyDivider();
691             isNeedDivider_ = false;
692             AddGroupHeaderDivider(previousNode, child, layoutProperty, index);
693             UpdateMenuItemChildren(child, previousNode);
694             AddGroupFooterDivider(previousNode, child, layoutProperty, index);
695             isNeedDivider_ = false;
696             auto accessibilityProperty =
697                 childItemNode->GetAccessibilityProperty<AccessibilityProperty>();
698             CHECK_NULL_VOID(accessibilityProperty);
699             accessibilityProperty->SetAccessibilityLevel(AccessibilityProperty::Level::NO_STR);
700         } else if (tag == V2::JS_FOR_EACH_ETS_TAG || tag == V2::JS_SYNTAX_ITEM_ETS_TAG
701             ||  tag == V2::JS_IF_ELSE_ETS_TAG || tag == V2::JS_REPEAT_ETS_TAG) {
702             UpdateMenuItemChildren(child, previousNode);
703         }
704         index++;
705     }
706 }
707 
UpdateMenuDividerWithMode(const RefPtr<UINode> & previousNode,const RefPtr<UINode> & currentNode,const RefPtr<MenuLayoutProperty> & property,int32_t & index)708 void MenuPattern::UpdateMenuDividerWithMode(const RefPtr<UINode>& previousNode, const RefPtr<UINode>& currentNode,
709     const RefPtr<MenuLayoutProperty>& property, int32_t& index)
710 {
711     CHECK_NULL_VOID(previousNode);
712     CHECK_NULL_VOID(currentNode);
713     CHECK_NULL_VOID(property);
714     auto currentFrameNode = AceType::DynamicCast<FrameNode>(currentNode);
715     CHECK_NULL_VOID(currentFrameNode);
716     auto currentPattern = currentFrameNode->GetPattern<MenuItemPattern>();
717     CHECK_NULL_VOID(currentPattern);
718     auto previousFrameNode = AceType::DynamicCast<FrameNode>(previousNode);
719     CHECK_NULL_VOID(previousFrameNode);
720     auto itemDividerMode = isNeedDivider_ ? property->GetItemDividerModeValue(DividerMode::FLOATING_ABOVE_MENU)
721                                           : property->GetItemGroupDividerModeValue(DividerMode::FLOATING_ABOVE_MENU);
722     if (previousFrameNode->GetTag() == V2::MENU_ITEM_GROUP_ETS_TAG) {
723         auto previousPattern = previousFrameNode->GetPattern<MenuItemGroupPattern>();
724         CHECK_NULL_VOID(previousPattern);
725         UpdateDividerProperty(previousPattern->GetBottomDivider(),
726             isNeedDivider_ ? property->GetItemDivider() : property->GetItemGroupDivider());
727         if (itemDividerMode == DividerMode::FLOATING_ABOVE_MENU) {
728             previousPattern->RemoveBottomDivider();
729         } else {
730             previousPattern->AttachBottomDivider();
731             index++;
732         }
733         currentPattern->SetTopDivider(previousPattern->GetBottomDivider());
734     } else {
735         auto previousPattern = previousFrameNode->GetPattern<MenuItemPattern>();
736         CHECK_NULL_VOID(previousPattern);
737         UpdateDividerProperty(previousPattern->GetBottomDivider(),
738             isNeedDivider_ ? property->GetItemDivider() : property->GetItemGroupDivider());
739         if (itemDividerMode == DividerMode::FLOATING_ABOVE_MENU) {
740             previousPattern->RemoveBottomDivider();
741         } else {
742             previousPattern->AttachBottomDivider();
743             index++;
744         }
745         currentPattern->SetTopDivider(previousPattern->GetBottomDivider());
746     }
747 }
748 
AddGroupHeaderDivider(RefPtr<UINode> & previousNode,const RefPtr<UINode> & currentNode,const RefPtr<MenuLayoutProperty> & property,int32_t & index)749 void MenuPattern::AddGroupHeaderDivider(RefPtr<UINode>& previousNode, const RefPtr<UINode>& currentNode,
750     const RefPtr<MenuLayoutProperty>& property, int32_t& index)
751 {
752     auto groupNode = AceType::DynamicCast<FrameNode>(currentNode);
753     CHECK_NULL_VOID(groupNode);
754     auto groupPattern = groupNode->GetPattern<MenuItemGroupPattern>();
755     CHECK_NULL_VOID(groupPattern && groupPattern->GetHeader());
756     auto itemDividerMode = property->GetItemGroupDividerModeValue(DividerMode::FLOATING_ABOVE_MENU);
757     auto previousFrameNode = AceType::DynamicCast<FrameNode>(previousNode);
758     CHECK_NULL_VOID(previousFrameNode);
759     if (previousFrameNode->GetTag() == V2::MENU_ITEM_GROUP_ETS_TAG) {
760         auto previousPattern = previousFrameNode->GetPattern<MenuItemGroupPattern>();
761         CHECK_NULL_VOID(previousPattern);
762         UpdateDividerProperty(previousPattern->GetBottomDivider(), property->GetItemGroupDivider());
763         if (itemDividerMode == DividerMode::FLOATING_ABOVE_MENU) {
764             previousPattern->RemoveBottomDivider();
765         } else {
766             previousPattern->AttachBottomDivider();
767             index++;
768         }
769     } else {
770         auto previousPattern = previousFrameNode->GetPattern<MenuItemPattern>();
771         CHECK_NULL_VOID(previousPattern);
772         UpdateDividerProperty(previousPattern->GetBottomDivider(), property->GetItemGroupDivider());
773         if (itemDividerMode == DividerMode::FLOATING_ABOVE_MENU) {
774             previousPattern->RemoveBottomDivider();
775         } else {
776             previousPattern->AttachBottomDivider();
777             index++;
778         }
779     }
780     // If the child of the group contains only the header.
781     if (groupNode->GetChildren().size() == 1) {
782         previousNode = currentNode;
783     } else {
784         previousNode = nullptr;
785     }
786 }
787 
AddGroupFooterDivider(RefPtr<UINode> & previousNode,const RefPtr<UINode> & currentNode,const RefPtr<MenuLayoutProperty> & property,int32_t & index)788 void MenuPattern::AddGroupFooterDivider(RefPtr<UINode>& previousNode, const RefPtr<UINode>& currentNode,
789     const RefPtr<MenuLayoutProperty>& property, int32_t& index)
790 {
791     auto groupNode = AceType::DynamicCast<FrameNode>(currentNode);
792     CHECK_NULL_VOID(groupNode);
793     auto groupPattern = groupNode->GetPattern<MenuItemGroupPattern>();
794     CHECK_NULL_VOID(groupPattern && groupPattern->GetFooter());
795     auto parent = groupNode->GetParent();
796     CHECK_NULL_VOID(parent);
797     auto itemDividerMode = property->GetItemGroupDividerModeValue(DividerMode::FLOATING_ABOVE_MENU);
798     // If the child of the group contains only the footer.
799     if (groupNode->GetChildren().size() == 1 && previousNode) {
800         if (previousNode->GetTag() == V2::MENU_ITEM_GROUP_ETS_TAG) {
801             auto previousFrameNode = AceType::DynamicCast<FrameNode>(previousNode);
802             CHECK_NULL_VOID(previousFrameNode);
803             auto previousPattern = previousFrameNode->GetPattern<MenuItemGroupPattern>();
804             CHECK_NULL_VOID(previousPattern);
805             UpdateDividerProperty(previousPattern->GetBottomDivider(), property->GetItemGroupDivider());
806             if (itemDividerMode == DividerMode::FLOATING_ABOVE_MENU) {
807                 previousPattern->RemoveBottomDivider();
808             } else {
809                 previousPattern->AttachBottomDivider();
810                 index++;
811             }
812         } else {
813             auto previousFrameNode = AceType::DynamicCast<FrameNode>(previousNode);
814             CHECK_NULL_VOID(previousFrameNode);
815             auto previousPattern = previousFrameNode->GetPattern<MenuItemPattern>();
816             CHECK_NULL_VOID(previousPattern);
817             UpdateDividerProperty(previousPattern->GetBottomDivider(), property->GetItemGroupDivider());
818             if (itemDividerMode == DividerMode::FLOATING_ABOVE_MENU) {
819                 previousPattern->RemoveBottomDivider();
820             } else {
821                 previousPattern->AttachBottomDivider();
822                 index++;
823             }
824         }
825     }
826     // When the group is not the last item of the menu.
827     if (parent->GetChildIndex(groupNode) < static_cast<int32_t>(parent->GetChildren().size()) - 1) {
828         UpdateDividerProperty(groupPattern->GetBottomDivider(), property->GetItemGroupDivider());
829         if (itemDividerMode == DividerMode::FLOATING_ABOVE_MENU) {
830             groupPattern->RemoveBottomDivider();
831         } else {
832             groupPattern->AttachBottomDivider();
833             index++;
834         }
835         previousNode = currentNode;
836     } else {
837         previousNode = nullptr;
838     }
839 }
840 
UpdateDividerProperty(const RefPtr<FrameNode> & dividerNode,const std::optional<V2::ItemDivider> & divider)841 void MenuPattern::UpdateDividerProperty(
842     const RefPtr<FrameNode>& dividerNode, const std::optional<V2::ItemDivider>& divider)
843 {
844     CHECK_NULL_VOID(dividerNode);
845     auto paintProperty = dividerNode->GetPaintProperty<MenuDividerPaintProperty>();
846     CHECK_NULL_VOID(paintProperty);
847     if (!divider.has_value()) {
848         paintProperty->ResetStrokeWidth();
849         paintProperty->ResetDividerColor();
850         paintProperty->ResetStartMargin();
851         paintProperty->ResetEndMargin();
852     } else {
853         auto value = divider.value();
854         paintProperty->UpdateStrokeWidth(value.strokeWidth);
855         paintProperty->UpdateDividerColor(value.color);
856         paintProperty->UpdateStartMargin(value.startMargin);
857         paintProperty->UpdateEndMargin(value.endMargin);
858     }
859 }
860 
UpdateSelectParam(const std::vector<SelectParam> & params)861     void MenuPattern::UpdateSelectParam(const std::vector<SelectParam>& params)
862 {
863     if (!isSelectMenu_) {
864         return;
865     }
866     auto host = GetHost();
867     CHECK_NULL_VOID(host);
868     const auto& children = GetOptions();
869     auto childCount = children.size();
870     auto paramCount = params.size();
871     size_t updateCount = std::min(paramCount, childCount);
872     auto childIt = children.begin();
873     for (size_t i = 0; i < updateCount; i++, childIt++) {
874         const auto& childNode = AceType::DynamicCast<FrameNode>(*childIt);
875         CHECK_NULL_VOID(childNode);
876         if (i == 0) {
877             auto props = childNode->GetPaintProperty<MenuItemPaintProperty>();
878             CHECK_NULL_VOID(props);
879             props->UpdateNeedDivider(false);
880             auto focusHub = childNode->GetOrCreateFocusHub();
881             CHECK_NULL_VOID(focusHub);
882             focusHub->SetIsDefaultFocus(true);
883         }
884         auto optionPattern = childNode->GetPattern<MenuItemPattern>();
885         CHECK_NULL_VOID(optionPattern);
886         optionPattern->UpdateText(params.at(i).text);
887         optionPattern->UpdateIcon(params.at(i).icon, params.at(i).symbolIcon);
888         childNode->MarkModifyDone();
889         childNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
890     }
891     for (size_t i = updateCount; i < paramCount; i++) {
892         auto optionNode = MenuView::CreateSelectOption(params.at(i), i);
893         auto optionPattern = optionNode->GetPattern<MenuItemPattern>();
894         CHECK_NULL_VOID(optionPattern);
895         optionPattern->SetIsSelectOption(true);
896         if (i == 0) {
897             auto props = optionNode->GetPaintProperty<MenuItemPaintProperty>();
898             props->UpdateNeedDivider(false);
899         }
900         MountOption(optionNode);
901         optionNode->MarkModifyDone();
902         optionNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
903     }
904     for (size_t i = childCount; i > updateCount; i--) {
905         RemoveOption();
906     }
907     host->MarkModifyDone();
908     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
909 }
910 
UpdateSelectOptionTextByIndex(int32_t index,const std::string & text)911 void MenuPattern::UpdateSelectOptionTextByIndex(int32_t index, const std::string& text)
912 {
913     if (!isSelectMenu_) {
914         return;
915     }
916     auto host = GetHost();
917     CHECK_NULL_VOID(host);
918     const auto& children = GetOptions();
919     if (index >= static_cast<int32_t>(children.size())) {
920         return;
921     }
922     auto childIt = children.at(index);
923     const auto& childNode = AceType::DynamicCast<FrameNode>(childIt);
924     CHECK_NULL_VOID(childNode);
925     auto optionPattern = childNode->GetPattern<MenuItemPattern>();
926     CHECK_NULL_VOID(optionPattern);
927     optionPattern->UpdateText(text);
928     childNode->MarkModifyDone();
929     childNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
930     host->MarkModifyDone();
931     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
932 }
933 
UpdateSelectOptionIconByIndex(int32_t index,const std::string & icon)934 void MenuPattern::UpdateSelectOptionIconByIndex(int32_t index, const std::string& icon)
935 {
936     if (!isSelectMenu_) {
937         return;
938     }
939     auto host = GetHost();
940     CHECK_NULL_VOID(host);
941     const auto& children = GetOptions();
942     if (index >= static_cast<int32_t>(children.size())) {
943         return;
944     }
945     auto childIt = children.at(index);
946     const auto& childNode = AceType::DynamicCast<FrameNode>(childIt);
947     CHECK_NULL_VOID(childNode);
948     auto optionPattern = childNode->GetPattern<MenuItemPattern>();
949     CHECK_NULL_VOID(optionPattern);
950     optionPattern->UpdateIcon(icon, nullptr);
951     childNode->MarkModifyDone();
952     childNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
953     host->MarkModifyDone();
954     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
955 }
956 
HideMenu(bool isMenuOnTouch,OffsetF position,const HideMenuType & reason) const957 void MenuPattern::HideMenu(bool isMenuOnTouch, OffsetF position, const HideMenuType& reason) const
958 {
959     auto host = GetHost();
960     CHECK_NULL_VOID(host);
961     auto pipeline = host->GetContextWithCheck();
962     CHECK_NULL_VOID(pipeline);
963     auto theme = pipeline->GetTheme<SelectTheme>();
964     CHECK_NULL_VOID(theme);
965     auto expandDisplay = theme->GetExpandDisplay();
966     auto rootMenuPattern = AceType::DynamicCast<MenuPattern>(host->GetPattern());
967     CHECK_NULL_VOID(rootMenuPattern);
968     // copy menu pattern properties to rootMenu
969     auto layoutProperty = rootMenuPattern->GetLayoutProperty<MenuLayoutProperty>();
970     CHECK_NULL_VOID(layoutProperty);
971     bool isShowInSubWindow = layoutProperty->GetShowInSubWindowValue(true);
972     auto wrapper = GetMenuWrapper();
973     CHECK_NULL_VOID(wrapper);
974     if (wrapper->GetTag() == V2::SELECT_OVERLAY_ETS_TAG) {
975         return;
976     }
977     if (((IsContextMenu() || (expandDisplay && isShowInSubWindow))) && (targetTag_ != V2::SELECT_ETS_TAG)) {
978         TAG_LOGI(AceLogTag::ACE_MENU, "will hide menu, tagetNode id %{public}d. reason %{public}d", targetId_, reason);
979         SubwindowManager::GetInstance()->HideMenuNG(wrapper, targetId_);
980         return;
981     }
982     if (targetTag_ == V2::SELECT_ETS_TAG && expandDisplay && layoutProperty->GetShowInSubWindowValue(false)) {
983         auto subWindowManager = SubwindowManager::GetInstance();
984         CHECK_NULL_VOID(subWindowManager);
985         auto containerId = Container::CurrentId();
986         auto subwindow = subWindowManager->GetSubwindowByType(containerId, SubwindowType::TYPE_MENU);
987         if (subWindowManager->IsSubwindowExist(subwindow)) {
988             subWindowManager->ClearMenuNG(containerId, targetId_);
989             return;
990         }
991     }
992 
993     if (HideStackExpandMenu(position)) {
994         return;
995     }
996 
997     auto overlayManager = pipeline->GetOverlayManager();
998     CHECK_NULL_VOID(overlayManager);
999     TAG_LOGI(AceLogTag::ACE_MENU, "will hide menu, tagetNode id %{public}d.", targetId_);
1000     overlayManager->HideMenu(wrapper, targetId_, isMenuOnTouch, reason);
1001     overlayManager->EraseMenuInfo(targetId_);
1002 }
1003 
HideStackExpandMenu(const OffsetF & position) const1004 bool MenuPattern::HideStackExpandMenu(const OffsetF& position) const
1005 {
1006     auto wrapper = GetMenuWrapper();
1007     CHECK_NULL_RETURN(wrapper, false);
1008     auto outterMenu = wrapper->GetFirstChild();
1009     CHECK_NULL_RETURN(outterMenu, false);
1010     auto menuWrapperPattern = wrapper->GetPattern<MenuWrapperPattern>();
1011     CHECK_NULL_RETURN(menuWrapperPattern, false);
1012     auto innerMenu = menuWrapperPattern->GetMenuChild(outterMenu);
1013     CHECK_NULL_RETURN(innerMenu, false);
1014     auto innerMenuPattern = AceType::DynamicCast<MenuPattern>(innerMenu->GetPattern());
1015     CHECK_NULL_RETURN(innerMenuPattern, false);
1016     auto layoutProps = innerMenuPattern->GetLayoutProperty<MenuLayoutProperty>();
1017     CHECK_NULL_RETURN(layoutProps, false);
1018     auto expandingMode = layoutProps->GetExpandingMode().value_or(SubMenuExpandingMode::SIDE);
1019     if (IsSubMenu() && expandingMode == SubMenuExpandingMode::STACK) {
1020         auto host = GetHost();
1021         CHECK_NULL_RETURN(host, false);
1022         auto hostZone = host->GetPaintRectOffset(false, true);
1023         auto scroll = host->GetFirstChild();
1024         CHECK_NULL_RETURN(scroll, false);
1025         auto column = scroll->GetFirstChild();
1026         CHECK_NULL_RETURN(column, false);
1027         auto clickAreaNode = AceType::DynamicCast<FrameNode>(column->GetFirstChild());
1028         CHECK_NULL_RETURN(clickAreaNode, false);
1029         auto clickAreaZone = clickAreaNode->GetGeometryNode()->GetFrameRect();
1030         clickAreaZone.SetLeft(hostZone.GetX());
1031         clickAreaZone.SetTop(hostZone.GetY());
1032         if (clickAreaZone.IsInRegion(PointF(position.GetX(), position.GetY()))) {
1033             HideStackMenu();
1034             return true;
1035         }
1036     } else if (expandingMode == SubMenuExpandingMode::STACK) {
1037         auto host = GetHost();
1038         CHECK_NULL_RETURN(host, false);
1039         auto hostZone = host->GetPaintRectOffset(false, true);
1040         auto clickAreaZone = host->GetGeometryNode()->GetFrameRect();
1041         clickAreaZone.SetLeft(hostZone.GetX());
1042         clickAreaZone.SetTop(hostZone.GetY());
1043         if (clickAreaZone.IsInRegion(PointF(position.GetX(), position.GetY()))) {
1044             auto wrapperPattern = wrapper->GetPattern<MenuWrapperPattern>();
1045             CHECK_NULL_RETURN(wrapperPattern, false);
1046             wrapperPattern->HideSubMenu();
1047             return true;
1048         }
1049     }
1050     return false;
1051 }
1052 
HideStackMenu() const1053 void MenuPattern::HideStackMenu() const
1054 {
1055     auto host = GetHost();
1056     CHECK_NULL_VOID(host);
1057     auto wrapper = GetMenuWrapper();
1058     CHECK_NULL_VOID(wrapper);
1059     AnimationOption option;
1060     option.SetOnFinishEvent(
1061         [weak = WeakClaim(RawPtr(wrapper)), subMenuWk = WeakClaim(RawPtr(host))] {
1062             auto subMenu = subMenuWk.Upgrade();
1063             CHECK_NULL_VOID(subMenu);
1064             auto pipeline = subMenu->GetContextWithCheck();
1065             CHECK_NULL_VOID(pipeline);
1066             auto taskExecutor = pipeline->GetTaskExecutor();
1067             CHECK_NULL_VOID(taskExecutor);
1068             taskExecutor->PostTask(
1069                 [weak, subMenuWk]() {
1070                     auto subMenuNode = subMenuWk.Upgrade();
1071                     CHECK_NULL_VOID(subMenuNode);
1072                     auto menuWrapper = weak.Upgrade();
1073                     CHECK_NULL_VOID(menuWrapper);
1074                     menuWrapper->RemoveChild(subMenuNode);
1075                     menuWrapper->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
1076                 },
1077                 TaskExecutor::TaskType::UI, "HideStackMenu");
1078     });
1079     auto menuPattern = AceType::DynamicCast<MenuPattern>(host->GetPattern());
1080     if (menuPattern) {
1081         menuPattern->RemoveParentHoverStyle();
1082         auto frameNode = FrameNode::GetFrameNode(menuPattern->GetTargetTag(), menuPattern->GetTargetId());
1083         CHECK_NULL_VOID(frameNode);
1084         auto menuItem = frameNode->GetPattern<MenuItemPattern>();
1085         if (menuItem) {
1086             menuItem->SetIsSubMenuShowed(false);
1087         }
1088     }
1089     auto menuNode = AceType::DynamicCast<FrameNode>(wrapper->GetFirstChild());
1090     CHECK_NULL_VOID(menuNode);
1091     ShowStackMenuDisappearAnimation(menuNode, host, option);
1092 }
1093 
HideSubMenu()1094 void MenuPattern::HideSubMenu()
1095 {
1096     if (!showedSubMenu_) {
1097         return;
1098     }
1099     auto subMenuPattern = showedSubMenu_->GetPattern<MenuPattern>();
1100     CHECK_NULL_VOID(subMenuPattern);
1101     subMenuPattern->RemoveParentHoverStyle();
1102 
1103     auto menuItem = subMenuPattern->GetParentMenuItem();
1104     CHECK_NULL_VOID(menuItem);
1105     auto menuItemPattern = menuItem->GetPattern<MenuItemPattern>();
1106     CHECK_NULL_VOID(menuItemPattern);
1107     menuItemPattern->SetIsSubMenuShowed(false);
1108     menuItemPattern->ClearHoverRegions();
1109     menuItemPattern->ResetWrapperMouseEvent();
1110 
1111     auto wrapper = GetMenuWrapper();
1112     CHECK_NULL_VOID(wrapper);
1113     wrapper->RemoveChild(showedSubMenu_);
1114     wrapper->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
1115     showedSubMenu_.Reset();
1116 }
1117 
GetMenuWrapper() const1118 RefPtr<FrameNode> MenuPattern::GetMenuWrapper() const
1119 {
1120     auto host = GetHost();
1121     CHECK_NULL_RETURN(host, nullptr);
1122     auto parent = host->GetParent();
1123     while (parent) {
1124         if (parent->GetTag() == V2::MENU_WRAPPER_ETS_TAG || parent->GetTag() == V2::SELECT_OVERLAY_ETS_TAG) {
1125             return AceType::DynamicCast<FrameNode>(parent);
1126         }
1127         parent = parent->GetParent();
1128     }
1129     return nullptr;
1130 }
1131 
1132 // search for inner <Menu> node, once found a <Menu> node, count the number of sibling <Menu>
GetInnerMenuCount() const1133 uint32_t MenuPattern::GetInnerMenuCount() const
1134 {
1135     if (type_ == MenuType::MULTI_MENU || type_ == MenuType::DESKTOP_MENU || IsSelectOverlayCustomMenu()) {
1136         return 0;
1137     }
1138 
1139     auto host = GetHost();
1140     CHECK_NULL_RETURN(host, 0);
1141     auto child = host->GetChildAtIndex(0);
1142     uint32_t depth = 0;
1143     while (child && depth < MAX_SEARCH_DEPTH) {
1144         // found component <Menu>
1145         if (child->GetTag() == V2::JS_VIEW_ETS_TAG) {
1146             child = child->GetFrameChildByIndex(0, false);
1147             if (child && child->GetTag() == V2::JS_VIEW_ETS_TAG) {
1148                 child = child->GetChildAtIndex(0);
1149                 ++depth;
1150             }
1151             continue;
1152         }
1153         if (child->GetTag() == V2::MENU_ETS_TAG) {
1154             auto parent = child->GetParent();
1155             CHECK_NULL_RETURN(parent, 0);
1156             return parent->GetChildren().size();
1157         }
1158         child = child->GetChildAtIndex(0);
1159         ++depth;
1160     }
1161     return 0;
1162 }
1163 
GetFirstInnerMenu() const1164 RefPtr<FrameNode> MenuPattern::GetFirstInnerMenu() const
1165 {
1166     if (type_ == MenuType::MULTI_MENU || type_ == MenuType::DESKTOP_MENU) {
1167         return nullptr;
1168     }
1169 
1170     auto host = GetHost();
1171     CHECK_NULL_RETURN(host, nullptr);
1172     uint32_t depth = 0;
1173     auto child = host->GetChildAtIndex(0);
1174     while (child && depth < MAX_SEARCH_DEPTH) {
1175         // found component <Menu>
1176         if (child->GetTag() == V2::JS_VIEW_ETS_TAG) {
1177             child = child->GetFrameChildByIndex(0, false);
1178             if (child && child->GetTag() == V2::JS_VIEW_ETS_TAG) {
1179                 child = child->GetChildAtIndex(0);
1180                 ++depth;
1181             }
1182             continue;
1183         }
1184         if (child->GetTag() == V2::MENU_ETS_TAG) {
1185             return AceType::DynamicCast<FrameNode>(child);
1186         }
1187         child = child->GetChildAtIndex(0);
1188         ++depth;
1189     }
1190     return nullptr;
1191 }
1192 
CopyMenuAttr(const RefPtr<FrameNode> & menuNode) const1193 void MenuPattern::CopyMenuAttr(const RefPtr<FrameNode>& menuNode) const
1194 {
1195     auto pattern = AceType::DynamicCast<MenuPattern>(menuNode->GetPattern());
1196     CHECK_NULL_VOID(pattern);
1197 
1198     auto host = GetHost();
1199     CHECK_NULL_VOID(host);
1200     auto rootMenuPattern = AceType::DynamicCast<MenuPattern>(host->GetPattern());
1201     CHECK_NULL_VOID(rootMenuPattern);
1202 
1203     // copy menu pattern properties to rootMenu
1204     auto layoutProperty = pattern->GetLayoutProperty<MenuLayoutProperty>();
1205     CHECK_NULL_VOID(layoutProperty);
1206     auto rootMenuLayoutProperty = rootMenuPattern->GetLayoutProperty<MenuLayoutProperty>();
1207     CHECK_NULL_VOID(rootMenuLayoutProperty);
1208     if (layoutProperty->GetBorderRadius().has_value()) {
1209         rootMenuLayoutProperty->UpdateBorderRadius(layoutProperty->GetBorderRadiusValue());
1210     }
1211 }
1212 
1213 // mount option on menu
MountOption(const RefPtr<FrameNode> & option)1214 void MenuPattern::MountOption(const RefPtr<FrameNode>& option)
1215 {
1216     auto column = GetMenuColumn();
1217     CHECK_NULL_VOID(column);
1218     auto pattern = option->GetPattern<MenuItemPattern>();
1219     CHECK_NULL_VOID(pattern);
1220     pattern->SetMenu(GetHost());
1221     AddOptionNode(option);
1222     option->MountToParent(column);
1223 }
1224 
1225 // remove option from menu
RemoveOption()1226 void MenuPattern::RemoveOption()
1227 {
1228     auto column = GetMenuColumn();
1229     CHECK_NULL_VOID(column);
1230     auto endOption = column->GetChildren().back();
1231     CHECK_NULL_VOID(endOption);
1232     column->RemoveChild(endOption);
1233     PopOptionNode();
1234 }
1235 
GetMenuColumn() const1236 RefPtr<FrameNode> MenuPattern::GetMenuColumn() const
1237 {
1238     auto menu = GetHost();
1239     CHECK_NULL_RETURN(menu, nullptr);
1240     auto scroll = menu->GetChildren().front();
1241     CHECK_NULL_RETURN(scroll, nullptr);
1242     auto column = scroll->GetChildren().front();
1243     return DynamicCast<FrameNode>(column);
1244 }
1245 
DisableTabInMenu()1246 void MenuPattern::DisableTabInMenu()
1247 {
1248     if (IsMultiMenu() || IsDesktopMenu()) {
1249         // multi menu not has scroll and column
1250         return;
1251     }
1252     // disable tab in menu
1253     auto column = GetMenuColumn();
1254     CHECK_NULL_VOID(column);
1255     auto columnFocusHub = column->GetOrCreateFocusHub();
1256     CHECK_NULL_VOID(columnFocusHub);
1257 
1258     auto onKeyEvent = [](const KeyEvent& event) -> bool {
1259         if (event.action != KeyAction::DOWN) {
1260             return false;
1261         }
1262         return event.code == KeyCode::KEY_TAB;
1263     };
1264     columnFocusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
1265 }
1266 
CreateLayoutAlgorithm()1267 RefPtr<LayoutAlgorithm> MenuPattern::CreateLayoutAlgorithm()
1268 {
1269     switch (type_) {
1270         case MenuType::MULTI_MENU:
1271         case MenuType::DESKTOP_MENU:
1272             return MakeRefPtr<MultiMenuLayoutAlgorithm>();
1273         case MenuType::SUB_MENU:
1274         case MenuType::SELECT_OVERLAY_SUB_MENU:
1275             return MakeRefPtr<SubMenuLayoutAlgorithm>();
1276         default:
1277             return MakeRefPtr<MenuLayoutAlgorithm>(targetId_, targetTag_, lastPosition_);
1278     }
1279 }
1280 
GetShadowFromTheme(ShadowStyle shadowStyle,Shadow & shadow)1281 bool MenuPattern::GetShadowFromTheme(ShadowStyle shadowStyle, Shadow& shadow)
1282 {
1283     if (shadowStyle == ShadowStyle::None) {
1284         return true;
1285     }
1286     auto host = GetHost();
1287     auto pipelineContext = host->GetContextRefPtr();
1288     CHECK_NULL_RETURN(pipelineContext, false);
1289     auto colorMode = pipelineContext->GetColorMode();
1290     auto shadowTheme = pipelineContext->GetTheme<ShadowTheme>();
1291     CHECK_NULL_RETURN(shadowTheme, false);
1292     shadow = shadowTheme->GetShadow(shadowStyle, colorMode);
1293     return true;
1294 }
1295 
ResetTheme(const RefPtr<FrameNode> & host,bool resetForDesktopMenu)1296 void MenuPattern::ResetTheme(const RefPtr<FrameNode>& host, bool resetForDesktopMenu)
1297 {
1298     auto renderContext = host->GetRenderContext();
1299     CHECK_NULL_VOID(renderContext);
1300     auto scroll = DynamicCast<FrameNode>(host->GetFirstChild());
1301     CHECK_NULL_VOID(scroll);
1302 
1303     if (resetForDesktopMenu) {
1304         // DesktopMenu apply shadow on inner Menu node
1305         Shadow shadow;
1306         if (GetShadowFromTheme(ShadowStyle::None, shadow)) {
1307             renderContext->UpdateBackShadow(shadow);
1308         }
1309     } else {
1310         Shadow shadow;
1311         auto shadowStyle = GetMenuDefaultShadowStyle();
1312         if (GetShadowFromTheme(shadowStyle, shadow)) {
1313             renderContext->UpdateBackShadow(shadow);
1314         }
1315     }
1316     // to enable inner menu shadow effect for desktopMenu, need to remove clipping from container
1317     bool clip = !resetForDesktopMenu;
1318     scroll->GetRenderContext()->SetClipToBounds(clip);
1319 
1320     // move padding from scroll to inner menu
1321     auto scrollProp = scroll->GetLayoutProperty();
1322     scrollProp->UpdatePadding(PaddingProperty());
1323 }
1324 
ResetScrollTheme(const RefPtr<FrameNode> & host)1325 void MenuPattern::ResetScrollTheme(const RefPtr<FrameNode>& host)
1326 {
1327     auto renderContext = host->GetRenderContext();
1328     CHECK_NULL_VOID(renderContext);
1329     auto scroll = DynamicCast<FrameNode>(host->GetFirstChild());
1330     CHECK_NULL_VOID(scroll);
1331     scroll->GetRenderContext()->UpdateClipEdge(false);
1332 }
1333 
InitTheme(const RefPtr<FrameNode> & host)1334 void MenuPattern::InitTheme(const RefPtr<FrameNode>& host)
1335 {
1336     CHECK_NULL_VOID(host);
1337     auto renderContext = host->GetRenderContext();
1338     CHECK_NULL_VOID(renderContext);
1339     auto pipeline = host->GetContextWithCheck();
1340     CHECK_NULL_VOID(pipeline);
1341     auto theme = pipeline->GetTheme<SelectTheme>();
1342     CHECK_NULL_VOID(theme);
1343     auto expandDisplay = theme->GetExpandDisplay();
1344     expandDisplay_ = expandDisplay;
1345     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN) || !renderContext->IsUniRenderEnabled()) {
1346         auto bgColor = theme->GetBackgroundColor();
1347         renderContext->UpdateBackgroundColor(bgColor);
1348     }
1349     Shadow shadow;
1350     auto defaultShadowStyle = GetMenuDefaultShadowStyle();
1351     if (GetShadowFromTheme(defaultShadowStyle, shadow)) {
1352         renderContext->UpdateBackShadow(shadow);
1353     }
1354     // make menu round rect
1355     BorderRadiusProperty borderRadius;
1356     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1357         borderRadius.SetRadius(theme->GetMenuDefaultRadius());
1358     } else {
1359         borderRadius.SetRadius(theme->GetMenuBorderRadius());
1360     }
1361     renderContext->UpdateBorderRadius(borderRadius);
1362     renderContext->UpdateOuterBorderRadius(borderRadius);
1363 }
1364 
InitTheme(const RefPtr<FrameNode> & host)1365 void InnerMenuPattern::InitTheme(const RefPtr<FrameNode>& host)
1366 {
1367     CHECK_NULL_VOID(host);
1368     MenuPattern::InitTheme(host);
1369     // inner menu applies shadow in OnModifyDone(), where it can determine if it's a DesktopMenu or a regular menu
1370 
1371     auto layoutProperty = host->GetLayoutProperty();
1372     if (layoutProperty->GetPaddingProperty()) {
1373         // if user defined padding exists, skip applying default padding
1374         return;
1375     }
1376     auto pipeline = host->GetContextWithCheck();
1377     CHECK_NULL_VOID(pipeline);
1378     auto theme = pipeline->GetTheme<SelectTheme>();
1379     CHECK_NULL_VOID(theme);
1380     // apply default padding from theme on inner menu
1381     PaddingProperty padding;
1382     padding.SetEdges(CalcLength(theme->GetMenuPadding()));
1383     host->GetLayoutProperty()->UpdatePadding(padding);
1384 
1385     host->GetRenderContext()->SetClipToBounds(true);
1386 }
1387 
SetAccessibilityAction()1388 void MenuPattern::SetAccessibilityAction()
1389 {
1390     auto host = GetHost();
1391     CHECK_NULL_VOID(host);
1392     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
1393     CHECK_NULL_VOID(accessibilityProperty);
1394     accessibilityProperty->SetActionScrollForward([weakPtr = WeakClaim(this)]() {
1395         const auto& pattern = weakPtr.Upgrade();
1396         auto host = pattern->GetHost();
1397         CHECK_NULL_VOID(host);
1398         auto firstChild = DynamicCast<FrameNode>(host->GetChildAtIndex(0));
1399         CHECK_NULL_VOID(firstChild);
1400         if (firstChild && firstChild->GetTag() == V2::SCROLL_ETS_TAG) {
1401             auto scrollPattern = firstChild->GetPattern<ScrollPattern>();
1402             CHECK_NULL_VOID(scrollPattern);
1403             scrollPattern->ScrollPage(false, true);
1404         }
1405     });
1406 
1407     accessibilityProperty->SetActionScrollBackward([weakPtr = WeakClaim(this)]() {
1408         const auto& pattern = weakPtr.Upgrade();
1409         auto host = pattern->GetHost();
1410         CHECK_NULL_VOID(host);
1411         auto firstChild = DynamicCast<FrameNode>(host->GetChildAtIndex(0));
1412         CHECK_NULL_VOID(firstChild);
1413         if (firstChild && firstChild->GetTag() == V2::SCROLL_ETS_TAG) {
1414             auto scrollPattern = firstChild->GetPattern<ScrollPattern>();
1415             CHECK_NULL_VOID(scrollPattern);
1416             scrollPattern->ScrollPage(true, true);
1417         }
1418     });
1419 }
1420 
GetTransformCenter() const1421 Offset MenuPattern::GetTransformCenter() const
1422 {
1423     auto host = GetHost();
1424     CHECK_NULL_RETURN(host, Offset());
1425     auto geometryNode = host->GetGeometryNode();
1426     CHECK_NULL_RETURN(geometryNode, Offset());
1427     auto size = geometryNode->GetFrameSize();
1428     auto layoutAlgorithmWrapper = host->GetLayoutAlgorithm();
1429     CHECK_NULL_RETURN(layoutAlgorithmWrapper, Offset());
1430     auto layoutAlgorithm = AceType::DynamicCast<MenuLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1431     CHECK_NULL_RETURN(layoutAlgorithm, Offset());
1432     auto placement = layoutAlgorithm->GetPlacement();
1433     switch (placement) {
1434         case Placement::BOTTOM_LEFT:
1435         case Placement::RIGHT_TOP:
1436             return Offset();
1437         case Placement::BOTTOM_RIGHT:
1438         case Placement::LEFT_TOP:
1439             return Offset(size.Width(), 0.0f);
1440         case Placement::TOP_LEFT:
1441         case Placement::RIGHT_BOTTOM:
1442             return Offset(0.0f, size.Height());
1443         case Placement::TOP_RIGHT:
1444         case Placement::LEFT_BOTTOM:
1445             return Offset(size.Width(), size.Height());
1446         case Placement::BOTTOM:
1447             return Offset(size.Width() / 2, 0.0f);
1448         case Placement::LEFT:
1449             return Offset(size.Width(), size.Height() / 2);
1450         case Placement::TOP:
1451             return Offset(size.Width() / 2, size.Height());
1452         case Placement::RIGHT:
1453             return Offset(0.0f, size.Height() / 2);
1454         default:
1455             return Offset();
1456     }
1457 }
1458 
DuplicateMenuNode(const RefPtr<FrameNode> & menuNode,const MenuParam & menuParam)1459 RefPtr<FrameNode> MenuPattern::DuplicateMenuNode(const RefPtr<FrameNode>& menuNode, const MenuParam& menuParam)
1460 {
1461     auto duplicateMenuNode = FrameNode::CreateFrameNode(V2::ROW_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
1462         AceType::MakeRefPtr<LinearLayoutPattern>(false));
1463     CHECK_NULL_RETURN(menuNode, duplicateMenuNode);
1464     auto menuLayoutProperty = menuNode->GetLayoutProperty<MenuLayoutProperty>();
1465     CHECK_NULL_RETURN(menuLayoutProperty, duplicateMenuNode);
1466     if (menuLayoutProperty->GetBorderRadius().has_value()) {
1467         BorderRadiusProperty borderRadius = menuLayoutProperty->GetBorderRadiusValue();
1468         UpdateBorderRadius(duplicateMenuNode, borderRadius);
1469     }
1470     SetMenuBackGroundStyle(duplicateMenuNode, menuParam);
1471     return duplicateMenuNode;
1472 }
1473 
SetMenuBackGroundStyle(const RefPtr<FrameNode> & menuNode,const MenuParam & menuParam)1474 void MenuPattern::SetMenuBackGroundStyle(const RefPtr<FrameNode>& menuNode, const MenuParam& menuParam)
1475 {
1476     CHECK_NULL_VOID(menuNode);
1477     auto renderContext = menuNode->GetRenderContext();
1478     CHECK_NULL_VOID(renderContext);
1479     BlurStyleOption styleOption;
1480     if (menuParam.backgroundColor.has_value()) {
1481         styleOption.blurStyle = BlurStyle::COMPONENT_ULTRA_THICK;
1482         renderContext->UpdateBackgroundColor(menuParam.backgroundColor.value_or(Color::TRANSPARENT));
1483     } else {
1484         styleOption.blurStyle = static_cast<BlurStyle>(
1485             menuParam.backgroundBlurStyle.value_or(static_cast<int32_t>(BlurStyle::COMPONENT_ULTRA_THICK)));
1486         renderContext->UpdateBackgroundColor(Color::TRANSPARENT);
1487     }
1488     renderContext->UpdateBackBlurStyle(styleOption);
1489     if (menuParam.backgroundBlurStyleOption.has_value()) {
1490         BlurStyleOption backgroundBlurStyleOption = menuParam.backgroundBlurStyleOption.value();
1491         if (renderContext->GetBackgroundEffect().has_value()) {
1492             renderContext->UpdateBackgroundEffect(std::nullopt);
1493         }
1494         renderContext->UpdateBackBlurStyle(backgroundBlurStyleOption);
1495         if (renderContext->GetBackBlurRadius().has_value()) {
1496             renderContext->UpdateBackBlurRadius(Dimension());
1497         }
1498     }
1499     if (menuParam.backgroundEffectOption.has_value()) {
1500         EffectOption backgroundEffectOption = menuParam.backgroundEffectOption.value();
1501         if (renderContext->GetBackBlurRadius().has_value()) {
1502             renderContext->UpdateBackBlurRadius(Dimension());
1503         }
1504         if (renderContext->GetBackBlurStyle().has_value()) {
1505             renderContext->UpdateBackBlurStyle(std::nullopt);
1506         }
1507         renderContext->UpdateBackgroundEffect(backgroundEffectOption);
1508     }
1509 }
1510 
ShowPreviewPositionAnimation(AnimationOption & option,int32_t delay)1511 void MenuPattern::ShowPreviewPositionAnimation(AnimationOption& option, int32_t delay)
1512 {
1513     auto menuWrapper = GetMenuWrapper();
1514     CHECK_NULL_VOID(menuWrapper);
1515     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
1516     CHECK_NULL_VOID(menuWrapperPattern);
1517 
1518     auto preview = isShowHoverImage_ ? menuWrapperPattern->GetHoverImageFlexNode() : menuWrapperPattern->GetPreview();
1519     CHECK_NULL_VOID(preview);
1520     bool isHoverImageTarget = isShowHoverImage_ && preview->GetTag() == V2::FLEX_ETS_TAG;
1521     auto previewRenderContext = preview->GetRenderContext();
1522     CHECK_NULL_VOID(previewRenderContext);
1523     auto previewGeometryNode = preview->GetGeometryNode();
1524     CHECK_NULL_VOID(previewGeometryNode);
1525     auto previewPosition = previewGeometryNode->GetFrameOffset();
1526     OffsetF previewOriginPosition = GetPreviewOriginOffset();
1527 
1528     previewRenderContext->UpdatePosition(
1529         OffsetT<Dimension>(Dimension(previewOriginPosition.GetX()), Dimension(previewOriginPosition.GetY())));
1530 
1531     if (menuWrapperPattern->GetHoverScaleInterruption()) {
1532         return;
1533     }
1534 
1535     if (isHoverImageTarget) {
1536         option.SetCurve(CUSTOM_PREVIEW_ANIMATION_CURVE);
1537         option.SetDelay(delay);
1538     }
1539     auto host = GetHost();
1540     CHECK_NULL_VOID(host);
1541     AnimationUtils::Animate(option, [previewRenderContext, previewPosition]() {
1542         CHECK_NULL_VOID(previewRenderContext);
1543         previewRenderContext->UpdatePosition(
1544             OffsetT<Dimension>(Dimension(previewPosition.GetX()), Dimension(previewPosition.GetY())));
1545     }, nullptr, nullptr, host->GetContextRefPtr());
1546 }
1547 
ShowPreviewMenuScaleAnimation(const RefPtr<MenuTheme> & menuTheme,AnimationOption & option,int32_t delay)1548 void MenuPattern::ShowPreviewMenuScaleAnimation(
1549     const RefPtr<MenuTheme>& menuTheme, AnimationOption& option, int32_t delay)
1550 {
1551     CHECK_NULL_VOID(menuTheme);
1552     auto menuWrapper = GetMenuWrapper();
1553     CHECK_NULL_VOID(menuWrapper);
1554     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
1555     CHECK_NULL_VOID(menuWrapperPattern);
1556 
1557     auto host = GetHost();
1558     CHECK_NULL_VOID(host);
1559     auto renderContext = host->GetRenderContext();
1560     CHECK_NULL_VOID(renderContext);
1561 
1562     auto menuPosition = host->GetPaintRectOffset(false, true);
1563     auto menuAnimationScale = menuTheme->GetMenuAnimationScale();
1564     renderContext->UpdateTransformCenter(DimensionOffset(GetTransformCenter()));
1565     renderContext->UpdateTransformScale(VectorF(menuAnimationScale, menuAnimationScale));
1566     renderContext->UpdatePosition(OffsetT<Dimension>(Dimension(originOffset_.GetX()), Dimension(originOffset_.GetY())));
1567 
1568     if (isShowHoverImage_) {
1569         option.SetCurve(CUSTOM_PREVIEW_ANIMATION_CURVE);
1570         option.SetDelay(delay);
1571     }
1572 
1573     AnimationUtils::Animate(option, [renderContext, menuPosition]() {
1574         CHECK_NULL_VOID(renderContext);
1575         renderContext->UpdateTransformScale(VectorF(1.0f, 1.0f));
1576         renderContext->UpdatePosition(
1577             OffsetT<Dimension>(Dimension(menuPosition.GetX()), Dimension(menuPosition.GetY())));
1578     }, nullptr, nullptr, host->GetContextRefPtr());
1579 }
1580 
ShowPreviewMenuAnimation()1581 void MenuPattern::ShowPreviewMenuAnimation()
1582 {
1583     CHECK_NULL_VOID(isFirstShow_ && previewMode_ != MenuPreviewMode::NONE);
1584 
1585     auto menuWrapper = GetMenuWrapper();
1586     CHECK_NULL_VOID(menuWrapper);
1587     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
1588     CHECK_NULL_VOID(menuWrapperPattern && !menuWrapperPattern->IsHide());
1589 
1590     auto pipeline = menuWrapper->GetContextWithCheck();
1591     CHECK_NULL_VOID(pipeline);
1592     auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
1593     CHECK_NULL_VOID(menuTheme);
1594 
1595     auto springMotionResponse = menuTheme->GetSpringMotionResponse();
1596     auto springMotionDampingFraction = menuTheme->GetSpringMotionDampingFraction();
1597     auto isHoverScaleInterrupt = menuWrapperPattern->GetHoverScaleInterruption();
1598     auto delay = isShowHoverImage_ ? menuTheme->GetHoverImageDelayDuration(isHoverScaleInterrupt) : 0;
1599 
1600     AnimationOption option = AnimationOption();
1601     auto motion = AceType::MakeRefPtr<ResponsiveSpringMotion>(springMotionResponse, springMotionDampingFraction);
1602     option.SetCurve(motion);
1603 
1604     auto host = GetHost();
1605     CHECK_NULL_VOID(host);
1606     MenuView::CalcHoverScaleInfo(host);
1607     InitPreviewMenuAnimationInfo(menuTheme);
1608 
1609     // customPreview position animation
1610     ShowPreviewPositionAnimation(option, delay);
1611 
1612     // menu position and scale animation
1613     ShowPreviewMenuScaleAnimation(menuTheme, option, delay);
1614 
1615     // image and hoverScale animation
1616     MenuView::ShowPixelMapAnimation(host);
1617     ShowMenuOpacityAnimation(menuTheme, host, delay);
1618     isFirstShow_ = false;
1619 }
1620 
ShowMenuAppearAnimation()1621 void MenuPattern::ShowMenuAppearAnimation()
1622 {
1623     auto host = GetHost();
1624     CHECK_NULL_VOID(host);
1625     if (isMenuShow_ && Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) &&
1626         previewMode_ == MenuPreviewMode::NONE) {
1627         auto renderContext = host->GetRenderContext();
1628         CHECK_NULL_VOID(renderContext);
1629         auto pipeline = host->GetContext();
1630         CHECK_NULL_VOID(pipeline);
1631         auto theme = pipeline->GetTheme<SelectTheme>();
1632         CHECK_NULL_VOID(theme);
1633         auto menuPosition = host->GetPaintRectOffset(false, true);
1634         if (IsSelectOverlayExtensionMenu() && !isExtensionMenuShow_) {
1635             menuPosition = GetEndOffset();
1636         }
1637         if (IsSelectOverlayExtensionMenu()) {
1638             SetEndOffset(menuPosition);
1639         }
1640         renderContext->UpdateTransformScale(VectorF(theme->GetMenuAnimationScale(), theme->GetMenuAnimationScale()));
1641         renderContext->UpdateOpacity(0.0f);
1642         AnimationOption option = AnimationOption();
1643         if (theme->GetMenuAnimationDuration()) {
1644             option.SetDuration(theme->GetMenuAnimationDuration());
1645             option.SetCurve(theme->GetMenuAnimationCurve());
1646             renderContext->UpdateTransformCenter(DimensionOffset(Offset()));
1647         } else {
1648             option.SetCurve(MAIN_MENU_ANIMATION_CURVE);
1649             renderContext->UpdateTransformCenter(DimensionOffset(GetTransformCenter()));
1650         }
1651         AnimationUtils::Animate(option, [this, renderContext, menuPosition]() {
1652             CHECK_NULL_VOID(renderContext);
1653             if (IsSelectOverlayExtensionMenu()) {
1654                 renderContext->UpdatePosition(
1655                     OffsetT<Dimension>(Dimension(menuPosition.GetX()), Dimension(menuPosition.GetY())));
1656             }
1657             renderContext->UpdateOpacity(1.0f);
1658             renderContext->UpdateTransformScale(VectorF(1.0f, 1.0f));
1659         }, nullptr, nullptr, host->GetContextRefPtr());
1660         isExtensionMenuShow_ = false;
1661     }
1662     isMenuShow_ = false;
1663 }
1664 
GetTitleContentNode(const RefPtr<FrameNode> & subMenuNode) const1665 RefPtr<FrameNode> MenuPattern::GetTitleContentNode(const RefPtr<FrameNode>& subMenuNode) const
1666 {
1667     auto scroll = subMenuNode->GetFirstChild();
1668     CHECK_NULL_RETURN(scroll, nullptr);
1669     auto subInnerMenu = scroll->GetFirstChild();
1670     CHECK_NULL_RETURN(subInnerMenu, nullptr);
1671     auto titleNode = DynamicCast<FrameNode>(subInnerMenu->GetFirstChild());
1672     CHECK_NULL_RETURN(titleNode, nullptr);
1673     auto titleItemPattern = titleNode->GetPattern<MenuItemPattern>();
1674     CHECK_NULL_RETURN(titleItemPattern, nullptr);
1675     return titleItemPattern->GetContentNode();
1676 }
1677 
ShowStackSubMenuAnimation(const RefPtr<FrameNode> & mainMenu,const RefPtr<FrameNode> & subMenuNode)1678 void MenuPattern::ShowStackSubMenuAnimation(const RefPtr<FrameNode>& mainMenu, const RefPtr<FrameNode>& subMenuNode)
1679 {
1680     CHECK_NULL_VOID(subMenuNode);
1681     auto subMenuContext = subMenuNode->GetRenderContext();
1682     CHECK_NULL_VOID(subMenuContext);
1683     subMenuContext->UpdateOpacity(0.0f);
1684     auto otherMenuItemContext = GetOtherMenuItemContext(subMenuNode);
1685     for (auto menuItemContext : otherMenuItemContext) {
1686         menuItemContext->UpdateOpacity(0.0f);
1687     }
1688     auto [originOffset, endOffset] = GetMenuOffset(mainMenu, subMenuNode);
1689     if (originOffset ==  OffsetF()) {
1690         TAG_LOGW(AceLogTag::ACE_MENU, "not found parent MenuItem when show stack sub menu");
1691     }
1692     subMenuContext->UpdatePosition(
1693         OffsetT<Dimension>(Dimension(originOffset.GetX()), Dimension(originOffset.GetY())));
1694 
1695     AnimationOption translateOption = AnimationOption();
1696     translateOption.SetCurve(STACK_SUB_MENU_ANIMATION_CURVE);
1697     translateOption.SetDuration(STACK_MENU_APPEAR_DURATION);
1698     AnimationUtils::Animate(translateOption, [weak = WeakClaim(RawPtr(subMenuContext)), menuPosition = endOffset]() {
1699         auto subMenuContextUpgrade = weak.Upgrade();
1700         CHECK_NULL_VOID(subMenuContextUpgrade);
1701         subMenuContextUpgrade->UpdatePosition(
1702             OffsetT<Dimension>(Dimension(menuPosition.GetX()), Dimension(menuPosition.GetY())));
1703     }, nullptr, nullptr, subMenuNode->GetContextRefPtr());
1704     AnimationOption opacityOption = AnimationOption();
1705     opacityOption.SetCurve(Curves::FRICTION);
1706     opacityOption.SetDuration(STACK_MENU_APPEAR_DURATION);
1707     AnimationUtils::Animate(opacityOption, [weak = WeakClaim(RawPtr(subMenuContext))]() {
1708         auto subMenuContextUpgrade = weak.Upgrade();
1709         CHECK_NULL_VOID(subMenuContextUpgrade);
1710         subMenuContextUpgrade->UpdateOpacity(1.0f);
1711     }, nullptr, nullptr, subMenuNode->GetContextRefPtr());
1712     AnimationOption contentOpacityOption = AnimationOption();
1713     contentOpacityOption.SetCurve(Curves::FRICTION);
1714     contentOpacityOption.SetDelay(STACK_SUB_MENU_APPEAR_DELAY);
1715     contentOpacityOption.SetDuration(STACK_MENU_APPEAR_DURATION);
1716     auto titleContentNode = GetTitleContentNode(subMenuNode);
1717     CHECK_NULL_VOID(titleContentNode);
1718     AnimationUtils::Animate(contentOpacityOption, [otherMenuItemContext, titleContentNode]() {
1719         for (auto menuItemContext : otherMenuItemContext) {
1720             CHECK_NULL_CONTINUE(menuItemContext);
1721             menuItemContext->UpdateOpacity(1.0f);
1722         }
1723         CHECK_NULL_VOID(titleContentNode);
1724         auto textProperty = titleContentNode->GetLayoutProperty<TextLayoutProperty>();
1725         CHECK_NULL_VOID(textProperty);
1726         textProperty->UpdateFontWeight(FontWeight::BOLD);
1727         titleContentNode->MarkDirtyNode();
1728     }, nullptr, nullptr, subMenuNode->GetContextRefPtr());
1729 
1730     ShowArrowRotateAnimation();
1731 }
1732 
ShowStackMainMenuAnimation(const RefPtr<FrameNode> & mainMenu,const RefPtr<FrameNode> & subMenuNode,const RefPtr<FrameNode> & menuWrapper)1733 void MenuPattern::ShowStackMainMenuAnimation(const RefPtr<FrameNode>& mainMenu, const RefPtr<FrameNode>& subMenuNode,
1734     const RefPtr<FrameNode>& menuWrapper)
1735 {
1736     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
1737     CHECK_NULL_VOID(menuWrapperPattern);
1738     auto preview = isShowHoverImage_ ? menuWrapperPattern->GetHoverImageFlexNode() : menuWrapperPattern->GetPreview();
1739     auto subMenuPattern = subMenuNode->GetPattern<MenuPattern>();
1740     CHECK_NULL_VOID(subMenuPattern);
1741     float translateYForStack = subMenuPattern->GetTranslateYForStack();
1742     AnimationOption translateOption = AnimationOption();
1743     translateOption.SetCurve(STACK_SUB_MENU_ANIMATION_CURVE);
1744     translateOption.SetDuration(STACK_MAIN_MENU_APPEAR_DURATION);
1745     AnimationUtils::Animate(translateOption, [mainMenu, translateYForStack, preview]() {
1746         CHECK_NULL_VOID(mainMenu);
1747         auto mainMenuOffset = mainMenu->GetPaintRectOffset(false, true);
1748         auto mainMenuContext = mainMenu->GetRenderContext();
1749         CHECK_NULL_VOID(mainMenuContext);
1750         mainMenuContext->UpdateTransformScale(VectorF(MOUNT_MENU_FINAL_SCALE, MOUNT_MENU_FINAL_SCALE));
1751         if (NearZero(translateYForStack)) {
1752             return;
1753         }
1754         auto mainMenuPattern = mainMenu->GetPattern<MenuPattern>();
1755         CHECK_NULL_VOID(mainMenuPattern);
1756         mainMenuPattern->SetOriginMenuYForStack(mainMenuOffset.GetY());
1757         mainMenuContext->UpdatePosition(
1758             OffsetT<Dimension>(Dimension(mainMenuOffset.GetX()),
1759                 Dimension(mainMenuOffset.GetY() - translateYForStack)));
1760         mainMenu->GetGeometryNode()->SetMarginFrameOffset(OffsetF(mainMenuOffset.GetX(),
1761             mainMenuOffset.GetY() - translateYForStack));
1762 
1763         CHECK_NULL_VOID(preview);
1764         auto previewFrameOffset = preview->GetGeometryNode()->GetFrameOffset();
1765         mainMenuPattern->SetOriginPreviewYForStack(previewFrameOffset.GetY());
1766         auto previewRenderContext = preview->GetRenderContext();
1767         CHECK_NULL_VOID(previewRenderContext);
1768         previewRenderContext->UpdatePosition(
1769             OffsetT<Dimension>(Dimension(previewFrameOffset.GetX()),
1770                 Dimension(previewFrameOffset.GetY() - translateYForStack)));
1771         preview->GetGeometryNode()->SetMarginFrameOffset(OffsetF(previewFrameOffset.GetX(),
1772             previewFrameOffset.GetY() - translateYForStack));
1773     }, nullptr, nullptr, menuWrapper->GetContextRefPtr());
1774     ShowStackMainMenuOpacityAnimation(mainMenu);
1775 }
1776 
ShowStackMainMenuOpacityAnimation(const RefPtr<FrameNode> & mainMenu)1777 void MenuPattern::ShowStackMainMenuOpacityAnimation(const RefPtr<FrameNode>& mainMenu)
1778 {
1779     CHECK_NULL_VOID(mainMenu);
1780     auto scroll = mainMenu->GetFirstChild();
1781     CHECK_NULL_VOID(scroll);
1782     auto innerMenu = scroll->GetFirstChild();
1783     CHECK_NULL_VOID(innerMenu);
1784     auto innerMenuFrameNode = DynamicCast<FrameNode>(innerMenu);
1785     CHECK_NULL_VOID(innerMenuFrameNode);
1786     auto innerMenuContext = innerMenuFrameNode->GetRenderContext();
1787     CHECK_NULL_VOID(innerMenuContext);
1788 
1789     AnimationOption opacityOption = AnimationOption();
1790     opacityOption.SetCurve(Curves::FRICTION);
1791     opacityOption.SetDuration(STACK_MAIN_MENU_APPEAR_DURATION);
1792     AnimationUtils::Animate(opacityOption, [innerMenuContext]() {
1793         if (innerMenuContext) {
1794             innerMenuContext->UpdateOpacity(MAIN_MENU_OPACITY);
1795         }
1796     }, nullptr, nullptr, mainMenu->GetContextRefPtr());
1797 }
1798 
ShowStackMenuAppearAnimation()1799 void MenuPattern::ShowStackMenuAppearAnimation()
1800 {
1801     auto host = GetHost();
1802     CHECK_NULL_VOID(host);
1803     if (!isSubMenuShow_) {
1804         return;
1805     }
1806     auto menuWrapper = GetMenuWrapper();
1807     CHECK_NULL_VOID(menuWrapper);
1808     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
1809     CHECK_NULL_VOID(menuWrapperPattern);
1810     auto mainMenu = menuWrapperPattern->GetMenu();
1811     CHECK_NULL_VOID(mainMenu);
1812     auto mainMenuAccessibilityProps = mainMenu->GetAccessibilityProperty<AccessibilityProperty>();
1813     CHECK_NULL_VOID(mainMenuAccessibilityProps);
1814     mainMenuAccessibilityProps->SetAccessibilityLevel(AccessibilityProperty::Level::NO_HIDE_DESCENDANTS);
1815 
1816     ShowStackMainMenuAnimation(mainMenu, host, menuWrapper);
1817     ShowStackSubMenuAnimation(mainMenu, host);
1818     isSubMenuShow_ = false;
1819 }
1820 
GetMenuOffset(const RefPtr<FrameNode> & mainMenu,const RefPtr<FrameNode> & subMenu,bool isNeedRestoreNodeId) const1821 std::pair<OffsetF, OffsetF> MenuPattern::GetMenuOffset(const RefPtr<FrameNode>& mainMenu,
1822     const RefPtr<FrameNode>& subMenu, bool isNeedRestoreNodeId) const
1823 {
1824     CHECK_NULL_RETURN(mainMenu, std::make_pair(OffsetF(), OffsetF()));
1825     auto scroll = mainMenu->GetFirstChild();
1826     CHECK_NULL_RETURN(scroll, std::make_pair(OffsetF(), OffsetF()));
1827     auto innerMenu = scroll->GetFirstChild();
1828     CHECK_NULL_RETURN(innerMenu, std::make_pair(OffsetF(), OffsetF()));
1829     auto children = innerMenu->GetChildren();
1830     MenuItemInfo menuItemInfo;
1831     for (auto child : children) {
1832         menuItemInfo = GetInnerMenuOffset(child, subMenu, isNeedRestoreNodeId);
1833         if (menuItemInfo.isFindTargetId) {
1834             break;
1835         }
1836     }
1837     return {menuItemInfo.originOffset, menuItemInfo.endOffset};
1838 }
1839 
GetInnerMenuOffset(const RefPtr<UINode> & child,const RefPtr<FrameNode> & subMenu,bool isNeedRestoreNodeId) const1840 MenuItemInfo MenuPattern::GetInnerMenuOffset(const RefPtr<UINode>& child, const RefPtr<FrameNode>& subMenu,
1841     bool isNeedRestoreNodeId) const
1842 {
1843     MenuItemInfo menuItemInfo;
1844     CHECK_NULL_RETURN(child, menuItemInfo);
1845     if (child->GetTag() == V2::MENU_ITEM_ETS_TAG) {
1846         menuItemInfo = GetMenuItemInfo(child, subMenu, isNeedRestoreNodeId);
1847         if (menuItemInfo.isFindTargetId) {
1848             return menuItemInfo;
1849         }
1850     } else {
1851         const auto& groupChildren = child->GetChildren();
1852         for (auto child : groupChildren) {
1853             menuItemInfo = GetInnerMenuOffset(child, subMenu, isNeedRestoreNodeId);
1854             if (menuItemInfo.isFindTargetId) {
1855                 return menuItemInfo;
1856             }
1857         }
1858     }
1859     return menuItemInfo;
1860 }
1861 
GetMenuItemInfo(const RefPtr<UINode> & child,const RefPtr<FrameNode> & subMenu,bool isNeedRestoreNodeId) const1862 MenuItemInfo MenuPattern::GetMenuItemInfo(const RefPtr<UINode>& child, const RefPtr<FrameNode>& subMenu,
1863     bool isNeedRestoreNodeId) const
1864 {
1865     MenuItemInfo menuItemInfo;
1866     auto menuItem = AceType::DynamicCast<FrameNode>(child);
1867     CHECK_NULL_RETURN(menuItem, menuItemInfo);
1868     if (menuItem->GetTag() == V2::MENU_ITEM_ETS_TAG) {
1869         auto menuItemPattern = menuItem->GetPattern<MenuItemPattern>();
1870         CHECK_NULL_RETURN(menuItemPattern, menuItemInfo);
1871         if (menuItem->GetId() == menuItemPattern->GetClickMenuItemId()) {
1872             auto host = GetHost();
1873             CHECK_NULL_RETURN(host, menuItemInfo);
1874             auto pipeline = host->GetContextWithCheck();
1875             CHECK_NULL_RETURN(pipeline, menuItemInfo);
1876             auto isContainerModal = pipeline->GetWindowModal() == WindowModal::CONTAINER_MODAL;
1877             auto offset = menuItem->GetPaintRectOffset(false, true);
1878             if (isContainerModal) {
1879                 offset -= OffsetF(0.0f, static_cast<float>(pipeline->GetCustomTitleHeight().ConvertToPx()));
1880             }
1881             menuItemInfo.originOffset = offset - OffsetF(PADDING.ConvertToPx(), PADDING.ConvertToPx());
1882             menuItemInfo.endOffset = OffsetF(menuItemInfo.originOffset.GetX(),
1883                 subMenu->GetPaintRectOffset(false, true).GetY());
1884             if (isContainerModal) {
1885                 menuItemInfo.endOffset -= OffsetF(0.0f,
1886                     static_cast<float>(pipeline->GetCustomTitleHeight().ConvertToPx())
1887                     + static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()));
1888             }
1889             menuItemInfo.isFindTargetId = true;
1890             if (isNeedRestoreNodeId) {
1891                 menuItemPattern->SetClickMenuItemId(-1);
1892             }
1893         }
1894     }
1895     return menuItemInfo;
1896 }
1897 
GetFirstMenuItem()1898 RefPtr<FrameNode> MenuPattern::GetFirstMenuItem()
1899 {
1900     auto host = GetHost();
1901     CHECK_NULL_RETURN(host, nullptr);
1902     uint32_t depth = 0;
1903     auto child = host->GetFirstChild();
1904     while (child && depth < MAX_SEARCH_DEPTH) {
1905         if (child->GetTag() == V2::MENU_ITEM_ETS_TAG) {
1906             return AceType::DynamicCast<FrameNode>(child);
1907         }
1908         if (child->GetTag() == V2::JS_VIEW_ETS_TAG) {
1909             child = child->GetFirstChild();
1910             if (child && child->GetTag() == V2::JS_VIEW_ETS_TAG) {
1911                 child  = child->GetFirstChild();
1912                 ++depth;
1913             }
1914             continue;
1915         }
1916         child = child->GetFirstChild();
1917         ++depth;
1918     }
1919     return nullptr;
1920 }
1921 
GetLastMenuItem()1922 RefPtr<FrameNode> MenuPattern::GetLastMenuItem()
1923 {
1924     auto host = GetHost();
1925     CHECK_NULL_RETURN(host, nullptr);
1926     uint32_t depth = 0;
1927     auto child = host->GetLastChild();
1928     while (child && depth < MAX_SEARCH_DEPTH) {
1929         if (child->GetTag() == V2::MENU_ITEM_ETS_TAG) {
1930             return AceType::DynamicCast<FrameNode>(child);
1931         }
1932         if (child->GetTag() == V2::JS_VIEW_ETS_TAG) {
1933             child = child->GetLastChild();
1934             if (child && child->GetTag() == V2::JS_VIEW_ETS_TAG) {
1935                 child  = child->GetLastChild();
1936                 ++depth;
1937             }
1938             continue;
1939         }
1940         child = child->GetLastChild();
1941         ++depth;
1942     }
1943     return nullptr;
1944 }
1945 
GetPreviewPositionY()1946 std::pair<float, float> MenuPattern::GetPreviewPositionY()
1947 {
1948     auto menuWrapper = GetMenuWrapper();
1949     CHECK_NULL_RETURN(menuWrapper, std::make_pair(0.0f, 0.0f));
1950     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
1951     CHECK_NULL_RETURN(menuWrapperPattern, std::make_pair(0.0f, 0.0f));
1952     auto preview = menuWrapperPattern->GetPreview();
1953     CHECK_NULL_RETURN(preview, std::make_pair(0.0f, 0.0f));
1954     auto geometryNode = preview->GetGeometryNode();
1955     CHECK_NULL_RETURN(geometryNode, std::make_pair(0.0f, 0.0f));
1956     return {preview->GetPaintRectOffset(false, true).GetY(),
1957         preview->GetPaintRectOffset(false, true).GetY() + geometryNode->GetMarginFrameSize().Height()};
1958 }
1959 
ShowArrowRotateAnimation() const1960 void MenuPattern::ShowArrowRotateAnimation() const
1961 {
1962     auto host = GetHost();
1963     CHECK_NULL_VOID(host);
1964     auto subImageNode = GetArrowNode(host);
1965     CHECK_NULL_VOID(subImageNode);
1966     auto subImageContext = subImageNode->GetRenderContext();
1967     CHECK_NULL_VOID(subImageContext);
1968     subImageContext->UpdateTransformRotate(Vector5F(0.0f, 0.0f, 1.0f, 0.0f, 0.0f));
1969     AnimationOption option = AnimationOption();
1970     option.SetCurve(AceType::MakeRefPtr<InterpolatingSpring>(0.0f, 1.0f, 228.0f, 22.0f));
1971     double arrowRotateDelay = 50.0f;
1972     double arrowRotateDuration = 400.0f;
1973     option.SetDelay(arrowRotateDelay);
1974     option.SetDuration(arrowRotateDuration);
1975     AnimationUtils::Animate(option, [subImageContext]() {
1976         if (subImageContext) {
1977             subImageContext->UpdateTransformRotate(Vector5F(0.0f, 0.0f, 1.0f, SEMI_CIRCLE_ANGEL, 0.0f));
1978         }
1979     }, nullptr, nullptr, host->GetContextRefPtr());
1980 }
1981 
ShowArrowReverseRotateAnimation() const1982 void MenuPattern::ShowArrowReverseRotateAnimation() const
1983 {
1984     auto host = GetHost();
1985     CHECK_NULL_VOID(host);
1986     auto subImageNode = GetArrowNode(host);
1987     CHECK_NULL_VOID(subImageNode);
1988     auto subImageContext = subImageNode->GetRenderContext();
1989     CHECK_NULL_VOID(subImageContext);
1990     AnimationOption option = AnimationOption();
1991     option.SetCurve(AceType::MakeRefPtr<InterpolatingSpring>(0.0f, 1.0f, 228.0f, 22.0f));
1992     double arrowRotateAnimationDuration = 200.0f;
1993     option.SetDuration(arrowRotateAnimationDuration);
1994     AnimationUtils::Animate(option, [subImageContext]() {
1995         if (subImageContext) {
1996             subImageContext->UpdateTransformRotate(Vector5F(0.0f, 0.0f, 1.0f, 0.0f, 0.0f));
1997         }
1998     }, nullptr, nullptr, host->GetContextRefPtr());
1999 }
2000 
GetArrowNode(const RefPtr<FrameNode> & host) const2001 RefPtr<FrameNode> MenuPattern::GetArrowNode(const RefPtr<FrameNode>& host) const
2002 {
2003     auto scroll = host->GetFirstChild();
2004     CHECK_NULL_RETURN(scroll, nullptr);
2005     auto innerMenu = scroll->GetFirstChild();
2006     CHECK_NULL_RETURN(innerMenu, nullptr);
2007     auto menuItem = innerMenu->GetFirstChild();
2008     CHECK_NULL_RETURN(menuItem, nullptr);
2009     auto rightRow = menuItem->GetChildAtIndex(1);
2010     CHECK_NULL_RETURN(rightRow, nullptr);
2011     auto image = AceType::DynamicCast<FrameNode>(rightRow->GetChildren().back());
2012     return image;
2013 }
2014 
GetOtherMenuItemContext(const RefPtr<FrameNode> & subMenuNode) const2015 std::vector<RefPtr<RenderContext>> MenuPattern::GetOtherMenuItemContext(const RefPtr<FrameNode>& subMenuNode) const
2016 {
2017     CHECK_NULL_RETURN(subMenuNode, {});
2018     auto scroll = subMenuNode->GetFirstChild();
2019     CHECK_NULL_RETURN(scroll, {});
2020     auto subInnerMenu = scroll->GetFirstChild();
2021     CHECK_NULL_RETURN(subInnerMenu, {});
2022     std::vector<RefPtr<RenderContext>> otherMenuItemContext;
2023     auto children = subInnerMenu->GetChildren();
2024     if (children.empty()) {
2025         LOGW("children is empty.");
2026         return {};
2027     }
2028     auto child = children.begin();
2029     for (advance(child, 1); child != children.end(); ++child) {
2030         auto menuItem = DynamicCast<FrameNode>(*child);
2031         CHECK_NULL_RETURN(menuItem, {});
2032         auto menuItemContext = menuItem->GetRenderContext();
2033         otherMenuItemContext.emplace_back(menuItemContext);
2034     }
2035     return otherMenuItemContext;
2036 }
2037 
ShowStackSubMenuDisappearAnimation(const RefPtr<FrameNode> & menuNode,const RefPtr<FrameNode> & subMenuNode) const2038 void MenuPattern::ShowStackSubMenuDisappearAnimation(const RefPtr<FrameNode>& menuNode,
2039     const RefPtr<FrameNode>& subMenuNode) const
2040 {
2041     CHECK_NULL_VOID(subMenuNode);
2042     auto titleContentNode = GetTitleContentNode(subMenuNode);
2043     CHECK_NULL_VOID(titleContentNode);
2044     auto otherMenuItemContext = GetOtherMenuItemContext(subMenuNode);
2045     AnimationOption contentOpacityOption = AnimationOption();
2046     contentOpacityOption.SetCurve(Curves::FRICTION);
2047     double subMenuDisappearDuration = 200.0f;
2048     contentOpacityOption.SetDuration(subMenuDisappearDuration);
2049     AnimationUtils::Animate(contentOpacityOption, [otherMenuItemContext, titleContentNode]() {
2050         CHECK_NULL_VOID(titleContentNode);
2051         auto textProperty = titleContentNode->GetLayoutProperty<TextLayoutProperty>();
2052         CHECK_NULL_VOID(textProperty);
2053         textProperty->UpdateFontWeight(FontWeight::MEDIUM);
2054         titleContentNode->MarkDirtyNode();
2055         for (auto menuItemContext : otherMenuItemContext) {
2056             menuItemContext->UpdateOpacity(0.0f);
2057         }
2058     }, nullptr, nullptr, subMenuNode->GetContextRefPtr());
2059 
2060     ShowArrowReverseRotateAnimation();
2061 
2062     auto [originOffset, endOffset] = GetMenuOffset(menuNode, subMenuNode, true);
2063     auto subMenuPos = subMenuNode->GetPaintRectOffset(false, true);
2064     auto menuPosition = OffsetF(subMenuPos.GetX(), originOffset.GetY());
2065     auto subMenuContext = subMenuNode->GetRenderContext();
2066     CHECK_NULL_VOID(subMenuContext);
2067     AnimationOption translateOption = AnimationOption();
2068     translateOption.SetCurve(AceType::MakeRefPtr<InterpolatingSpring>(0.0f, 1.0f, 228.0f, 26.0f));
2069     translateOption.SetDuration(STACK_MENU_DISAPPEAR_DURATION);
2070     AnimationUtils::Animate(translateOption, [menuPosition, subMenuContext]() {
2071         CHECK_NULL_VOID(subMenuContext);
2072         subMenuContext->UpdatePosition(
2073             OffsetT<Dimension>(Dimension(menuPosition.GetX()), Dimension(menuPosition.GetY())));
2074     }, nullptr, nullptr, subMenuNode->GetContextRefPtr());
2075     AnimationOption opacityOption = AnimationOption();
2076     opacityOption.SetCurve(Curves::FRICTION);
2077     opacityOption.SetDuration(STACK_MENU_DISAPPEAR_DURATION);
2078     AnimationUtils::Animate(opacityOption, [subMenuContext]() {
2079         CHECK_NULL_VOID(subMenuContext);
2080         subMenuContext->UpdateOpacity(0.0f);
2081     }, nullptr, nullptr, subMenuNode->GetContextRefPtr());
2082 }
2083 
ShowStackMainMenuDisappearAnimation(const RefPtr<FrameNode> & menuNode,const RefPtr<FrameNode> & subMenuNode,AnimationOption & option) const2084 void MenuPattern::ShowStackMainMenuDisappearAnimation(const RefPtr<FrameNode>& menuNode,
2085     const RefPtr<FrameNode>& subMenuNode, AnimationOption& option) const
2086 {
2087     auto [originOffset, endOffset] = GetMenuOffset(menuNode, subMenuNode, true);
2088     auto subMenuPos = subMenuNode->GetPaintRectOffset(false, true);
2089     auto menuPosition = OffsetF(subMenuPos.GetX(), originOffset.GetY());
2090 
2091     option.SetCurve(STACK_SUB_MENU_ANIMATION_CURVE);
2092     auto menuWrapper = GetMenuWrapper();
2093     CHECK_NULL_VOID(menuWrapper);
2094     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
2095     CHECK_NULL_VOID(menuWrapperPattern);
2096     auto preview = isShowHoverImage_ ? menuWrapperPattern->GetHoverImageFlexNode() : menuWrapperPattern->GetPreview();
2097     CHECK_NULL_VOID(preview);
2098     AnimationUtils::Animate(option, [menuNode, menuPosition, preview]() {
2099         CHECK_NULL_VOID(menuNode);
2100         auto menuContext = menuNode->GetRenderContext();
2101         if (menuContext) {
2102             menuContext->UpdateTransformScale(VectorF(1.0f, 1.0f));
2103             auto menuPattern = menuNode->GetPattern<MenuPattern>();
2104             if (GreatNotEqual(menuPattern->GetOriginMenuYForStack(), 0.0f)) {
2105                 menuContext->UpdatePosition(
2106                     OffsetT<Dimension>(Dimension(menuPosition.GetX()),
2107                         Dimension(menuPattern->GetOriginMenuYForStack())));
2108                 menuNode->GetGeometryNode()->SetMarginFrameOffset(OffsetF(menuPosition.GetX(),
2109                     menuPattern->GetOriginMenuYForStack()));
2110             }
2111             if (GreatNotEqual(menuPattern->GetOriginPreviewYForStack(), 0.0f)) {
2112                 CHECK_NULL_VOID(preview);
2113                 auto previewRenderContext = preview->GetRenderContext();
2114                 CHECK_NULL_VOID(previewRenderContext);
2115                 auto previewFrameOffsetX = preview->GetGeometryNode()->GetFrameOffset().GetX();
2116                 previewRenderContext->UpdatePosition(
2117                     OffsetT<Dimension>(Dimension(previewFrameOffsetX),
2118                         Dimension(menuPattern->GetOriginPreviewYForStack())));
2119                 preview->GetGeometryNode()->SetMarginFrameOffset(OffsetF(previewFrameOffsetX,
2120                     menuPattern->GetOriginPreviewYForStack()));
2121             }
2122         }
2123     }, nullptr, nullptr, preview->GetContextRefPtr());
2124 
2125     ShowStackMainMenuDisappearOpacityAnimation(menuNode, option);
2126 }
2127 
ShowStackMainMenuDisappearOpacityAnimation(const RefPtr<FrameNode> & menuNode,AnimationOption & option) const2128 void MenuPattern::ShowStackMainMenuDisappearOpacityAnimation(const RefPtr<FrameNode>& menuNode,
2129     AnimationOption& option) const
2130 {
2131     CHECK_NULL_VOID(menuNode);
2132     option.SetCurve(Curves::FRICTION);
2133     option.SetDelay(STACK_MAIN_MENU_DISAPPEAR_DELAY);
2134     option.SetDuration(STACK_MENU_DISAPPEAR_DURATION);
2135     AnimationUtils::Animate(option, [menuNode]() {
2136         CHECK_NULL_VOID(menuNode);
2137         auto scroll = menuNode->GetFirstChild();
2138         CHECK_NULL_VOID(scroll);
2139         auto innerMenu = scroll->GetFirstChild();
2140         CHECK_NULL_VOID(innerMenu);
2141         auto innerMenuFrameNode = DynamicCast<FrameNode>(innerMenu);
2142         CHECK_NULL_VOID(innerMenuFrameNode);
2143         auto innerMenuContext = innerMenuFrameNode->GetRenderContext();
2144         CHECK_NULL_VOID(innerMenuContext);
2145         innerMenuContext->UpdateOpacity(1.0f);
2146     }, option.GetOnFinishEvent(), nullptr, menuNode->GetContextRefPtr());
2147 }
2148 
ShowStackMenuDisappearAnimation(const RefPtr<FrameNode> & menuNode,const RefPtr<FrameNode> & subMenuNode,AnimationOption & option) const2149 void MenuPattern::ShowStackMenuDisappearAnimation(const RefPtr<FrameNode>& menuNode,
2150     const RefPtr<FrameNode>& subMenuNode, AnimationOption& option) const
2151 {
2152     CHECK_NULL_VOID(menuNode);
2153     auto menuNodeAccessibilityProps = menuNode->GetAccessibilityProperty<AccessibilityProperty>();
2154     CHECK_NULL_VOID(menuNodeAccessibilityProps);
2155     menuNodeAccessibilityProps->SetAccessibilityLevel(AccessibilityProperty::Level::AUTO);
2156     CHECK_NULL_VOID(subMenuNode);
2157     ShowStackSubMenuDisappearAnimation(menuNode, subMenuNode);
2158     ShowStackMainMenuDisappearAnimation(menuNode, subMenuNode, option);
2159 }
2160 
ShowMenuDisappearAnimation()2161 void MenuPattern::ShowMenuDisappearAnimation()
2162 {
2163     if (!Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
2164         return;
2165     }
2166     auto host = GetHost();
2167     CHECK_NULL_VOID(host);
2168     auto menuContext = host->GetRenderContext();
2169     MAIN_MENU_ANIMATION_CURVE->UpdateMinimumAmplitudeRatio(MINIMUM_AMPLITUDE_RATION);
2170     auto menuPosition = GetEndOffset();
2171     AnimationOption option = AnimationOption();
2172     option.SetCurve(MAIN_MENU_ANIMATION_CURVE);
2173     AnimationUtils::Animate(option, [menuContext, menuPosition]() {
2174         if (menuContext) {
2175             menuContext->UpdatePosition(
2176                 OffsetT<Dimension>(Dimension(menuPosition.GetX()), Dimension(menuPosition.GetY())));
2177             menuContext->UpdateTransformScale(VectorF(MENU_ORIGINAL_SCALE, MENU_ORIGINAL_SCALE));
2178             menuContext->UpdateOpacity(0.0f);
2179         }
2180     }, nullptr, nullptr, host->GetContextRefPtr());
2181 }
2182 
UpdateClipPath(const RefPtr<LayoutWrapper> & dirty)2183 void MenuPattern::UpdateClipPath(const RefPtr<LayoutWrapper>& dirty)
2184 {
2185     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
2186     CHECK_NULL_VOID(layoutAlgorithmWrapper);
2187     auto menuLayoutAlgorithm = DynamicCast<MenuLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
2188     CHECK_NULL_VOID(menuLayoutAlgorithm);
2189     auto clipPath = menuLayoutAlgorithm->GetClipPath();
2190     auto host = dirty->GetHostNode();
2191     CHECK_NULL_VOID(host);
2192     auto paintProperty = host->GetPaintProperty<MenuPaintProperty>();
2193     CHECK_NULL_VOID(paintProperty);
2194     paintProperty->UpdateClipPath(clipPath);
2195 }
2196 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)2197 bool MenuPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
2198 {
2199     ShowPreviewMenuAnimation();
2200     ShowMenuAppearAnimation();
2201     ShowStackMenuAppearAnimation();
2202     auto host = GetHost();
2203     CHECK_NULL_RETURN(host, false);
2204     auto menuPosition = host->GetPaintRectOffset(false, true);
2205     SetEndOffset(menuPosition);
2206     if (config.skipMeasure || dirty->SkipMeasureContent()) {
2207         return false;
2208     }
2209 
2210     UpdateClipPath(dirty);
2211     auto pipeline = host->GetContextWithCheck();
2212     CHECK_NULL_RETURN(pipeline, false);
2213     auto theme = pipeline->GetTheme<SelectTheme>();
2214     CHECK_NULL_RETURN(theme, false);
2215     auto renderContext = dirty->GetHostNode()->GetRenderContext();
2216     CHECK_NULL_RETURN(renderContext, false);
2217     renderContext->UpdateClipShape(nullptr);
2218     renderContext->ResetClipShape();
2219 
2220     auto menuProp = DynamicCast<MenuLayoutProperty>(dirty->GetLayoutProperty());
2221     CHECK_NULL_RETURN(menuProp, false);
2222     BorderRadiusProperty radius;
2223     auto defaultRadius = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) ?
2224         theme->GetMenuDefaultRadius() : theme->GetMenuBorderRadius();
2225     radius.SetRadius(defaultRadius);
2226     if (menuProp->GetBorderRadius().has_value()) {
2227         auto borderRadius = menuProp->GetBorderRadiusValue();
2228         auto geometryNode = dirty->GetGeometryNode();
2229         CHECK_NULL_RETURN(geometryNode, false);
2230         auto idealSize = geometryNode->GetMarginFrameSize();
2231         radius = CalcIdealBorderRadius(borderRadius, idealSize);
2232         UpdateBorderRadius(dirty->GetHostNode(), radius);
2233     }
2234     auto menuWrapper = GetMenuWrapper();
2235     DragAnimationHelper::ShowGatherAnimationWithMenu(menuWrapper);
2236     return true;
2237 }
2238 
CalcIdealBorderRadius(const BorderRadiusProperty & borderRadius,const SizeF & menuSize)2239 BorderRadiusProperty MenuPattern::CalcIdealBorderRadius(const BorderRadiusProperty& borderRadius, const SizeF& menuSize)
2240 {
2241     Dimension defaultDimension(0);
2242     BorderRadiusProperty radius = { defaultDimension, defaultDimension, defaultDimension, defaultDimension };
2243     auto host = GetHost();
2244     CHECK_NULL_RETURN(host, radius);
2245     auto pipeline = host->GetContextWithCheck();
2246     CHECK_NULL_RETURN(pipeline, radius);
2247     auto theme = pipeline->GetTheme<SelectTheme>();
2248     CHECK_NULL_RETURN(theme, radius);
2249     auto defaultRadius = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) ?
2250         theme->GetMenuDefaultRadius() : theme->GetMenuBorderRadius();
2251     radius.SetRadius(defaultRadius);
2252     auto radiusTopLeft = borderRadius.radiusTopLeft.value_or(Dimension()).ConvertToPx();
2253     auto radiusTopRight = borderRadius.radiusTopRight.value_or(Dimension()).ConvertToPx();
2254     auto radiusBottomLeft = borderRadius.radiusBottomLeft.value_or(Dimension()).ConvertToPx();
2255     auto radiusBottomRight = borderRadius.radiusBottomRight.value_or(Dimension()).ConvertToPx();
2256     auto maxRadiusW = std::max(radiusTopLeft + radiusTopRight, radiusBottomLeft + radiusBottomRight);
2257     auto maxRadiusH = std::max(radiusTopLeft + radiusBottomLeft, radiusTopRight + radiusBottomRight);
2258     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
2259         if (LessOrEqual(maxRadiusW, menuSize.Width()) && LessOrEqual(maxRadiusH, menuSize.Height())) {
2260             radius = borderRadius;
2261         }
2262     } else {
2263         if (LessNotEqual(maxRadiusW, menuSize.Width())) {
2264             radius = borderRadius;
2265         }
2266     }
2267 
2268     return radius;
2269 }
2270 
UpdateBorderRadius(const RefPtr<FrameNode> & menuNode,const BorderRadiusProperty & borderRadius)2271 void MenuPattern::UpdateBorderRadius(const RefPtr<FrameNode>& menuNode, const BorderRadiusProperty& borderRadius)
2272 {
2273     CHECK_NULL_VOID(menuNode);
2274     auto menuRenderContext = menuNode->GetRenderContext();
2275     CHECK_NULL_VOID(menuRenderContext);
2276     menuRenderContext->UpdateBorderRadius(borderRadius);
2277     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
2278         menuRenderContext->UpdateOuterBorderRadius(borderRadius);
2279     }
2280     auto node = menuNode->GetChildren().front();
2281     CHECK_NULL_VOID(node);
2282     auto scrollNode = AceType::DynamicCast<FrameNode>(node);
2283     CHECK_NULL_VOID(scrollNode);
2284     auto scrollRenderContext = scrollNode->GetRenderContext();
2285     CHECK_NULL_VOID(scrollRenderContext);
2286     scrollRenderContext->UpdateBorderRadius(borderRadius);
2287 }
2288 
UpdateBorderRadius(const RefPtr<FrameNode> & menuNode,const BorderRadiusProperty & borderRadius)2289 void InnerMenuPattern::UpdateBorderRadius(const RefPtr<FrameNode>& menuNode, const BorderRadiusProperty& borderRadius)
2290 {
2291     CHECK_NULL_VOID(menuNode);
2292     auto menuRenderContext = menuNode->GetRenderContext();
2293     CHECK_NULL_VOID(menuRenderContext);
2294     menuRenderContext->UpdateBorderRadius(borderRadius);
2295 }
2296 
GetMainMenuPattern() const2297 RefPtr<MenuPattern> MenuPattern::GetMainMenuPattern() const
2298 {
2299     auto wrapperFrameNode = GetMenuWrapper();
2300     CHECK_NULL_RETURN(wrapperFrameNode, nullptr);
2301     auto mainMenuUINode = wrapperFrameNode->GetChildAtIndex(0);
2302     CHECK_NULL_RETURN(mainMenuUINode, nullptr);
2303     auto mainMenuFrameNode = AceType::DynamicCast<FrameNode>(mainMenuUINode);
2304     return mainMenuFrameNode->GetPattern<MenuPattern>();
2305 }
2306 
RecordItemsAndGroups()2307 void InnerMenuPattern::RecordItemsAndGroups()
2308 {
2309     itemsAndGroups_.clear();
2310     auto host = GetHost();
2311     std::stack<RefPtr<UINode>> nodeStack;
2312     nodeStack.emplace(host);
2313     bool isMenu = true;
2314 
2315     while (!nodeStack.empty()) {
2316         auto currentNode = nodeStack.top();
2317         nodeStack.pop();
2318         // push items and item groups, skip menu node
2319         if (!isMenu && AceType::InstanceOf<FrameNode>(currentNode)) {
2320             itemsAndGroups_.emplace_back(WeakClaim(RawPtr(currentNode)));
2321             continue;
2322         }
2323         isMenu = false;
2324         // skip other type UiNode, such as ForEachNode
2325         for (int32_t index = static_cast<int32_t>(currentNode->GetChildren().size()) - 1; index >= 0; index--) {
2326             nodeStack.push(currentNode->GetChildAtIndex(index));
2327         }
2328     }
2329 }
2330 
FindSiblingMenuCount()2331 uint32_t InnerMenuPattern::FindSiblingMenuCount()
2332 {
2333     auto host = GetHost();
2334     CHECK_NULL_RETURN(host, 0);
2335     auto parent = host->GetParent();
2336     CHECK_NULL_RETURN(parent, 0);
2337     auto siblings = parent->GetChildren();
2338     uint32_t count = 0;
2339     for (auto&& sibling : siblings) {
2340         if (sibling->GetTag() == V2::MENU_ETS_TAG && sibling != host) {
2341             ++count;
2342         }
2343     }
2344     return count;
2345 }
2346 
ApplyDesktopMenuTheme()2347 void InnerMenuPattern::ApplyDesktopMenuTheme()
2348 {
2349     auto host = GetHost();
2350     CHECK_NULL_VOID(host);
2351     Shadow shadow;
2352     if (GetShadowFromTheme(ShadowStyle::OuterDefaultSM, shadow)) {
2353         host->GetRenderContext()->UpdateBackShadow(shadow);
2354     }
2355 }
2356 
ApplyMultiMenuTheme()2357 void InnerMenuPattern::ApplyMultiMenuTheme()
2358 {
2359     auto host = GetHost();
2360     CHECK_NULL_VOID(host);
2361     Shadow shadow;
2362     if (GetShadowFromTheme(ShadowStyle::None, shadow)) {
2363         host->GetRenderContext()->UpdateBackShadow(shadow);
2364     }
2365 }
2366 
OnColorConfigurationUpdate()2367 void MenuPattern::OnColorConfigurationUpdate()
2368 {
2369     auto host = GetHost();
2370     CHECK_NULL_VOID(host);
2371     auto pipeline = host->GetContextWithCheck();
2372     CHECK_NULL_VOID(pipeline);
2373 
2374     auto menuTheme = pipeline->GetTheme<SelectTheme>();
2375     CHECK_NULL_VOID(menuTheme);
2376 
2377     auto menuPattern = host->GetPattern<MenuPattern>();
2378     CHECK_NULL_VOID(menuPattern);
2379 
2380     auto renderContext = host->GetRenderContext();
2381     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN) || !renderContext->IsUniRenderEnabled()
2382         || menuTheme->GetMenuBlendBgColor()) {
2383         if (!isDisableMenuBgColorByUser_) {
2384             renderContext->UpdateBackgroundColor(menuTheme->GetBackgroundColor());
2385         }
2386     } else {
2387         renderContext->UpdateBackBlurStyle(renderContext->GetBackBlurStyle());
2388     }
2389 
2390     auto optionNode = menuPattern->GetOptions();
2391     for (const auto& child : optionNode) {
2392         auto optionsPattern = child->GetPattern<MenuItemPattern>();
2393         optionsPattern->SetFontColor(menuTheme->GetFontColor());
2394 
2395         child->MarkModifyDone();
2396         child->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
2397     }
2398     host->SetNeedCallChildrenUpdate(false);
2399 
2400     auto menuLayoutProperty = GetLayoutProperty<MenuLayoutProperty>();
2401     CHECK_NULL_VOID(menuLayoutProperty);
2402     if (SystemProperties::ConfigChangePerform() && !menuLayoutProperty->GetFontColorSetByUser().value_or(false)) {
2403         auto themeFontColor = menuTheme->GetMenuFontColor();
2404         menuLayoutProperty->UpdateFontColor(themeFontColor);
2405         host->MarkModifyDone();
2406         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2407     }
2408 }
2409 
InitPanEvent(const RefPtr<GestureEventHub> & gestureHub)2410 void MenuPattern::InitPanEvent(const RefPtr<GestureEventHub>& gestureHub)
2411 {
2412     CHECK_NULL_VOID(gestureHub);
2413     auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
2414         auto pattern = weak.Upgrade();
2415         CHECK_NULL_VOID(pattern);
2416         if (pattern->IsMenuScrollable()) {
2417             return;
2418         }
2419         auto offsetX = static_cast<float>(info.GetOffsetX());
2420         auto offsetY = static_cast<float>(info.GetOffsetY());
2421         auto offsetPerSecondX = info.GetVelocity().GetOffsetPerSecond().GetX();
2422         auto offsetPerSecondY = info.GetVelocity().GetOffsetPerSecond().GetY();
2423         auto velocity =
2424             static_cast<float>(std::sqrt(offsetPerSecondX * offsetPerSecondX + offsetPerSecondY * offsetPerSecondY));
2425         pattern->HandleDragEnd(offsetX, offsetY, velocity);
2426     };
2427     auto actionScrollEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
2428         auto pattern = weak.Upgrade();
2429         CHECK_NULL_VOID(pattern);
2430         if (pattern->IsMenuScrollable()) {
2431             return;
2432         }
2433         auto offsetX = static_cast<float>(info.GetOffsetX());
2434         auto offsetY = static_cast<float>(info.GetOffsetY());
2435         auto offsetPerSecondX = info.GetVelocity().GetOffsetPerSecond().GetX();
2436         auto offsetPerSecondY = info.GetVelocity().GetOffsetPerSecond().GetY();
2437         auto velocity =
2438             static_cast<float>(std::sqrt(offsetPerSecondX * offsetPerSecondX + offsetPerSecondY * offsetPerSecondY));
2439         pattern->HandleScrollDragEnd(offsetX, offsetY, velocity);
2440     };
2441     PanDirection panDirection;
2442     panDirection.type = PanDirection::ALL;
2443     auto panEvent = MakeRefPtr<PanEvent>(nullptr, nullptr, std::move(actionEndTask), nullptr);
2444     PanDistanceMap distanceMap = { { SourceTool::UNKNOWN, DEFAULT_PAN_DISTANCE.ConvertToPx() },
2445         { SourceTool::PEN, DEFAULT_PEN_PAN_DISTANCE.ConvertToPx() } };
2446     gestureHub->AddPanEvent(panEvent, panDirection, 1, distanceMap);
2447     gestureHub->AddPreviewMenuHandleDragEnd(std::move(actionScrollEndTask));
2448 }
2449 
HandleDragEnd(float offsetX,float offsetY,float velocity)2450 void MenuPattern::HandleDragEnd(float offsetX, float offsetY, float velocity)
2451 {
2452     if ((LessOrEqual(std::abs(offsetY), std::abs(offsetX)) || LessOrEqual(offsetY, 0.0f)) &&
2453         LessOrEqual(velocity, PAN_MAX_VELOCITY)) {
2454         return;
2455     }
2456     auto menuWrapper = GetMenuWrapper();
2457     CHECK_NULL_VOID(menuWrapper);
2458     auto wrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
2459     CHECK_NULL_VOID(wrapperPattern);
2460     TAG_LOGI(AceLogTag::ACE_DRAG, "will hide menu.");
2461     wrapperPattern->HideMenu(HideMenuType::MENU_DRAG_END);
2462 }
2463 
HandleScrollDragEnd(float offsetX,float offsetY,float velocity)2464 void MenuPattern::HandleScrollDragEnd(float offsetX, float offsetY, float velocity)
2465 {
2466     if ((LessOrEqual(std::abs(offsetY), std::abs(offsetX)) || !NearZero(offsetY)) &&
2467         LessOrEqual(velocity, PAN_MAX_VELOCITY)) {
2468         return;
2469     }
2470     auto menuWrapper = GetMenuWrapper();
2471     CHECK_NULL_VOID(menuWrapper);
2472     auto wrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
2473     CHECK_NULL_VOID(wrapperPattern);
2474     TAG_LOGI(AceLogTag::ACE_DRAG, "will hide menu.");
2475     wrapperPattern->HideMenu(HideMenuType::SCROLL_DRAG_END);
2476 }
2477 
DumpInfo()2478 void MenuPattern::DumpInfo()
2479 {
2480     DumpLog::GetInstance().AddDesc(
2481         std::string("MenuType: ").append(std::to_string(static_cast<int32_t>(GetMenuType()))));
2482 }
2483 
GetSelectMenuWidth()2484 float MenuPattern::GetSelectMenuWidth()
2485 {
2486     auto minSelectWidth = MIN_SELECT_MENU_WIDTH.ConvertToPx();
2487     RefPtr<GridColumnInfo> columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
2488     CHECK_NULL_RETURN(columnInfo, minSelectWidth);
2489     auto parent = columnInfo->GetParent();
2490     CHECK_NULL_RETURN(parent, minSelectWidth);
2491     parent->BuildColumnWidth();
2492     auto defaultWidth = static_cast<float>(columnInfo->GetWidth(COLUMN_NUM));
2493     float finalWidth = minSelectWidth;
2494 
2495     if (IsWidthModifiedBySelect()) {
2496         auto menuLayoutProperty = GetLayoutProperty<MenuLayoutProperty>();
2497         auto selectmodifiedwidth = menuLayoutProperty->GetSelectMenuModifiedWidth();
2498         finalWidth = selectmodifiedwidth.value_or(0.0f);
2499     } else {
2500         finalWidth = defaultWidth;
2501     }
2502 
2503     if (LessNotEqual(finalWidth, minSelectWidth)) {
2504         finalWidth = defaultWidth;
2505     }
2506 
2507     return finalWidth;
2508 }
2509 
OnItemPressed(const RefPtr<UINode> & parent,int32_t index,bool press,bool hover)2510 void MenuPattern::OnItemPressed(const RefPtr<UINode>& parent, int32_t index, bool press, bool hover)
2511 {
2512     CHECK_NULL_VOID(parent);
2513     if (parent->GetTag() == V2::MENU_ITEM_GROUP_ETS_TAG) {
2514         auto pattern = DynamicCast<FrameNode>(parent)->GetPattern<MenuItemGroupPattern>();
2515         CHECK_NULL_VOID(pattern);
2516         pattern->OnIntItemPressed(index, press);
2517     }
2518     HandleNextPressed(parent, index, press, hover);
2519     HandlePrevPressed(parent, index, press);
2520 }
2521 
HandleNextPressed(const RefPtr<UINode> & parent,int32_t index,bool press,bool hover)2522 void MenuPattern::HandleNextPressed(const RefPtr<UINode>& parent, int32_t index, bool press, bool hover)
2523 {
2524     CHECK_NULL_VOID(parent);
2525     RefPtr<UINode> nextNode = nullptr;
2526     const auto childrenSize = parent->GetChildren().size();
2527     auto syntaxNode = GetSyntaxNode(parent);
2528     if (index == static_cast<int32_t>(childrenSize - 1) && syntaxNode) {
2529         nextNode = GetForEachMenuItem(syntaxNode, true);
2530     } else if (parent->GetTag() == V2::JS_IF_ELSE_ETS_TAG && index == static_cast<int32_t>(childrenSize - 1)) {
2531         nextNode = GetOutsideForEachMenuItem(parent, true);
2532     } else {
2533         if (index >= static_cast<int32_t>(childrenSize - 1)) {
2534             return;
2535         }
2536         nextNode = parent->GetChildAtIndex(index + 1);
2537     }
2538     CHECK_NULL_VOID(nextNode);
2539     if (nextNode->GetTag() == V2::JS_FOR_EACH_ETS_TAG) {
2540         nextNode = GetForEachMenuItem(nextNode, true);
2541     }
2542     CHECK_NULL_VOID(nextNode);
2543     if (nextNode->GetTag() == V2::JS_IF_ELSE_ETS_TAG) {
2544         nextNode = GetIfElseMenuItem(nextNode, true);
2545     }
2546     CHECK_NULL_VOID(nextNode);
2547     if (nextNode->GetTag() == V2::MENU_ITEM_GROUP_ETS_TAG) {
2548         auto pattern = DynamicCast<FrameNode>(nextNode)->GetPattern<MenuItemGroupPattern>();
2549         CHECK_NULL_VOID(pattern);
2550         pattern->OnExtItemPressed(press, true);
2551     }
2552     if (nextNode->GetTag() == V2::MENU_ITEM_ETS_TAG) {
2553         auto props = DynamicCast<FrameNode>(nextNode)->GetPaintProperty<MenuItemPaintProperty>();
2554         CHECK_NULL_VOID(props);
2555         // need save needDivider property due to some items shoud not have divide in not pressed state
2556         hover ? props->UpdateHover(press) : props->UpdatePress(press);
2557         nextNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2558     }
2559 }
2560 
HandlePrevPressed(const RefPtr<UINode> & parent,int32_t index,bool press)2561 void MenuPattern::HandlePrevPressed(const RefPtr<UINode>& parent, int32_t index, bool press)
2562 {
2563     CHECK_NULL_VOID(parent);
2564     RefPtr<UINode> prevNode = nullptr;
2565     if (index > 0) {
2566         prevNode = parent->GetChildAtIndex(index - 1);
2567         CHECK_NULL_VOID(prevNode);
2568         if (prevNode->GetTag() == V2::JS_FOR_EACH_ETS_TAG) {
2569             prevNode = GetForEachMenuItem(prevNode, false);
2570         }
2571     } else {
2572         auto syntaxNode = GetSyntaxNode(parent);
2573         auto grandParent = parent->GetParent();
2574         CHECK_NULL_VOID(grandParent);
2575         if (grandParent->GetChildIndex(parent) == 0 && syntaxNode) {
2576             prevNode = GetForEachMenuItem(syntaxNode, false);
2577         }
2578         bool matchFirstItemInIfElse = parent->GetTag() == V2::MENU_ITEM_GROUP_ETS_TAG &&
2579             grandParent->GetChildIndex(parent) == 0 &&
2580             grandParent->GetTag() == V2::JS_IF_ELSE_ETS_TAG;
2581         if (matchFirstItemInIfElse) { // the first item in first group in ifElse
2582             prevNode = GetOutsideForEachMenuItem(grandParent, false);
2583         }
2584         bool notFirstGroupInMenu = grandParent->GetChildIndex(parent) > 0 &&
2585             parent->GetTag() == V2::MENU_ITEM_GROUP_ETS_TAG;
2586         if (notFirstGroupInMenu) {
2587             prevNode = GetOutsideForEachMenuItem(parent, false);
2588         }
2589     }
2590     CHECK_NULL_VOID(prevNode);
2591     if (prevNode->GetTag() == V2::JS_IF_ELSE_ETS_TAG) {
2592         prevNode = GetIfElseMenuItem(prevNode, false);
2593     }
2594     CHECK_NULL_VOID(prevNode);
2595     if (prevNode->GetTag() == V2::MENU_ITEM_GROUP_ETS_TAG) {
2596         auto preFrameNode = DynamicCast<FrameNode>(prevNode);
2597         CHECK_NULL_VOID(preFrameNode);
2598         auto pattern = preFrameNode->GetPattern<MenuItemGroupPattern>();
2599         CHECK_NULL_VOID(pattern);
2600         pattern->OnExtItemPressed(press, false);
2601     }
2602 }
2603 
GetForEachMenuItem(const RefPtr<UINode> & parent,bool next)2604 RefPtr<UINode> MenuPattern::GetForEachMenuItem(const RefPtr<UINode>& parent, bool next)
2605 {
2606     CHECK_NULL_RETURN(parent, nullptr);
2607     if (parent->GetTag() == V2::JS_FOR_EACH_ETS_TAG) {
2608         auto nextSyntax = next ? parent->GetFirstChild() : parent->GetLastChild();
2609         CHECK_NULL_RETURN(nextSyntax, nullptr);
2610         return next ? nextSyntax->GetFirstChild() : nextSyntax->GetLastChild();
2611     }
2612     auto node = GetSyntaxNode(parent);
2613     CHECK_NULL_RETURN(node, nullptr);
2614     auto forEachNode = AceType::DynamicCast<UINode>(node->GetParent());
2615     if (node->GetTag() == V2::JS_SYNTAX_ITEM_ETS_TAG) {
2616         CHECK_NULL_RETURN(forEachNode, nullptr);
2617         auto syntIndex = forEachNode->GetChildIndex(node);
2618         const auto& children = forEachNode->GetChildren();
2619         if (next) {
2620             if (syntIndex < static_cast<int32_t>(children.size() - 1)) { // next is inside forEach
2621                 auto nextSyntax = forEachNode->GetChildAtIndex(syntIndex + 1);
2622                 CHECK_NULL_RETURN(nextSyntax, nullptr);
2623                 return nextSyntax->GetFirstChild();
2624             } else { // next is after forEach
2625                 return GetOutsideForEachMenuItem(forEachNode, true);
2626             }
2627         } else {
2628             if (syntIndex > 0) { // prev is inside forEach
2629                 auto prevSyntax = forEachNode->GetChildAtIndex(syntIndex - 1);
2630                 CHECK_NULL_RETURN(prevSyntax, nullptr);
2631                 return prevSyntax->GetLastChild();
2632             } else { // prev is before forEach
2633                 return GetOutsideForEachMenuItem(forEachNode, false);
2634             }
2635         }
2636     }
2637     return nullptr;
2638 }
2639 
GetOutsideForEachMenuItem(const RefPtr<UINode> & forEachNode,bool next)2640 RefPtr<UINode> MenuPattern::GetOutsideForEachMenuItem(const RefPtr<UINode>& forEachNode, bool next)
2641 {
2642     auto parentForEachNode = AceType::DynamicCast<UINode>(forEachNode->GetParent());
2643     CHECK_NULL_RETURN(parentForEachNode, nullptr);
2644     auto forEachIndex = parentForEachNode->GetChildIndex(forEachNode);
2645     int32_t shift = next ? 1 : -1;
2646     const auto& children = parentForEachNode->GetChildren();
2647     if ((forEachIndex + shift) >= 0 && (forEachIndex + shift) <= static_cast<int32_t>(children.size() - 1)) {
2648         return parentForEachNode->GetChildAtIndex(forEachIndex + shift);
2649     } else {
2650         return nullptr;
2651     }
2652 }
2653 
GetSyntaxNode(const RefPtr<UINode> & parent)2654 RefPtr<UINode> MenuPattern::GetSyntaxNode(const RefPtr<UINode>& parent)
2655 {
2656     CHECK_NULL_RETURN(parent, nullptr);
2657     auto node = parent;
2658     while (node) {
2659         if (node->GetTag() == V2::MENU_ETS_TAG) {
2660             return nullptr;
2661         }
2662         if (node->GetTag() == V2::JS_SYNTAX_ITEM_ETS_TAG) {
2663             return node;
2664         }
2665         node = node->GetParent();
2666     }
2667     return nullptr;
2668 }
2669 
GetIfElseMenuItem(const RefPtr<UINode> & parent,bool next)2670 RefPtr<UINode> MenuPattern::GetIfElseMenuItem(const RefPtr<UINode>& parent, bool next)
2671 {
2672     CHECK_NULL_RETURN(parent, nullptr);
2673     auto nextItem = next ? parent->GetFirstChild() : parent->GetLastChild();
2674     return nextItem;
2675 }
2676 
IsMenuScrollable() const2677 bool MenuPattern::IsMenuScrollable() const
2678 {
2679     auto host = GetHost();
2680     CHECK_NULL_RETURN(host, false);
2681     auto firstChild = DynamicCast<FrameNode>(host->GetChildAtIndex(0));
2682     CHECK_NULL_RETURN(firstChild, false);
2683     if (firstChild->GetTag() == V2::SCROLL_ETS_TAG) {
2684         auto scrollPattern = firstChild->GetPattern<ScrollPattern>();
2685         CHECK_NULL_RETURN(scrollPattern, false);
2686         return scrollPattern->IsScrollable() && Positive(scrollPattern->GetScrollableDistance());
2687     }
2688     return false;
2689 }
2690 
DumpInfo(std::unique_ptr<JsonValue> & json)2691 void MenuPattern::DumpInfo(std::unique_ptr<JsonValue>& json)
2692 {
2693     json->Put("MenuType", static_cast<int32_t>(GetMenuType()));
2694 }
2695 
GetPreviewMenuAnimationOffset(const OffsetF & previewCenter,const SizeF & previewSize,float scale) const2696 OffsetF MenuPattern::GetPreviewMenuAnimationOffset(
2697     const OffsetF& previewCenter, const SizeF& previewSize, float scale) const
2698 {
2699     auto host = GetHost();
2700     CHECK_NULL_RETURN(host, OffsetF());
2701     auto geometryNode = host->GetGeometryNode();
2702     CHECK_NULL_RETURN(geometryNode, OffsetF());
2703     auto size = geometryNode->GetFrameSize();
2704     auto menuPattern = host->GetPattern<MenuPattern>();
2705     CHECK_NULL_RETURN(menuPattern, OffsetF());
2706     auto placement = menuPattern->GetLastPlacement().value_or(Placement::NONE);
2707 
2708     auto space = TARGET_SPACE.ConvertToPx();
2709     auto menuWidth = size.Width();
2710     auto menuHeight = size.Height();
2711 
2712     auto top = previewCenter.GetY() - previewSize.Height() * scale / HALF;
2713     auto bottom = previewCenter.GetY() + previewSize.Height() * scale / HALF;
2714     auto left = previewCenter.GetX() - previewSize.Width() * scale / HALF;
2715     auto right = previewCenter.GetX() + previewSize.Width() * scale / HALF;
2716 
2717     switch (placement) {
2718         case Placement::TOP:
2719             return OffsetF(previewCenter.GetX() - menuWidth / HALF, top - space - menuHeight);
2720         case Placement::TOP_LEFT:
2721             return OffsetF(left, top - space - menuHeight);
2722         case Placement::TOP_RIGHT:
2723             return OffsetF(right - menuWidth, top - space - menuHeight);
2724         case Placement::BOTTOM:
2725             return OffsetF(previewCenter.GetX() - menuWidth / HALF, bottom + space);
2726         case Placement::BOTTOM_LEFT:
2727             return OffsetF(left, bottom + space);
2728         case Placement::BOTTOM_RIGHT:
2729             return OffsetF(right - menuWidth, bottom + space);
2730         case Placement::LEFT:
2731             return OffsetF(left - space - menuWidth, previewCenter.GetY() - menuHeight / HALF);
2732         case Placement::LEFT_TOP:
2733             return OffsetF(left - space - menuWidth, top);
2734         case Placement::LEFT_BOTTOM:
2735             return OffsetF(left - space - menuWidth, bottom - menuHeight);
2736         case Placement::RIGHT:
2737             return OffsetF(right + space, previewCenter.GetY() - menuHeight / HALF);
2738         case Placement::RIGHT_TOP:
2739             return OffsetF(right + space, top);
2740         case Placement::RIGHT_BOTTOM:
2741             return OffsetF(right + space, bottom - menuHeight);
2742         default:
2743             return OffsetF(left, bottom + space);
2744     }
2745 }
2746 
InitPreviewMenuAnimationInfo(const RefPtr<MenuTheme> & menuTheme)2747 void MenuPattern::InitPreviewMenuAnimationInfo(const RefPtr<MenuTheme>& menuTheme)
2748 {
2749     CHECK_NULL_VOID(menuTheme);
2750 
2751     auto menuWrapper = GetMenuWrapper();
2752     CHECK_NULL_VOID(menuWrapper);
2753     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
2754     CHECK_NULL_VOID(menuWrapperPattern);
2755     auto hasTransition = menuWrapperPattern->HasTransitionEffect() || menuWrapperPattern->HasPreviewTransitionEffect();
2756     if (hasTransition) {
2757         disappearOffset_ = endOffset_;
2758         return;
2759     }
2760 
2761     auto preview = menuWrapperPattern->GetPreview();
2762     CHECK_NULL_VOID(preview);
2763     auto previewGeometryNode = preview->GetGeometryNode();
2764     CHECK_NULL_VOID(previewGeometryNode);
2765     auto previewSize = previewGeometryNode->GetMarginFrameSize();
2766 
2767     auto scaleBefore = GetPreviewBeforeAnimationScale();
2768     auto appearScale = LessNotEqual(scaleBefore, 0.0) ? menuTheme->GetPreviewBeforeAnimationScale() : scaleBefore;
2769     auto disappearScale = 1.0;
2770 
2771     if (previewMode_ == MenuPreviewMode::IMAGE) {
2772         auto imagePattern = preview->GetPattern<ImagePattern>();
2773         CHECK_NULL_VOID(imagePattern);
2774         auto imageRawSize = imagePattern->GetRawImageSize();
2775         auto isOriginSizeGreater = previewSize.IsPositive() && imageRawSize.IsPositive() && imageRawSize > previewSize;
2776         if (isOriginSizeGreater) {
2777             appearScale *= imageRawSize.Width() / previewSize.Width();
2778         }
2779 
2780         disappearScale = targetSize_.Width() / previewSize.Width();
2781     }
2782 
2783     OffsetF previewCenter = GetPreviewOriginOffset() + OffsetF(previewSize.Width() / HALF, previewSize.Height() / HALF);
2784     if (isShowHoverImage_) {
2785         auto previewPattern = preview->GetPattern<MenuPreviewPattern>();
2786         CHECK_NULL_VOID(previewPattern);
2787         appearScale = previewPattern->GetHoverImageScaleTo();
2788         appearScale = LessOrEqual(appearScale, 0.0) ? menuTheme->GetPreviewBeforeAnimationScale() : appearScale;
2789 
2790         disappearScale = previewPattern->GetHoverImageScaleFrom();
2791         disappearScale =
2792             LessNotEqual(disappearScale, 0.0) ? menuTheme->GetPreviewBeforeAnimationScale() : disappearScale;
2793 
2794         previewSize =
2795             SizeF(previewPattern->GetHoverImageAfterScaleWidth(), previewPattern->GetHoverImageAfterScaleHeight());
2796         auto clipEndWidth = previewPattern->GetStackAfterScaleActualWidth();
2797         auto clipEndHeight = previewPattern->GetStackAfterScaleActualHeight();
2798         previewCenter = GetPreviewOriginOffset() + OffsetF(clipEndWidth / HALF, clipEndHeight / HALF);
2799     }
2800 
2801     originOffset_ = GetPreviewMenuAnimationOffset(previewCenter, previewSize, appearScale);
2802     disappearOffset_ = GetPreviewMenuAnimationOffset(previewCenter, previewSize, disappearScale);
2803 }
2804 
UpdateMenuPathParams(std::optional<MenuPathParams> pathParams)2805 void MenuPattern::UpdateMenuPathParams(std::optional<MenuPathParams> pathParams)
2806 {
2807     pathParams_ = pathParams;
2808     auto wrapperNode = GetMenuWrapper();
2809     CHECK_NULL_VOID(wrapperNode);
2810     auto pattern = wrapperNode->GetPattern<MenuWrapperPattern>();
2811     CHECK_NULL_VOID(pattern);
2812     pattern->RequestPathRender();
2813 }
2814 
OnDetachFromMainTree()2815 void MenuPattern::OnDetachFromMainTree()
2816 {
2817     auto wrapperNode = GetMenuWrapper();
2818     CHECK_NULL_VOID(wrapperNode);
2819     auto pattern = wrapperNode->GetPattern<MenuWrapperPattern>();
2820     CHECK_NULL_VOID(pattern);
2821     pattern->RequestPathRender();
2822 }
2823 
GetSelectMenuWidthFromTheme() const2824 float MenuPattern::GetSelectMenuWidthFromTheme() const
2825 {
2826     auto minSelectWidth = MIN_SELECT_MENU_WIDTH.ConvertToPx();
2827     RefPtr<GridColumnInfo> columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
2828     CHECK_NULL_RETURN(columnInfo, minSelectWidth);
2829     auto parent = columnInfo->GetParent();
2830     CHECK_NULL_RETURN(parent, minSelectWidth);
2831     parent->BuildColumnWidth();
2832     auto defaultWidth = static_cast<float>(columnInfo->GetWidth(COLUMN_NUM));
2833     auto host = GetHost();
2834     CHECK_NULL_RETURN(host, minSelectWidth);
2835     auto context = host->GetContext();
2836     CHECK_NULL_RETURN(context, minSelectWidth);
2837     auto theme = context->GetTheme<SelectTheme>();
2838     CHECK_NULL_RETURN(theme, minSelectWidth);
2839     float finalWidth = (theme->GetMenuNormalWidth() + OPTION_MARGIN).ConvertToPx();
2840     if (LessNotEqual(finalWidth, minSelectWidth)) {
2841         finalWidth = defaultWidth;
2842     }
2843     return finalWidth;
2844 }
2845 
IsSelectOverlayDefaultModeRightClickMenu()2846 bool MenuPattern::IsSelectOverlayDefaultModeRightClickMenu()
2847 {
2848     CHECK_NULL_RETURN(IsSelectOverlayRightClickMenu(), false);
2849     auto menuWrapper = GetMenuWrapper();
2850     CHECK_NULL_RETURN(menuWrapper, false);
2851     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
2852     CHECK_NULL_RETURN(menuWrapperPattern, false);
2853     return !menuWrapperPattern->GetIsSelectOverlaySubWindowWrapper();
2854 }
2855 } // namespace OHOS::Ace::NG
2856