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