• 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 <stack>
19 
20 #include "base/log/dump_log.h"
21 #include "base/memory/ace_type.h"
22 #include "base/memory/referenced.h"
23 #include "base/utils/utils.h"
24 #include "core/animation/animation_pub.h"
25 #include "core/animation/spring_curve.h"
26 #include "core/common/container.h"
27 #include "core/components/common/layout/grid_system_manager.h"
28 #include "core/components/common/properties/shadow_config.h"
29 #include "core/components/select/select_theme.h"
30 #include "core/components_ng/base/ui_node.h"
31 #include "core/components_ng/pattern/menu/menu_item/menu_item_layout_property.h"
32 #include "core/components_ng/pattern/menu/menu_item/menu_item_pattern.h"
33 #include "core/components_ng/pattern/menu/menu_theme.h"
34 #include "core/components_ng/pattern/menu/multi_menu_layout_algorithm.h"
35 #include "core/components_ng/pattern/menu/preview/menu_preview_pattern.h"
36 #include "core/components_ng/pattern/menu/sub_menu_layout_algorithm.h"
37 #include "core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.h"
38 #include "core/components_ng/pattern/option/option_pattern.h"
39 #include "core/components_ng/pattern/option/option_view.h"
40 #include "core/components_ng/pattern/scroll/scroll_pattern.h"
41 #include "core/components_ng/pattern/text/text_layout_property.h"
42 #include "core/components_v2/inspector/inspector_constants.h"
43 #include "core/event/touch_event.h"
44 #include "core/pipeline/pipeline_base.h"
45 #include "core/pipeline_ng/pipeline_context.h"
46 
47 namespace OHOS::Ace::NG {
48 namespace {
49 constexpr float PAN_MAX_VELOCITY = 2000.0f;
50 constexpr Dimension MIN_SELECT_MENU_WIDTH = 64.0_vp;
51 constexpr int32_t COLUMN_NUM = 2;
52 
UpdateFontStyle(RefPtr<MenuLayoutProperty> & menuProperty,RefPtr<MenuItemLayoutProperty> & itemProperty,RefPtr<MenuItemPattern> & itemPattern,bool & contentChanged,bool & labelChanged)53 void UpdateFontStyle(RefPtr<MenuLayoutProperty>& menuProperty, RefPtr<MenuItemLayoutProperty>& itemProperty,
54     RefPtr<MenuItemPattern>& itemPattern, bool& contentChanged, bool& labelChanged)
55 {
56     auto contentNode = itemPattern->GetContentNode();
57     CHECK_NULL_VOID(contentNode);
58     auto textLayoutProperty = contentNode->GetLayoutProperty<TextLayoutProperty>();
59     CHECK_NULL_VOID(textLayoutProperty);
60     auto label = itemPattern->GetLabelNode();
61     RefPtr<TextLayoutProperty> labelProperty = label ? label->GetLayoutProperty<TextLayoutProperty>() : nullptr;
62     if (menuProperty->GetItalicFontStyle().has_value()) {
63         if (!itemProperty->GetItalicFontStyle().has_value()) {
64             textLayoutProperty->UpdateItalicFontStyle(menuProperty->GetItalicFontStyle().value());
65             contentChanged = true;
66         }
67         if (labelProperty && !itemProperty->GetLabelItalicFontStyle().has_value()) {
68             labelProperty->UpdateItalicFontStyle(menuProperty->GetItalicFontStyle().value());
69             labelChanged = true;
70         }
71     }
72     if (menuProperty->GetFontFamily().has_value()) {
73         if (!itemProperty->GetFontFamily().has_value()) {
74             textLayoutProperty->UpdateFontFamily(menuProperty->GetFontFamily().value());
75             contentChanged = true;
76         }
77         if (labelProperty && !itemProperty->GetLabelFontFamily().has_value()) {
78             labelProperty->UpdateFontFamily(menuProperty->GetFontFamily().value());
79             labelChanged = true;
80         }
81     }
82 }
83 
UpdateMenuItemTextNode(RefPtr<MenuLayoutProperty> & menuProperty,RefPtr<MenuItemLayoutProperty> & itemProperty,RefPtr<MenuItemPattern> & itemPattern)84 void UpdateMenuItemTextNode(RefPtr<MenuLayoutProperty>& menuProperty, RefPtr<MenuItemLayoutProperty>& itemProperty,
85     RefPtr<MenuItemPattern>& itemPattern)
86 {
87     auto contentNode = itemPattern->GetContentNode();
88     CHECK_NULL_VOID(contentNode);
89     auto textLayoutProperty = contentNode->GetLayoutProperty<TextLayoutProperty>();
90     CHECK_NULL_VOID(textLayoutProperty);
91     auto label = itemPattern->GetLabelNode();
92     RefPtr<TextLayoutProperty> labelProperty = label ? label->GetLayoutProperty<TextLayoutProperty>() : nullptr;
93     bool contentChanged = false;
94     bool labelChanged = false;
95     if (menuProperty->GetFontSize().has_value()) {
96         if (!itemProperty->GetFontSize().has_value()) {
97             textLayoutProperty->UpdateFontSize(menuProperty->GetFontSize().value());
98             contentChanged = true;
99         }
100         if (labelProperty && !itemProperty->GetLabelFontSize().has_value()) {
101             labelProperty->UpdateFontSize(menuProperty->GetFontSize().value());
102             labelChanged = true;
103         }
104     }
105     if (menuProperty->GetFontWeight().has_value()) {
106         if (!itemProperty->GetFontWeight().has_value()) {
107             textLayoutProperty->UpdateFontWeight(menuProperty->GetFontWeight().value());
108             contentChanged = true;
109         }
110         if (labelProperty && !itemProperty->GetLabelFontWeight().has_value()) {
111             labelProperty->UpdateFontWeight(menuProperty->GetFontWeight().value());
112             labelChanged = true;
113         }
114     }
115     if (menuProperty->GetFontColor().has_value()) {
116         if (!itemProperty->GetFontColor().has_value()) {
117             textLayoutProperty->UpdateTextColor(menuProperty->GetFontColor().value());
118             contentChanged = true;
119         }
120         if (labelProperty && !itemProperty->GetLabelFontColor().has_value()) {
121             labelProperty->UpdateTextColor(menuProperty->GetFontColor().value());
122             labelChanged = true;
123         }
124     }
125     UpdateFontStyle(menuProperty, itemProperty, itemPattern, contentChanged, labelChanged);
126     if (contentChanged) {
127         contentNode->MarkModifyDone();
128         contentNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
129     }
130     if (labelChanged) {
131         label->MarkModifyDone();
132         label->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
133     }
134 }
135 
ShowMenuOpacityAnimation(const RefPtr<MenuTheme> & menuTheme,const RefPtr<RenderContext> & renderContext)136 void ShowMenuOpacityAnimation(const RefPtr<MenuTheme>& menuTheme, const RefPtr<RenderContext>& renderContext)
137 {
138     CHECK_NULL_VOID(menuTheme);
139     CHECK_NULL_VOID(renderContext);
140 
141     renderContext->UpdateOpacity(0.0);
142     AnimationOption option = AnimationOption();
143     option.SetCurve(Curves::FRICTION);
144     option.SetDuration(menuTheme->GetContextMenuAppearDuration());
145     AnimationUtils::Animate(option, [renderContext]() {
146         if (renderContext) {
147             renderContext->UpdateOpacity(1.0);
148         }
149     });
150 }
151 } // namespace
152 
OnAttachToFrameNode()153 void MenuPattern::OnAttachToFrameNode()
154 {
155     RegisterOnTouch();
156     auto host = GetHost();
157     CHECK_NULL_VOID(host);
158     auto focusHub = host->GetOrCreateFocusHub();
159     CHECK_NULL_VOID(focusHub);
160     RegisterOnKeyEvent(focusHub);
161     DisableTabInMenu();
162     InitTheme(host);
163     auto pipelineContext = PipelineContext::GetCurrentContext();
164     CHECK_NULL_VOID(pipelineContext);
165     auto targetNode = FrameNode::GetFrameNode(targetTag_, targetId_);
166     CHECK_NULL_VOID(targetNode);
167     auto eventHub = targetNode->GetEventHub<EventHub>();
168     CHECK_NULL_VOID(eventHub);
169     OnAreaChangedFunc onAreaChangedFunc = [menuNodeWk = WeakPtr<FrameNode>(host)](const RectF& oldRect,
170                                               const OffsetF& oldOrigin, const RectF& /* rect */,
171                                               const OffsetF& /* origin */) {
172         // Not handle first change
173         if (oldRect.IsEmpty() && oldOrigin.NonOffset()) {
174             return;
175         }
176 
177         auto pipelineContext = PipelineContext::GetCurrentContext();
178         AnimationOption option;
179         option.SetCurve(pipelineContext->GetSafeAreaManager()->GetSafeAreaCurve());
180         AnimationUtils::Animate(
181             option,
182             [weakPipeline = WeakPtr<PipelineContext>(pipelineContext), weakMenu = menuNodeWk]() {
183                 auto menu = weakMenu.Upgrade();
184                 CHECK_NULL_VOID(menu);
185                 auto menuPattern = menu->GetPattern<MenuPattern>();
186                 CHECK_NULL_VOID(menuPattern);
187                 auto menuWarpper = menuPattern->GetMenuWrapper();
188                 CHECK_NULL_VOID(menuWarpper);
189                 auto warpperPattern = menuWarpper->GetPattern<MenuWrapperPattern>();
190                 CHECK_NULL_VOID(warpperPattern);
191                 if (!warpperPattern->IsHided()) {
192                     auto pipeline = weakPipeline.Upgrade();
193                     CHECK_NULL_VOID(pipeline);
194                     menu->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
195                     pipeline->FlushUITasks();
196                 }
197             });
198         pipelineContext->FlushPipelineImmediately();
199     };
200     eventHub->AddInnerOnAreaChangedCallback(host->GetId(), std::move(onAreaChangedFunc));
201 }
202 
OnModifyDone()203 void MenuPattern::OnModifyDone()
204 {
205     Pattern::OnModifyDone();
206     auto host = GetHost();
207     CHECK_NULL_VOID(host);
208     UpdateMenuItemChildren(host);
209 
210     auto innerMenuCount = GetInnerMenuCount();
211     if (innerMenuCount == 1) {
212         ResetTheme(host, false);
213     } else if (innerMenuCount > 1) {
214         // multiple inner menus, reset outer container's shadow for desktop UX
215         ResetTheme(host, true);
216     }
217     auto menuFirstNode = GetFirstInnerMenu();
218     if (menuFirstNode) {
219         CopyMenuAttr(menuFirstNode);
220     }
221     SetAccessibilityAction();
222 
223     if (previewMode_ != MenuPreviewMode::NONE) {
224         auto node = host->GetChildren().front();
225         CHECK_NULL_VOID(node);
226         auto scroll = AceType::DynamicCast<FrameNode>(node);
227         CHECK_NULL_VOID(scroll);
228         auto hub = scroll->GetEventHub<EventHub>();
229         CHECK_NULL_VOID(hub);
230         auto gestureHub = hub->GetOrCreateGestureEventHub();
231         CHECK_NULL_VOID(gestureHub);
232         InitPanEvent(gestureHub);
233     }
234 }
235 
BeforeCreateLayoutWrapper()236 void InnerMenuPattern::BeforeCreateLayoutWrapper()
237 {
238     RecordItemsAndGroups();
239 
240     // determine menu type based on sibling menu count
241     auto count = FindSiblingMenuCount();
242     if (count > 0) {
243         SetType(MenuType::DESKTOP_MENU);
244         ApplyDesktopMenuTheme();
245     } else {
246         SetType(MenuType::MULTI_MENU);
247         ApplyMultiMenuTheme();
248     }
249 }
250 
OnModifyDone()251 void InnerMenuPattern::OnModifyDone()
252 {
253     Pattern::OnModifyDone();
254     auto host = GetHost();
255     CHECK_NULL_VOID(host);
256     UpdateMenuItemChildren(host);
257     SetAccessibilityAction();
258 }
259 
260 // close menu on touch up
RegisterOnTouch()261 void MenuPattern::RegisterOnTouch()
262 {
263     CHECK_NULL_VOID(!onTouch_);
264     auto host = GetHost();
265     CHECK_NULL_VOID(host);
266     auto gesture = host->GetOrCreateGestureEventHub();
267     CHECK_NULL_VOID(gesture);
268     auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
269         auto pattern = weak.Upgrade();
270         CHECK_NULL_VOID(pattern);
271         pattern->OnTouchEvent(info);
272     };
273     onTouch_ = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
274     gesture->AddTouchEvent(onTouch_);
275 }
276 
OnTouchEvent(const TouchEventInfo & info)277 void MenuPattern::OnTouchEvent(const TouchEventInfo& info)
278 {
279     if (GetInnerMenuCount() > 0 || IsMultiMenu() || IsDesktopMenu()|| IsSelectOverlayCustomMenu()) {
280         // not click hide menu for multi menu or select overlay custom menu
281         return;
282     }
283     if (!options_.empty()) {
284         // not click hide menu for select and bindMenu default option
285         return;
286     }
287     auto touchType = info.GetTouches().front().GetTouchType();
288     if (touchType == TouchType::DOWN) {
289         lastTouchOffset_ = info.GetTouches().front().GetLocalLocation();
290     } else if (touchType == TouchType::UP) {
291         auto touchUpOffset = info.GetTouches().front().GetLocalLocation();
292         if (lastTouchOffset_.has_value() &&
293             (touchUpOffset - lastTouchOffset_.value()).GetDistance() <= DEFAULT_CLICK_DISTANCE) {
294             HideMenu(true);
295         }
296         lastTouchOffset_.reset();
297     }
298 }
299 
RegisterOnKeyEvent(const RefPtr<FocusHub> & focusHub)300 void MenuPattern::RegisterOnKeyEvent(const RefPtr<FocusHub>& focusHub)
301 {
302     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
303         auto pattern = wp.Upgrade();
304         CHECK_NULL_RETURN(pattern, false);
305         return pattern->OnKeyEvent(event);
306     };
307     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
308 }
309 
OnKeyEvent(const KeyEvent & event) const310 bool MenuPattern::OnKeyEvent(const KeyEvent& event) const
311 {
312     if (event.action != KeyAction::DOWN || IsMultiMenu() || IsDesktopMenu()) {
313         return false;
314     }
315     if ((event.code == KeyCode::KEY_DPAD_LEFT || event.code == KeyCode::KEY_ESCAPE) &&
316         (IsSubMenu() || IsSelectOverlaySubMenu())) {
317         auto menuWrapper = GetMenuWrapper();
318         CHECK_NULL_RETURN(menuWrapper, true);
319         auto wrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
320         CHECK_NULL_RETURN(wrapperPattern, true);
321         wrapperPattern->HideSubMenu();
322         return true;
323     }
324     return false;
325 }
326 
RemoveParentHoverStyle()327 void MenuPattern::RemoveParentHoverStyle()
328 {
329     if (!IsSubMenu()) {
330         return;
331     }
332     auto menuItemParent = GetParentMenuItem();
333     CHECK_NULL_VOID(menuItemParent);
334     auto menuItemPattern = menuItemParent->GetPattern<MenuItemPattern>();
335     CHECK_NULL_VOID(menuItemPattern);
336     menuItemPattern->SetIsSubMenuShowed(false);
337 
338     auto pipeline = PipelineBase::GetCurrentContext();
339     CHECK_NULL_VOID(pipeline);
340     auto theme = pipeline->GetTheme<SelectTheme>();
341     CHECK_NULL_VOID(theme);
342     menuItemPattern->SetBgBlendColor(Color::TRANSPARENT);
343     menuItemPattern->PlayBgColorAnimation();
344 }
345 
UpdateMenuItemChildren(RefPtr<FrameNode> & host)346 void MenuPattern::UpdateMenuItemChildren(RefPtr<FrameNode>& host)
347 {
348     auto layoutProperty = GetLayoutProperty<MenuLayoutProperty>();
349     CHECK_NULL_VOID(layoutProperty);
350     const auto& children = host->GetChildren();
351     for (auto child : children) {
352         if (child->GetTag() == V2::MENU_ITEM_ETS_TAG) {
353             auto itemNode = AceType::DynamicCast<FrameNode>(child);
354             CHECK_NULL_VOID(itemNode);
355             auto itemProperty = itemNode->GetLayoutProperty<MenuItemLayoutProperty>();
356             CHECK_NULL_VOID(itemProperty);
357             auto itemPattern = itemNode->GetPattern<MenuItemPattern>();
358             CHECK_NULL_VOID(itemPattern);
359             UpdateMenuItemTextNode(layoutProperty, itemProperty, itemPattern);
360         } else if (child->GetTag() == V2::MENU_ITEM_GROUP_ETS_TAG) {
361             auto itemGroupNode = AceType::DynamicCast<FrameNode>(child);
362             CHECK_NULL_VOID(itemGroupNode);
363             UpdateMenuItemChildren(itemGroupNode);
364         } else {
365             // do nothing
366         }
367     }
368 }
369 
UpdateSelectParam(const std::vector<SelectParam> & params)370 void MenuPattern::UpdateSelectParam(const std::vector<SelectParam>& params)
371 {
372     if (!isSelectMenu_) {
373         return;
374     }
375     auto host = GetHost();
376     CHECK_NULL_VOID(host);
377     const auto& children = GetOptions();
378     auto childCount = children.size();
379     auto paramCount = params.size();
380     size_t updateCount = std::min(paramCount, childCount);
381     auto childIt = children.begin();
382     for (size_t i = 0; i < updateCount; i++, childIt++) {
383         const auto& childNode = AceType::DynamicCast<FrameNode>(*childIt);
384         CHECK_NULL_VOID(childNode);
385         if (i == 0) {
386             auto props = childNode->GetPaintProperty<OptionPaintProperty>();
387             CHECK_NULL_VOID(props);
388             props->UpdateNeedDivider(false);
389         }
390         auto optionPattern = childNode->GetPattern<OptionPattern>();
391         CHECK_NULL_VOID(optionPattern);
392         optionPattern->UpdateText(params.at(i).first);
393         optionPattern->UpdateIcon(params.at(i).second);
394         childNode->MarkModifyDone();
395         childNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
396     }
397     for (size_t i = updateCount; i < paramCount; i++) {
398         auto optionNode = OptionView::CreateSelectOption(params.at(i).first, params.at(i).second, i);
399         if (i == 0) {
400             auto props = optionNode->GetPaintProperty<OptionPaintProperty>();
401             props->UpdateNeedDivider(false);
402         }
403         MountOption(optionNode);
404         optionNode->MarkModifyDone();
405         optionNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
406     }
407     for (size_t i = childCount; i > updateCount; i--) {
408         RemoveOption();
409     }
410     host->MarkModifyDone();
411     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
412 }
413 
HideMenu(bool isMenuOnTouch) const414 void MenuPattern::HideMenu(bool isMenuOnTouch) const
415 {
416     auto pipeline = PipelineContext::GetCurrentContext();
417     CHECK_NULL_VOID(pipeline);
418     auto theme = pipeline->GetTheme<SelectTheme>();
419     CHECK_NULL_VOID(theme);
420     auto expandDisplay = theme->GetExpandDisplay();
421     auto host = GetHost();
422     CHECK_NULL_VOID(host);
423     auto rootMenuPattern = AceType::DynamicCast<MenuPattern>(host->GetPattern());
424     CHECK_NULL_VOID(rootMenuPattern);
425     // copy menu pattern properties to rootMenu
426     auto layoutProperty = rootMenuPattern->GetLayoutProperty<MenuLayoutProperty>();
427     CHECK_NULL_VOID(layoutProperty);
428     bool isShowInSubWindow = layoutProperty->GetShowInSubWindowValue(true);
429     auto wrapper = GetMenuWrapper();
430     CHECK_NULL_VOID(wrapper);
431     if (wrapper->GetTag() == V2::SELECT_OVERLAY_ETS_TAG) {
432         return;
433     }
434     if (((IsContextMenu() || (expandDisplay && isShowInSubWindow))) && (targetTag_ != V2::SELECT_ETS_TAG)) {
435         SubwindowManager::GetInstance()->HideMenuNG(wrapper, targetId_);
436         return;
437     }
438 
439     auto overlayManager = pipeline->GetOverlayManager();
440     CHECK_NULL_VOID(overlayManager);
441     overlayManager->HideMenu(wrapper, targetId_, isMenuOnTouch);
442 }
443 
HideSubMenu()444 void MenuPattern::HideSubMenu()
445 {
446     if (!showedSubMenu_) {
447         return;
448     }
449     auto subMenuPattern = showedSubMenu_->GetPattern<MenuPattern>();
450     CHECK_NULL_VOID(subMenuPattern);
451     subMenuPattern->RemoveParentHoverStyle();
452 
453     auto menuItem = subMenuPattern->GetParentMenuItem();
454     CHECK_NULL_VOID(menuItem);
455     auto menuItemPattern = menuItem->GetPattern<MenuItemPattern>();
456     CHECK_NULL_VOID(menuItemPattern);
457     menuItemPattern->SetIsSubMenuShowed(false);
458     menuItemPattern->ClearHoverRegions();
459     menuItemPattern->ResetWrapperMouseEvent();
460 
461     auto wrapper = GetMenuWrapper();
462     CHECK_NULL_VOID(wrapper);
463     wrapper->RemoveChild(showedSubMenu_);
464     wrapper->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
465     showedSubMenu_.Reset();
466 }
467 
GetMenuWrapper() const468 RefPtr<FrameNode> MenuPattern::GetMenuWrapper() const
469 {
470     auto host = GetHost();
471     CHECK_NULL_RETURN(host, nullptr);
472     auto parent = host->GetParent();
473     while (parent) {
474         if (parent->GetTag() == V2::MENU_WRAPPER_ETS_TAG || parent->GetTag() == V2::SELECT_OVERLAY_ETS_TAG) {
475             return AceType::DynamicCast<FrameNode>(parent);
476         }
477         parent = parent->GetParent();
478     }
479     return nullptr;
480 }
481 
482 // search for inner <Menu> node, once found a <Menu> node, count the number of sibling <Menu>
GetInnerMenuCount() const483 uint32_t MenuPattern::GetInnerMenuCount() const
484 {
485     if (type_ == MenuType::MULTI_MENU || type_ == MenuType::DESKTOP_MENU || IsSelectOverlayCustomMenu()) {
486         return 0;
487     }
488 
489     auto host = GetHost();
490     CHECK_NULL_RETURN(host, 0);
491     auto child = host->GetChildAtIndex(0);
492     uint32_t depth = 0;
493     while (child && depth < MAX_SEARCH_DEPTH) {
494         // found component <Menu>
495         if (child->GetTag() == V2::JS_VIEW_ETS_TAG) {
496             child = child->GetFrameChildByIndex(0, false);
497             continue;
498         }
499         if (child->GetTag() == V2::MENU_ETS_TAG) {
500             auto parent = child->GetParent();
501             CHECK_NULL_RETURN(parent, 0);
502             return parent->GetChildren().size();
503         }
504         child = child->GetChildAtIndex(0);
505         ++depth;
506     }
507     return 0;
508 }
509 
GetFirstInnerMenu() const510 RefPtr<FrameNode> MenuPattern::GetFirstInnerMenu() const
511 {
512     if (type_ == MenuType::MULTI_MENU || type_ == MenuType::DESKTOP_MENU) {
513         return nullptr;
514     }
515 
516     auto host = GetHost();
517     CHECK_NULL_RETURN(host, nullptr);
518     uint32_t depth = 0;
519     auto child = host->GetChildAtIndex(0);
520     while (child && depth < MAX_SEARCH_DEPTH) {
521         // found component <Menu>
522         if (child->GetTag() == V2::JS_VIEW_ETS_TAG) {
523             child = child->GetFrameChildByIndex(0, false);
524             continue;
525         }
526         if (child->GetTag() == V2::MENU_ETS_TAG) {
527             return AceType::DynamicCast<FrameNode>(child);
528         }
529         child = child->GetChildAtIndex(0);
530         ++depth;
531     }
532     return nullptr;
533 }
534 
CopyMenuAttr(const RefPtr<FrameNode> & menuNode) const535 void MenuPattern::CopyMenuAttr(const RefPtr<FrameNode>& menuNode) const
536 {
537     auto pattern = AceType::DynamicCast<MenuPattern>(menuNode->GetPattern());
538     CHECK_NULL_VOID(pattern);
539 
540     auto host = GetHost();
541     CHECK_NULL_VOID(host);
542     auto rootMenuPattern = AceType::DynamicCast<MenuPattern>(host->GetPattern());
543     CHECK_NULL_VOID(rootMenuPattern);
544 
545     // copy menu pattern properties to rootMenu
546     auto layoutProperty = pattern->GetLayoutProperty<MenuLayoutProperty>();
547     CHECK_NULL_VOID(layoutProperty);
548     auto rootMenuLayoutProperty = rootMenuPattern->GetLayoutProperty<MenuLayoutProperty>();
549     CHECK_NULL_VOID(rootMenuLayoutProperty);
550     if (layoutProperty->GetBorderRadius().has_value()) {
551         rootMenuLayoutProperty->UpdateBorderRadius(layoutProperty->GetBorderRadiusValue());
552     }
553 }
554 
555 // mount option on menu
MountOption(const RefPtr<FrameNode> & option)556 void MenuPattern::MountOption(const RefPtr<FrameNode>& option)
557 {
558     auto column = GetMenuColumn();
559     CHECK_NULL_VOID(column);
560     auto pattern = option->GetPattern<OptionPattern>();
561     CHECK_NULL_VOID(pattern);
562     pattern->SetMenu(GetHost());
563     AddOptionNode(option);
564     option->MountToParent(column);
565 }
566 
567 // remove option from menu
RemoveOption()568 void MenuPattern::RemoveOption()
569 {
570     auto column = GetMenuColumn();
571     CHECK_NULL_VOID(column);
572     auto endOption = column->GetChildren().back();
573     CHECK_NULL_VOID(endOption);
574     column->RemoveChild(endOption);
575     PopOptionNode();
576 }
577 
GetMenuColumn() const578 RefPtr<FrameNode> MenuPattern::GetMenuColumn() const
579 {
580     auto menu = GetHost();
581     CHECK_NULL_RETURN(menu, nullptr);
582     auto scroll = menu->GetChildren().front();
583     CHECK_NULL_RETURN(scroll, nullptr);
584     auto column = scroll->GetChildren().front();
585     return DynamicCast<FrameNode>(column);
586 }
587 
DisableTabInMenu()588 void MenuPattern::DisableTabInMenu()
589 {
590     if (IsMultiMenu() || IsDesktopMenu()) {
591         // multi menu not has scroll and column
592         return;
593     }
594     // disable tab in menu
595     auto column = GetMenuColumn();
596     CHECK_NULL_VOID(column);
597     auto columnFocusHub = column->GetOrCreateFocusHub();
598     CHECK_NULL_VOID(columnFocusHub);
599 
600     auto onKeyEvent = [](const KeyEvent& event) -> bool {
601         if (event.action != KeyAction::DOWN) {
602             return false;
603         }
604         return event.code == KeyCode::KEY_TAB;
605     };
606     columnFocusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
607 }
608 
CreateLayoutAlgorithm()609 RefPtr<LayoutAlgorithm> MenuPattern::CreateLayoutAlgorithm()
610 {
611     switch (type_) {
612         case MenuType::MULTI_MENU:
613         case MenuType::DESKTOP_MENU:
614             return MakeRefPtr<MultiMenuLayoutAlgorithm>();
615         case MenuType::SUB_MENU:
616         case MenuType::SELECT_OVERLAY_SUB_MENU:
617             return MakeRefPtr<SubMenuLayoutAlgorithm>();
618         default:
619             return MakeRefPtr<MenuLayoutAlgorithm>(targetId_, targetTag_);
620     }
621 }
622 
ResetTheme(const RefPtr<FrameNode> & host,bool resetForDesktopMenu)623 void MenuPattern::ResetTheme(const RefPtr<FrameNode>& host, bool resetForDesktopMenu)
624 {
625     auto renderContext = host->GetRenderContext();
626     CHECK_NULL_VOID(renderContext);
627     auto scroll = DynamicCast<FrameNode>(host->GetFirstChild());
628     CHECK_NULL_VOID(scroll);
629 
630     if (resetForDesktopMenu) {
631         // DesktopMenu apply shadow on inner Menu node
632         renderContext->UpdateBackShadow(ShadowConfig::NoneShadow);
633     } else {
634         renderContext->UpdateBackShadow(ShadowConfig::DefaultShadowM);
635     }
636     // to enable inner menu shadow effect for desktopMenu, need to remove clipping from container
637     bool clip = !resetForDesktopMenu;
638     scroll->GetRenderContext()->SetClipToBounds(clip);
639 
640     // move padding from scroll to inner menu
641     auto scrollProp = scroll->GetLayoutProperty();
642     scrollProp->UpdatePadding(PaddingProperty());
643 }
644 
ResetScrollTheme(const RefPtr<FrameNode> & host)645 void MenuPattern::ResetScrollTheme(const RefPtr<FrameNode>& host)
646 {
647     auto renderContext = host->GetRenderContext();
648     CHECK_NULL_VOID(renderContext);
649     auto scroll = DynamicCast<FrameNode>(host->GetFirstChild());
650     CHECK_NULL_VOID(scroll);
651     scroll->GetRenderContext()->UpdateClipEdge(false);
652 }
653 
InitTheme(const RefPtr<FrameNode> & host)654 void MenuPattern::InitTheme(const RefPtr<FrameNode>& host)
655 {
656     auto renderContext = host->GetRenderContext();
657     CHECK_NULL_VOID(renderContext);
658 
659     auto pipeline = PipelineBase::GetCurrentContext();
660     CHECK_NULL_VOID(pipeline);
661     auto theme = pipeline->GetTheme<SelectTheme>();
662     CHECK_NULL_VOID(theme);
663     auto expandDisplay = theme->GetExpandDisplay();
664     expandDisplay_ = expandDisplay;
665     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN) || !renderContext->IsUniRenderEnabled()) {
666         auto bgColor = theme->GetBackgroundColor();
667         renderContext->UpdateBackgroundColor(bgColor);
668     }
669     renderContext->UpdateBackShadow(ShadowConfig::DefaultShadowM);
670     // make menu round rect
671     BorderRadiusProperty borderRadius;
672     borderRadius.SetRadius(theme->GetMenuBorderRadius());
673     renderContext->UpdateBorderRadius(borderRadius);
674 }
675 
InitTheme(const RefPtr<FrameNode> & host)676 void InnerMenuPattern::InitTheme(const RefPtr<FrameNode>& host)
677 {
678     MenuPattern::InitTheme(host);
679     // inner menu applies shadow in OnModifyDone(), where it can determine if it's a DesktopMenu or a regular menu
680 
681     auto layoutProperty = host->GetLayoutProperty();
682     if (layoutProperty->GetPaddingProperty()) {
683         // if user defined padding exists, skip applying default padding
684         return;
685     }
686     auto pipeline = PipelineBase::GetCurrentContext();
687     CHECK_NULL_VOID(pipeline);
688     auto theme = pipeline->GetTheme<SelectTheme>();
689     // apply default padding from theme on inner menu
690     PaddingProperty padding;
691     padding.SetEdges(CalcLength(theme->GetOutPadding()));
692     host->GetLayoutProperty()->UpdatePadding(padding);
693 
694     host->GetRenderContext()->SetClipToBounds(true);
695 }
696 
SetAccessibilityAction()697 void MenuPattern::SetAccessibilityAction()
698 {
699     auto host = GetHost();
700     CHECK_NULL_VOID(host);
701     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
702     CHECK_NULL_VOID(accessibilityProperty);
703     accessibilityProperty->SetActionScrollForward([weakPtr = WeakClaim(this)]() {
704         const auto& pattern = weakPtr.Upgrade();
705         auto host = pattern->GetHost();
706         CHECK_NULL_VOID(host);
707         auto firstChild = DynamicCast<FrameNode>(host->GetChildAtIndex(0));
708         CHECK_NULL_VOID(firstChild);
709         if (firstChild && firstChild->GetTag() == V2::SCROLL_ETS_TAG) {
710             auto scrollPattern = firstChild->GetPattern<ScrollPattern>();
711             CHECK_NULL_VOID(scrollPattern);
712             scrollPattern->ScrollPage(false, true);
713         }
714     });
715 
716     accessibilityProperty->SetActionScrollBackward([weakPtr = WeakClaim(this)]() {
717         const auto& pattern = weakPtr.Upgrade();
718         auto host = pattern->GetHost();
719         CHECK_NULL_VOID(host);
720         auto firstChild = DynamicCast<FrameNode>(host->GetChildAtIndex(0));
721         CHECK_NULL_VOID(firstChild);
722         if (firstChild && firstChild->GetTag() == V2::SCROLL_ETS_TAG) {
723             auto scrollPattern = firstChild->GetPattern<ScrollPattern>();
724             CHECK_NULL_VOID(scrollPattern);
725             scrollPattern->ScrollPage(true, true);
726         }
727     });
728 }
729 
GetTransformCenter() const730 Offset MenuPattern::GetTransformCenter() const
731 {
732     auto host = GetHost();
733     CHECK_NULL_RETURN(host, Offset());
734     auto geometryNode = host->GetGeometryNode();
735     CHECK_NULL_RETURN(geometryNode, Offset());
736     auto size = geometryNode->GetFrameSize();
737     auto layoutAlgorithmWrapper = host->GetLayoutAlgorithm();
738     CHECK_NULL_RETURN(layoutAlgorithmWrapper, Offset());
739     auto layoutAlgorithm = AceType::DynamicCast<MenuLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
740     CHECK_NULL_RETURN(layoutAlgorithm, Offset());
741     auto placement = layoutAlgorithm->GetPlacement();
742     switch (placement) {
743         case Placement::BOTTOM_LEFT:
744         case Placement::RIGHT_TOP:
745             return Offset();
746         case Placement::BOTTOM_RIGHT:
747         case Placement::LEFT_TOP:
748             return Offset(size.Width(), 0.0f);
749         case Placement::TOP_LEFT:
750         case Placement::RIGHT_BOTTOM:
751             return Offset(0.0f, size.Height());
752         case Placement::TOP_RIGHT:
753         case Placement::LEFT_BOTTOM:
754             return Offset(size.Width(), size.Height());
755         case Placement::BOTTOM:
756             return Offset(size.Width() / 2, 0.0f);
757         case Placement::LEFT:
758             return Offset(size.Width(), size.Height() / 2);
759         case Placement::TOP:
760             return Offset(size.Width() / 2, size.Height());
761         case Placement::RIGHT:
762             return Offset(0.0f, size.Height() / 2);
763         default:
764             return Offset();
765     }
766 }
767 
ShowPreviewMenuAnimation()768 void MenuPattern::ShowPreviewMenuAnimation()
769 {
770     auto host = GetHost();
771     CHECK_NULL_VOID(host);
772     if (isFirstShow_ && previewMode_ != MenuPreviewMode::NONE) {
773         auto menuWrapper = GetMenuWrapper();
774         CHECK_NULL_VOID(menuWrapper);
775         auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
776         CHECK_NULL_VOID(menuWrapperPattern);
777         auto preview = menuWrapperPattern->GetPreview();
778         CHECK_NULL_VOID(preview);
779         auto previewRenderContext = preview->GetRenderContext();
780         CHECK_NULL_VOID(previewRenderContext);
781         auto previewGeometryNode = preview->GetGeometryNode();
782         CHECK_NULL_VOID(previewGeometryNode);
783         auto previewPosition = previewGeometryNode->GetFrameOffset();
784         auto host = GetHost();
785         CHECK_NULL_VOID(host);
786         auto renderContext = host->GetRenderContext();
787         CHECK_NULL_VOID(renderContext);
788         renderContext->UpdateTransformCenter(DimensionOffset(GetTransformCenter()));
789         auto menuPosition = host->GetPaintRectOffset();
790         OffsetF previewOriginPosition = GetPreviewOriginOffset();
791 
792         auto pipeline = PipelineBase::GetCurrentContext();
793         CHECK_NULL_VOID(pipeline);
794         auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
795         CHECK_NULL_VOID(menuTheme);
796         auto menuAnimationScale = menuTheme->GetMenuAnimationScale();
797         auto springMotionResponse = menuTheme->GetSpringMotionResponse();
798         auto springMotionDampingFraction = menuTheme->GetSpringMotionDampingFraction();
799 
800         renderContext->UpdateTransformScale(VectorF(menuAnimationScale, menuAnimationScale));
801 
802         previewRenderContext->UpdatePosition(
803             OffsetT<Dimension>(Dimension(previewOriginPosition.GetX()), Dimension(previewOriginPosition.GetY())));
804         renderContext->UpdatePosition(
805             OffsetT<Dimension>(Dimension(originOffset_.GetX()), Dimension(originOffset_.GetY())));
806 
807         ShowMenuOpacityAnimation(menuTheme, renderContext);
808 
809         AnimationOption scaleOption = AnimationOption();
810         auto motion = AceType::MakeRefPtr<ResponsiveSpringMotion>(springMotionResponse, springMotionDampingFraction);
811         scaleOption.SetCurve(motion);
812         AnimationUtils::Animate(scaleOption, [renderContext, menuPosition, previewRenderContext, previewPosition]() {
813             if (renderContext) {
814                 renderContext->UpdateTransformScale(VectorF(1.0f, 1.0f));
815                 renderContext->UpdatePosition(
816                     OffsetT<Dimension>(Dimension(menuPosition.GetX()), Dimension(menuPosition.GetY())));
817             }
818 
819             if (previewRenderContext) {
820                 previewRenderContext->UpdatePosition(
821                     OffsetT<Dimension>(Dimension(previewPosition.GetX()), Dimension(previewPosition.GetY())));
822             }
823         });
824     }
825     isFirstShow_ = false;
826 }
827 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)828 bool MenuPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
829 {
830     ShowPreviewMenuAnimation();
831     if (config.skipMeasure || dirty->SkipMeasureContent()) {
832         return false;
833     }
834 
835     auto pipeline = PipelineBase::GetCurrentContext();
836     CHECK_NULL_RETURN(pipeline, false);
837     auto theme = pipeline->GetTheme<SelectTheme>();
838     CHECK_NULL_RETURN(theme, false);
839     auto renderContext = dirty->GetHostNode()->GetRenderContext();
840     CHECK_NULL_RETURN(renderContext, false);
841 
842     auto menuProp = DynamicCast<MenuLayoutProperty>(dirty->GetLayoutProperty());
843     CHECK_NULL_RETURN(menuProp, false);
844     BorderRadiusProperty radius;
845     auto defaultRadius = theme->GetMenuBorderRadius();
846     radius.SetRadius(defaultRadius);
847     if (menuProp->GetBorderRadius().has_value()) {
848         auto borderRadius = menuProp->GetBorderRadiusValue();
849         auto topRadius =
850             borderRadius.radiusTopLeft.value_or(Dimension()) + borderRadius.radiusTopRight.value_or(Dimension());
851         auto bottomRadius =
852             borderRadius.radiusBottomLeft.value_or(Dimension()) + borderRadius.radiusBottomRight.value_or(Dimension());
853         auto menuRadius = std::max(topRadius.ConvertToPx(), bottomRadius.ConvertToPx());
854         auto geometryNode = dirty->GetGeometryNode();
855         CHECK_NULL_RETURN(geometryNode, false);
856         auto idealSize = geometryNode->GetMarginFrameSize();
857         if (LessNotEqual(menuRadius, idealSize.Width())) {
858             radius = borderRadius;
859         }
860         renderContext->UpdateBorderRadius(radius);
861     }
862     return true;
863 }
864 
GetMainMenuPattern() const865 RefPtr<MenuPattern> MenuPattern::GetMainMenuPattern() const
866 {
867     auto wrapperFrameNode = GetMenuWrapper();
868     CHECK_NULL_RETURN(wrapperFrameNode, nullptr);
869     auto mainMenuUINode = wrapperFrameNode->GetChildAtIndex(0);
870     CHECK_NULL_RETURN(mainMenuUINode, nullptr);
871     auto mainMenuFrameNode = AceType::DynamicCast<FrameNode>(mainMenuUINode);
872     return mainMenuFrameNode->GetPattern<MenuPattern>();
873 }
874 
RecordItemsAndGroups()875 void InnerMenuPattern::RecordItemsAndGroups()
876 {
877     itemsAndGroups_.clear();
878     auto host = GetHost();
879     std::stack<RefPtr<UINode>> nodeStack;
880     nodeStack.emplace(host);
881     bool isMenu = true;
882 
883     while (!nodeStack.empty()) {
884         auto currentNode = nodeStack.top();
885         nodeStack.pop();
886         // push items and item groups, skip menu node
887         if (!isMenu && AceType::InstanceOf<FrameNode>(currentNode)) {
888             itemsAndGroups_.emplace_back(WeakClaim(RawPtr(currentNode)));
889             continue;
890         }
891         isMenu = false;
892         // skip other type UiNode, such as ForEachNode
893         for (int32_t index = currentNode->GetChildren().size() - 1; index >= 0; index--) {
894             nodeStack.push(currentNode->GetChildAtIndex(index));
895         }
896     }
897 }
898 
FindSiblingMenuCount()899 uint32_t InnerMenuPattern::FindSiblingMenuCount()
900 {
901     auto host = GetHost();
902     CHECK_NULL_RETURN(host, 0);
903     auto parent = host->GetParent();
904     CHECK_NULL_RETURN(parent, 0);
905     auto siblings = parent->GetChildren();
906     uint32_t count = 0;
907     for (auto&& sibling : siblings) {
908         if (sibling->GetTag() == V2::MENU_ETS_TAG && sibling != host) {
909             ++count;
910         }
911     }
912     return count;
913 }
914 
ApplyDesktopMenuTheme()915 void InnerMenuPattern::ApplyDesktopMenuTheme()
916 {
917     auto host = GetHost();
918     CHECK_NULL_VOID(host);
919     host->GetRenderContext()->UpdateBackShadow(ShadowConfig::DefaultShadowS);
920 }
921 
ApplyMultiMenuTheme()922 void InnerMenuPattern::ApplyMultiMenuTheme()
923 {
924     auto host = GetHost();
925     CHECK_NULL_VOID(host);
926     host->GetRenderContext()->UpdateBackShadow(ShadowConfig::NoneShadow);
927 }
928 
OnColorConfigurationUpdate()929 void MenuPattern::OnColorConfigurationUpdate()
930 {
931     auto host = GetHost();
932     auto pipeline = PipelineBase::GetCurrentContext();
933     CHECK_NULL_VOID(pipeline);
934 
935     auto menuTheme = pipeline->GetTheme<SelectTheme>();
936     CHECK_NULL_VOID(menuTheme);
937 
938     auto menuPattern = host->GetPattern<MenuPattern>();
939     CHECK_NULL_VOID(menuPattern);
940 
941     auto renderContext = host->GetRenderContext();
942     renderContext->UpdateBackgroundColor(menuTheme->GetBackgroundColor());
943 
944     auto optionNode = menuPattern->GetOptions();
945     for (const auto& child : optionNode) {
946         auto optionsPattern = child->GetPattern<OptionPattern>();
947         optionsPattern->SetFontColor(menuTheme->GetFontColor());
948 
949         child->MarkModifyDone();
950         child->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
951     }
952     host->SetNeedCallChildrenUpdate(false);
953 }
954 
InitPanEvent(const RefPtr<GestureEventHub> & gestureHub)955 void MenuPattern::InitPanEvent(const RefPtr<GestureEventHub>& gestureHub)
956 {
957     CHECK_NULL_VOID(gestureHub);
958     auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
959         auto pattern = weak.Upgrade();
960         CHECK_NULL_VOID(pattern);
961         auto offsetX = static_cast<float>(info.GetOffsetX());
962         auto offsetY = static_cast<float>(info.GetOffsetY());
963         auto offsetPerSecondX = info.GetVelocity().GetOffsetPerSecond().GetX();
964         auto offsetPerSecondY = info.GetVelocity().GetOffsetPerSecond().GetY();
965         auto velocity =
966             static_cast<float>(std::sqrt(offsetPerSecondX * offsetPerSecondX + offsetPerSecondY * offsetPerSecondY));
967         pattern->HandleDragEnd(offsetX, offsetY, velocity);
968     };
969     auto actionScrollEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
970         auto pattern = weak.Upgrade();
971         CHECK_NULL_VOID(pattern);
972         auto offsetX = static_cast<float>(info.GetOffsetX());
973         auto offsetY = static_cast<float>(info.GetOffsetY());
974         auto offsetPerSecondX = info.GetVelocity().GetOffsetPerSecond().GetX();
975         auto offsetPerSecondY = info.GetVelocity().GetOffsetPerSecond().GetY();
976         auto velocity =
977             static_cast<float>(std::sqrt(offsetPerSecondX * offsetPerSecondX + offsetPerSecondY * offsetPerSecondY));
978         pattern->HandleScrollDragEnd(offsetX, offsetY, velocity);
979     };
980     PanDirection panDirection;
981     panDirection.type = PanDirection::ALL;
982     auto panEvent = MakeRefPtr<PanEvent>(nullptr, nullptr, std::move(actionEndTask), nullptr);
983     gestureHub->AddPanEvent(panEvent, panDirection, 1, DEFAULT_PAN_DISTANCE);
984     gestureHub->AddPreviewMenuHandleDragEnd(std::move(actionScrollEndTask));
985 }
986 
HandleDragEnd(float offsetX,float offsetY,float velocity)987 void MenuPattern::HandleDragEnd(float offsetX, float offsetY, float velocity)
988 {
989     if ((LessOrEqual(std::abs(offsetY), std::abs(offsetX)) || LessOrEqual(offsetY, 0.0f)) &&
990         LessOrEqual(velocity, PAN_MAX_VELOCITY)) {
991         return;
992     }
993     auto menuWrapper = GetMenuWrapper();
994     CHECK_NULL_VOID(menuWrapper);
995     auto wrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
996     CHECK_NULL_VOID(wrapperPattern);
997     wrapperPattern->HideMenu();
998 }
999 
HandleScrollDragEnd(float offsetX,float offsetY,float velocity)1000 void MenuPattern::HandleScrollDragEnd(float offsetX, float offsetY, float velocity)
1001 {
1002     if ((LessOrEqual(std::abs(offsetY), std::abs(offsetX)) || !NearZero(offsetY)) &&
1003         LessOrEqual(velocity, PAN_MAX_VELOCITY)) {
1004         return;
1005     }
1006     auto menuWrapper = GetMenuWrapper();
1007     CHECK_NULL_VOID(menuWrapper);
1008     auto wrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
1009     CHECK_NULL_VOID(wrapperPattern);
1010     wrapperPattern->HideMenu();
1011 }
1012 
DumpInfo()1013 void MenuPattern::DumpInfo()
1014 {
1015     DumpLog::GetInstance().AddDesc(
1016         std::string("MenuType: ").append(std::to_string(static_cast<int32_t>(GetMenuType()))));
1017 }
1018 
GetSelectMenuWidth()1019 float MenuPattern::GetSelectMenuWidth()
1020 {
1021     RefPtr<GridColumnInfo> columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
1022     CHECK_NULL_RETURN(columnInfo, MIN_SELECT_MENU_WIDTH.ConvertToPx());
1023     auto parent = columnInfo->GetParent();
1024     CHECK_NULL_RETURN(parent, MIN_SELECT_MENU_WIDTH.ConvertToPx());
1025     parent->BuildColumnWidth();
1026     auto defaultWidth = static_cast<float>(columnInfo->GetWidth(COLUMN_NUM));
1027     float finalWidth = MIN_SELECT_MENU_WIDTH.ConvertToPx();
1028 
1029     if (IsWidthModifiedBySelect()) {
1030         auto menuLayoutProperty = GetLayoutProperty<MenuLayoutProperty>();
1031         auto selectmodifiedwidth = menuLayoutProperty->GetSelectMenuModifiedWidth();
1032         finalWidth = selectmodifiedwidth.value();
1033     } else {
1034         finalWidth = defaultWidth;
1035     }
1036 
1037     if (finalWidth < MIN_SELECT_MENU_WIDTH.ConvertToPx()) {
1038         finalWidth = defaultWidth;
1039     }
1040 
1041     return finalWidth;
1042 }
1043 } // namespace OHOS::Ace::NG
1044