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