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