• 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/components/common/properties/shadow_config.h"
25 #include "core/components/select/select_theme.h"
26 #include "core/components_ng/base/ui_node.h"
27 #include "core/components_ng/pattern/menu/menu_item/menu_item_layout_property.h"
28 #include "core/components_ng/pattern/menu/menu_item/menu_item_pattern.h"
29 #include "core/components_ng/pattern/menu/multi_menu_layout_algorithm.h"
30 #include "core/components_ng/pattern/menu/sub_menu_layout_algorithm.h"
31 #include "core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.h"
32 #include "core/components_ng/pattern/option/option_pattern.h"
33 #include "core/components_ng/pattern/option/option_view.h"
34 #include "core/components_ng/pattern/scroll/scroll_pattern.h"
35 #include "core/components_ng/pattern/text/text_layout_property.h"
36 #include "core/components_v2/inspector/inspector_constants.h"
37 #include "core/event/touch_event.h"
38 #include "core/pipeline/pipeline_base.h"
39 #include "core/pipeline_ng/pipeline_context.h"
40 
41 namespace OHOS::Ace::NG {
42 namespace {
UpdateFontStyle(RefPtr<MenuLayoutProperty> & menuProperty,RefPtr<MenuItemLayoutProperty> & itemProperty,RefPtr<MenuItemPattern> & itemPattern,bool & contentChanged,bool & labelChanged)43 void UpdateFontStyle(RefPtr<MenuLayoutProperty>& menuProperty, RefPtr<MenuItemLayoutProperty>& itemProperty,
44     RefPtr<MenuItemPattern>& itemPattern, bool& contentChanged, bool& labelChanged)
45 {
46     auto contentNode = itemPattern->GetContentNode();
47     CHECK_NULL_VOID(contentNode);
48     auto textLayoutProperty = contentNode->GetLayoutProperty<TextLayoutProperty>();
49     CHECK_NULL_VOID(textLayoutProperty);
50     auto label = itemPattern->GetLabelNode();
51     RefPtr<TextLayoutProperty> labelProperty = label ? label->GetLayoutProperty<TextLayoutProperty>() : nullptr;
52     if (menuProperty->GetItalicFontStyle().has_value()) {
53         if (!itemProperty->GetItalicFontStyle().has_value()) {
54             textLayoutProperty->UpdateItalicFontStyle(menuProperty->GetItalicFontStyle().value());
55             contentChanged = true;
56         }
57         if (labelProperty && !itemProperty->GetLabelItalicFontStyle().has_value()) {
58             labelProperty->UpdateItalicFontStyle(menuProperty->GetItalicFontStyle().value());
59             labelChanged = true;
60         }
61     }
62     if (menuProperty->GetFontFamily().has_value()) {
63         if (!itemProperty->GetFontFamily().has_value()) {
64             textLayoutProperty->UpdateFontFamily(menuProperty->GetFontFamily().value());
65             contentChanged = true;
66         }
67         if (labelProperty && !itemProperty->GetLabelFontFamily().has_value()) {
68             labelProperty->UpdateFontFamily(menuProperty->GetFontFamily().value());
69             labelChanged = true;
70         }
71     }
72 }
73 
UpdateMenuItemTextNode(RefPtr<MenuLayoutProperty> & menuProperty,RefPtr<MenuItemLayoutProperty> & itemProperty,RefPtr<MenuItemPattern> & itemPattern)74 void UpdateMenuItemTextNode(RefPtr<MenuLayoutProperty>& menuProperty, RefPtr<MenuItemLayoutProperty>& itemProperty,
75     RefPtr<MenuItemPattern>& itemPattern)
76 {
77     auto contentNode = itemPattern->GetContentNode();
78     CHECK_NULL_VOID(contentNode);
79     auto textLayoutProperty = contentNode->GetLayoutProperty<TextLayoutProperty>();
80     CHECK_NULL_VOID(textLayoutProperty);
81     auto label = itemPattern->GetLabelNode();
82     RefPtr<TextLayoutProperty> labelProperty = label ? label->GetLayoutProperty<TextLayoutProperty>() : nullptr;
83     bool contentChanged = false;
84     bool labelChanged = false;
85     if (menuProperty->GetFontSize().has_value()) {
86         if (!itemProperty->GetFontSize().has_value()) {
87             textLayoutProperty->UpdateFontSize(menuProperty->GetFontSize().value());
88             contentChanged = true;
89         }
90         if (labelProperty && !itemProperty->GetLabelFontSize().has_value()) {
91             labelProperty->UpdateFontSize(menuProperty->GetFontSize().value());
92             labelChanged = true;
93         }
94     }
95     if (menuProperty->GetFontWeight().has_value()) {
96         if (!itemProperty->GetFontWeight().has_value()) {
97             textLayoutProperty->UpdateFontWeight(menuProperty->GetFontWeight().value());
98             contentChanged = true;
99         }
100         if (labelProperty && !itemProperty->GetLabelFontWeight().has_value()) {
101             labelProperty->UpdateFontWeight(menuProperty->GetFontWeight().value());
102             labelChanged = true;
103         }
104     }
105     if (menuProperty->GetFontColor().has_value()) {
106         if (!itemProperty->GetFontColor().has_value()) {
107             textLayoutProperty->UpdateTextColor(menuProperty->GetFontColor().value());
108             contentChanged = true;
109         }
110         if (labelProperty && !itemProperty->GetLabelFontColor().has_value()) {
111             labelProperty->UpdateTextColor(menuProperty->GetFontColor().value());
112             labelChanged = true;
113         }
114     }
115     UpdateFontStyle(menuProperty, itemProperty, itemPattern, contentChanged, labelChanged);
116     if (contentChanged) {
117         contentNode->MarkModifyDone();
118         contentNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
119     }
120     if (labelChanged) {
121         label->MarkModifyDone();
122         label->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
123     }
124 }
125 } // namespace
126 
OnAttachToFrameNode()127 void MenuPattern::OnAttachToFrameNode()
128 {
129     RegisterOnTouch();
130     auto host = GetHost();
131     CHECK_NULL_VOID(host);
132     auto focusHub = host->GetOrCreateFocusHub();
133     CHECK_NULL_VOID(focusHub);
134     RegisterOnKeyEvent(focusHub);
135     DisableTabInMenu();
136     InitTheme(host);
137 }
138 
OnModifyDone()139 void MenuPattern::OnModifyDone()
140 {
141     Pattern::OnModifyDone();
142     auto host = GetHost();
143     CHECK_NULL_VOID(host);
144     UpdateMenuItemChildren(host);
145 
146     auto innerMenuCount = GetInnerMenuCount();
147     if (innerMenuCount == 1) {
148         ResetTheme(host, false);
149     } else if (innerMenuCount > 1) {
150         // multiple inner menus, reset outer container's shadow for desktop UX
151         ResetTheme(host, true);
152     }
153     auto menuFirstNode = GetFirstInnerMenu();
154     if (menuFirstNode) {
155         CopyMenuAttr(menuFirstNode);
156     }
157     SetAccessibilityAction();
158 }
159 
BeforeCreateLayoutWrapper()160 void InnerMenuPattern::BeforeCreateLayoutWrapper()
161 {
162     RecordItemsAndGroups();
163 
164     // determine menu type based on sibling menu count
165     auto count = FindSiblingMenuCount();
166     if (count > 0) {
167         SetType(MenuType::DESKTOP_MENU);
168         ApplyDesktopMenuTheme();
169     } else {
170         SetType(MenuType::MULTI_MENU);
171         ApplyMultiMenuTheme();
172     }
173 }
174 
OnModifyDone()175 void InnerMenuPattern::OnModifyDone()
176 {
177     Pattern::OnModifyDone();
178     auto host = GetHost();
179     CHECK_NULL_VOID(host);
180     UpdateMenuItemChildren(host);
181     SetAccessibilityAction();
182 }
183 
184 // close menu on touch up
RegisterOnTouch()185 void MenuPattern::RegisterOnTouch()
186 {
187     CHECK_NULL_VOID_NOLOG(!onTouch_);
188     auto host = GetHost();
189     CHECK_NULL_VOID(host);
190     auto gesture = host->GetOrCreateGestureEventHub();
191     CHECK_NULL_VOID(gesture);
192     auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
193         auto pattern = weak.Upgrade();
194         CHECK_NULL_VOID_NOLOG(pattern);
195         pattern->OnTouchEvent(info);
196     };
197     onTouch_ = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
198     gesture->AddTouchEvent(onTouch_);
199 }
200 
OnTouchEvent(const TouchEventInfo & info)201 void MenuPattern::OnTouchEvent(const TouchEventInfo& info)
202 {
203     if (GetInnerMenuCount() > 0 || IsMultiMenu() || IsSelectOverlayCustomMenu()) {
204         // not click hide menu for multi menu or select overlay custom menu
205         return;
206     }
207     if (!options_.empty()) {
208         // not click hide menu for select and bindMenu default option
209         return;
210     }
211     auto touchType = info.GetTouches().front().GetTouchType();
212     if (touchType == TouchType::DOWN) {
213         lastTouchOffset_ = info.GetTouches().front().GetLocalLocation();
214     } else if (touchType == TouchType::UP) {
215         auto touchUpOffset = info.GetTouches().front().GetLocalLocation();
216         if (lastTouchOffset_.has_value() &&
217             (touchUpOffset - lastTouchOffset_.value()).GetDistance() <= DEFAULT_CLICK_DISTANCE) {
218             HideMenu(true);
219         }
220         lastTouchOffset_.reset();
221     }
222 }
223 
RegisterOnKeyEvent(const RefPtr<FocusHub> & focusHub)224 void MenuPattern::RegisterOnKeyEvent(const RefPtr<FocusHub>& focusHub)
225 {
226     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
227         auto pattern = wp.Upgrade();
228         CHECK_NULL_RETURN_NOLOG(pattern, false);
229         return pattern->OnKeyEvent(event);
230     };
231     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
232 }
233 
OnKeyEvent(const KeyEvent & event) const234 bool MenuPattern::OnKeyEvent(const KeyEvent& event) const
235 {
236     if (event.action != KeyAction::DOWN || IsMultiMenu()) {
237         return false;
238     }
239     if ((event.code == KeyCode::KEY_DPAD_LEFT || event.code == KeyCode::KEY_ESCAPE) &&
240         (IsSubMenu() || IsSelectOverlaySubMenu())) {
241         auto menuWrapper = GetMenuWrapper();
242         CHECK_NULL_RETURN(menuWrapper, true);
243         auto wrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
244         CHECK_NULL_RETURN(wrapperPattern, true);
245         wrapperPattern->HideSubMenu();
246         return true;
247     }
248     return false;
249 }
250 
RemoveParentHoverStyle()251 void MenuPattern::RemoveParentHoverStyle()
252 {
253     if (!IsSubMenu()) {
254         return;
255     }
256     auto menuItemParent = GetParentMenuItem();
257     CHECK_NULL_VOID(menuItemParent);
258     auto menuItemPattern = menuItemParent->GetPattern<MenuItemPattern>();
259     CHECK_NULL_VOID(menuItemPattern);
260     menuItemPattern->SetIsSubMenuShowed(false);
261 
262     auto pipeline = PipelineBase::GetCurrentContext();
263     CHECK_NULL_VOID(pipeline);
264     auto theme = pipeline->GetTheme<SelectTheme>();
265     CHECK_NULL_VOID(theme);
266     menuItemPattern->SetBgBlendColor(Color::TRANSPARENT);
267     menuItemPattern->PlayBgColorAnimation();
268 }
269 
UpdateMenuItemChildren(RefPtr<FrameNode> & host)270 void MenuPattern::UpdateMenuItemChildren(RefPtr<FrameNode>& host)
271 {
272     auto layoutProperty = GetLayoutProperty<MenuLayoutProperty>();
273     CHECK_NULL_VOID(layoutProperty);
274     const auto& children = host->GetChildren();
275     for (auto child : children) {
276         if (child->GetTag() == V2::MENU_ITEM_ETS_TAG) {
277             auto itemNode = AceType::DynamicCast<FrameNode>(child);
278             CHECK_NULL_VOID(itemNode);
279             auto itemProperty = itemNode->GetLayoutProperty<MenuItemLayoutProperty>();
280             CHECK_NULL_VOID(itemProperty);
281             auto itemPattern = itemNode->GetPattern<MenuItemPattern>();
282             CHECK_NULL_VOID(itemPattern);
283             UpdateMenuItemTextNode(layoutProperty, itemProperty, itemPattern);
284         } else if (child->GetTag() == V2::MENU_ITEM_GROUP_ETS_TAG) {
285             auto itemGroupNode = AceType::DynamicCast<FrameNode>(child);
286             CHECK_NULL_VOID(itemGroupNode);
287             UpdateMenuItemChildren(itemGroupNode);
288         } else {
289             // do nothing
290         }
291     }
292 }
293 
UpdateSelectParam(const std::vector<SelectParam> & params)294 void MenuPattern::UpdateSelectParam(const std::vector<SelectParam>& params)
295 {
296     if (!isSelectMenu_) {
297         return;
298     }
299     auto host = GetHost();
300     CHECK_NULL_VOID(host);
301     const auto& children = GetOptions();
302     auto childCount = children.size();
303     auto paramCount = params.size();
304     size_t updateCount = std::min(paramCount, childCount);
305     auto childIt = children.begin();
306     for (size_t i = 0; i < updateCount; i++, childIt++) {
307         const auto& childNode = AceType::DynamicCast<FrameNode>(*childIt);
308         CHECK_NULL_VOID(childNode);
309         if (i == 0) {
310             auto props = childNode->GetPaintProperty<OptionPaintProperty>();
311             CHECK_NULL_VOID(props);
312             props->UpdateNeedDivider(false);
313         }
314         auto optionPattern = childNode->GetPattern<OptionPattern>();
315         CHECK_NULL_VOID(optionPattern);
316         optionPattern->UpdateText(params.at(i).first);
317         optionPattern->UpdateIcon(params.at(i).second);
318         childNode->MarkModifyDone();
319         childNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
320     }
321     for (size_t i = updateCount; i < paramCount; i++) {
322         auto optionNode = OptionView::CreateSelectOption(params.at(i).first, params.at(i).second, i);
323         if (i == 0) {
324             auto props = optionNode->GetPaintProperty<OptionPaintProperty>();
325             props->UpdateNeedDivider(false);
326         }
327         MountOption(optionNode);
328         optionNode->MarkModifyDone();
329         optionNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
330     }
331     for (size_t i = childCount; i > updateCount; i--) {
332         RemoveOption();
333     }
334     host->MarkModifyDone();
335     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
336 }
337 
HideMenu(bool isMenuOnTouch) const338 void MenuPattern::HideMenu(bool isMenuOnTouch) const
339 {
340     auto wrapper = GetMenuWrapper();
341     CHECK_NULL_VOID(wrapper);
342     if (wrapper->GetTag() == V2::SELECT_OVERLAY_ETS_TAG) {
343         return;
344     }
345     if (IsContextMenu()) {
346         SubwindowManager::GetInstance()->HideMenuNG(wrapper, targetId_);
347         return;
348     }
349     auto pipeline = PipelineContext::GetCurrentContext();
350     CHECK_NULL_VOID(pipeline);
351     auto overlayManager = pipeline->GetOverlayManager();
352     CHECK_NULL_VOID(overlayManager);
353     overlayManager->HideMenu(wrapper, targetId_, isMenuOnTouch);
354     LOGI("MenuPattern closing menu %{public}d", targetId_);
355 }
356 
HideSubMenu()357 void MenuPattern::HideSubMenu()
358 {
359     if (!showedSubMenu_) {
360         return;
361     }
362     auto subMenuPattern = showedSubMenu_->GetPattern<MenuPattern>();
363     CHECK_NULL_VOID(subMenuPattern);
364     subMenuPattern->RemoveParentHoverStyle();
365 
366     auto menuItem = subMenuPattern->GetParentMenuItem();
367     CHECK_NULL_VOID(menuItem);
368     auto menuItemPattern = menuItem->GetPattern<MenuItemPattern>();
369     CHECK_NULL_VOID(menuItemPattern);
370     menuItemPattern->SetIsSubMenuShowed(false);
371     menuItemPattern->ClearHoverRegions();
372     menuItemPattern->ResetWrapperMouseEvent();
373 
374     auto wrapper = GetMenuWrapper();
375     CHECK_NULL_VOID(wrapper);
376     wrapper->RemoveChild(showedSubMenu_);
377     wrapper->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
378     showedSubMenu_.Reset();
379 }
380 
GetMenuWrapper() const381 RefPtr<FrameNode> MenuPattern::GetMenuWrapper() const
382 {
383     auto host = GetHost();
384     CHECK_NULL_RETURN(host, nullptr);
385     auto parent = host->GetParent();
386     while (parent) {
387         if (parent->GetTag() == V2::MENU_WRAPPER_ETS_TAG || parent->GetTag() == V2::SELECT_OVERLAY_ETS_TAG) {
388             return AceType::DynamicCast<FrameNode>(parent);
389         }
390         parent = parent->GetParent();
391     }
392     return nullptr;
393 }
394 
395 // search for inner <Menu> node, once found a <Menu> node, count the number of sibling <Menu>
GetInnerMenuCount() const396 uint32_t MenuPattern::GetInnerMenuCount() const
397 {
398     if (type_ == MenuType::MULTI_MENU || type_ == MenuType::DESKTOP_MENU) {
399         return 0;
400     }
401 
402     auto host = GetHost();
403     CHECK_NULL_RETURN(host, 0);
404     auto child = host->GetChildAtIndex(0);
405     uint32_t depth = 0;
406     while (child && depth < MAX_SEARCH_DEPTH) {
407         // found component <Menu>
408         if (child->GetTag() == V2::MENU_ETS_TAG) {
409             auto parent = child->GetParent();
410             CHECK_NULL_RETURN(parent, 0);
411             return parent->GetChildren().size();
412         }
413         child = child->GetChildAtIndex(0);
414         ++depth;
415     }
416     return 0;
417 }
418 
GetFirstInnerMenu() const419 RefPtr<FrameNode> MenuPattern::GetFirstInnerMenu() const
420 {
421     if (type_ == MenuType::MULTI_MENU || type_ == MenuType::DESKTOP_MENU) {
422         return nullptr;
423     }
424 
425     auto host = GetHost();
426     CHECK_NULL_RETURN(host, nullptr);
427     uint32_t depth = 0;
428     auto child = host->GetChildAtIndex(0);
429     while (child && depth < MAX_SEARCH_DEPTH) {
430         // found component <Menu>
431         if (child->GetTag() == V2::MENU_ETS_TAG) {
432             return AceType::DynamicCast<FrameNode>(child);
433         }
434         child = child->GetChildAtIndex(0);
435         ++depth;
436     }
437     return nullptr;
438 }
439 
CopyMenuAttr(const RefPtr<FrameNode> & menuNode) const440 void MenuPattern::CopyMenuAttr(const RefPtr<FrameNode>& menuNode) const
441 {
442     auto pattern = AceType::DynamicCast<MenuPattern>(menuNode->GetPattern());
443     CHECK_NULL_VOID(pattern);
444 
445     auto host = GetHost();
446     CHECK_NULL_VOID(host);
447     auto rootMenuPattern = AceType::DynamicCast<MenuPattern>(host->GetPattern());
448     CHECK_NULL_VOID(rootMenuPattern);
449 
450     // copy menu pattern properties to rootMenu
451     auto layoutProperty = pattern->GetLayoutProperty<MenuLayoutProperty>();
452     CHECK_NULL_VOID(layoutProperty);
453     auto rootMenuLayoutProperty = rootMenuPattern->GetLayoutProperty<MenuLayoutProperty>();
454     CHECK_NULL_VOID(rootMenuLayoutProperty);
455     if (layoutProperty->GetBorderRadius().has_value()) {
456         rootMenuLayoutProperty->UpdateBorderRadius(layoutProperty->GetBorderRadiusValue());
457     }
458 }
459 
460 // mount option on menu
MountOption(const RefPtr<FrameNode> & option)461 void MenuPattern::MountOption(const RefPtr<FrameNode>& option)
462 {
463     auto column = GetMenuColumn();
464     CHECK_NULL_VOID(column);
465     auto pattern = option->GetPattern<OptionPattern>();
466     CHECK_NULL_VOID(pattern);
467     pattern->SetMenu(GetHost());
468     AddOptionNode(option);
469     option->MountToParent(column);
470 }
471 
472 // remove option from menu
RemoveOption()473 void MenuPattern::RemoveOption()
474 {
475     auto column = GetMenuColumn();
476     CHECK_NULL_VOID(column);
477     auto endOption = column->GetChildren().back();
478     CHECK_NULL_VOID(endOption);
479     column->RemoveChild(endOption);
480     PopOptionNode();
481 }
482 
GetMenuColumn() const483 RefPtr<FrameNode> MenuPattern::GetMenuColumn() const
484 {
485     auto menu = GetHost();
486     CHECK_NULL_RETURN(menu, nullptr);
487     auto scroll = menu->GetChildren().front();
488     CHECK_NULL_RETURN(scroll, nullptr);
489     auto column = scroll->GetChildren().front();
490     return DynamicCast<FrameNode>(column);
491 }
492 
DisableTabInMenu()493 void MenuPattern::DisableTabInMenu()
494 {
495     if (IsMultiMenu()) {
496         // multi menu not has scroll and column
497         return;
498     }
499     // disable tab in menu
500     auto column = GetMenuColumn();
501     CHECK_NULL_VOID(column);
502     auto columnFocusHub = column->GetOrCreateFocusHub();
503     CHECK_NULL_VOID(columnFocusHub);
504 
505     auto onKeyEvent = [](const KeyEvent& event) -> bool {
506         if (event.action != KeyAction::DOWN) {
507             return false;
508         }
509         return event.code == KeyCode::KEY_TAB;
510     };
511     columnFocusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
512 }
513 
CreateLayoutAlgorithm()514 RefPtr<LayoutAlgorithm> MenuPattern::CreateLayoutAlgorithm()
515 {
516     switch (type_) {
517         case MenuType::MULTI_MENU:
518         case MenuType::DESKTOP_MENU:
519             return MakeRefPtr<MultiMenuLayoutAlgorithm>();
520         case MenuType::SUB_MENU:
521         case MenuType::SELECT_OVERLAY_SUB_MENU:
522             return MakeRefPtr<SubMenuLayoutAlgorithm>();
523         default:
524             return MakeRefPtr<MenuLayoutAlgorithm>(targetId_, targetTag_);
525     }
526 }
527 
ResetTheme(const RefPtr<FrameNode> & host,bool resetForDesktopMenu)528 void MenuPattern::ResetTheme(const RefPtr<FrameNode>& host, bool resetForDesktopMenu)
529 {
530     auto renderContext = host->GetRenderContext();
531     CHECK_NULL_VOID(renderContext);
532     auto scroll = DynamicCast<FrameNode>(host->GetFirstChild());
533     CHECK_NULL_VOID(scroll);
534 
535     renderContext->UpdateBackgroundColor(Color::TRANSPARENT);
536 
537     if (resetForDesktopMenu) {
538         // DesktopMenu apply shadow on inner Menu node
539         renderContext->UpdateBackShadow(ShadowConfig::NoneShadow);
540     } else {
541         renderContext->UpdateBackShadow(ShadowConfig::DefaultShadowM);
542     }
543     // to enable inner menu shadow effect for desktopMenu, need to remove clipping from container
544     bool clip = !resetForDesktopMenu;
545     scroll->GetRenderContext()->SetClipToBounds(clip);
546 
547     // move padding from scroll to inner menu
548     auto scrollProp = scroll->GetLayoutProperty();
549     scrollProp->UpdatePadding(PaddingProperty());
550 }
551 
InitTheme(const RefPtr<FrameNode> & host)552 void MenuPattern::InitTheme(const RefPtr<FrameNode>& host)
553 {
554     auto renderContext = host->GetRenderContext();
555     CHECK_NULL_VOID(renderContext);
556 
557     auto pipeline = PipelineBase::GetCurrentContext();
558     CHECK_NULL_VOID(pipeline);
559     auto theme = pipeline->GetTheme<SelectTheme>();
560     CHECK_NULL_VOID(theme);
561 
562     auto bgColor = theme->GetBackgroundColor();
563     renderContext->UpdateBackgroundColor(bgColor);
564     renderContext->UpdateBackShadow(ShadowConfig::DefaultShadowM);
565     // make menu round rect
566     BorderRadiusProperty borderRadius;
567     borderRadius.SetRadius(theme->GetMenuBorderRadius());
568     renderContext->UpdateBorderRadius(borderRadius);
569 }
570 
InitTheme(const RefPtr<FrameNode> & host)571 void InnerMenuPattern::InitTheme(const RefPtr<FrameNode>& host)
572 {
573     MenuPattern::InitTheme(host);
574     // inner menu applies shadow in OnModifyDone(), where it can determine if it's a DesktopMenu or a regular menu
575 
576     auto layoutProperty = host->GetLayoutProperty();
577     if (layoutProperty->GetPaddingProperty()) {
578         // if user defined padding exists, skip applying default padding
579         return;
580     }
581     auto pipeline = PipelineBase::GetCurrentContext();
582     CHECK_NULL_VOID(pipeline);
583     auto theme = pipeline->GetTheme<SelectTheme>();
584     // apply default padding from theme on inner menu
585     PaddingProperty padding;
586     padding.SetEdges(CalcLength(theme->GetOutPadding()));
587     host->GetLayoutProperty()->UpdatePadding(padding);
588 
589     host->GetRenderContext()->SetClipToBounds(true);
590 }
591 
SetAccessibilityAction()592 void MenuPattern::SetAccessibilityAction()
593 {
594     auto host = GetHost();
595     CHECK_NULL_VOID(host);
596     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
597     CHECK_NULL_VOID(accessibilityProperty);
598     accessibilityProperty->SetActionScrollForward([weakPtr = WeakClaim(this)]() {
599         const auto& pattern = weakPtr.Upgrade();
600         auto host = pattern->GetHost();
601         CHECK_NULL_VOID(host);
602         auto firstChild = DynamicCast<FrameNode>(host->GetChildAtIndex(0));
603         CHECK_NULL_VOID(firstChild);
604         if (firstChild && firstChild->GetTag() == V2::SCROLL_ETS_TAG) {
605             auto scrollPattern = firstChild->GetPattern<ScrollPattern>();
606             CHECK_NULL_VOID(scrollPattern);
607             scrollPattern->ScrollPage(false, true);
608         }
609     });
610 
611     accessibilityProperty->SetActionScrollBackward([weakPtr = WeakClaim(this)]() {
612         const auto& pattern = weakPtr.Upgrade();
613         auto host = pattern->GetHost();
614         CHECK_NULL_VOID(host);
615         auto firstChild = DynamicCast<FrameNode>(host->GetChildAtIndex(0));
616         CHECK_NULL_VOID(firstChild);
617         if (firstChild && firstChild->GetTag() == V2::SCROLL_ETS_TAG) {
618             auto scrollPattern = firstChild->GetPattern<ScrollPattern>();
619             CHECK_NULL_VOID(scrollPattern);
620             scrollPattern->ScrollPage(true, true);
621         }
622     });
623 }
624 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)625 bool MenuPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
626 {
627     if (config.skipMeasure || dirty->SkipMeasureContent()) {
628         return false;
629     }
630 
631     auto pipeline = PipelineBase::GetCurrentContext();
632     CHECK_NULL_RETURN(pipeline, false);
633     auto theme = pipeline->GetTheme<SelectTheme>();
634     CHECK_NULL_RETURN(theme, false);
635     auto renderContext = dirty->GetHostNode()->GetRenderContext();
636     CHECK_NULL_RETURN(renderContext, false);
637 
638     auto menuProp = DynamicCast<MenuLayoutProperty>(dirty->GetLayoutProperty());
639     CHECK_NULL_RETURN(menuProp, false);
640     BorderRadiusProperty radius;
641     auto defaultRadius = theme->GetMenuBorderRadius();
642     radius.SetRadius(defaultRadius);
643     if (menuProp->GetBorderRadius().has_value()) {
644         auto borderRadius = menuProp->GetBorderRadiusValue();
645         auto topRadius =
646             borderRadius.radiusTopLeft.value_or(Dimension()) + borderRadius.radiusTopRight.value_or(Dimension());
647         auto bottomRadius =
648             borderRadius.radiusBottomLeft.value_or(Dimension()) + borderRadius.radiusBottomRight.value_or(Dimension());
649         auto menuRadius = std::max(topRadius.ConvertToPx(), bottomRadius.ConvertToPx());
650         auto geometryNode = dirty->GetGeometryNode();
651         CHECK_NULL_RETURN(geometryNode, false);
652         auto idealSize = geometryNode->GetMarginFrameSize();
653         if (LessNotEqual(menuRadius, idealSize.Width())) {
654             radius = borderRadius;
655         }
656     }
657     renderContext->UpdateBorderRadius(radius);
658     return true;
659 }
660 
GetMainMenuPattern() const661 RefPtr<MenuPattern> MenuPattern::GetMainMenuPattern() const
662 {
663     auto wrapperFrameNode = GetMenuWrapper();
664     CHECK_NULL_RETURN(wrapperFrameNode, nullptr);
665     auto mainMenuUINode = wrapperFrameNode->GetChildAtIndex(0);
666     CHECK_NULL_RETURN(mainMenuUINode, nullptr);
667     auto mainMenuFrameNode = AceType::DynamicCast<FrameNode>(mainMenuUINode);
668     return mainMenuFrameNode->GetPattern<MenuPattern>();
669 }
670 
RecordItemsAndGroups()671 void InnerMenuPattern::RecordItemsAndGroups()
672 {
673     itemsAndGroups_.clear();
674     auto host = GetHost();
675     std::stack<RefPtr<UINode>> nodeStack;
676     nodeStack.emplace(host);
677     bool isMenu = true;
678 
679     while (!nodeStack.empty()) {
680         auto currentNode = nodeStack.top();
681         nodeStack.pop();
682         // push items and item groups, skip menu node
683         if (!isMenu && AceType::InstanceOf<FrameNode>(currentNode)) {
684             itemsAndGroups_.emplace_back(WeakClaim(RawPtr(currentNode)));
685             continue;
686         }
687         isMenu = false;
688         // skip other type UiNode, such as ForEachNode
689         for (int32_t index = currentNode->GetChildren().size() - 1; index >= 0; index--) {
690             nodeStack.push(currentNode->GetChildAtIndex(index));
691         }
692     }
693 }
694 
FindSiblingMenuCount()695 uint32_t InnerMenuPattern::FindSiblingMenuCount()
696 {
697     auto host = GetHost();
698     CHECK_NULL_RETURN(host, 0);
699     auto parent = host->GetParent();
700     CHECK_NULL_RETURN(parent, 0);
701     auto siblings = parent->GetChildren();
702     uint32_t count = 0;
703     for (auto&& sibling : siblings) {
704         if (sibling->GetTag() == V2::MENU_ETS_TAG && sibling != host) {
705             ++count;
706         }
707     }
708     return count;
709 }
710 
ApplyDesktopMenuTheme()711 void InnerMenuPattern::ApplyDesktopMenuTheme()
712 {
713     auto host = GetHost();
714     CHECK_NULL_VOID(host);
715     host->GetRenderContext()->UpdateBackShadow(ShadowConfig::DefaultShadowS);
716 }
717 
ApplyMultiMenuTheme()718 void InnerMenuPattern::ApplyMultiMenuTheme()
719 {
720     auto host = GetHost();
721     CHECK_NULL_VOID(host);
722     host->GetRenderContext()->UpdateBackShadow(ShadowConfig::NoneShadow);
723 }
724 
OnColorConfigurationUpdate()725 void MenuPattern::OnColorConfigurationUpdate()
726 {
727     auto host = GetHost();
728     auto pipeline = PipelineBase::GetCurrentContext();
729     CHECK_NULL_VOID(pipeline);
730 
731     auto menuTheme = pipeline->GetTheme<SelectTheme>();
732     CHECK_NULL_VOID(menuTheme);
733 
734     auto menuPattern = host->GetPattern<MenuPattern>();
735     CHECK_NULL_VOID(menuPattern);
736 
737     auto renderContext = host->GetRenderContext();
738     renderContext->UpdateBackgroundColor(menuTheme->GetBackgroundColor());
739 
740     auto optionNode = menuPattern->GetOptions();
741     for (const auto& child : optionNode) {
742         auto optionsPattern = child->GetPattern<OptionPattern>();
743         optionsPattern->SetFontColor(menuTheme->GetFontColor());
744 
745         child->MarkModifyDone();
746         child->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
747     }
748     host->SetNeedCallChildrenUpdate(false);
749 }
DumpInfo()750 void MenuPattern::DumpInfo()
751 {
752     DumpLog::GetInstance().AddDesc(
753         std::string("MenuType: ").append(std::to_string(static_cast<int32_t>(GetMenuType()))));
754 }
755 } // namespace OHOS::Ace::NG
756