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