• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/menu/menu_view.h"
17 
18 #include "base/geometry/dimension.h"
19 #include "base/memory/ace_type.h"
20 #include "base/subwindow/subwindow_manager.h"
21 #include "core/components_ng/base/view_stack_processor.h"
22 #include "core/components_ng/manager/drag_drop/drag_drop_func_wrapper.h"
23 #include "core/components_ng/manager/drag_drop/utils/drag_animation_helper.h"
24 #include "core/components_ng/pattern/flex/flex_layout_pattern.h"
25 #include "core/components_ng/pattern/image/image_layout_property.h"
26 #include "core/components_ng/pattern/image/image_pattern.h"
27 #include "core/components_ng/pattern/linear_layout/linear_layout_pattern.h"
28 #include "core/components_ng/pattern/menu/menu_item/menu_item_row_pattern.h"
29 #include "core/components_ng/pattern/menu/menu_layout_property.h"
30 #include "core/components_ng/pattern/menu/menu_pattern.h"
31 #include "core/components_ng/pattern/menu/menu_theme.h"
32 #include "core/components_ng/pattern/menu/preview/menu_preview_pattern.h"
33 #include "core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.h"
34 #include "core/components_ng/pattern/overlay/overlay_manager.h"
35 #include "core/components_ng/pattern/security_component/paste_button/paste_button_common.h"
36 #include "core/components_ng/pattern/security_component/paste_button/paste_button_model_ng.h"
37 #include "core/components_ng/pattern/security_component/security_component_pattern.h"
38 #include "core/components_ng/pattern/scroll/scroll_pattern.h"
39 #include "core/components_ng/pattern/stack/stack_pattern.h"
40 #include "core/components_ng/pattern/text/text_pattern.h"
41 #include "core/components_ng/property/menu_property.h"
42 #include "core/components_v2/inspector/inspector_constants.h"
43 #include "core/components/text_overlay/text_overlay_theme.h"
44 #include "core/components_ng/manager/drag_drop/drag_drop_global_controller.h"
45 #include "core/components/button/button_theme.h"
46 #include "frameworks/base/utils/measure_util.h"
47 
48 namespace OHOS::Ace::NG {
49 
50 /**
51  * The structure of menu is designed as follows :
52  * |--menuWrapper(size is same as root)
53  *   |--menu
54  *      |--scroll
55  *          |--column(for bindMenu/select)
56  *            |--options
57  *          |--customNode(for custom builder)
58  */
59 
60 namespace {
61 constexpr float PAN_MAX_VELOCITY = 2000.0f;
62 constexpr float HALF_DIVIDE = 2.0f;
63 constexpr float PREVIEW_ORIGIN_SCALE = 1.0f;
64 const RefPtr<Curve> CUSTOM_PREVIEW_ANIMATION_CURVE =
65     AceType::MakeRefPtr<InterpolatingSpring>(0.0f, 1.0f, 380.0f, 34.0f);
66 const std::string HOVER_IMAGE_CLIP_PROPERTY_NAME = "hoverImageClip";
67 constexpr float MIN_HOVER_SCALE_DIFF = 0.0001f;
68 constexpr int32_t HALF_NUMBER = 2;
69 constexpr int32_t HALF_NUMBER_NEGATIVE = -2;
70 constexpr int32_t MENU_ANIMATION_DURATION = 300;
71 
GetMenuTheme(const RefPtr<FrameNode> & frameNode)72 static RefPtr<MenuTheme> GetMenuTheme(const RefPtr<FrameNode>& frameNode)
73 {
74     CHECK_NULL_RETURN(frameNode, nullptr);
75     auto pipeline = frameNode->GetContext();
76     CHECK_NULL_RETURN(pipeline, nullptr);
77     return pipeline->GetTheme<NG::MenuTheme>();
78 }
79 
GetMenuTargetNode(const RefPtr<MenuWrapperPattern> & wrapperPattern)80 static RefPtr<FrameNode> GetMenuTargetNode(const RefPtr<MenuWrapperPattern>& wrapperPattern)
81 {
82     CHECK_NULL_RETURN(wrapperPattern, nullptr);
83     auto menu = wrapperPattern->GetMenu();
84     CHECK_NULL_RETURN(menu, nullptr);
85     auto menuPattern = menu->GetPattern<MenuPattern>();
86     CHECK_NULL_RETURN(menuPattern, nullptr);
87     return FrameNode::GetFrameNodeOnly(menuPattern->GetTargetTag(), menuPattern->GetTargetId());
88 }
89 
MountTextNode(const RefPtr<FrameNode> & wrapperNode,const RefPtr<UINode> & previewCustomNode=nullptr)90 void MountTextNode(const RefPtr<FrameNode>& wrapperNode, const RefPtr<UINode>& previewCustomNode = nullptr)
91 {
92     CHECK_NULL_VOID(previewCustomNode);
93     auto pipeline = PipelineContext::GetMainPipelineContext();
94     CHECK_NULL_VOID(pipeline);
95     auto manager = pipeline->GetOverlayManager();
96     CHECK_NULL_VOID(manager);
97     auto gatherNode = manager->GetGatherNode();
98     CHECK_NULL_VOID(gatherNode);
99     auto textNode = FrameNode::GetOrCreateFrameNode(V2::TEXT_ETS_TAG,
100         ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<TextPattern>(); });
101     CHECK_NULL_VOID(textNode);
102     textNode->MountToParent(wrapperNode);
103     textNode->MarkModifyDone();
104 }
105 
CreatePreviewLayoutConstraint(const RefPtr<LayoutProperty> & layoutProperty)106 LayoutConstraintF CreatePreviewLayoutConstraint(const RefPtr<LayoutProperty>& layoutProperty)
107 {
108     CHECK_NULL_RETURN(layoutProperty, {});
109     LayoutConstraintF constraint = layoutProperty->GetLayoutConstraint().value_or(LayoutConstraintF());
110 
111     auto currentId = Container::CurrentId();
112     auto subWindow = SubwindowManager::GetInstance()->GetSubwindowByType(currentId, SubwindowType::TYPE_MENU);
113     CHECK_NULL_RETURN(subWindow, constraint);
114     auto subwindowSize = subWindow->GetRect().GetSize();
115     if (!subwindowSize.IsPositive()) {
116         return constraint;
117     }
118 
119     if (subwindowSize != constraint.maxSize || subwindowSize != constraint.percentReference) {
120         constraint.maxSize.SetSizeT(subwindowSize);
121         constraint.percentReference.SetSizeT(subwindowSize);
122     }
123     return constraint;
124 }
125 
CustomPreviewNodeProc(const RefPtr<FrameNode> & previewNode,const MenuParam & menuParam,const RefPtr<UINode> & previewCustomNode=nullptr)126 void CustomPreviewNodeProc(const RefPtr<FrameNode>& previewNode, const MenuParam& menuParam,
127     const RefPtr<UINode>& previewCustomNode = nullptr)
128 {
129     CHECK_NULL_VOID(previewCustomNode);
130     CHECK_NULL_VOID(previewNode);
131     auto previewPattern = previewNode->GetPattern<MenuPreviewPattern>();
132     CHECK_NULL_VOID(previewPattern);
133     previewPattern->SetHasPreviewTransitionEffect(menuParam.hasPreviewTransitionEffect);
134     auto layoutProperty = previewNode->GetLayoutProperty();
135     CHECK_NULL_VOID(layoutProperty);
136     layoutProperty->UpdateVisibility(VisibleType::VISIBLE, true);
137     previewNode->AddChild(previewCustomNode);
138 
139     CHECK_NULL_VOID(menuParam.isShowHoverImage);
140     auto pipeline = previewNode->GetContextWithCheck();
141     CHECK_NULL_VOID(pipeline);
142     ScopedLayout scope(pipeline);
143     auto layoutConstraint = CreatePreviewLayoutConstraint(layoutProperty);
144     previewNode->Measure(layoutConstraint);
145     auto previewSize = previewNode->GetGeometryNode()->GetFrameSize();
146     previewPattern->SetIsShowHoverImage(true);
147     previewPattern->SetCustomPreviewWidth(previewSize.Width());
148     previewPattern->SetCustomPreviewHeight(previewSize.Height());
149 
150     auto previewScaleTo = menuParam.previewAnimationOptions.scaleTo;
151     CHECK_NULL_VOID(previewScaleTo);
152     auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
153     CHECK_NULL_VOID(menuTheme);
154     previewScaleTo = LessOrEqual(previewScaleTo, 0.0) ? menuTheme->GetPreviewAfterAnimationScale() : previewScaleTo;
155     previewPattern->SetCustomPreviewScaleTo(previewScaleTo);
156 }
157 
158 // create menuWrapper and menu node, update menu props
CreateMenu(int32_t targetId,const std::string & targetTag="",MenuType type=MenuType::MENU)159 std::pair<RefPtr<FrameNode>, RefPtr<FrameNode>> CreateMenu(int32_t targetId, const std::string& targetTag = "",
160     MenuType type = MenuType::MENU)
161 {
162     // use wrapper to detect click events outside menu
163     auto wrapperNode = FrameNode::CreateFrameNode(V2::MENU_WRAPPER_ETS_TAG,
164         ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<MenuWrapperPattern>(targetId, targetTag));
165 
166     auto nodeId = ElementRegister::GetInstance()->MakeUniqueId();
167     auto menuNode = FrameNode::CreateFrameNode(
168         V2::MENU_ETS_TAG, nodeId, AceType::MakeRefPtr<MenuPattern>(targetId, targetTag, type));
169 
170     auto renderContext = menuNode->GetRenderContext();
171     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) && renderContext->IsUniRenderEnabled()) {
172         BlurStyleOption styleOption;
173         auto pipeLineContext = menuNode->GetContextWithCheck();
174         if (!pipeLineContext) {
175             return { wrapperNode, menuNode };
176         }
177         auto selectTheme = pipeLineContext->GetTheme<SelectTheme>();
178         if (!selectTheme) {
179             return { wrapperNode, menuNode };
180         }
181         if (selectTheme->GetMenuBlendBgColor()) {
182             styleOption.blurStyle = static_cast<BlurStyle>(selectTheme->GetMenuNormalBackgroundBlurStyle());
183             renderContext->UpdateBackgroundColor(selectTheme->GetBackgroundColor());
184         } else {
185             styleOption.blurStyle = static_cast<BlurStyle>(selectTheme->GetMenuBackgroundBlurStyle());
186             renderContext->UpdateBackgroundColor(Color::TRANSPARENT);
187         }
188         renderContext->UpdateBackBlurStyle(styleOption);
189     }
190 
191     menuNode->MountToParent(wrapperNode);
192 
193     return { wrapperNode, menuNode };
194 }
195 
CreateTitleNode(const std::string & title,RefPtr<FrameNode> & column)196 void CreateTitleNode(const std::string& title, RefPtr<FrameNode>& column)
197 {
198     auto textNode = FrameNode::CreateFrameNode(
199         V2::TEXT_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<TextPattern>());
200     CHECK_NULL_VOID(textNode);
201     auto textProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
202     CHECK_NULL_VOID(textProperty);
203 
204     auto pipeline = textNode->GetContextWithCheck();
205     CHECK_NULL_VOID(pipeline);
206     auto theme = pipeline->GetTheme<SelectTheme>();
207     CHECK_NULL_VOID(theme);
208     auto padding = static_cast<float>(theme->GetMenuItemHorIntervalPadding().ConvertToPx());
209     PaddingProperty textPadding;
210     textPadding.left = CalcLength(padding);
211     textPadding.right = CalcLength(padding);
212     textProperty->UpdatePadding(textPadding);
213     textProperty->UpdateFontSize(theme->GetMenuTitleFontSize());
214     textProperty->UpdateFontWeight(FontWeight::MEDIUM);
215     textProperty->UpdateItalicFontStyle(Ace::FontStyle::NORMAL);
216     textProperty->UpdateTextColor(theme->GetMenuTitleFontColor());
217     textProperty->UpdateContent(title);
218     textProperty->UpdateMaxLines(1);
219     textProperty->UpdateTextOverflow(TextOverflow::ELLIPSIS);
220 
221     CalcSize idealSize;
222     idealSize.SetHeight(CalcLength(theme->GetMenuTitleHeight()));
223     MeasureProperty layoutConstraint;
224     layoutConstraint.selfIdealSize = idealSize;
225     textProperty->UpdateCalcLayoutProperty(layoutConstraint);
226 
227     textNode->MountToParent(column);
228     textNode->MarkModifyDone();
229 }
230 
CreateMenuScroll(const RefPtr<UINode> & node)231 RefPtr<FrameNode> CreateMenuScroll(const RefPtr<UINode>& node)
232 {
233     auto scroll = FrameNode::CreateFrameNode(
234         V2::SCROLL_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ScrollPattern>());
235     CHECK_NULL_RETURN(scroll, nullptr);
236     auto props = scroll->GetLayoutProperty<ScrollLayoutProperty>();
237     props->UpdateAxis(Axis::VERTICAL);
238     props->UpdateAlignment(Alignment::CENTER_LEFT);
239     auto pipeline = scroll->GetContextWithCheck();
240     CHECK_NULL_RETURN(pipeline, nullptr);
241     auto theme = pipeline->GetTheme<SelectTheme>();
242     float contentPadding = 0.0f;
243     if (theme) {
244         contentPadding = static_cast<float>(theme->GetMenuPadding().ConvertToPx());
245     }
246     PaddingProperty padding;
247     padding.left = padding.right = padding.top = padding.bottom = CalcLength(contentPadding);
248     props->UpdatePadding(padding);
249     if (node) {
250         node->MountToParent(scroll);
251     }
252     auto renderContext = scroll->GetRenderContext();
253     CHECK_NULL_RETURN(renderContext, nullptr);
254     BorderRadiusProperty borderRadius;
255     if (theme) {
256         auto defaultRadius = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) ?
257             theme->GetMenuDefaultRadius() : theme->GetMenuBorderRadius();
258         borderRadius.SetRadius(defaultRadius);
259     }
260     renderContext->UpdateBorderRadius(borderRadius);
261     return scroll;
262 }
263 
MountScrollToMenu(const RefPtr<UINode> & customNode,RefPtr<FrameNode> scroll,MenuType type,RefPtr<FrameNode> menuNode)264 void MountScrollToMenu(
265     const RefPtr<UINode>& customNode, RefPtr<FrameNode> scroll, MenuType type, RefPtr<FrameNode> menuNode)
266 {
267     auto customMenuNode = AceType::DynamicCast<FrameNode>(customNode);
268     if (customMenuNode) {
269         customMenuNode->SetDraggable(false);
270     }
271     scroll->MountToParent(menuNode);
272     scroll->MarkModifyDone();
273 }
274 
OptionKeepMenu(RefPtr<FrameNode> & option,WeakPtr<FrameNode> & menuWeak)275 void OptionKeepMenu(RefPtr<FrameNode>& option, WeakPtr<FrameNode>& menuWeak)
276 {
277     auto pattern = option->GetPattern<MenuItemPattern>();
278     CHECK_NULL_VOID(pattern);
279     pattern->SetMenu(menuWeak);
280 }
281 
GetHasIcon(const std::vector<OptionParam> & params)282 bool GetHasIcon(const std::vector<OptionParam>& params)
283 {
284     for (size_t i = 0; i < params.size(); ++i) {
285         if (!params[i].icon.empty() || params[i].isPasteOption) {
286             return true;
287         }
288     }
289     return false;
290 }
291 
GetHasSymbol(const std::vector<OptionParam> & params)292 bool GetHasSymbol(const std::vector<OptionParam>& params)
293 {
294     for (size_t i = 0; i < params.size(); ++i) {
295         if (params[i].symbol != nullptr) {
296             return true;
297         }
298     }
299     return false;
300 }
301 
GetFloatImageOffset(const RefPtr<FrameNode> & frameNode)302 OffsetF GetFloatImageOffset(const RefPtr<FrameNode>& frameNode)
303 {
304     auto offsetToWindow = frameNode->GetPaintRectOffset(false, true);
305     auto offsetX = offsetToWindow.GetX();
306     auto offsetY = offsetToWindow.GetY();
307     return OffsetF(offsetX, offsetY);
308 }
309 
UpdateContainerIdealSizeConstraint(const RefPtr<FrameNode> & node,const CalcSize & idealSize)310 void UpdateContainerIdealSizeConstraint(const RefPtr<FrameNode>& node, const CalcSize& idealSize)
311 {
312     CHECK_NULL_VOID(node);
313     MeasureProperty layoutConstraint;
314     layoutConstraint.selfIdealSize = idealSize;
315     auto nodeLayoutProperty = node->GetLayoutProperty<LayoutProperty>();
316     CHECK_NULL_VOID(nodeLayoutProperty);
317     nodeLayoutProperty->UpdateCalcLayoutProperty(layoutConstraint);
318 }
319 
ShowBorderRadiusAndShadowAnimation(const RefPtr<MenuTheme> & menuTheme,const RefPtr<FrameNode> & imageNode,bool isShowHoverImage)320 void ShowBorderRadiusAndShadowAnimation(
321     const RefPtr<MenuTheme>& menuTheme, const RefPtr<FrameNode>& imageNode, bool isShowHoverImage)
322 {
323     CHECK_NULL_VOID(imageNode);
324     auto imageContext = imageNode->GetRenderContext();
325     CHECK_NULL_VOID(imageContext);
326     auto shadow = imageContext->GetBackShadow();
327     if (!shadow.has_value()) {
328         shadow = Shadow::CreateShadow(ShadowStyle::None);
329     }
330     AnimationOption option;
331     option.SetDuration(menuTheme->GetPreviewAnimationDuration());
332     option.SetCurve(Curves::SHARP);
333     auto previewBorderRadius = BorderRadiusProperty(menuTheme->GetPreviewBorderRadius());
334     if (auto presetRad = imageContext->GetBorderRadius(); presetRad) {
335         previewBorderRadius = presetRad.value();
336         imageContext->ResetBorderRadius();
337     }
338 
339     imageContext->UpdateBorderRadius(imageContext->GetBorderRadius().value_or(BorderRadiusProperty()));
340     auto pipelineContext = imageNode->GetContext();
341     CHECK_NULL_VOID(pipelineContext);
342     pipelineContext->AddAfterLayoutTask([option, weakImage = AceType::WeakClaim(AceType::RawPtr(imageNode)),
343                                             previewBorderRadius, shadow, isShowHoverImage]() {
344         auto imageNode = weakImage.Upgrade();
345         CHECK_NULL_VOID(imageNode);
346         auto imageContext = imageNode->GetRenderContext();
347         CHECK_NULL_VOID(imageContext);
348         AnimationUtils::Animate(
349             option,
350             [imageContext, previewBorderRadius, shadow, isShowHoverImage]() mutable {
351                 CHECK_NULL_VOID(imageContext && shadow);
352                 auto color = shadow->GetColor();
353                 auto newColor = Color::FromARGB(100, color.GetRed(), color.GetGreen(), color.GetBlue());
354                 shadow->SetColor(newColor);
355                 imageContext->UpdateBackShadow(shadow.value());
356 
357                 CHECK_NULL_VOID(!isShowHoverImage);
358                 imageContext->UpdateBorderRadius(previewBorderRadius);
359             },
360             option.GetOnFinishEvent(), nullptr, imageNode->GetContextRefPtr());
361     });
362 }
363 
UpdateOpacityInFinishEvent(const RefPtr<FrameNode> & previewNode,const RefPtr<RenderContext> & imageContext,const RefPtr<MenuTheme> & menuTheme)364 void UpdateOpacityInFinishEvent(const RefPtr<FrameNode>& previewNode, const RefPtr<RenderContext>& imageContext,
365     const RefPtr<MenuTheme>& menuTheme)
366 {
367     CHECK_NULL_VOID(imageContext && menuTheme);
368     // hover image disappear opacity animation
369     AnimationOption option;
370     option.SetDuration(menuTheme->GetHoverImageSwitchToPreviewOpacityDuration());
371     option.SetCurve(Curves::FRICTION);
372 
373     CHECK_NULL_VOID(previewNode);
374     auto previewContext = previewNode->GetRenderContext();
375     CHECK_NULL_VOID(previewContext);
376 
377     imageContext->UpdateOpacity(1.0);
378     previewContext->UpdateOpacity(0.0);
379     AnimationUtils::Animate(
380         option, [previewContext]() {
381             CHECK_NULL_VOID(previewContext);
382             previewContext->UpdateOpacity(1.0);
383         }, nullptr, nullptr, previewNode->GetContextRefPtr());
384 }
385 
GetPreviewBorderRadiusFromPattern(const RefPtr<MenuPreviewPattern> & previewPattern,const RefPtr<MenuTheme> & menuTheme)386 RadiusF GetPreviewBorderRadiusFromPattern(
387     const RefPtr<MenuPreviewPattern>& previewPattern, const RefPtr<MenuTheme>& menuTheme)
388 {
389     CHECK_NULL_RETURN(menuTheme, RadiusF(EdgeF()));
390     auto previewBorderRadius = menuTheme->GetPreviewBorderRadius().ConvertToPx();
391     auto topLeft = previewBorderRadius;
392     auto topRight = previewBorderRadius;
393     auto bottomLeft = previewBorderRadius;
394     auto bottomRight = previewBorderRadius;
395 
396     auto radius = RadiusF(EdgeF(topLeft, topLeft), EdgeF(topRight, topRight), EdgeF(bottomLeft, bottomLeft),
397         EdgeF(bottomRight, bottomRight));
398 
399     CHECK_NULL_RETURN(previewPattern, radius);
400     auto menuWrapper = previewPattern->GetMenuWrapper();
401     CHECK_NULL_RETURN(menuWrapper, radius);
402     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
403     CHECK_NULL_RETURN(menuWrapperPattern, radius);
404 
405     auto borderRadius = menuWrapperPattern->GetMenuParam().previewBorderRadius;
406     if (!borderRadius.has_value()) {
407         return radius;
408     }
409 
410     auto previewWidth = previewPattern->GetCustomPreviewWidth();
411     if (borderRadius->radiusTopLeft.has_value() && borderRadius->radiusTopLeft->IsNonNegative()) {
412         topLeft = borderRadius->radiusTopLeft->ConvertToPxWithSize(previewWidth);
413     }
414 
415     if (borderRadius->radiusTopRight.has_value() && borderRadius->radiusTopRight->IsNonNegative()) {
416         topRight = borderRadius->radiusTopRight->ConvertToPxWithSize(previewWidth);
417     }
418 
419     if (borderRadius->radiusBottomLeft.has_value() && borderRadius->radiusBottomLeft->IsNonNegative()) {
420         bottomLeft = borderRadius->radiusBottomLeft->ConvertToPxWithSize(previewWidth);
421     }
422 
423     if (borderRadius->radiusBottomRight.has_value() && borderRadius->radiusBottomRight->IsNonNegative()) {
424         bottomRight = borderRadius->radiusBottomRight->ConvertToPxWithSize(previewWidth);
425     }
426 
427     return RadiusF(EdgeF(topLeft, topLeft), EdgeF(topRight, topRight), EdgeF(bottomLeft, bottomLeft),
428         EdgeF(bottomRight, bottomRight));
429 }
430 
GetHoverImagePreviewProperty(const RefPtr<MenuPreviewPattern> & previewPattern,const RadiusF & radius)431 RefPtr<NodeAnimatablePropertyFloat> GetHoverImagePreviewProperty(
432     const RefPtr<MenuPreviewPattern>& previewPattern, const RadiusF& radius)
433 {
434     CHECK_NULL_RETURN(previewPattern, nullptr);
435     auto menuWrapper = previewPattern->GetMenuWrapper();
436     CHECK_NULL_RETURN(menuWrapper, nullptr);
437     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
438     CHECK_NULL_RETURN(menuWrapperPattern, nullptr);
439 
440     BorderRadiusProperty previewRadius;
441     previewRadius.radiusTopLeft = Dimension(radius.GetCorner(RoundRect::CornerPos::TOP_LEFT_POS).x);
442     previewRadius.radiusTopRight = Dimension(radius.GetCorner(RoundRect::CornerPos::TOP_RIGHT_POS).x);
443     previewRadius.radiusBottomLeft = Dimension(radius.GetCorner(RoundRect::CornerPos::BOTTOM_LEFT_POS).x);
444     previewRadius.radiusBottomRight = Dimension(radius.GetCorner(RoundRect::CornerPos::BOTTOM_RIGHT_POS).x);
445 
446     auto callback = [menuWrapperPattern, previewRadius](float rate) {
447         CHECK_NULL_VOID(menuWrapperPattern && !menuWrapperPattern->IsHide());
448         menuWrapperPattern->SetAnimationClipRate(rate);
449         menuWrapperPattern->SetAnimationBorderRadius(rate, previewRadius);
450     };
451 
452     return AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(-1.0, std::move(callback));
453 }
454 
UpdateHoverImagePreviewScale(const RefPtr<FrameNode> & hoverImageStackNode,const RefPtr<MenuPreviewPattern> & previewPattern,const RefPtr<MenuTheme> & menuTheme)455 void UpdateHoverImagePreviewScale(const RefPtr<FrameNode>& hoverImageStackNode,
456     const RefPtr<MenuPreviewPattern>& previewPattern, const RefPtr<MenuTheme>& menuTheme)
457 {
458     CHECK_NULL_VOID(hoverImageStackNode && previewPattern && menuTheme);
459     auto stackContext = hoverImageStackNode->GetRenderContext();
460     CHECK_NULL_VOID(stackContext);
461 
462     auto scaleBefore = previewPattern->GetHoverImageScaleTo();
463     auto scaleFrom =
464         LessOrEqual(scaleBefore, 0.0) ? menuTheme->GetPreviewBeforeAnimationScale() : scaleBefore;
465     stackContext->UpdateTransformScale(VectorF(scaleFrom, scaleFrom));
466 
467     auto scaleAfter = previewPattern->GetCustomPreviewScaleTo();
468     auto scaleTo =
469         LessOrEqual(scaleAfter, 0.0) ? menuTheme->GetPreviewAfterAnimationScale() : scaleAfter;
470 
471     auto startWidth = previewPattern->GetHoverImageAfterScaleWidth();
472     auto startHeight = previewPattern->GetHoverImageAfterScaleHeight();
473     auto offset = previewPattern->GetHoverImageAfterScaleOffset();
474     auto clipStartRect = RectF(offset, SizeF(startWidth - offset.GetX(), startHeight - offset.GetY()));
475     stackContext->ClipWithRRect(clipStartRect, RadiusF(EdgeF(0.0f, 0.0f)));
476 
477     AnimationOption scaleOption = AnimationOption();
478     scaleOption.SetCurve(CUSTOM_PREVIEW_ANIMATION_CURVE);
479     previewPattern->SetIsHoverImagePreviewScalePlaying(true);
480     scaleOption.SetOnFinishEvent([weak = WeakPtr<MenuPreviewPattern>(previewPattern)] {
481         auto previewPattern = weak.Upgrade();
482         CHECK_NULL_VOID(previewPattern);
483         previewPattern->SetIsHoverImagePreviewScalePlaying(false);
484     });
485 
486     auto endWidth = previewPattern->GetStackAfterScaleActualWidth();
487     auto endHeight = previewPattern->GetStackAfterScaleActualHeight();
488     auto clipRect = RectF(OffsetF(), SizeF(endWidth, endHeight));
489     auto clipRadius = GetPreviewBorderRadiusFromPattern(previewPattern, menuTheme);
490 
491     auto animateProperty = GetHoverImagePreviewProperty(previewPattern, clipRadius);
492     CHECK_NULL_VOID(animateProperty);
493     stackContext->AttachNodeAnimatableProperty(animateProperty);
494     animateProperty->Set(0.0);
495 
496     AnimationUtils::Animate(
497         scaleOption,
498         [stackContext, scaleTo, clipRect, clipRadius, animateProperty, previewPattern]() {
499             CHECK_NULL_VOID(stackContext);
500             stackContext->UpdateTransformScale(VectorF(scaleTo, scaleTo));
501             stackContext->ClipWithRRect(clipRect, clipRadius);
502             MenuView::UpdateHoverImagePreivewPosition(previewPattern);
503 
504             CHECK_NULL_VOID(animateProperty);
505             animateProperty->Set(1.0);
506         },
507         scaleOption.GetOnFinishEvent(), nullptr, hoverImageStackNode->GetContextRefPtr());
508 }
509 
SetHoverImageFinishEvent(const RefPtr<FrameNode> & hoverImageStackNode,const RefPtr<FrameNode> & previewNode,const RefPtr<RenderContext> & imageContext,const RefPtr<MenuTheme> & menuTheme,const RefPtr<MenuWrapperPattern> & wrapperPattern)510 void SetHoverImageFinishEvent(const RefPtr<FrameNode>& hoverImageStackNode, const RefPtr<FrameNode>& previewNode,
511     const RefPtr<RenderContext>& imageContext, const RefPtr<MenuTheme>& menuTheme,
512     const RefPtr<MenuWrapperPattern>& wrapperPattern)
513 {
514     CHECK_NULL_VOID(previewNode);
515     auto previewPattern = previewNode->GetPattern<MenuPreviewPattern>();
516     CHECK_NULL_VOID(previewPattern);
517     previewPattern->SetIsHoverImageScalePlaying(false);
518 
519     CHECK_NULL_VOID(wrapperPattern);
520     // if the animation is interrupted during the image hover phase, the next dynamic effects are not processed.
521     CHECK_NULL_VOID(!wrapperPattern->IsStopHoverImageAnimation());
522 
523     if (!MenuView::CheckHoverImageFinishForInterruption(wrapperPattern, previewPattern, hoverImageStackNode)) {
524         return;
525     }
526 
527     UpdateOpacityInFinishEvent(previewNode, imageContext, menuTheme);
528 
529     UpdateHoverImagePreviewScale(hoverImageStackNode, previewPattern, menuTheme);
530 }
531 
ShowHoverImageAnimationProc(const RefPtr<FrameNode> & hoverImageStackNode,const RefPtr<FrameNode> & previewNode,const RefPtr<RenderContext> & imageContext,const RefPtr<MenuWrapperPattern> & wrapperPattern)532 void ShowHoverImageAnimationProc(const RefPtr<FrameNode>& hoverImageStackNode, const RefPtr<FrameNode>& previewNode,
533     const RefPtr<RenderContext>& imageContext, const RefPtr<MenuWrapperPattern>& wrapperPattern)
534 {
535     CHECK_NULL_VOID(wrapperPattern && wrapperPattern->GetIsShowHoverImage());
536     if (wrapperPattern->GetHoverScaleInterruption()) {
537         MenuView::ShowHoverImageForInterruption(hoverImageStackNode, previewNode, imageContext, wrapperPattern);
538         return;
539     }
540 
541     CHECK_NULL_VOID(hoverImageStackNode && previewNode);
542     auto stackContext = hoverImageStackNode->GetRenderContext();
543     CHECK_NULL_VOID(stackContext);
544     stackContext->UpdateClipEdge(true);
545     auto previewPattern = previewNode->GetPattern<MenuPreviewPattern>();
546     CHECK_NULL_VOID(previewPattern);
547     auto pipeline = previewNode->GetContextWithCheck();
548     CHECK_NULL_VOID(pipeline);
549     auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
550     CHECK_NULL_VOID(menuTheme);
551 
552     DragEventActuator::ExecutePreDragAction(PreDragStatus::PREVIEW_LIFT_STARTED);
553     auto scaleBefore = previewPattern->GetHoverImageScaleFrom();
554     auto scaleFrom =
555         LessOrEqual(scaleBefore, 0.0) ? menuTheme->GetPreviewBeforeAnimationScale() : scaleBefore;
556     stackContext->UpdateTransformScale(VectorF(scaleFrom, scaleFrom));
557 
558     auto scaleAfter = previewPattern->GetHoverImageScaleTo();
559     auto scaleTo =
560         LessOrEqual(scaleAfter, 0.0) ? menuTheme->GetPreviewAfterAnimationScale() : scaleAfter;
561 
562     previewPattern->SetIsHoverImageScalePlaying(true);
563     // when the scaling start and end sizes are the same, the end callback method should not be relied on
564     AnimationOption scaleOption = AnimationOption();
565     scaleOption.SetOnFinishEvent([hoverImageStackNode, previewNode, imageContext, menuTheme, wrapperPattern]() {
566         DragEventActuator::ExecutePreDragAction(PreDragStatus::PREVIEW_LIFT_FINISHED);
567         SetHoverImageFinishEvent(hoverImageStackNode, previewNode, imageContext, menuTheme, wrapperPattern);
568     });
569     scaleOption.SetDuration(menuTheme->GetHoverImageDelayDuration());
570     scaleOption.SetCurve(Curves::SHARP);
571     scaleTo += NearEqual(scaleFrom, scaleTo) ? MIN_HOVER_SCALE_DIFF : 0.f;
572     AnimationUtils::Animate(
573         scaleOption, [stackContext, scaleTo]() {
574             CHECK_NULL_VOID(stackContext);
575             stackContext->UpdateTransformScale(VectorF(scaleTo, scaleTo));
576         },
577         scaleOption.GetOnFinishEvent(), nullptr, hoverImageStackNode->GetContextRefPtr());
578 }
579 
ShowPixelMapScaleAnimationProc(const RefPtr<MenuTheme> & menuTheme,const RefPtr<FrameNode> & imageNode,const RefPtr<MenuPattern> & menuPattern)580 void ShowPixelMapScaleAnimationProc(
581     const RefPtr<MenuTheme>& menuTheme, const RefPtr<FrameNode>& imageNode, const RefPtr<MenuPattern>& menuPattern)
582 {
583     CHECK_NULL_VOID(menuPattern && menuTheme);
584     auto scaleBefore = menuPattern->GetPreviewBeforeAnimationScale();
585     auto scaleAfter = menuPattern->GetPreviewAfterAnimationScale();
586     auto previewBeforeAnimationScale =
587         LessNotEqual(scaleBefore, 0.0) ? menuTheme->GetPreviewBeforeAnimationScale() : scaleBefore;
588     auto previewAfterAnimationScale =
589         LessNotEqual(scaleAfter, 0.0) ? menuTheme->GetPreviewAfterAnimationScale() : scaleAfter;
590 
591     CHECK_NULL_VOID(imageNode);
592     auto imagePattern = imageNode->GetPattern<ImagePattern>();
593     CHECK_NULL_VOID(imagePattern);
594     DragEventActuator::ExecutePreDragAction(PreDragStatus::PREVIEW_LIFT_STARTED);
595     auto imageRawSize = imagePattern->GetRawImageSize();
596     auto geometryNode = imageNode->GetGeometryNode();
597     CHECK_NULL_VOID(geometryNode);
598     auto geometrySize = geometryNode->GetFrameSize();
599     if (geometrySize.IsPositive() && imageRawSize.IsPositive() && imageRawSize > geometrySize) {
600         previewBeforeAnimationScale *= imageRawSize.Width() / geometrySize.Width();
601     }
602 
603     auto imageContext = imageNode->GetRenderContext();
604     CHECK_NULL_VOID(imageContext);
605     imageContext->UpdateTransformScale(VectorF(previewBeforeAnimationScale, previewBeforeAnimationScale));
606 
607     AnimationOption scaleOption = AnimationOption();
608     auto motion = AceType::MakeRefPtr<ResponsiveSpringMotion>(
609         menuTheme->GetSpringMotionResponse(), menuTheme->GetSpringMotionDampingFraction());
610     scaleOption.SetCurve(motion);
611     scaleOption.SetOnFinishEvent(
612         []() { DragEventActuator::ExecutePreDragAction(PreDragStatus::PREVIEW_LIFT_FINISHED); });
613 
614     AnimationUtils::Animate(
615         scaleOption,
616         [imageContext, previewAfterAnimationScale]() {
617             CHECK_NULL_VOID(imageContext);
618             imageContext->UpdateTransformScale(VectorF(previewAfterAnimationScale, previewAfterAnimationScale));
619         },
620         scaleOption.GetOnFinishEvent(), nullptr, imageNode->GetContextRefPtr());
621 }
622 
HandleDragEnd(float offsetX,float offsetY,float velocity,const RefPtr<FrameNode> & menuWrapper)623 void HandleDragEnd(float offsetX, float offsetY, float velocity, const RefPtr<FrameNode>& menuWrapper)
624 {
625     if ((LessOrEqual(std::abs(offsetY), std::abs(offsetX)) || LessOrEqual(offsetY, 0.0f)) &&
626         LessOrEqual(velocity, PAN_MAX_VELOCITY)) {
627         return;
628     }
629     CHECK_NULL_VOID(menuWrapper);
630     auto wrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
631     CHECK_NULL_VOID(wrapperPattern);
632     TAG_LOGI(AceLogTag::ACE_MENU, "will hide menu");
633     wrapperPattern->HideMenu(HideMenuType::VIEW_DRAG_END);
634 }
635 
InitPanEvent(const RefPtr<GestureEventHub> & targetGestureHub,const RefPtr<GestureEventHub> & gestureHub,const RefPtr<FrameNode> & menuWrapper)636 void InitPanEvent(const RefPtr<GestureEventHub>& targetGestureHub, const RefPtr<GestureEventHub>& gestureHub,
637     const RefPtr<FrameNode>& menuWrapper)
638 {
639     auto dragEventActuator = targetGestureHub->GetDragEventActuator();
640     auto actionStartTask = [actuator = AceType::WeakClaim(AceType::RawPtr(dragEventActuator))](
641                                const GestureEvent& info) {
642         auto dragEventActuator = actuator.Upgrade();
643         CHECK_NULL_VOID(dragEventActuator);
644         dragEventActuator->RestartDragTask(info);
645     };
646     auto actionEndTask = [menuWrapper](const GestureEvent& info) {
647         auto offsetX = static_cast<float>(info.GetOffsetX());
648         auto offsetY = static_cast<float>(info.GetOffsetY());
649         auto offsetPerSecondX = info.GetVelocity().GetOffsetPerSecond().GetX();
650         auto offsetPerSecondY = info.GetVelocity().GetOffsetPerSecond().GetY();
651         auto velocity =
652             static_cast<float>(std::sqrt(offsetPerSecondX * offsetPerSecondX + offsetPerSecondY * offsetPerSecondY));
653         HandleDragEnd(offsetX, offsetY, velocity, menuWrapper);
654     };
655     PanDirection panDirection;
656     panDirection.type = PanDirection::ALL;
657     auto panEvent =
658         AceType::MakeRefPtr<PanEvent>(std::move(actionStartTask), nullptr, std::move(actionEndTask), nullptr);
659     auto distance = SystemProperties::GetDragStartPanDistanceThreshold();
660     gestureHub->AddPanEvent(panEvent, panDirection, 1, Dimension(distance, DimensionUnit::VP));
661 
662     // add TouchEvent for Menu dragStart Move
663     auto touchTask = [actuator = AceType::WeakClaim(AceType::RawPtr(dragEventActuator))](const TouchEventInfo& info) {
664         auto dragEventActuator = actuator.Upgrade();
665         CHECK_NULL_VOID(dragEventActuator);
666         dragEventActuator->HandleTouchEvent(info, true);
667     };
668     auto touchListener = AceType::MakeRefPtr<TouchEventImpl>(std::move(touchTask));
669     gestureHub->AddTouchEvent(touchListener);
670 }
671 
GetHoverImageCustomPreviewBaseScaleInfo(const MenuParam & menuParam,int32_t width,int32_t height,const RefPtr<MenuPreviewPattern> & previewPattern)672 float GetHoverImageCustomPreviewBaseScaleInfo(const MenuParam& menuParam, int32_t width, int32_t height,
673     const RefPtr<MenuPreviewPattern>& previewPattern)
674 {
675     float scaleRet = PREVIEW_ORIGIN_SCALE;
676     CHECK_NULL_RETURN(menuParam.isShowHoverImage, scaleRet);
677     CHECK_NULL_RETURN(previewPattern, scaleRet);
678     // if the parent container is smaller than the child component, the child container will be squeezed
679     auto previewWidth = previewPattern->GetStackAfterScaleActualWidth();
680     auto previewHeight = previewPattern->GetStackAfterScaleActualHeight();
681     CHECK_NULL_RETURN(width > 0 && height > 0, scaleRet);
682     if (LessOrEqual(previewWidth / width, previewHeight / height)) {
683         CHECK_EQUAL_RETURN(previewWidth, 0, scaleRet);
684         scaleRet = width / previewWidth;
685     } else {
686         CHECK_EQUAL_RETURN(previewHeight, 0, scaleRet);
687         scaleRet = height / previewHeight;
688     }
689     return scaleRet;
690 }
691 
SetHoverImageCustomPreviewInfo(const RefPtr<FrameNode> & previewNode,const MenuParam & menuParam,int32_t width,int32_t height)692 void SetHoverImageCustomPreviewInfo(const RefPtr<FrameNode>& previewNode, const MenuParam& menuParam,
693     int32_t width, int32_t height)
694 {
695     CHECK_NULL_VOID(previewNode);
696     auto previewPattern = previewNode->GetPattern<MenuPreviewPattern>();
697     CHECK_NULL_VOID(previewPattern);
698     auto baseScale = GetHoverImageCustomPreviewBaseScaleInfo(menuParam, width, height, previewPattern);
699     CHECK_NULL_VOID(baseScale);
700 
701     auto hoverImageScaleFrom = menuParam.hoverImageAnimationOptions.scaleFrom;
702     hoverImageScaleFrom = LessOrEqual(hoverImageScaleFrom, 0.0) ? PREVIEW_ORIGIN_SCALE : hoverImageScaleFrom;
703     previewPattern->SetHoverImageScaleFrom(baseScale * hoverImageScaleFrom);
704 
705     auto hoverImageScaleTo = menuParam.hoverImageAnimationOptions.scaleTo;
706     hoverImageScaleTo = LessOrEqual(hoverImageScaleTo, 0.0) ? PREVIEW_ORIGIN_SCALE : hoverImageScaleTo;
707     auto hoverImageScaleToNew = baseScale * hoverImageScaleTo;
708     previewPattern->SetHoverImageScaleTo(hoverImageScaleToNew);
709 
710     // get actual area size for clip visible area
711     previewPattern->SetHoverImageAfterScaleWidth(width / baseScale);
712     previewPattern->SetHoverImageAfterScaleHeight(height  / baseScale);
713 
714     previewPattern->SetHoverImageAfterScaleOffset(OffsetF((previewPattern->GetStackAfterScaleActualWidth() -
715         previewPattern->GetHoverImageAfterScaleWidth()) / HALF_DIVIDE,
716         (previewPattern->GetStackAfterScaleActualHeight() -
717         previewPattern->GetHoverImageAfterScaleHeight()) / HALF_DIVIDE));
718 }
719 
SetAccessibilityPixelMap(const RefPtr<FrameNode> & targetNode,RefPtr<FrameNode> & imageNode)720 void SetAccessibilityPixelMap(const RefPtr<FrameNode>& targetNode, RefPtr<FrameNode>& imageNode)
721 {
722     auto targetProps = targetNode->GetAccessibilityProperty<AccessibilityProperty>();
723     CHECK_NULL_VOID(targetProps);
724     targetProps->SetOnAccessibilityFocusCallback([targetWK = AceType::WeakClaim(AceType::RawPtr(targetNode)),
725         imageWK = AceType::WeakClaim(AceType::RawPtr(imageNode))](bool focus) {
726         if (!focus) {
727             auto targetNode = targetWK.Upgrade();
728             CHECK_NULL_VOID(targetNode);
729             auto context = targetNode->GetRenderContext();
730             CHECK_NULL_VOID(context);
731             auto pixelMap = context->GetThumbnailPixelMap();
732             CHECK_NULL_VOID(pixelMap);
733             auto imageNode = imageWK.Upgrade();
734             CHECK_NULL_VOID(imageNode);
735             auto props = imageNode->GetLayoutProperty<ImageLayoutProperty>();
736             CHECK_NULL_VOID(props);
737             props->UpdateAutoResize(false);
738             props->UpdateImageSourceInfo(ImageSourceInfo(pixelMap));
739             imageNode->MarkModifyDone();
740         }
741     });
742 }
743 
GetPreviewBorderRadiusFromNode(const RefPtr<FrameNode> & previewNode,const MenuParam & menuParam)744 BorderRadiusProperty GetPreviewBorderRadiusFromNode(const RefPtr<FrameNode>& previewNode, const MenuParam& menuParam)
745 {
746     CHECK_NULL_RETURN(previewNode, {});
747     auto pipelineContext = previewNode->GetContextWithCheck();
748     CHECK_NULL_RETURN(pipelineContext, {});
749     auto menuTheme = pipelineContext->GetTheme<NG::MenuTheme>();
750     CHECK_NULL_RETURN(menuTheme, {});
751     auto previewBorderRadiusValue = menuTheme->GetPreviewBorderRadius();
752     BorderRadiusProperty previewBorderRadius = BorderRadiusProperty(Dimension(previewBorderRadiusValue));
753     if (menuParam.previewBorderRadius.has_value()) {
754         if (menuParam.previewBorderRadius->radiusTopLeft.has_value() &&
755             menuParam.previewBorderRadius->radiusTopLeft->IsNonNegative()) {
756             previewBorderRadius.radiusTopLeft = menuParam.previewBorderRadius->radiusTopLeft;
757         }
758         if (menuParam.previewBorderRadius->radiusTopRight.has_value() &&
759             menuParam.previewBorderRadius->radiusTopRight->IsNonNegative()) {
760             previewBorderRadius.radiusTopRight = menuParam.previewBorderRadius->radiusTopRight;
761         }
762         if (menuParam.previewBorderRadius->radiusBottomLeft.has_value() &&
763             menuParam.previewBorderRadius->radiusBottomLeft->IsNonNegative()) {
764             previewBorderRadius.radiusBottomLeft = menuParam.previewBorderRadius->radiusBottomLeft;
765         }
766         if (menuParam.previewBorderRadius->radiusBottomRight.has_value() &&
767             menuParam.previewBorderRadius->radiusBottomRight->IsNonNegative()) {
768             previewBorderRadius.radiusBottomRight = menuParam.previewBorderRadius->radiusBottomRight;
769         }
770     }
771     return previewBorderRadius;
772 }
773 
SetPixelMap(const RefPtr<FrameNode> & target,const RefPtr<FrameNode> & wrapperNode,const RefPtr<FrameNode> & hoverImageStackNode,const RefPtr<FrameNode> & previewNode,const MenuParam & menuParam)774 void SetPixelMap(const RefPtr<FrameNode>& target, const RefPtr<FrameNode>& wrapperNode,
775     const RefPtr<FrameNode>& hoverImageStackNode, const RefPtr<FrameNode>& previewNode, const MenuParam& menuParam)
776 {
777     CHECK_NULL_VOID(target);
778     auto eventHub = target->GetOrCreateEventHub<NG::EventHub>();
779     CHECK_NULL_VOID(eventHub);
780     auto gestureHub = eventHub->GetGestureEventHub();
781     CHECK_NULL_VOID(gestureHub);
782     RefPtr<PixelMap> pixelMap = gestureHub->GetPixelMap();
783     CHECK_NULL_VOID(pixelMap);
784     auto width = pixelMap->GetWidth();
785     auto height = pixelMap->GetHeight();
786     auto imageOffset = GetFloatImageOffset(target);
787     auto imageNode = FrameNode::GetOrCreateFrameNode(V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
788         []() { return AceType::MakeRefPtr<ImagePattern>(); });
789     imageNode->GetPaintProperty<ImageRenderProperty>()->UpdateImageInterpolation(ImageInterpolation::HIGH);
790     auto props = imageNode->GetLayoutProperty<ImageLayoutProperty>();
791     props->UpdateAutoResize(false);
792     props->UpdateImageSourceInfo(ImageSourceInfo(pixelMap));
793     imageNode->GetPattern<ImagePattern>()->SetSyncLoad(true);
794     SetAccessibilityPixelMap(target, imageNode);
795     auto hub = imageNode->GetOrCreateEventHub<EventHub>();
796     CHECK_NULL_VOID(hub);
797     auto imageGestureHub = hub->GetOrCreateGestureEventHub();
798     CHECK_NULL_VOID(imageGestureHub);
799     InitPanEvent(gestureHub, imageGestureHub, wrapperNode);
800 
801     if (menuParam.isShowHoverImage) {
802         props->UpdateImageFit(ImageFit::CONTAIN);
803 
804         imageNode->GetLayoutProperty()->UpdateMeasureType(MeasureType::MATCH_PARENT);
805         imageNode->MarkModifyDone();
806         imageNode->MountToParent(hoverImageStackNode);
807     } else {
808         auto targetSize = CalcSize(NG::CalcLength(width), NG::CalcLength(height));
809         props->UpdateUserDefinedIdealSize(targetSize);
810         props->UpdateImageFit(ImageFit::FILL);
811 
812         auto imageContext = imageNode->GetRenderContext();
813         CHECK_NULL_VOID(imageContext);
814         BorderRadiusProperty borderRadius = GetPreviewBorderRadiusFromNode(previewNode, menuParam);
815         if (menuParam.previewBorderRadius) {
816             imageContext->UpdateBorderRadius(borderRadius);
817         }
818         imageNode->MarkModifyDone();
819         imageNode->MountToParent(wrapperNode);
820         DragAnimationHelper::UpdateGatherNodeToTop();
821         DragDropFuncWrapper::UpdatePositionFromFrameNode(imageNode, target, width, height);
822         imageOffset = DragDropFuncWrapper::GetPaintRectCenterToScreen(target) -
823             OffsetF(width / HALF_DIVIDE, height / HALF_DIVIDE);
824         imageOffset -= DragDropFuncWrapper::GetCurrentWindowOffset(imageNode->GetContextRefPtr());
825         MountTextNode(wrapperNode, previewNode);
826     }
827 
828     auto geometryNode = imageNode->GetGeometryNode();
829     CHECK_NULL_VOID(geometryNode);
830     geometryNode->SetFrameOffset(imageOffset);
831 }
832 
CreateFilterColumnNode()833 static RefPtr<FrameNode> CreateFilterColumnNode()
834 {
835     auto columnNode = FrameNode::CreateFrameNode(V2::COLUMN_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
836         AceType::MakeRefPtr<LinearLayoutPattern>(true));
837     CHECK_NULL_RETURN(columnNode, nullptr);
838     auto layoutProperty = columnNode->GetLayoutProperty();
839     CHECK_NULL_RETURN(layoutProperty, nullptr);
840     layoutProperty->UpdateMeasureType(MeasureType::MATCH_PARENT);
841     auto accessibilityProperty = columnNode->GetAccessibilityProperty<NG::AccessibilityProperty>();
842     if (accessibilityProperty) {
843         accessibilityProperty->SetAccessibilityHoverPriority(true); // consume barrierfree hover event
844     }
845 
846     return columnNode;
847 }
848 
GetFilterParentNode(const RefPtr<UINode> & targetNode,const RefPtr<OverlayManager> & manager,bool isShowInSubWindow)849 static RefPtr<UINode> GetFilterParentNode(
850     const RefPtr<UINode>& targetNode, const RefPtr<OverlayManager>& manager, bool isShowInSubWindow)
851 {
852     CHECK_NULL_RETURN((targetNode && manager), nullptr);
853     RefPtr<UINode> parent = nullptr;
854     if (isShowInSubWindow) {
855         parent = targetNode->GetParent();
856         CHECK_NULL_RETURN(parent, nullptr);
857         while (parent->GetDepth() != 1) {
858             parent = parent->GetParent();
859             CHECK_NULL_RETURN(parent, nullptr);
860         }
861     } else {
862         auto rootNodeWeak = manager->GetRootNode();
863         parent = rootNodeWeak.Upgrade();
864     }
865     return parent;
866 }
867 
SetFilter(const RefPtr<FrameNode> & targetNode,const RefPtr<FrameNode> & menuWrapperNode)868 static void SetFilter(const RefPtr<FrameNode>& targetNode, const RefPtr<FrameNode>& menuWrapperNode)
869 {
870     CHECK_NULL_VOID(targetNode && menuWrapperNode);
871     auto parent = targetNode->GetParent();
872     CHECK_NULL_VOID(parent);
873     auto containerId = Container::CurrentId();
874     if (containerId >= MIN_SUBCONTAINER_ID) {
875         containerId = SubwindowManager::GetInstance()->GetParentContainerId(containerId);
876     }
877     ContainerScope scope(containerId);
878     auto container = Container::Current();
879     CHECK_NULL_VOID(container);
880     auto pipelineContext = menuWrapperNode->GetContext();
881     CHECK_NULL_VOID(pipelineContext);
882     auto manager = pipelineContext->GetOverlayManager();
883     CHECK_NULL_VOID(manager);
884     auto menuTheme = pipelineContext->GetTheme<NG::MenuTheme>();
885     CHECK_NULL_VOID(menuTheme);
886     if (!manager->GetHasFilterWithCheck() && !manager->GetIsOnAnimation()) {
887         auto menuWrapperPattern = menuWrapperNode->GetPattern<MenuWrapperPattern>();
888         CHECK_NULL_VOID(menuWrapperPattern);
889         bool isBindOverlayValue = targetNode->GetLayoutProperty()->GetIsBindOverlayValue(false);
890         CHECK_NULL_VOID((menuWrapperPattern->GetMenuMaskEnable() || isBindOverlayValue) && menuTheme->GetHasFilter());
891         // insert columnNode to rootNode
892         auto columnNode = CreateFilterColumnNode();
893         CHECK_NULL_VOID(columnNode);
894         // set filter
895         menuWrapperPattern->SetFilterColumnNode(columnNode);
896         auto isShowInSubWindow =
897             menuWrapperPattern->GetMenuParam().isShowInSubWindow || menuWrapperPattern->IsContextMenu();
898         if (container->IsSceneBoardWindow()) {
899             auto windowScene = manager->FindWindowScene(targetNode);
900             manager->MountFilterToWindowScene(columnNode, windowScene);
901             manager->ShowFilterAnimation(columnNode, menuWrapperNode);
902         } else if (container->IsUIExtensionWindow() && isShowInSubWindow) {
903             // mount filter node on subwindow to ensure filter node's size equals to host window's size
904             menuWrapperPattern->SetIsFilterInSubwindow(true);
905         } else {
906             auto parent = GetFilterParentNode(targetNode, manager, isShowInSubWindow);
907             CHECK_NULL_VOID(parent);
908             columnNode->MountToParent(parent);
909             columnNode->OnMountToParentDone();
910             manager->SetHasFilter(true);
911             manager->SetFilterActive(true);
912             manager->SetFilterColumnNode(columnNode);
913             parent->MarkDirtyNode(NG::PROPERTY_UPDATE_BY_CHILD_REQUEST);
914             manager->ShowFilterAnimation(columnNode, menuWrapperNode);
915         }
916     }
917 }
918 
SetFilterToMenu(const RefPtr<FrameNode> & targetNode,const RefPtr<FrameNode> & wrapperNode,const MenuParam & menuParam,bool isLongPressDrag)919 void SetFilterToMenu(const RefPtr<FrameNode>& targetNode, const RefPtr<FrameNode>& wrapperNode,
920     const MenuParam& menuParam, bool isLongPressDrag)
921 {
922     if (menuParam.maskEnable.has_value()) {
923         if (menuParam.maskEnable.value()) {
924             SetFilter(targetNode, wrapperNode);
925         }
926     } else if (menuParam.previewMode != MenuPreviewMode::NONE || isLongPressDrag) {
927         DragDropGlobalController::GetInstance().UpdateDragFilterShowingStatus(true);
928         SetFilter(targetNode, wrapperNode);
929     }
930 }
931 
SetPreviewInfoToMenu(const RefPtr<FrameNode> & targetNode,const RefPtr<FrameNode> & wrapperNode,const RefPtr<FrameNode> & hoverImageStackNode,const RefPtr<FrameNode> & previewNode,const MenuParam & menuParam)932 void SetPreviewInfoToMenu(const RefPtr<FrameNode>& targetNode, const RefPtr<FrameNode>& wrapperNode,
933     const RefPtr<FrameNode>& hoverImageStackNode, const RefPtr<FrameNode>& previewNode, const MenuParam& menuParam)
934 {
935     CHECK_NULL_VOID(targetNode);
936     auto eventHub = targetNode->GetOrCreateEventHub<EventHub>();
937     CHECK_NULL_VOID(eventHub);
938     auto gestureEventHub = eventHub->GetGestureEventHub();
939     CHECK_NULL_VOID(gestureEventHub);
940     auto isAllowedDrag = gestureEventHub->IsAllowedDrag(eventHub) && !gestureEventHub->GetTextDraggable();
941     auto isLiftingDisabled = targetNode->GetDragPreviewOption().isLiftingDisabled;
942     if (targetNode->GetTag() == V2::TEXT_ETS_TAG && targetNode->IsDraggable() && !targetNode->IsCustomerSet()) {
943         auto textPattern = targetNode->GetPattern<TextPattern>();
944         if (textPattern && textPattern->GetCopyOptions() == CopyOptions::None) {
945             isAllowedDrag = false;
946         }
947     }
948     auto isLongPressDrag = menuParam.menuBindType == MenuBindingType::LONG_PRESS && isAllowedDrag && !isLiftingDisabled;
949     SetFilterToMenu(targetNode, wrapperNode, menuParam, isLongPressDrag);
950     if (menuParam.previewMode == MenuPreviewMode::IMAGE ||
951         (menuParam.previewMode == MenuPreviewMode::NONE && isLongPressDrag) || menuParam.isShowHoverImage) {
952         SetPixelMap(targetNode, wrapperNode, hoverImageStackNode, previewNode, menuParam);
953     }
954     if (menuParam.previewMode == MenuPreviewMode::NONE && isAllowedDrag && !isLiftingDisabled) {
955         CHECK_NULL_VOID(wrapperNode);
956         auto pixelMapNode = AceType::DynamicCast<FrameNode>(wrapperNode->GetChildAtIndex(1));
957         CHECK_NULL_VOID(pixelMapNode);
958         auto renderContext = pixelMapNode->GetRenderContext();
959         CHECK_NULL_VOID(renderContext);
960         renderContext->UpdateZIndex(-1);
961         auto menuNode = AceType::DynamicCast<FrameNode>(wrapperNode->GetChildAtIndex(0));
962         if (menuNode) {
963             MenuView::ShowPixelMapAnimation(menuNode);
964         }
965         // if filter set in subwindow, need to adjust zOrder to show in back.
966         auto menuWrapperPattern = wrapperNode->GetPattern<MenuWrapperPattern>();
967         CHECK_NULL_VOID(menuWrapperPattern);
968         if (menuWrapperPattern->GetIsFilterInSubwindow()) {
969             auto columnNode = menuWrapperPattern->GetFilterColumnNode();
970             CHECK_NULL_VOID(columnNode);
971             auto columnRenderContext = columnNode->GetRenderContext();
972             CHECK_NULL_VOID(columnRenderContext);
973             columnRenderContext->UpdateZIndex(-1);
974         }
975     }
976 }
977 
SetHasCustomRadius(const RefPtr<FrameNode> & menuWrapperNode,const RefPtr<FrameNode> & menuNode,const MenuParam & menuParam)978 void SetHasCustomRadius(
979     const RefPtr<FrameNode>& menuWrapperNode, const RefPtr<FrameNode>& menuNode, const MenuParam& menuParam)
980 {
981     CHECK_NULL_VOID(menuWrapperNode);
982     CHECK_NULL_VOID(menuNode);
983     auto menuWrapperPattern = menuWrapperNode->GetPattern<MenuWrapperPattern>();
984     CHECK_NULL_VOID(menuWrapperPattern);
985     if (menuParam.borderRadius.has_value()) {
986         menuWrapperPattern->SetHasCustomRadius(true);
987         auto menuProperty = menuNode->GetLayoutProperty<MenuLayoutProperty>();
988         CHECK_NULL_VOID(menuProperty);
989         menuProperty->UpdateBorderRadius(menuParam.borderRadius.value());
990     } else {
991         auto menuProperty = menuNode->GetLayoutProperty<MenuLayoutProperty>();
992         CHECK_NULL_VOID(menuProperty);
993         menuWrapperPattern->SetHasCustomRadius(false);
994         auto pipeline = menuWrapperNode->GetContext();
995         CHECK_NULL_VOID(pipeline);
996         auto theme = pipeline->GetTheme<SelectTheme>();
997         CHECK_NULL_VOID(theme);
998         Dimension defaultDimension(0);
999         BorderRadiusProperty radius = { defaultDimension, defaultDimension, defaultDimension, defaultDimension };
1000         auto defaultRadius = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)
1001                                  ? theme->GetMenuDefaultRadius()
1002                                  : theme->GetMenuBorderRadius();
1003         radius.SetRadius(defaultRadius);
1004         menuProperty->UpdateBorderRadius(radius);
1005     }
1006 }
1007 
SetMenuFocusRule(const RefPtr<FrameNode> & menuNode)1008 void SetMenuFocusRule(const RefPtr<FrameNode>& menuNode)
1009 {
1010     CHECK_NULL_VOID(menuNode);
1011     auto focusHub = menuNode->GetFocusHub();
1012     CHECK_NULL_VOID(focusHub);
1013 
1014     auto pipelineContext = menuNode->GetContextWithCheck();
1015     CHECK_NULL_VOID(pipelineContext);
1016     auto menuTheme = pipelineContext->GetTheme<NG::MenuTheme>();
1017     CHECK_NULL_VOID(menuTheme);
1018     focusHub->SetDirectionalKeyFocus(menuTheme->GetEnableDirectionalKeyFocus());
1019 }
1020 
ConvertTxtTextAlign(bool IsRightToLeft,TextAlign textAlign)1021 Alignment ConvertTxtTextAlign(bool IsRightToLeft, TextAlign textAlign)
1022 {
1023     Alignment convertValue;
1024     switch (textAlign) {
1025         case TextAlign::LEFT:
1026             convertValue = Alignment::CENTER_LEFT;
1027             break;
1028         case TextAlign::CENTER:
1029             convertValue = Alignment::CENTER;
1030             break;
1031         case TextAlign::RIGHT:
1032             convertValue = Alignment::CENTER_RIGHT;
1033             break;
1034         case TextAlign::START:
1035             convertValue = IsRightToLeft ? Alignment::CENTER_RIGHT : Alignment::CENTER_LEFT;
1036             break;
1037         case TextAlign::END:
1038             convertValue = IsRightToLeft ? Alignment::CENTER_LEFT : Alignment::CENTER_RIGHT;
1039             break;
1040         default:
1041             break;
1042     }
1043     return convertValue;
1044 }
1045 
SetBackgroundBlurStyle(const RefPtr<FrameNode> & host,const BlurStyleOption & bgBlurStyle)1046 void SetBackgroundBlurStyle(const RefPtr<FrameNode>& host, const BlurStyleOption& bgBlurStyle)
1047 {
1048     CHECK_NULL_VOID(host);
1049     auto pipeline = host->GetContext();
1050     CHECK_NULL_VOID(pipeline);
1051     if (bgBlurStyle.policy == BlurStyleActivePolicy::FOLLOWS_WINDOW_ACTIVE_STATE) {
1052         pipeline->AddWindowFocusChangedCallback(host->GetId());
1053     } else {
1054         pipeline->RemoveWindowFocusChangedCallback(host->GetId());
1055     }
1056     auto renderContext = host->GetRenderContext();
1057     if (renderContext) {
1058         if (renderContext->GetBackgroundEffect().has_value()) {
1059             renderContext->UpdateBackgroundEffect(std::nullopt);
1060         }
1061         renderContext->UpdateBackBlurStyle(bgBlurStyle);
1062         if (renderContext->GetBackBlurRadius().has_value()) {
1063             renderContext->UpdateBackBlurRadius(Dimension());
1064         }
1065     }
1066 }
1067 
SetBackgroundEffect(const RefPtr<FrameNode> & host,const EffectOption & effectOption)1068 void SetBackgroundEffect(const RefPtr<FrameNode>& host, const EffectOption &effectOption)
1069 {
1070     CHECK_NULL_VOID(host);
1071     auto pipeline = host->GetContext();
1072     CHECK_NULL_VOID(pipeline);
1073     if (effectOption.policy == BlurStyleActivePolicy::FOLLOWS_WINDOW_ACTIVE_STATE) {
1074         pipeline->AddWindowFocusChangedCallback(host->GetId());
1075     } else {
1076         pipeline->RemoveWindowFocusChangedCallback(host->GetId());
1077     }
1078     auto renderContext = host->GetRenderContext();
1079     if (renderContext) {
1080         if (renderContext->GetBackBlurRadius().has_value()) {
1081             renderContext->UpdateBackBlurRadius(Dimension());
1082         }
1083         if (renderContext->GetBackBlurStyle().has_value()) {
1084             renderContext->UpdateBackBlurStyle(std::nullopt);
1085         }
1086         renderContext->UpdateBackgroundEffect(effectOption);
1087     }
1088 }
1089 
UpdateMenuBackgroundStyleOption(const RefPtr<FrameNode> & menuNode,const MenuParam & menuParam)1090 void UpdateMenuBackgroundStyleOption(const RefPtr<FrameNode>& menuNode, const MenuParam& menuParam)
1091 {
1092     if (menuParam.backgroundBlurStyleOption.has_value()) {
1093         BlurStyleOption backgroundBlurStyleOption = menuParam.backgroundBlurStyleOption.value();
1094         SetBackgroundBlurStyle(menuNode, backgroundBlurStyleOption);
1095     }
1096     if (menuParam.backgroundEffectOption.has_value()) {
1097         EffectOption backgroundEffectOption = menuParam.backgroundEffectOption.value();
1098         SetBackgroundEffect(menuNode, backgroundEffectOption);
1099     }
1100 }
1101 
UpdateMenuMaskType(const RefPtr<FrameNode> & wrapperNode)1102 void UpdateMenuMaskType(const RefPtr<FrameNode>& wrapperNode)
1103 {
1104     CHECK_NULL_VOID(wrapperNode);
1105     auto menuWrapperPattern = wrapperNode->GetPattern<MenuWrapperPattern>();
1106     CHECK_NULL_VOID(menuWrapperPattern);
1107     auto maskEnable = menuWrapperPattern->GetMenuMaskEnable();
1108     if (maskEnable) {
1109         menuWrapperPattern->UpdateFilterMaskType();
1110     }
1111 }
1112 } // namespace
1113 
1114 thread_local std::unordered_map<int32_t, MenuHoverScaleStatus> menuHoverStatus_;
1115 
SetMenuHoverScaleStatus(int32_t targetId,MenuHoverScaleStatus status)1116 void MenuView::SetMenuHoverScaleStatus(int32_t targetId, MenuHoverScaleStatus status)
1117 {
1118     menuHoverStatus_[targetId] = status;
1119 }
1120 
RemoveMenuHoverScaleStatus(int32_t targetId)1121 void MenuView::RemoveMenuHoverScaleStatus(int32_t targetId)
1122 {
1123     menuHoverStatus_.erase(targetId);
1124 }
1125 
GetMenuHoverScaleStatus(int32_t targetId)1126 MenuHoverScaleStatus MenuView::GetMenuHoverScaleStatus(int32_t targetId)
1127 {
1128     auto result = menuHoverStatus_.find(targetId);
1129     return result != menuHoverStatus_.end() ? result->second : MenuHoverScaleStatus::NONE;
1130 }
1131 
ShowMenuTargetScaleToOrigin(const RefPtr<MenuWrapperPattern> & wrapperPattern,const RefPtr<MenuPreviewPattern> & previewPattern)1132 void MenuView::ShowMenuTargetScaleToOrigin(
1133     const RefPtr<MenuWrapperPattern>& wrapperPattern, const RefPtr<MenuPreviewPattern>& previewPattern)
1134 {
1135     CHECK_NULL_VOID(wrapperPattern);
1136     auto menuParam = wrapperPattern->GetMenuParam();
1137     auto scaleFrom = menuParam.hoverImageAnimationOptions.scaleFrom;
1138 
1139     CHECK_NULL_VOID(previewPattern);
1140     auto scale = previewPattern->GetHoverTargetOriginScale() * scaleFrom;
1141 
1142     auto targetNode = GetMenuTargetNode(wrapperPattern);
1143     CHECK_NULL_VOID(targetNode);
1144     auto targetRenderContext = targetNode->GetRenderContext();
1145     CHECK_NULL_VOID(targetRenderContext);
1146 
1147     auto menuTheme = GetMenuTheme(wrapperPattern->GetHost());
1148     CHECK_NULL_VOID(menuTheme);
1149     AnimationOption option = AnimationOption(Curves::SHARP, menuTheme->GetHoverImageDelayDuration(true));
1150     AnimationUtils::Animate(option, [targetRenderContext, scale]() {
1151         CHECK_NULL_VOID(targetRenderContext);
1152         targetRenderContext->UpdateTransformScale(scale);
1153     }, nullptr, nullptr, targetNode->GetContextRefPtr());
1154 }
1155 
UpdateHoverImagePreivewPosition(const RefPtr<MenuPreviewPattern> & previewPattern)1156 void MenuView::UpdateHoverImagePreivewPosition(const RefPtr<MenuPreviewPattern>& previewPattern)
1157 {
1158     CHECK_NULL_VOID(previewPattern);
1159     auto menuWrapper = previewPattern->GetMenuWrapper();
1160     CHECK_NULL_VOID(menuWrapper);
1161     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
1162     CHECK_NULL_VOID(menuWrapperPattern);
1163 
1164     auto flexNode = menuWrapperPattern->GetHoverImageFlexNode();
1165     CHECK_NULL_VOID(flexNode);
1166     auto flexRenderContext = flexNode->GetRenderContext();
1167     CHECK_NULL_VOID(flexRenderContext);
1168 
1169     auto previewGeometryNode = flexNode->GetGeometryNode();
1170     CHECK_NULL_VOID(previewGeometryNode);
1171     auto previewPosition = previewGeometryNode->GetFrameOffset();
1172 
1173     flexRenderContext->UpdatePosition(
1174         OffsetT<Dimension>(Dimension(previewPosition.GetX()), Dimension(previewPosition.GetY())));
1175 }
1176 
ShowHoverImageForInterruption(const RefPtr<FrameNode> & hoverImageStackNode,const RefPtr<FrameNode> & previewNode,const RefPtr<RenderContext> & imageContext,const RefPtr<MenuWrapperPattern> & wrapperPattern)1177 void MenuView::ShowHoverImageForInterruption(const RefPtr<FrameNode>& hoverImageStackNode,
1178     const RefPtr<FrameNode>& previewNode, const RefPtr<RenderContext>& imageContext,
1179     const RefPtr<MenuWrapperPattern>& wrapperPattern)
1180 {
1181     CHECK_NULL_VOID(wrapperPattern);
1182     auto stackContext = hoverImageStackNode->GetRenderContext();
1183     CHECK_NULL_VOID(stackContext);
1184 
1185     CHECK_NULL_VOID(previewNode);
1186     auto previewPattern = previewNode->GetPattern<MenuPreviewPattern>();
1187     CHECK_NULL_VOID(previewPattern);
1188     auto menuTheme = GetMenuTheme(previewNode);
1189     CHECK_NULL_VOID(menuTheme);
1190 
1191     auto menuParam = wrapperPattern->GetMenuParam();
1192     auto scaleFrom = menuParam.hoverImageAnimationOptions.scaleFrom;
1193     auto scaleTo = menuParam.hoverImageAnimationOptions.scaleTo;
1194 
1195     scaleFrom = LessOrEqual(scaleFrom, 0.0) ? PREVIEW_ORIGIN_SCALE : scaleFrom;
1196     scaleTo = LessOrEqual(scaleTo, 0.0) ? PREVIEW_ORIGIN_SCALE : scaleTo;
1197     scaleTo += NearEqual(scaleFrom, scaleTo) ? MIN_HOVER_SCALE_DIFF : 0.f;
1198 
1199     auto targetNode = GetMenuTargetNode(wrapperPattern);
1200     CHECK_NULL_VOID(targetNode);
1201     auto targetRenderContext = targetNode->GetRenderContext();
1202     CHECK_NULL_VOID(targetRenderContext);
1203     auto targetScale = targetRenderContext->GetTransformScaleValue({ 1.0f, 1.0f });
1204     previewPattern->SetHoverTargetOriginScale(targetScale);
1205 
1206     stackContext->UpdateClipEdge(true);
1207     stackContext->UpdateOpacity(0.0);
1208     targetRenderContext->UpdateTransformScale(targetScale * scaleFrom);
1209 
1210     previewPattern->SetIsHoverImageScalePlaying(true);
1211     DragEventActuator::ExecutePreDragAction(PreDragStatus::PREVIEW_LIFT_STARTED);
1212 
1213     AnimationOption option = AnimationOption(Curves::SHARP, menuTheme->GetHoverImageDelayDuration(true));
1214     option.SetOnFinishEvent([hoverImageStackNode, previewNode, imageContext, menuTheme, wrapperPattern]() {
1215         DragEventActuator::ExecutePreDragAction(PreDragStatus::PREVIEW_LIFT_FINISHED);
1216         SetHoverImageFinishEvent(hoverImageStackNode, previewNode, imageContext, menuTheme, wrapperPattern);
1217     });
1218     AnimationUtils::Animate(
1219         option,
1220         [targetRenderContext, scale = targetScale * scaleTo]() {
1221             CHECK_NULL_VOID(targetRenderContext);
1222             targetRenderContext->UpdateTransformScale(scale);
1223         },
1224         option.GetOnFinishEvent(), nullptr, targetNode->GetContextRefPtr());
1225 }
1226 
CheckHoverImageFinishForInterruption(const RefPtr<MenuWrapperPattern> & wrapperPattern,const RefPtr<MenuPreviewPattern> & previewPattern,const RefPtr<FrameNode> & hoverImageStackNode)1227 bool MenuView::CheckHoverImageFinishForInterruption(const RefPtr<MenuWrapperPattern>& wrapperPattern,
1228     const RefPtr<MenuPreviewPattern>& previewPattern, const RefPtr<FrameNode>& hoverImageStackNode)
1229 {
1230     CHECK_NULL_RETURN(wrapperPattern, false);
1231     if (!wrapperPattern->GetHoverScaleInterruption()) {
1232         return true;
1233     }
1234 
1235     MenuView::ShowMenuTargetScaleToOrigin(wrapperPattern, previewPattern);
1236     auto targetId = wrapperPattern->GetTargetId();
1237     auto hoverStatus = MenuView::GetMenuHoverScaleStatus(targetId);
1238     TAG_LOGI(AceLogTag::ACE_MENU, "menu hoverScale animation finish, hoverStatus: %{public}d", hoverStatus);
1239     if (hoverStatus != MenuHoverScaleStatus::MENU_SHOW) {
1240         auto menu = wrapperPattern->GetMenu();
1241         CHECK_NULL_RETURN(menu, false);
1242         auto menuRenderContext = menu->GetRenderContext();
1243         CHECK_NULL_RETURN(menuRenderContext, false);
1244         menuRenderContext->UpdateOpacity(0.0);
1245         MenuView::SetMenuHoverScaleStatus(targetId, MenuHoverScaleStatus::INTERRUPT);
1246         SubwindowManager::GetInstance()->HideMenuNG(wrapperPattern->GetHost(), targetId);
1247         return false;
1248     }
1249 
1250     CHECK_NULL_RETURN(hoverImageStackNode, false);
1251     auto stackContext = hoverImageStackNode->GetRenderContext();
1252     CHECK_NULL_RETURN(stackContext, false);
1253     stackContext->UpdateOpacity(1.0);
1254 
1255     wrapperPattern->CallMenuAboutToAppearCallback();
1256     wrapperPattern->CheckAndShowAnimation();
1257     return true;
1258 }
1259 
SetHasCustomOutline(const RefPtr<FrameNode> & menuWrapperNode,const RefPtr<FrameNode> & menuNode,const MenuParam & menuParam)1260 void MenuView::SetHasCustomOutline(
1261     const RefPtr<FrameNode>& menuWrapperNode, const RefPtr<FrameNode>& menuNode, const MenuParam& menuParam)
1262 {
1263     CHECK_NULL_VOID(menuWrapperNode);
1264     CHECK_NULL_VOID(menuNode);
1265     auto menuWrapperPattern = menuWrapperNode->GetPattern<MenuWrapperPattern>();
1266     CHECK_NULL_VOID(menuWrapperPattern);
1267     if (!menuParam.outlineWidth.has_value() || menuParam.outlineWidth->leftDimen->IsNegative() ||
1268         menuParam.outlineWidth->rightDimen->IsNegative() || menuParam.outlineWidth->topDimen->IsNegative() ||
1269         menuParam.outlineWidth->bottomDimen->IsNegative()) {
1270         menuWrapperPattern->SetHasCustomOutlineWidth(false);
1271         menuWrapperPattern->SetHasCustomOutlineColor(false);
1272         return;
1273     }
1274     menuWrapperPattern->SetHasCustomOutlineWidth(true);
1275     menuWrapperPattern->SetHasCustomOutlineColor(true);
1276 }
1277 
CalcHoverScaleInfo(const RefPtr<FrameNode> & menuNode)1278 void MenuView::CalcHoverScaleInfo(const RefPtr<FrameNode>& menuNode)
1279 {
1280     CHECK_NULL_VOID(menuNode);
1281     auto menuPattern = menuNode->GetPattern<MenuPattern>();
1282     CHECK_NULL_VOID(menuPattern);
1283     CHECK_NULL_VOID(menuPattern->GetIsShowHoverImage());
1284 
1285     auto wrapperNode = menuPattern->GetMenuWrapper();
1286     CHECK_NULL_VOID(wrapperNode);
1287     auto menuWrapperPattern = wrapperNode->GetPattern<MenuWrapperPattern>();
1288     CHECK_NULL_VOID(menuWrapperPattern);
1289     auto imageNode = menuWrapperPattern->GetHoverImagePreview();
1290     CHECK_NULL_VOID(imageNode);
1291     auto imagePattern = imageNode->GetPattern<ImagePattern>();
1292     CHECK_NULL_VOID(imagePattern);
1293 
1294     auto preview = menuWrapperPattern->GetPreview();
1295     CHECK_NULL_VOID(preview);
1296     auto previewGeometryNode = preview->GetGeometryNode();
1297     CHECK_NULL_VOID(previewGeometryNode);
1298     auto previewPattern = preview->GetPattern<MenuPreviewPattern>();
1299     CHECK_NULL_VOID(previewPattern);
1300     auto previewSize = previewGeometryNode->GetMarginFrameSize();
1301     previewPattern->SetStackAfterScaleActualWidth(previewSize.Width());
1302     previewPattern->SetStackAfterScaleActualHeight(previewSize.Height());
1303 
1304     auto menuParam = menuWrapperPattern->GetMenuParam();
1305     auto imageRawSize = imagePattern->GetRawImageSize();
1306     SetHoverImageCustomPreviewInfo(preview, menuParam, imageRawSize.Width(), imageRawSize.Height());
1307 }
1308 
ShowPixelMapAnimation(const RefPtr<FrameNode> & menuNode)1309 void MenuView::ShowPixelMapAnimation(const RefPtr<FrameNode>& menuNode)
1310 {
1311     CHECK_NULL_VOID(menuNode);
1312     auto menuPattern = menuNode->GetPattern<MenuPattern>();
1313     CHECK_NULL_VOID(menuPattern);
1314     auto wrapperNode = menuPattern->GetMenuWrapper();
1315     CHECK_NULL_VOID(wrapperNode);
1316     auto menuWrapperPattern = wrapperNode->GetPattern<MenuWrapperPattern>();
1317     CHECK_NULL_VOID(menuWrapperPattern);
1318 
1319     auto preview = AceType::DynamicCast<FrameNode>(wrapperNode->GetChildAtIndex(1));
1320     CHECK_NULL_VOID(preview);
1321     auto imageNode = preview->GetTag() == V2::FLEX_ETS_TAG ? menuWrapperPattern->GetHoverImagePreview() : preview;
1322     CHECK_NULL_VOID(imageNode);
1323     auto imageContext = imageNode->GetRenderContext();
1324     CHECK_NULL_VOID(imageContext);
1325     imageContext->SetClipToBounds(true);
1326 
1327     auto pipelineContext = menuNode->GetContext();
1328     CHECK_NULL_VOID(pipelineContext);
1329     auto menuTheme = pipelineContext->GetTheme<NG::MenuTheme>();
1330     CHECK_NULL_VOID(menuTheme);
1331 
1332     auto isShowHoverImage = menuPattern->GetIsShowHoverImage();
1333     if (menuWrapperPattern->HasPreviewTransitionEffect()) {
1334         auto layoutProperty = imageNode->GetLayoutProperty();
1335         layoutProperty->UpdateVisibility(VisibleType::VISIBLE, true);
1336     }
1337     if (isShowHoverImage) {
1338         auto hoverImageStackNode = menuWrapperPattern->GetHoverImageStackNode();
1339         auto previewNode = menuWrapperPattern->GetHoverImageCustomPreview();
1340         ShowHoverImageAnimationProc(hoverImageStackNode, previewNode, imageContext, menuWrapperPattern);
1341     } else {
1342         ShowPixelMapScaleAnimationProc(menuTheme, imageNode, menuPattern);
1343     }
1344     ShowBorderRadiusAndShadowAnimation(menuTheme, imageNode, isShowHoverImage);
1345 }
1346 
GetMenuPixelMap(const RefPtr<FrameNode> & targetNode,const MenuParam & menuParam,const RefPtr<FrameNode> & wrapperNode)1347 void MenuView::GetMenuPixelMap(
1348     const RefPtr<FrameNode>& targetNode, const MenuParam& menuParam, const RefPtr<FrameNode>& wrapperNode)
1349 {
1350     CHECK_NULL_VOID(targetNode);
1351     CHECK_NULL_VOID(wrapperNode);
1352     MenuType type = MenuType::MENU;
1353     auto nodeId = ElementRegister::GetInstance()->MakeUniqueId();
1354     auto previewNode = FrameNode::CreateFrameNode(V2::MENU_PREVIEW_ETS_TAG,
1355         ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<MenuPreviewPattern>());
1356     CHECK_NULL_VOID(previewNode);
1357     auto menuNode = FrameNode::CreateFrameNode(
1358         V2::MENU_ETS_TAG, nodeId, AceType::MakeRefPtr<MenuPattern>(targetNode->GetId(), targetNode->GetTag(), type));
1359     CHECK_NULL_VOID(menuNode);
1360     ContextMenuChildMountProc(targetNode, wrapperNode, previewNode, menuNode, menuParam);
1361     MountTextNode(wrapperNode, nullptr);
1362 }
1363 
1364 // create menu with MenuElement array
Create(std::vector<OptionParam> && params,int32_t targetId,const std::string & targetTag,MenuType type,const MenuParam & menuParam)1365 RefPtr<FrameNode> MenuView::Create(std::vector<OptionParam>&& params, int32_t targetId, const std::string& targetTag,
1366     MenuType type, const MenuParam& menuParam)
1367 {
1368     auto [wrapperNode, menuNode] = CreateMenu(targetId, targetTag, type);
1369     CHECK_NULL_RETURN(wrapperNode && menuNode, nullptr);
1370     ReloadMenuParam(menuNode, menuParam);
1371     UpdateMenuBackgroundStyle(menuNode, menuParam);
1372     auto column = FrameNode::CreateFrameNode(V2::COLUMN_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
1373         AceType::MakeRefPtr<LinearLayoutPattern>(true));
1374     if (!menuParam.title.empty()) {
1375         CreateTitleNode(menuParam.title, column);
1376     }
1377     SetHasCustomRadius(wrapperNode, menuNode, menuParam);
1378     SetHasCustomOutline(wrapperNode, menuNode, menuParam);
1379     SetMenuFocusRule(menuNode);
1380     MountOptionToColumn(params, menuNode, menuParam, column);
1381     auto menuWrapperPattern = wrapperNode->GetPattern<MenuWrapperPattern>();
1382     CHECK_NULL_RETURN(menuWrapperPattern, nullptr);
1383     menuWrapperPattern->SetHoverMode(menuParam.enableHoverMode);
1384     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) && !menuParam.enableArrow.value_or(false)) {
1385         UpdateMenuBorderEffect(menuNode, wrapperNode, menuParam);
1386     }
1387     menuWrapperPattern->SetMenuParam(menuParam);
1388     auto menuProperty = menuNode->GetLayoutProperty<MenuLayoutProperty>();
1389     if (menuProperty) {
1390         menuProperty->UpdateTitle(menuParam.title);
1391         menuProperty->UpdatePositionOffset(menuParam.positionOffset);
1392         if (menuParam.placement.has_value() && !menuParam.anchorPosition.has_value()) {
1393             menuProperty->UpdateMenuPlacement(menuParam.placement.value_or(OHOS::Ace::Placement::BOTTOM));
1394         }
1395         menuProperty->UpdateShowInSubWindow(menuParam.isShowInSubWindow);
1396         if (menuParam.anchorPosition.has_value()) {
1397             menuProperty->UpdateAnchorPosition(menuParam.anchorPosition.value());
1398             if (menuParam.previewMode != MenuPreviewMode::NONE) {
1399                 menuProperty->UpdateMenuPlacement(menuParam.placement.value_or(OHOS::Ace::Placement::BOTTOM));
1400             }
1401         }
1402     }
1403     UpdateMenuPaintProperty(menuNode, menuParam, type);
1404     auto scroll = CreateMenuScroll(column);
1405     CHECK_NULL_RETURN(scroll, nullptr);
1406     scroll->MountToParent(menuNode);
1407     scroll->MarkModifyDone();
1408     menuNode->MarkModifyDone();
1409 
1410     if (menuParam.maskEnable.value_or(false)) {
1411         menuWrapperPattern->SetMenuParam(menuParam);
1412         auto targetNode = FrameNode::GetFrameNode(targetTag, targetId);
1413         SetFilter(targetNode, wrapperNode);
1414     }
1415     return wrapperNode;
1416 }
1417 
SetPreviewTransitionEffect(const RefPtr<FrameNode> & menuWrapperNode,const MenuParam & menuParam)1418 void SetPreviewTransitionEffect(const RefPtr<FrameNode> &menuWrapperNode, const MenuParam &menuParam)
1419 {
1420     TAG_LOGD(AceLogTag::ACE_DIALOG, "set menu transition effect");
1421     CHECK_NULL_VOID(menuWrapperNode);
1422     auto pattern = menuWrapperNode->GetPattern<MenuWrapperPattern>();
1423     CHECK_NULL_VOID(pattern);
1424     pattern->SetHasPreviewTransitionEffect(menuParam.hasPreviewTransitionEffect);
1425 }
1426 
SetPreviewScaleAndHoverImageScale(const RefPtr<FrameNode> & menuNode,const MenuParam & menuParam)1427 void SetPreviewScaleAndHoverImageScale(const RefPtr<FrameNode>& menuNode, const MenuParam& menuParam)
1428 {
1429     auto pattern = menuNode->GetPattern<MenuPattern>();
1430     CHECK_NULL_VOID(pattern);
1431     pattern->SetPreviewMode(menuParam.previewMode);
1432     pattern->SetPreviewBeforeAnimationScale(menuParam.previewAnimationOptions.scaleFrom);
1433     pattern->SetPreviewAfterAnimationScale(menuParam.previewAnimationOptions.scaleTo);
1434     pattern->SetIsShowHoverImage(menuParam.isShowHoverImage);
1435 }
1436 
CustomPreviewParentNodeCreate(const RefPtr<FrameNode> & stackNode,const RefPtr<FrameNode> & posNode,const RefPtr<FrameNode> & wrapperNode,const RefPtr<FrameNode> & previewNode)1437 void MenuView::CustomPreviewParentNodeCreate(const RefPtr<FrameNode>& stackNode, const RefPtr<FrameNode>& posNode,
1438     const RefPtr<FrameNode>& wrapperNode, const RefPtr<FrameNode>& previewNode)
1439 {
1440     CHECK_NULL_VOID(previewNode);
1441     auto previewPattern = previewNode->GetPattern<MenuPreviewPattern>();
1442     CHECK_NULL_VOID(previewPattern);
1443     auto previewWidth = previewPattern->GetCustomPreviewWidth();
1444     auto previewHeight = previewPattern->GetCustomPreviewHeight();
1445     CHECK_NULL_VOID(stackNode);
1446     CalcSize maxSize = { CalcLength(previewWidth), CalcLength(previewHeight) };
1447 
1448     CHECK_NULL_VOID(posNode);
1449     UpdateContainerIdealSizeConstraint(posNode, maxSize);
1450     auto posProps = posNode->GetLayoutProperty<FlexLayoutProperty>();
1451     CHECK_NULL_VOID(posProps);
1452     posProps->UpdateMainAxisAlign(FlexAlign::CENTER);
1453     posProps->UpdateCrossAxisAlign(FlexAlign::CENTER);
1454     stackNode->MountToParent(posNode);
1455     stackNode->MarkModifyDone();
1456 
1457     CHECK_NULL_VOID(wrapperNode);
1458     posNode->MountToParent(wrapperNode);
1459     posNode->MarkModifyDone();
1460 }
1461 
1462 // contextmenu mount info proc
ContextMenuChildMountProc(const RefPtr<FrameNode> & targetNode,const RefPtr<FrameNode> & wrapperNode,const RefPtr<FrameNode> & previewNode,const RefPtr<FrameNode> & menuNode,const MenuParam & menuParam)1463 void MenuView::ContextMenuChildMountProc(const RefPtr<FrameNode>& targetNode, const RefPtr<FrameNode>& wrapperNode,
1464     const RefPtr<FrameNode>& previewNode, const RefPtr<FrameNode>& menuNode, const MenuParam& menuParam)
1465 {
1466     // stack to put image and custom preview and control visible area when hoverImage api is using
1467     auto hoverImageStackNode = FrameNode::GetOrCreateFrameNode(V2::STACK_ETS_TAG,
1468         ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<StackPattern>(); });
1469     CHECK_NULL_VOID(hoverImageStackNode);
1470 
1471     // flex  to control visible area position
1472     auto hoverImagePosNode = FrameNode::CreateFrameNode(V2::FLEX_ETS_TAG,
1473         ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<FlexLayoutPattern>(false));
1474     CHECK_NULL_VOID(hoverImagePosNode);
1475 
1476     if (menuParam.isShowHoverImage) {
1477         CustomPreviewParentNodeCreate(hoverImageStackNode, hoverImagePosNode, wrapperNode, previewNode);
1478         CHECK_NULL_VOID(wrapperNode);
1479         auto menuWrapperPattern = wrapperNode->GetPattern<MenuWrapperPattern>();
1480         CHECK_NULL_VOID(menuWrapperPattern);
1481         menuWrapperPattern->SetIsShowHoverImage(menuParam.isShowHoverImage);
1482         menuWrapperPattern->SetHoverScaleInterruption(menuParam.hoverScaleInterruption);
1483         auto previewRenderContext = previewNode->GetRenderContext();
1484         CHECK_NULL_VOID(previewRenderContext);
1485         previewRenderContext->UpdateOpacity(0.0);
1486     }
1487 
1488     if (menuNode) {
1489         SetPreviewInfoToMenu(targetNode, wrapperNode, hoverImageStackNode, previewNode, menuParam);
1490     }
1491 
1492     if (menuParam.previewMode == MenuPreviewMode::CUSTOM) {
1493         previewNode->MountToParent(menuParam.isShowHoverImage ? hoverImageStackNode : wrapperNode);
1494         previewNode->MarkModifyDone();
1495     }
1496 }
1497 
1498 // create menu with custom node from a builder
Create(const RefPtr<UINode> & customNode,int32_t targetId,const std::string & targetTag,const MenuParam & menuParam,bool withWrapper,const RefPtr<UINode> & previewCustomNode)1499 RefPtr<FrameNode> MenuView::Create(const RefPtr<UINode>& customNode, int32_t targetId, const std::string& targetTag,
1500     const MenuParam& menuParam, bool withWrapper, const RefPtr<UINode>& previewCustomNode)
1501 {
1502     auto type = menuParam.type;
1503     auto [wrapperNode, menuNode] = CreateMenu(targetId, targetTag, type);
1504     CHECK_NULL_RETURN(wrapperNode && menuNode, nullptr);
1505     // create previewNode
1506     auto previewNode = FrameNode::CreateFrameNode(V2::MENU_PREVIEW_ETS_TAG,
1507         ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<MenuPreviewPattern>());
1508     CHECK_NULL_RETURN(previewNode, nullptr);
1509     auto menuWrapperPattern = wrapperNode->GetPattern<MenuWrapperPattern>();
1510     CHECK_NULL_RETURN(menuWrapperPattern, nullptr);
1511     ReloadMenuParam(menuNode, menuParam);
1512     menuWrapperPattern->SetMenuParam(menuParam);
1513     menuWrapperPattern->SetHoverMode(menuParam.enableHoverMode);
1514 
1515     CustomPreviewNodeProc(previewNode, menuParam, previewCustomNode);
1516     UpdateMenuBackgroundStyle(menuNode, menuParam);
1517     SetPreviewTransitionEffect(wrapperNode, menuParam);
1518     SetHasCustomRadius(wrapperNode, menuNode, menuParam);
1519     SetHasCustomOutline(wrapperNode, menuNode, menuParam);
1520     SetMenuFocusRule(menuNode);
1521 
1522     SetPreviewScaleAndHoverImageScale(menuNode, menuParam);
1523     // put custom node in a scroll to limit its height
1524     auto scroll = CreateMenuScroll(customNode);
1525     CHECK_NULL_RETURN(scroll, nullptr);
1526     MountScrollToMenu(customNode, scroll, type, menuNode);
1527     UpdateMenuProperties(wrapperNode, menuNode, menuParam, type);
1528 
1529     if (type == MenuType::SUB_MENU || type == MenuType::SELECT_OVERLAY_SUB_MENU || !withWrapper) {
1530         wrapperNode->RemoveChild(menuNode);
1531         wrapperNode.Reset();
1532         return menuNode;
1533     }
1534     if (type == MenuType::CONTEXT_MENU) {
1535         auto targetNode = FrameNode::GetFrameNode(targetTag, targetId);
1536         ContextMenuChildMountProc(targetNode, wrapperNode, previewNode, menuNode, menuParam);
1537         MountTextNode(wrapperNode, previewCustomNode);
1538     } else if (type == MenuType::MENU && menuParam.maskEnable.value_or(false)) {
1539         auto targetNode = FrameNode::GetFrameNode(targetTag, targetId);
1540         SetFilter(targetNode, wrapperNode);
1541     }
1542     return wrapperNode;
1543 }
1544 
ReloadMenuParam(const RefPtr<FrameNode> & menuNode,const MenuParam & menuParam)1545 void MenuView::ReloadMenuParam(const RefPtr<FrameNode>& menuNode, const MenuParam& menuParam)
1546 {
1547     CHECK_NULL_VOID(menuNode);
1548     auto pipeline = menuNode->GetContext();
1549     CHECK_NULL_VOID(pipeline);
1550     auto colorMode = pipeline->GetColorMode();
1551     auto isCurDarkMode = colorMode == ColorMode::DARK;
1552     MenuParam& menuParamValue = const_cast<MenuParam&>(menuParam);
1553     if (SystemProperties::ConfigChangePerform() && menuParam.isDarkMode != isCurDarkMode && !menuParam.isWithTheme) {
1554         //Because the Menu is created outside the light/dark mode switching process,
1555         //it is necessary to manually set the reloading state to trigger the color inversion process.
1556         bool isReloading = ResourceParseUtils::IsReloading();
1557         ResourceParseUtils::SetIsReloading(true);
1558         menuParamValue.ReloadResources();
1559         if (menuParamValue.borderRadius) {
1560             menuParamValue.borderRadius->ReloadResources();
1561         }
1562         if (menuParamValue.previewBorderRadius) {
1563             menuParamValue.previewBorderRadius->ReloadResources();
1564         }
1565         if (menuParamValue.outlineColor) {
1566             menuParamValue.outlineColor->ReloadResources();
1567         }
1568         if (menuParamValue.outlineWidth) {
1569             menuParamValue.outlineWidth->ReloadResources();
1570         }
1571         menuParamValue.isDarkMode = !menuParamValue.isDarkMode;
1572         ResourceParseUtils::SetIsReloading(isReloading);
1573     }
1574 }
1575 
UpdateMenuParam(const RefPtr<FrameNode> & wrapperNode,const RefPtr<FrameNode> & menuNode,const MenuParam & menuParam)1576 void MenuView::UpdateMenuParam(
1577     const RefPtr<FrameNode>& wrapperNode, const RefPtr<FrameNode>& menuNode, const MenuParam& menuParam)
1578 {
1579     auto menuWrapperPattern = wrapperNode->GetPattern<MenuWrapperPattern>();
1580     CHECK_NULL_VOID(menuWrapperPattern);
1581     menuWrapperPattern->SetHoverMode(menuParam.enableHoverMode);
1582     auto menuPattern = menuNode->GetPattern<MenuPattern>();
1583     CHECK_NULL_VOID(menuPattern);
1584     UpdateMenuBackgroundStyle(menuNode, menuParam);
1585     SetPreviewTransitionEffect(wrapperNode, menuParam);
1586     SetHasCustomRadius(wrapperNode, menuNode, menuParam);
1587     UpdateMenuMaskType(wrapperNode);
1588 }
1589 
UpdateMenuProperties(const RefPtr<FrameNode> & wrapperNode,const RefPtr<FrameNode> & menuNode,const MenuParam & menuParam,const MenuType & type)1590 void MenuView::UpdateMenuProperties(const RefPtr<FrameNode>& wrapperNode, const RefPtr<FrameNode>& menuNode,
1591     const MenuParam& menuParam, const MenuType& type)
1592 {
1593     CHECK_NULL_VOID(menuNode);
1594     CHECK_NULL_VOID(wrapperNode);
1595     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) && !menuParam.enableArrow.value_or(false)) {
1596         UpdateMenuBorderEffect(menuNode, wrapperNode, menuParam);
1597     } else {
1598         UpdateMenuOutlineWithArrow(menuNode, wrapperNode, menuParam);
1599     }
1600     menuNode->MarkModifyDone();
1601 
1602     auto menuProperty = menuNode->GetLayoutProperty<MenuLayoutProperty>();
1603     if (menuProperty) {
1604         menuProperty->UpdateTitle(menuParam.title);
1605         menuProperty->UpdatePositionOffset(menuParam.positionOffset);
1606         if (menuParam.placement.has_value() && !menuParam.anchorPosition.has_value()) {
1607             menuProperty->UpdateMenuPlacement(menuParam.placement.value());
1608         }
1609         menuProperty->UpdateShowInSubWindow(menuParam.isShowInSubWindow);
1610         if (menuParam.anchorPosition.has_value()) {
1611             menuProperty->UpdateAnchorPosition(menuParam.anchorPosition.value());
1612             if (menuParam.placement.has_value() && menuParam.previewMode != MenuPreviewMode::NONE) {
1613                 menuProperty->UpdateMenuPlacement(menuParam.placement.value());
1614             }
1615         }
1616     }
1617     UpdateMenuPaintProperty(menuNode, menuParam, type);
1618 }
1619 
UpdateMenuPaintProperty(const RefPtr<FrameNode> & menuNode,const MenuParam & menuParam,const MenuType & type)1620 void MenuView::UpdateMenuPaintProperty(
1621     const RefPtr<FrameNode>& menuNode, const MenuParam& menuParam, const MenuType& type)
1622 {
1623     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1624         if (!(type == MenuType::CONTEXT_MENU || type == MenuType::MENU)) {
1625             return;
1626         }
1627     } else {
1628         if (!(type == MenuType::CONTEXT_MENU)) {
1629             return;
1630         }
1631     }
1632 
1633     auto paintProperty = menuNode->GetPaintProperty<MenuPaintProperty>();
1634     CHECK_NULL_VOID(paintProperty);
1635     paintProperty->UpdateEnableArrow(menuParam.enableArrow.value_or(false));
1636     paintProperty->UpdateArrowOffset(menuParam.arrowOffset.value_or(Dimension(0)));
1637 }
1638 
Create(const std::vector<SelectParam> & params,int32_t targetId,const std::string & targetTag,bool autoWrapFlag)1639 RefPtr<FrameNode> MenuView::Create(
1640     const std::vector<SelectParam>& params, int32_t targetId, const std::string& targetTag, bool autoWrapFlag)
1641 {
1642     auto [wrapperNode, menuNode] = CreateMenu(targetId, targetTag);
1643     auto column = FrameNode::CreateFrameNode(V2::COLUMN_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
1644         AceType::MakeRefPtr<LinearLayoutPattern>(true));
1645     CHECK_NULL_RETURN(wrapperNode && menuNode, nullptr);
1646     SetMenuFocusRule(menuNode);
1647     auto menuPattern = menuNode->GetPattern<MenuPattern>();
1648     CHECK_NULL_RETURN(menuPattern, nullptr);
1649     auto menuProperty = menuNode->GetLayoutProperty<MenuLayoutProperty>();
1650     CHECK_NULL_RETURN(menuProperty, nullptr);
1651     menuProperty->UpdateShowInSubWindow(false);
1652     for (size_t i = 0; i < params.size(); ++i) {
1653         auto optionNode = CreateSelectOption(params[i], i, autoWrapFlag);
1654         auto optionPattern = optionNode->GetPattern<MenuItemPattern>();
1655         CHECK_NULL_RETURN(optionPattern, nullptr);
1656         optionPattern->SetIsSelectOption(true);
1657         menuPattern->AddOptionNode(optionNode);
1658         auto menuWeak = AceType::WeakClaim(AceType::RawPtr(menuNode));
1659         OptionKeepMenu(optionNode, menuWeak);
1660         // first node never paints divider
1661         if (i == 0) {
1662             auto props = optionNode->GetPaintProperty<MenuItemPaintProperty>();
1663             props->UpdateNeedDivider(false);
1664             auto focusHub = optionNode->GetOrCreateFocusHub();
1665             CHECK_NULL_RETURN(focusHub, nullptr);
1666             focusHub->SetIsDefaultFocus(true);
1667         }
1668         optionNode->MarkModifyDone();
1669         optionNode->MountToParent(column);
1670     }
1671     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
1672         MenuParam menuParam;
1673         UpdateMenuBorderEffect(menuNode, wrapperNode, menuParam);
1674     }
1675     auto scroll = CreateMenuScroll(column);
1676     CHECK_NULL_RETURN(scroll, nullptr);
1677     auto scrollPattern = scroll->GetPattern<ScrollPattern>();
1678     CHECK_NULL_RETURN(scrollPattern, nullptr);
1679     scrollPattern->SetIsSelectScroll(true);
1680     scroll->MountToParent(menuNode);
1681     scroll->MarkModifyDone();
1682     menuPattern->SetIsSelectMenu(true);
1683     menuNode->MarkModifyDone();
1684     return wrapperNode;
1685 }
1686 
CreateEffectOption(Dimension radius,double saturation,double brightness,Color color)1687 EffectOption CreateEffectOption(Dimension radius, double saturation, double brightness, Color color)
1688 {
1689     EffectOption option;
1690     option.radius = radius;
1691     option.saturation = saturation;
1692     option.brightness = brightness;
1693     option.color = color;
1694     return option;
1695 }
1696 
UpdateMenuBackgroundEffect(const RefPtr<FrameNode> & menuNode)1697 void MenuView::UpdateMenuBackgroundEffect(const RefPtr<FrameNode>& menuNode)
1698 {
1699     CHECK_NULL_VOID(menuNode);
1700     auto pipeLineContext = menuNode->GetContextWithCheck();
1701     CHECK_NULL_VOID(pipeLineContext);
1702     auto menuTheme = pipeLineContext->GetTheme<NG::MenuTheme>();
1703     CHECK_NULL_VOID(menuTheme);
1704     if (menuTheme->GetBgBlurEffectEnable()) {
1705         auto renderContext = menuNode->GetRenderContext();
1706         CHECK_NULL_VOID(renderContext);
1707         auto saturation = menuTheme->GetBgEffectSaturation();
1708         auto brightness = menuTheme->GetBgEffectBrightness();
1709         auto radius = menuTheme->GetBgEffectRadius();
1710         auto color = menuTheme->GetBgEffectColor();
1711         EffectOption option = CreateEffectOption(radius, saturation, brightness, color);
1712         renderContext->UpdateBackgroundColor(Color::TRANSPARENT);
1713         renderContext->UpdateBackgroundEffect(option);
1714     }
1715 }
1716 
UpdateMenuBorderEffect(const RefPtr<FrameNode> & menuNode,const RefPtr<FrameNode> & wrapperNode,const MenuParam & menuParam)1717 void MenuView::UpdateMenuBorderEffect(
1718     const RefPtr<FrameNode>& menuNode, const RefPtr<FrameNode>& wrapperNode, const MenuParam& menuParam)
1719 {
1720     CHECK_NULL_VOID(wrapperNode);
1721     auto menuWrapperPattern = wrapperNode->GetPattern<MenuWrapperPattern>();
1722     CHECK_NULL_VOID(menuWrapperPattern);
1723     CHECK_NULL_VOID(menuNode);
1724     auto pipeLineContext = menuNode->GetContextWithCheck();
1725     CHECK_NULL_VOID(pipeLineContext);
1726     auto menuTheme = pipeLineContext->GetTheme<NG::MenuTheme>();
1727     CHECK_NULL_VOID(menuTheme);
1728     if (menuTheme->GetDoubleBorderEnable() || menuWrapperPattern->GetHasCustomOutlineWidth()) {
1729         auto renderContext = menuNode->GetRenderContext();
1730         CHECK_NULL_VOID(renderContext);
1731         BorderStyleProperty styleProp;
1732         styleProp.SetBorderStyle(BorderStyle::SOLID);
1733         auto theme = pipeLineContext->GetTheme<SelectTheme>();
1734         CHECK_NULL_VOID(theme);
1735         BorderRadiusProperty outerRadiusProp;
1736         outerRadiusProp.SetRadius(Dimension(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) ?
1737             theme->GetMenuDefaultRadius() : theme->GetMenuBorderRadius()));
1738         if (menuWrapperPattern->GetHasCustomOutlineWidth()) {
1739             renderContext->SetOuterBorderWidth(menuParam.outlineWidth.value_or(BorderWidthProperty()));
1740             renderContext->SetOuterBorderColor(menuParam.outlineColor.value_or(BorderColorProperty()));
1741         } else {
1742             BorderWidthProperty outerWidthProp;
1743             outerWidthProp.SetBorderWidth(Dimension(menuTheme->GetOuterBorderWidth()));
1744             renderContext->SetOuterBorderWidth(outerWidthProp);
1745             BorderColorProperty outerColorProp;
1746             outerColorProp.SetColor(menuTheme->GetOuterBorderColor());
1747             renderContext->SetOuterBorderColor(outerColorProp);
1748         }
1749         renderContext->SetOuterBorderStyle(styleProp);
1750         renderContext->UpdateOuterBorderRadius(outerRadiusProp);
1751         BorderColorProperty innerColorProp;
1752         innerColorProp.SetColor(menuTheme->GetInnerBorderColor());
1753         BorderRadiusProperty innerRadiusProp;
1754         innerRadiusProp.SetRadius(Dimension(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) ?
1755             theme->GetMenuDefaultRadius() : theme->GetMenuBorderRadius()));
1756         BorderWidthProperty innerWidthProp;
1757         innerWidthProp.SetBorderWidth(Dimension(menuTheme->GetInnerBorderWidth()));
1758         renderContext->SetBorderStyle(styleProp);
1759         renderContext->SetBorderColor(innerColorProp);
1760         renderContext->UpdateBorderRadius(innerRadiusProp);
1761         renderContext->SetBorderWidth(innerWidthProp);
1762     }
1763 }
1764 
UpdateMenuBackgroundStyle(const RefPtr<FrameNode> & menuNode,const MenuParam & menuParam)1765 void MenuView::UpdateMenuBackgroundStyle(const RefPtr<FrameNode>& menuNode, const MenuParam& menuParam)
1766 {
1767     auto menuNodeRenderContext = menuNode->GetRenderContext();
1768     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) &&
1769         menuNodeRenderContext->IsUniRenderEnabled()) {
1770         auto pipeLineContext = menuNode->GetContextWithCheck();
1771         CHECK_NULL_VOID(pipeLineContext);
1772         auto selectTheme = pipeLineContext->GetTheme<SelectTheme>();
1773         CHECK_NULL_VOID(selectTheme);
1774         BlurStyleOption styleOption;
1775         if (menuParam.blurStyleOption.has_value()) {
1776             styleOption = menuParam.blurStyleOption.value();
1777             if (styleOption.policy == BlurStyleActivePolicy::FOLLOWS_WINDOW_ACTIVE_STATE) {
1778                 pipeLineContext->AddWindowFocusChangedCallback(menuNode->GetId());
1779             } else {
1780                 pipeLineContext->RemoveWindowFocusChangedCallback(menuNode->GetId());
1781             }
1782         }
1783         Color color;
1784         if (selectTheme->GetMenuBlendBgColor()) {
1785             styleOption.blurStyle = static_cast<BlurStyle>(
1786                 menuParam.backgroundBlurStyle.value_or(selectTheme->GetMenuNormalBackgroundBlurStyle()));
1787             color = menuParam.backgroundColor.value_or(selectTheme->GetBackgroundColor());
1788         } else {
1789             auto menuTheme = pipeLineContext->GetTheme<NG::MenuTheme>();
1790             CHECK_NULL_VOID(menuTheme);
1791             styleOption.blurStyle = static_cast<BlurStyle>(
1792                 menuParam.backgroundBlurStyle.value_or(menuTheme->GetMenuBackgroundBlurStyle()));
1793             color = menuParam.backgroundColor.value_or(Color::TRANSPARENT);
1794         }
1795         if (menuParam.blurStyleOption.has_value() && menuNodeRenderContext->GetBackgroundEffect().has_value()) {
1796             menuNodeRenderContext->UpdateBackgroundEffect(std::nullopt);
1797         }
1798         menuNodeRenderContext->UpdateBackBlurStyle(styleOption);
1799         menuNodeRenderContext->UpdateBackgroundColor(color);
1800         if (menuParam.effectOption.has_value()) {
1801             if (menuParam.effectOption->policy == BlurStyleActivePolicy::FOLLOWS_WINDOW_ACTIVE_STATE) {
1802                 pipeLineContext->AddWindowFocusChangedCallback(menuNode->GetId());
1803             } else {
1804                 pipeLineContext->RemoveWindowFocusChangedCallback(menuNode->GetId());
1805             }
1806             if (menuNodeRenderContext->GetBackBlurStyle().has_value()) {
1807                 menuNodeRenderContext->UpdateBackBlurStyle(std::nullopt);
1808             }
1809             menuNodeRenderContext->UpdateBackgroundEffect(menuParam.effectOption.value());
1810         }
1811         UpdateMenuBackgroundStyleOption(menuNode, menuParam);
1812     } else {
1813         UpdateMenuBackgroundStyleSub(menuNode, menuParam);
1814     }
1815 }
1816 
UpdateMenuBackgroundStyleSub(const RefPtr<FrameNode> & menuNode,const MenuParam & menuParam)1817 void MenuView::UpdateMenuBackgroundStyleSub(const RefPtr<FrameNode>& menuNode, const MenuParam& menuParam)
1818 {
1819     auto menuNodeRenderContext = menuNode->GetRenderContext();
1820     auto pipeLineContext = menuNode->GetContextWithCheck();
1821     CHECK_NULL_VOID(pipeLineContext);
1822     auto selectTheme = pipeLineContext->GetTheme<SelectTheme>();
1823     CHECK_NULL_VOID(selectTheme);
1824     menuNodeRenderContext->UpdateBackgroundColor(
1825         menuParam.backgroundColor.value_or(selectTheme->GetBackgroundColor()));
1826     UpdateMenuBackgroundStyleOption(menuNode, menuParam);
1827 }
1828 
NeedAgingUpdateNode(const RefPtr<FrameNode> & optionNode)1829 void MenuView::NeedAgingUpdateNode(const RefPtr<FrameNode>& optionNode)
1830 {
1831     CHECK_NULL_VOID(optionNode);
1832     auto pipeline = optionNode->GetContextWithCheck();
1833     CHECK_NULL_VOID(pipeline);
1834     auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
1835     CHECK_NULL_VOID(menuTheme);
1836     auto fontScale = pipeline->GetFontScale();
1837     if (NearEqual(fontScale, menuTheme->GetBigFontSizeScale()) ||
1838         NearEqual(fontScale, menuTheme->GetLargeFontSizeScale()) ||
1839         NearEqual(fontScale, menuTheme->GetMaxFontSizeScale())) {
1840         auto optionPattern = optionNode->GetPattern<MenuItemPattern>();
1841         CHECK_NULL_VOID(optionPattern);
1842         auto textNode = AceType::DynamicCast<FrameNode>(optionPattern->GetTextNode());
1843         CHECK_NULL_VOID(textNode);
1844         auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
1845         CHECK_NULL_VOID(textLayoutProperty);
1846         textLayoutProperty->UpdateMaxLines(menuTheme->GetTextMaxLines());
1847     }
1848 }
1849 
CreateOption(bool optionsHasIcon,std::vector<OptionParam> & params,int32_t index,const RefPtr<FrameNode> & row,const RefPtr<FrameNode> & option)1850 void MenuView::CreateOption(bool optionsHasIcon, std::vector<OptionParam>& params, int32_t index,
1851     const RefPtr<FrameNode>& row, const RefPtr<FrameNode>& option)
1852 {
1853     auto pattern = option->GetPattern<MenuItemPattern>();
1854     CHECK_NULL_VOID(pattern);
1855     if (optionsHasIcon) {
1856         auto iconNode = CreateSymbol(params[index].symbol, row, nullptr, params[index].symbolUserDefinedIdealFontSize);
1857         pattern->SetIconNode(iconNode);
1858     }
1859     auto textNode = CreateText(params[index].value, row, false,
1860         params[index].isAIMenuOption || params[index].isAskCeliaOption);
1861     row->MountToParent(option);
1862     row->MarkModifyDone();
1863     pattern->SetTextNode(textNode);
1864     pattern->SetBlockClick(params[index].disableSystemClick);
1865 
1866     auto eventHub = option->GetOrCreateEventHub<MenuItemEventHub>();
1867     CHECK_NULL_VOID(eventHub);
1868     eventHub->SetMenuOnClick(params[index].action);
1869 }
1870 
CreateOption(const OptionValueInfo & value,const std::string & icon,const RefPtr<FrameNode> & row,const RefPtr<FrameNode> & option,const std::function<void ()> & onClickFunc)1871 void MenuView::CreateOption(const OptionValueInfo& value, const std::string& icon,
1872     const RefPtr<FrameNode>& row, const RefPtr<FrameNode>& option, const std::function<void()>& onClickFunc)
1873 {
1874     auto pattern = option->GetPattern<MenuItemPattern>();
1875     CHECK_NULL_VOID(pattern);
1876     if (value.optionsHasIcon) {
1877         auto iconNode = CreateIcon(icon, row);
1878         pattern->SetIconNode(iconNode);
1879         pattern->SetIcon(icon);
1880     }
1881     auto textNode = CreateText(value.content, row, false, value.isAIMenuOption);
1882     row->MountToParent(option);
1883     row->MarkModifyDone();
1884     pattern->SetTextNode(textNode);
1885 
1886     auto eventHub = option->GetOrCreateEventHub<MenuItemEventHub>();
1887     CHECK_NULL_VOID(eventHub);
1888     eventHub->SetMenuOnClick(onClickFunc);
1889 }
1890 
CreateMenuOption(bool optionsHasIcon,std::vector<OptionParam> & params,int32_t index)1891 RefPtr<FrameNode> MenuView::CreateMenuOption(bool optionsHasIcon, std::vector<OptionParam>& params, int32_t index)
1892 {
1893     auto option = Create(index);
1894     CHECK_NULL_RETURN(option, nullptr);
1895     auto row = FrameNode::CreateFrameNode(V2::ROW_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
1896         AceType::MakeRefPtr<MenuItemRowPattern>());
1897 
1898 #ifdef OHOS_PLATFORM
1899     std::string buttonPasteText = "Paste";
1900     auto pipeline = PipelineBase::GetCurrentContextSafelyWithCheck();
1901     if (pipeline) {
1902         auto theme = pipeline->GetTheme<ButtonTheme>();
1903         if (theme) {
1904             buttonPasteText = theme->GetPasteText();
1905         }
1906     }
1907     if (params[index].value == buttonPasteText) {
1908         CreatePasteButton(optionsHasIcon, option, row, params[index].action);
1909         auto accessibilityProperty = option->GetAccessibilityProperty<AccessibilityProperty>();
1910         if (accessibilityProperty) {
1911             accessibilityProperty->SetAccessibilityLevel(AccessibilityProperty::Level::NO_STR);
1912         }
1913     } else {
1914         CreateOption(optionsHasIcon, params, index, row, option);
1915     }
1916 #else
1917     CreateOption(optionsHasIcon, params, index, row, option);
1918 #endif
1919     return option;
1920 }
1921 
CreateMenuOption(const OptionValueInfo & value,const std::function<void ()> & onClickFunc,int32_t index,const std::string & icon)1922 RefPtr<FrameNode> MenuView::CreateMenuOption(const OptionValueInfo& value,
1923     const std::function<void()>& onClickFunc, int32_t index, const std::string& icon)
1924 {
1925     auto option = Create(index);
1926     CHECK_NULL_RETURN(option, nullptr);
1927     auto row = FrameNode::CreateFrameNode(V2::ROW_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
1928         AceType::MakeRefPtr<MenuItemRowPattern>());
1929 
1930 #ifdef OHOS_PLATFORM
1931     std::string buttonPasteText = "Paste";
1932     auto pipeline = PipelineBase::GetCurrentContextSafelyWithCheck();
1933     if (pipeline) {
1934         auto theme = pipeline->GetTheme<ButtonTheme>();
1935         if (theme) {
1936             buttonPasteText = theme->GetPasteText();
1937         }
1938     }
1939     if (value.content == buttonPasteText) {
1940         CreatePasteButton(value.optionsHasIcon, option, row, onClickFunc, icon);
1941         auto accessibilityProperty = option->GetAccessibilityProperty<AccessibilityProperty>();
1942         if (accessibilityProperty) {
1943             accessibilityProperty->SetAccessibilityLevel(AccessibilityProperty::Level::NO_STR);
1944         }
1945     } else {
1946         CreateOption({ .optionsHasIcon = value.optionsHasIcon,
1947                        .content = value.content, .isAIMenuOption = value.isAIMenuOption },
1948                      icon, row, option, onClickFunc);
1949     }
1950 #else
1951     CreateOption({ .optionsHasIcon = value.optionsHasIcon,
1952         .content = value.content, .isAIMenuOption = value.isAIMenuOption }, icon, row, option, onClickFunc);
1953 #endif
1954     return option;
1955 }
1956 
MountOptionToColumn(std::vector<OptionParam> & params,const RefPtr<FrameNode> & menuNode,const MenuParam & menuParam,RefPtr<FrameNode> column)1957 void MenuView::MountOptionToColumn(std::vector<OptionParam>& params, const RefPtr<FrameNode>& menuNode,
1958     const MenuParam& menuParam, RefPtr<FrameNode> column)
1959 {
1960     bool optionsHasIcon = GetHasIcon(params);
1961     bool optionsHasSymbol = GetHasSymbol(params);
1962     RefPtr<FrameNode> optionNode = nullptr;
1963     // append options to menu
1964     for (size_t i = 0; i < params.size(); ++i) {
1965         if (params[i].symbol != nullptr) {
1966             optionNode = CreateMenuOption(optionsHasSymbol, params, i);
1967         } else {
1968             optionNode = CreateMenuOption(
1969                 { .optionsHasIcon = optionsHasIcon, .content = params[i].value,
1970                   .isPasteOption = params[i].isPasteOption,
1971                   .isAIMenuOption = (params[i].isAIMenuOption || params[i].isAskCeliaOption) },
1972                 params[i].action, i, params[i].icon);
1973             if (optionNode) {
1974                 auto optionPattern = optionNode->GetPattern<MenuItemPattern>();
1975                 optionPattern->SetBlockClick(params[i].disableSystemClick);
1976             }
1977         }
1978         if (!optionNode) {
1979             continue;
1980         }
1981         NeedAgingUpdateNode(optionNode);
1982         auto menuPattern = menuNode->GetPattern<MenuPattern>();
1983         CHECK_NULL_VOID(menuPattern);
1984         menuPattern->AddOptionNode(optionNode);
1985         auto menuWeak = AceType::WeakClaim(AceType::RawPtr(menuNode));
1986         auto eventHub = optionNode->GetOrCreateEventHub<EventHub>();
1987         CHECK_NULL_VOID(eventHub);
1988         eventHub->SetEnabled(params[i].enabled);
1989         auto focusHub = optionNode->GetFocusHub();
1990         CHECK_NULL_VOID(focusHub);
1991         focusHub->SetEnabled(params[i].enabled);
1992 
1993         OptionKeepMenu(optionNode, menuWeak);
1994         // first node never paints divider
1995         auto props = optionNode->GetPaintProperty<MenuItemPaintProperty>();
1996         if (i == 0 && menuParam.title.empty()) {
1997             props->UpdateNeedDivider(false);
1998         }
1999         if (optionsHasIcon) {
2000             props->UpdateHasIcon(true);
2001         }
2002         optionNode->MountToParent(column);
2003         optionNode->MarkModifyDone();
2004     }
2005 }
2006 
CreatePasteButton(bool optionsHasIcon,const RefPtr<FrameNode> & option,const RefPtr<FrameNode> & row,const std::function<void ()> & onClickFunc,const std::string & icon)2007 void MenuView::CreatePasteButton(bool optionsHasIcon, const RefPtr<FrameNode>& option, const RefPtr<FrameNode>& row,
2008     const std::function<void()>& onClickFunc, const std::string& icon)
2009 {
2010     auto pipeline = PipelineBase::GetCurrentContextSafelyWithCheck();
2011     CHECK_NULL_VOID(pipeline);
2012     auto theme = pipeline->GetTheme<SelectTheme>();
2013     CHECK_NULL_VOID(theme);
2014     auto overlayTheme = pipeline->GetTheme<TextOverlayTheme>();
2015     CHECK_NULL_VOID(overlayTheme);
2016     RefPtr<FrameNode> pasteNode;
2017     pasteNode = PasteButtonModelNG::GetInstance()->CreateNode(static_cast<int32_t>(PasteButtonPasteDescription::PASTE),
2018         static_cast<int32_t>(PasteButtonIconStyle::ICON_NULL),
2019         static_cast<int32_t>(ButtonType::NORMAL),
2020         true,
2021         optionsHasIcon ? overlayTheme->GetPasteSymbolId() : static_cast<int32_t>(PasteButtonIconStyle::ICON_NULL));
2022     CHECK_NULL_VOID(pasteNode);
2023     auto pattern = option->GetPattern<MenuItemPattern>();
2024     CHECK_NULL_VOID(pattern);
2025     auto pasteLayoutProperty = pasteNode->GetLayoutProperty<SecurityComponentLayoutProperty>();
2026     CHECK_NULL_VOID(pasteLayoutProperty);
2027     auto pastePaintProperty = pasteNode->GetPaintProperty<SecurityComponentPaintProperty>();
2028     CHECK_NULL_VOID(pastePaintProperty);
2029 
2030     pasteLayoutProperty->UpdateFontSize(theme->GetMenuFontSize());
2031     pasteLayoutProperty->UpdateFontWeight(FontWeight::REGULAR);
2032     pastePaintProperty->UpdateFontColor(theme->GetMenuFontColor());
2033     pastePaintProperty->UpdateBackgroundColor(Color::TRANSPARENT);
2034     pasteLayoutProperty->UpdateBackgroundBorderRadius(BorderRadiusProperty(theme->GetInnerBorderRadius()));
2035     pasteLayoutProperty->UpdateIconSize(theme->GetIconSideLength());
2036     pastePaintProperty->UpdateIconColor(theme->GetMenuIconColor());
2037     pasteLayoutProperty->UpdateStateEffect(false);
2038     pasteLayoutProperty->UpdateHoverEffect(HoverEffectType::NONE);
2039     if (optionsHasIcon) {
2040         pasteLayoutProperty->UpdateTextIconSpace(theme->GetIconContentPadding());
2041     }
2042     pasteNode->MountToParent(row);
2043     pasteNode->MarkModifyDone();
2044     row->MountToParent(option);
2045     row->MarkModifyDone();
2046     auto eventHub = option->GetOrCreateEventHub<MenuItemEventHub>();
2047     CHECK_NULL_VOID(eventHub);
2048     pasteNode->GetOrCreateGestureEventHub()->SetUserOnClick([onClickFunc](GestureEvent& info) {
2049         if (!PasteButtonModelNG::GetInstance()->IsClickResultSuccess(info)) {
2050             return;
2051         }
2052         if (onClickFunc) {
2053             onClickFunc();
2054         }
2055     });
2056     pattern->SetPasteButton(pasteNode);
2057 }
2058 
CreateSymbol(const std::function<void (WeakPtr<NG::FrameNode>)> & symbolApply,const RefPtr<FrameNode> & parent,const RefPtr<FrameNode> & child,const std::optional<Dimension> & symbolUserDefinedIdealFontSize)2059 RefPtr<FrameNode> MenuView::CreateSymbol(const std::function<void(WeakPtr<NG::FrameNode>)>& symbolApply,
2060     const RefPtr<FrameNode>& parent, const RefPtr<FrameNode>& child,
2061     const std::optional<Dimension>& symbolUserDefinedIdealFontSize)
2062 {
2063     auto iconNode = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
2064         []() { return AceType::MakeRefPtr<TextPattern>(); });
2065     CHECK_NULL_RETURN(iconNode, nullptr);
2066     auto props = iconNode->GetLayoutProperty<TextLayoutProperty>();
2067     CHECK_NULL_RETURN(props, nullptr);
2068     CHECK_NULL_RETURN(parent, nullptr);
2069     auto pipeline = parent->GetContext();
2070     CHECK_NULL_RETURN(pipeline, nullptr);
2071     auto theme = pipeline->GetTheme<SelectTheme>();
2072     CHECK_NULL_RETURN(theme, nullptr);
2073     props->UpdateFontSize(theme->GetEndIconWidth());
2074     props->UpdateSymbolColorList({theme->GetMenuIconColor()});
2075     props->UpdateAlignment(Alignment::CENTER_LEFT);
2076     MarginProperty margin;
2077     margin.right = CalcLength(theme->GetIconContentPadding());
2078     props->UpdateMargin(margin);
2079     if (symbolApply != nullptr) {
2080         symbolApply(AccessibilityManager::WeakClaim(AccessibilityManager::RawPtr(iconNode)));
2081     }
2082     if (symbolUserDefinedIdealFontSize.has_value()) {
2083         props->UpdateFontSize(symbolUserDefinedIdealFontSize.value());
2084     }
2085     if (child) {
2086         parent->ReplaceChild(child, iconNode);
2087     } else {
2088         iconNode->MountToParent(parent, 0);
2089     }
2090     iconNode->MarkModifyDone();
2091     return iconNode;
2092 }
2093 
CreateText(const std::string & value,const RefPtr<FrameNode> & parent,bool autoWrapFlag,bool isAIMenuOption)2094 RefPtr<FrameNode> MenuView::CreateText(const std::string& value, const RefPtr<FrameNode>& parent,
2095                                        bool autoWrapFlag, bool isAIMenuOption)
2096 {
2097     // create child text node
2098     auto textId = ElementRegister::GetInstance()->MakeUniqueId();
2099     auto textNode = FrameNode::CreateFrameNode(V2::TEXT_ETS_TAG, textId, AceType::MakeRefPtr<TextPattern>());
2100     CHECK_NULL_RETURN(textNode, nullptr);
2101 
2102     auto textProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
2103     CHECK_NULL_RETURN(textProperty, nullptr);
2104 
2105     CHECK_NULL_RETURN(parent, nullptr);
2106     auto pipeline = parent->GetContext();
2107     CHECK_NULL_RETURN(pipeline, nullptr);
2108     auto theme = pipeline->GetTheme<SelectTheme>();
2109     CHECK_NULL_RETURN(theme, nullptr);
2110     TAG_LOGI(AceLogTag::ACE_MENU, "MenuView::CreateText autoWrapFlag: %{public}d", autoWrapFlag);
2111     if (!autoWrapFlag) {
2112         textProperty->UpdateMaxLines(1);
2113         textProperty->UpdateTextOverflow(TextOverflow::ELLIPSIS);
2114     } else {
2115         textProperty->UpdateMaxLines(std::numeric_limits<int32_t>::max());
2116     }
2117     textProperty->UpdateFontSize(theme->GetMenuFontSize());
2118     textProperty->UpdateFontWeight(FontWeight::REGULAR);
2119     textProperty->UpdateTextColor(theme->GetMenuFontColor());
2120     // set default foregroundColor
2121     auto textRenderContext = textNode->GetRenderContext();
2122     textRenderContext->UpdateForegroundColor(theme->GetMenuFontColor());
2123     textProperty->UpdateContent(value);
2124     auto padding = theme->GetOptionContentNormalLeftRightPadding();
2125     PaddingProperty textPadding;
2126     textPadding.left = CalcLength(padding);
2127     textPadding.right = CalcLength(padding);
2128     textProperty->UpdatePadding(textPadding);
2129     auto layoutDirection = textProperty->GetNonAutoLayoutDirection();
2130     auto IsRightToLeft = layoutDirection == TextDirection::RTL;
2131     auto textAlign = static_cast<TextAlign>(theme->GetOptionContentNormalAlign());
2132     auto convertValue = ConvertTxtTextAlign(IsRightToLeft, textAlign);
2133     textProperty->UpdateAlignment(convertValue);
2134     textProperty->UpdateWordBreak(theme->GetWordBreak());
2135     auto textOverlayTheme = pipeline->GetTheme<TextOverlayTheme>();
2136     if (isAIMenuOption && textOverlayTheme) {
2137         auto textStyle = theme->GetOptionTextStyle();
2138         auto textSize = MeasureUtil::MeasureTextSize(textStyle, value);
2139         FontForegroudGradiantColor colorInfo;
2140         colorInfo.points = { NG::PointF(0, 0),
2141             NG::PointF(static_cast<float>(textSize.Width()), static_cast<float>(textSize.Height())) };
2142         colorInfo.colors = textOverlayTheme->GetAiMenuFontGradientColors();
2143         colorInfo.scalars = textOverlayTheme->GetAiMenuFontGradientScalars();
2144         textProperty->UpdateFontForegroudGradiantColor(colorInfo);
2145     }
2146     textNode->MountToParent(parent);
2147     textNode->MarkModifyDone();
2148 
2149     return textNode;
2150 }
2151 
CreateIcon(const std::string & icon,const RefPtr<FrameNode> & parent,const RefPtr<FrameNode> & child)2152 RefPtr<FrameNode> MenuView::CreateIcon(const std::string& icon, const RefPtr<FrameNode>& parent,
2153     const RefPtr<FrameNode>& child)
2154 {
2155     auto iconNode = FrameNode::CreateFrameNode(
2156         V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
2157     CHECK_NULL_RETURN(iconNode, nullptr);
2158     auto props = iconNode->GetLayoutProperty<ImageLayoutProperty>();
2159     auto pipeline = parent->GetContext();
2160     CHECK_NULL_RETURN(pipeline, nullptr);
2161     auto theme = pipeline->GetTheme<SelectTheme>();
2162     CHECK_NULL_RETURN(theme, nullptr);
2163     if (!icon.empty()) {
2164         ImageSourceInfo info(icon);
2165         props->UpdateImageSourceInfo(info);
2166     }
2167     props->UpdateUserDefinedIdealSize(
2168         CalcSize(CalcLength(theme->GetIconSideLength()), CalcLength(theme->GetIconSideLength())));
2169     props->UpdateAlignment(Alignment::CENTER_LEFT);
2170 
2171     if (child) {
2172         parent->ReplaceChild(child, iconNode);
2173     } else {
2174         iconNode->MountToParent(parent, 0);
2175     }
2176     iconNode->MarkModifyDone();
2177     return iconNode;
2178 }
2179 
CreateSelectOption(const SelectParam & param,int32_t index,bool autoWrapFlag)2180 RefPtr<FrameNode> MenuView::CreateSelectOption(const SelectParam& param, int32_t index, bool autoWrapFlag)
2181 {
2182     auto option = Create(index);
2183     auto row = FrameNode::CreateFrameNode(V2::ROW_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
2184         AceType::MakeRefPtr<MenuItemRowPattern>());
2185     row->MountToParent(option);
2186 
2187     auto pattern = option->GetPattern<MenuItemPattern>();
2188     CHECK_NULL_RETURN(pattern, option);
2189     // create icon node
2190     RefPtr<FrameNode> iconNode;
2191     if (param.symbolIcon != nullptr) {
2192         iconNode = CreateSymbol(param.symbolIcon, row);
2193     } else if (!param.icon.empty()) {
2194         iconNode = CreateIcon(param.icon, row);
2195         pattern->SetIcon(param.icon);
2196     }
2197     pattern->SetIconNode(iconNode);
2198 
2199     auto text = CreateText(param.text, row, autoWrapFlag);
2200     pattern->SetTextNode(text);
2201     return option;
2202 }
2203 
ExecuteMenuDisappearAnimation(const PreparedInfoForDrag & data)2204 void MenuView::ExecuteMenuDisappearAnimation(const PreparedInfoForDrag& data)
2205 {
2206     auto menuNode = data.menuNode;
2207     CHECK_NULL_VOID(menuNode);
2208     RefPtr<Curve> menuOpacityCurve = AceType::MakeRefPtr<InterpolatingSpring>(0.2f, 0.0f, 0.2f, 1.0f);
2209     RefPtr<Curve> menuScaleCurve = AceType::MakeRefPtr<InterpolatingSpring>(0.4f, 0.0f, 1.0f, 1.0f);
2210     AnimationOption optionOpacity;
2211     AnimationOption optionScale;
2212     optionOpacity.SetCurve(menuOpacityCurve);
2213     optionOpacity.SetDuration(MENU_ANIMATION_DURATION);
2214     optionScale.SetCurve(menuScaleCurve);
2215     optionScale.SetDuration(MENU_ANIMATION_DURATION);
2216     auto menuNodeRenderContext = menuNode->GetRenderContext();
2217     CHECK_NULL_VOID(menuNodeRenderContext);
2218     menuNodeRenderContext->UpdateOpacity(1.0f);
2219     menuNodeRenderContext->UpdateTransformScale({ 0.95f, 0.95f });
2220     AnimationUtils::Animate(
2221         optionOpacity, [menuNodeRenderContext]() { menuNodeRenderContext->UpdateOpacity(0.0f); },
2222         optionOpacity.GetOnFinishEvent(), nullptr, menuNode->GetContextRefPtr());
2223     AnimationUtils::Animate(
2224         optionScale, [menuNode, data]() { UpdateMenuNodeByAnimation(menuNode, data); }, optionScale.GetOnFinishEvent(),
2225         nullptr, menuNode->GetContextRefPtr());
2226 }
2227 
2228 // update the alignment rules according to the positional relationship between the menu and the menu preview.
UpdateMenuNodePosition(const PreparedInfoForDrag & data)2229 void MenuView::UpdateMenuNodePosition(const PreparedInfoForDrag& data)
2230 {
2231     auto relativeContainerNode = data.relativeContainerNode;
2232     CHECK_NULL_VOID(relativeContainerNode);
2233     auto stackNode = AceType::DynamicCast<FrameNode>(relativeContainerNode->GetChildByIndex(0));
2234     CHECK_NULL_VOID(stackNode);
2235     stackNode->UpdateInspectorId("__stack__");
2236     auto menuNode = data.menuNode;
2237     CHECK_NULL_VOID(menuNode);
2238     auto scrollNode = data.scrollNode;
2239     CHECK_NULL_VOID(scrollNode);
2240     auto menuUINode = scrollNode->GetParent();
2241     CHECK_NULL_VOID(menuUINode);
2242     menuUINode->RemoveChild(scrollNode);
2243     menuUINode->RebuildRenderContextTree();
2244     menuUINode->MarkDirtyNode(PROPERTY_UPDATE_BY_CHILD_REQUEST);
2245     menuNode->AddChild(scrollNode);
2246     auto menuNodeLayoutProperty = menuNode->GetLayoutProperty();
2247     CHECK_NULL_VOID(menuNodeLayoutProperty);
2248     auto biasMenuLeft = (data.menuPositionLeft - data.menuPositionRight) / HALF_NUMBER;
2249     auto biasMenuTop = (data.menuPositionTop - data.menuPositionBottom) / HALF_NUMBER;
2250     MarginProperty menuNodeMargin;
2251     std::map<AlignDirection, AlignRule> menuNodeAlignRules;
2252     std::map<std::string, AlignRule> alignMap = { { "top", { .anchor = "__stack__", .vertical = VerticalAlign::TOP } },
2253         { "center", { .anchor = "__stack__", .vertical = VerticalAlign::CENTER } },
2254         { "bottom", { .anchor = "__stack__", .vertical = VerticalAlign::BOTTOM } },
2255         { "start", { .anchor = "__stack__", .horizontal = HorizontalAlign::START } },
2256         { "middle", { .anchor = "__stack__", .horizontal = HorizontalAlign::CENTER } },
2257         { "end", { .anchor = "__stack__", .horizontal = HorizontalAlign::END } } };
2258     auto layoutDirection = menuNodeLayoutProperty->GetNonAutoLayoutDirection();
2259     if (layoutDirection == TextDirection::RTL) {
2260         alignMap["start"] = { .anchor = "__stack__", .horizontal = HorizontalAlign::END };
2261         alignMap["end"] = { .anchor = "__stack__", .horizontal = HorizontalAlign::START };
2262     }
2263     if (data.menuPosition == Placement::TOP_LEFT || data.menuPosition == Placement::BOTTOM_LEFT ||
2264         data.menuPosition == Placement::TOP || data.menuPosition == Placement::BOTTOM ||
2265         data.menuPosition == Placement::TOP_RIGHT || data.menuPosition == Placement::BOTTOM_RIGHT) {
2266         // when the menu appears at the top or bottom of the menu preview, the top or bottom of the menu needs to be
2267         // anchored to the bottom or top of the menu preview.
2268         UpdateMenuNodePositionTop(menuNodeMargin, menuNodeAlignRules, data, biasMenuLeft, alignMap);
2269         menuNodeLayoutProperty->UpdateAlignRules(menuNodeAlignRules);
2270     } else if (data.menuPosition == Placement::LEFT_TOP || data.menuPosition == Placement::RIGHT_TOP ||
2271                data.menuPosition == Placement::LEFT || data.menuPosition == Placement::RIGHT ||
2272                data.menuPosition == Placement::LEFT_BOTTOM || data.menuPosition == Placement::RIGHT_BOTTOM) {
2273         // when the menu appears on the left or right side of the menu preview, the left or right side of the menu needs
2274         // to be anchored to the left or right side of the menu preview.
2275         UpdateMenuNodePositionLeft(menuNodeMargin, menuNodeAlignRules, data, biasMenuTop, alignMap);
2276         menuNodeLayoutProperty->UpdateAlignRules(menuNodeAlignRules);
2277     }
2278     menuNodeLayoutProperty->UpdateMeasureType(MeasureType::MATCH_PARENT);
2279     menuNodeLayoutProperty->UpdateMargin(menuNodeMargin);
2280 }
2281 
Create(int32_t index)2282 RefPtr<FrameNode> MenuView::Create(int32_t index)
2283 {
2284     auto Id = ElementRegister::GetInstance()->MakeUniqueId();
2285     ACE_LAYOUT_SCOPED_TRACE("Create[%s][self:%d]", V2::OPTION_ETS_TAG, Id);
2286     auto node = FrameNode::CreateFrameNode(V2::OPTION_ETS_TAG, Id, AceType::MakeRefPtr<MenuItemPattern>(true, index));
2287     CHECK_NULL_RETURN(node, nullptr);
2288 
2289     // set border radius
2290     auto renderContext = node->GetRenderContext();
2291     CHECK_NULL_RETURN(renderContext, nullptr);
2292     auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
2293     CHECK_NULL_RETURN(pipeline, nullptr);
2294     auto theme = pipeline->GetTheme<SelectTheme>();
2295     CHECK_NULL_RETURN(theme, nullptr);
2296     BorderRadiusProperty border;
2297     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
2298         border.SetRadius(theme->GetMenuDefaultInnerRadius());
2299     } else {
2300         border.SetRadius(theme->GetInnerBorderRadius());
2301     }
2302     renderContext->UpdateBorderRadius(border);
2303 
2304     auto props = node->GetPaintProperty<MenuItemPaintProperty>();
2305     CHECK_NULL_RETURN(props, nullptr);
2306     props->UpdateHover(false);
2307     props->UpdatePress(false);
2308 
2309     auto layoutProp = node->GetLayoutProperty();
2310     CHECK_NULL_RETURN(layoutProp, nullptr);
2311     MarginProperty margin;
2312     auto verticalMargin = CalcLength(theme->GetOptionNormalTopBottomMargin());
2313     auto leftRightMargin = CalcLength(theme->GetOptionFocusedLeftRightMargin());
2314     margin.SetEdges(leftRightMargin, leftRightMargin, verticalMargin, verticalMargin);
2315     layoutProp->UpdateMargin(margin);
2316     return node;
2317 }
2318 
2319 // if the menu is at the top of the menu preview, then both the top and the bottom of the menu will be aligned with the
2320 // top of the menu preview. Conversely, if it is in other situations, they will be aligned with the bottom.
UpdateMenuPositionTop(MarginProperty & menuNodeMargin,std::map<AlignDirection,AlignRule> & menuNodeAlignRules,AlignRule & alignMap,float biasMenuTop,float biasMenuBottom)2321 void MenuView::UpdateMenuPositionTop(MarginProperty& menuNodeMargin,
2322     std::map<AlignDirection, AlignRule>& menuNodeAlignRules, AlignRule& alignMap, float biasMenuTop,
2323     float biasMenuBottom)
2324 {
2325     menuNodeAlignRules[AlignDirection::TOP] = alignMap;
2326     menuNodeAlignRules[AlignDirection::BOTTOM] = alignMap;
2327     menuNodeMargin.top = CalcLength(Dimension(biasMenuTop));
2328     menuNodeMargin.bottom = CalcLength(Dimension(biasMenuBottom));
2329 }
2330 
2331 // if the menu is on the left side of the menu preview, then both the left and the right sides of the menu will be
2332 // aligned with the left side of the menu preview. Conversely, they will be aligned with the right side.
UpdateMenuPositionLeft(MarginProperty & menuNodeMargin,std::map<AlignDirection,AlignRule> & menuNodeAlignRules,AlignRule & alignMap,float biasMenuLeft,float biasMenuRight)2333 void MenuView::UpdateMenuPositionLeft(MarginProperty& menuNodeMargin,
2334     std::map<AlignDirection, AlignRule>& menuNodeAlignRules, AlignRule& alignMap, float biasMenuLeft,
2335     float biasMenuRight)
2336 {
2337     menuNodeAlignRules[AlignDirection::LEFT] = alignMap;
2338     menuNodeAlignRules[AlignDirection::RIGHT] = alignMap;
2339     menuNodeMargin.left = CalcLength(Dimension(biasMenuLeft));
2340     menuNodeMargin.right = CalcLength(Dimension(biasMenuRight));
2341 }
2342 
2343 // if the menu is at the top or bottom of the menu preview, then the left and right sides of the menu will adjust the
2344 // anchoring rules according to the position where the menu appears.
UpdateMenuNodePositionTop(MarginProperty & menuNodeMargin,std::map<AlignDirection,AlignRule> & menuNodeAlignRules,const PreparedInfoForDrag & data,float biasMenuLeft,std::map<std::string,AlignRule> & alignMap)2345 void MenuView::UpdateMenuNodePositionTop(MarginProperty& menuNodeMargin,
2346     std::map<AlignDirection, AlignRule>& menuNodeAlignRules, const PreparedInfoForDrag& data, float biasMenuLeft,
2347     std::map<std::string, AlignRule>& alignMap)
2348 {
2349     if (data.menuPosition == Placement::BOTTOM_LEFT || data.menuPosition == Placement::BOTTOM ||
2350         data.menuPosition == Placement::BOTTOM_RIGHT) {
2351         UpdateMenuPositionTop(menuNodeMargin, menuNodeAlignRules, alignMap["bottom"],
2352             data.menuPositionTop - data.frameNodeRect.Height(), data.menuPositionBottom);
2353     } else {
2354         UpdateMenuPositionTop(menuNodeMargin, menuNodeAlignRules, alignMap["top"], data.menuPositionTop,
2355             data.menuPositionBottom - data.frameNodeRect.Height());
2356     }
2357     switch (data.menuPosition) {
2358         case Placement::BOTTOM_LEFT:
2359             UpdateMenuPositionLeft(menuNodeMargin, menuNodeAlignRules, alignMap["start"], data.menuPositionLeft,
2360                 (-1) * data.menuPositionLeft - data.menuRect.Width());
2361             break;
2362         case Placement::BOTTOM:
2363             UpdateMenuPositionLeft(menuNodeMargin, menuNodeAlignRules, alignMap["middle"],
2364                 (data.menuRect.Width() / HALF_NUMBER_NEGATIVE) + biasMenuLeft,
2365                 (data.menuRect.Width() / HALF_NUMBER_NEGATIVE) - biasMenuLeft);
2366             break;
2367         case Placement::BOTTOM_RIGHT:
2368             UpdateMenuPositionLeft(menuNodeMargin, menuNodeAlignRules, alignMap["end"],
2369                 (-1) * data.menuPositionRight - data.menuRect.Width(), data.menuPositionRight);
2370             break;
2371         case Placement::TOP_LEFT:
2372             UpdateMenuPositionLeft(menuNodeMargin, menuNodeAlignRules, alignMap["start"], data.menuPositionLeft,
2373                 (-1) * data.menuPositionLeft - data.menuRect.Width());
2374             break;
2375         case Placement::TOP:
2376             UpdateMenuPositionLeft(menuNodeMargin, menuNodeAlignRules, alignMap["middle"],
2377                 (data.menuRect.Width() / HALF_NUMBER_NEGATIVE) + biasMenuLeft,
2378                 (data.menuRect.Width() / HALF_NUMBER_NEGATIVE) - biasMenuLeft);
2379             break;
2380         case Placement::TOP_RIGHT:
2381             UpdateMenuPositionLeft(menuNodeMargin, menuNodeAlignRules, alignMap["end"],
2382                 (-1) * data.menuPositionRight - data.menuRect.Width(), data.menuPositionRight);
2383             break;
2384         default:
2385             break;
2386     }
2387 }
2388 
2389 // if the menu is on the left or right side of the menu preview, then the top and bottom of the menu will adjust the
2390 // anchoring rules according to the position where the menu appears.
UpdateMenuNodePositionLeft(MarginProperty & menuNodeMargin,std::map<AlignDirection,AlignRule> & menuNodeAlignRules,const PreparedInfoForDrag & data,float biasMenuTop,std::map<std::string,AlignRule> & alignMap)2391 void MenuView::UpdateMenuNodePositionLeft(MarginProperty& menuNodeMargin,
2392     std::map<AlignDirection, AlignRule>& menuNodeAlignRules, const PreparedInfoForDrag& data, float biasMenuTop,
2393     std::map<std::string, AlignRule>& alignMap)
2394 {
2395     if (data.menuPosition == Placement::LEFT_TOP || data.menuPosition == Placement::LEFT ||
2396         data.menuPosition == Placement::LEFT_BOTTOM) {
2397         UpdateMenuPositionLeft(menuNodeMargin, menuNodeAlignRules, alignMap["start"], data.menuPositionLeft,
2398             data.menuPositionRight - data.frameNodeRect.Width());
2399     } else {
2400         UpdateMenuPositionLeft(menuNodeMargin, menuNodeAlignRules, alignMap["end"],
2401             data.menuPositionLeft - data.frameNodeRect.Width(), data.menuPositionRight);
2402     }
2403     switch (data.menuPosition) {
2404         case Placement::LEFT_TOP:
2405             UpdateMenuPositionTop(menuNodeMargin, menuNodeAlignRules, alignMap["top"], data.menuPositionTop,
2406                 (-1) * data.menuPositionTop - data.menuRect.Height());
2407             break;
2408         case Placement::LEFT:
2409             UpdateMenuPositionTop(menuNodeMargin, menuNodeAlignRules, alignMap["center"],
2410                 (data.menuRect.Height() / HALF_NUMBER_NEGATIVE) + biasMenuTop,
2411                 (data.menuRect.Height() / HALF_NUMBER_NEGATIVE) - biasMenuTop);
2412             break;
2413         case Placement::LEFT_BOTTOM:
2414             UpdateMenuPositionTop(menuNodeMargin, menuNodeAlignRules, alignMap["bottom"],
2415                 (-1) * data.menuPositionBottom - data.menuRect.Height(), data.menuPositionBottom);
2416             break;
2417         case Placement::RIGHT_TOP:
2418             UpdateMenuPositionTop(menuNodeMargin, menuNodeAlignRules, alignMap["top"], data.menuPositionTop,
2419                 (-1) * data.menuPositionTop - data.menuRect.Height());
2420             break;
2421         case Placement::RIGHT:
2422             UpdateMenuPositionTop(menuNodeMargin, menuNodeAlignRules, alignMap["center"],
2423                 (data.menuRect.Height() / HALF_NUMBER_NEGATIVE) + biasMenuTop,
2424                 (data.menuRect.Height() / HALF_NUMBER_NEGATIVE) - biasMenuTop);
2425             break;
2426         case Placement::RIGHT_BOTTOM:
2427             UpdateMenuPositionTop(menuNodeMargin, menuNodeAlignRules, alignMap["bottom"],
2428                 (-1) * data.menuPositionBottom - data.menuRect.Height(), data.menuPositionBottom);
2429             break;
2430         default:
2431             break;
2432     }
2433 }
2434 
2435 // Update the animation indentation point according to the position where the menu appears.
UpdateMenuNodeByAnimation(const RefPtr<FrameNode> & menuNode,const PreparedInfoForDrag & data)2436 void MenuView::UpdateMenuNodeByAnimation(const RefPtr<FrameNode>& menuNode, const PreparedInfoForDrag& data)
2437 {
2438     CHECK_NULL_VOID(menuNode);
2439     auto menuNodeRenderContext = menuNode->GetRenderContext();
2440     CHECK_NULL_VOID(menuNodeRenderContext);
2441     auto menuRect = data.menuRect;
2442     auto menuWidth = menuRect.Width();
2443     auto menuHeight = menuRect.Height();
2444     auto biasMenuLeft = (data.menuPositionLeft - data.menuPositionRight) / HALF_NUMBER;
2445     auto biasMenuTop = (data.menuPositionTop - data.menuPositionBottom) / HALF_NUMBER;
2446     double x = 0.0;
2447     double y = 0.0;
2448     Placement placement = data.menuPosition;
2449     if (placement == Placement::LEFT_TOP || placement == Placement::LEFT || placement == Placement::LEFT_BOTTOM) {
2450         x = menuWidth;
2451     } else if (placement == Placement::BOTTOM || placement == Placement::TOP) {
2452         x = (menuWidth / HALF_NUMBER) - biasMenuLeft;
2453     } else if (placement == Placement::BOTTOM_RIGHT || placement == Placement::TOP_RIGHT) {
2454         x = menuWidth;
2455     }
2456     if (placement == Placement::TOP_LEFT || placement == Placement::TOP || placement == Placement::TOP_RIGHT) {
2457         y = menuHeight;
2458     } else if (placement == Placement::LEFT || placement == Placement::RIGHT) {
2459         y = (menuHeight / HALF_NUMBER) - biasMenuTop;
2460     } else if (placement == Placement::LEFT_BOTTOM || placement == Placement::RIGHT_BOTTOM) {
2461         y = menuHeight;
2462     }
2463     menuNodeRenderContext->UpdateTransformCenter(DimensionOffset(Offset(x, y)));
2464     menuNodeRenderContext->UpdateTransformScale({ 0.4f, 0.4f });
2465 }
2466 
UpdateMenuOutlineWithArrow(const RefPtr<FrameNode> & menuNode,const RefPtr<FrameNode> & wrapperNode,const MenuParam & menuParam)2467 void MenuView::UpdateMenuOutlineWithArrow(
2468     const RefPtr<FrameNode>& menuNode, const RefPtr<FrameNode>& wrapperNode, const MenuParam& menuParam)
2469 {
2470     CHECK_NULL_VOID(wrapperNode);
2471     auto menuWrapperPattern = wrapperNode->GetPattern<MenuWrapperPattern>();
2472     CHECK_NULL_VOID(menuWrapperPattern);
2473     if (!menuWrapperPattern->GetHasCustomOutlineWidth()) {
2474         return;
2475     }
2476     CHECK_NULL_VOID(menuNode);
2477     auto renderContext = menuNode->GetRenderContext();
2478     CHECK_NULL_VOID(renderContext);
2479     BorderWidthProperty outerWidthProp;
2480     outerWidthProp.SetBorderWidth(Dimension(0));
2481     renderContext->SetOuterBorderWidth(outerWidthProp);
2482     BorderColorProperty outerColorProp;
2483     outerColorProp.SetColor(Color::TRANSPARENT);
2484     renderContext->SetOuterBorderColor(outerColorProp);
2485     menuWrapperPattern->SetMenuParam(menuParam);
2486 }
2487 
TouchEventGenerator(const RefPtr<FrameNode> & actionNode,TouchEvent & event)2488 void MenuView::TouchEventGenerator(const RefPtr<FrameNode>& actionNode, TouchEvent& event)
2489 {
2490     CHECK_NULL_VOID(actionNode);
2491     event.id = actionNode->GetId();
2492     event.originalId = actionNode->GetId();
2493     auto childOffset = actionNode->GetPaintRectOffset(false, true);
2494     auto rectWithRender = actionNode->GetRectWithRender();
2495     event.x = childOffset.GetX() + static_cast<double>(rectWithRender.Width()) / HALF_DIVIDE;
2496     event.y = childOffset.GetY() + static_cast<double>(rectWithRender.Height()) / HALF_DIVIDE;
2497     event.postEventNodeId = actionNode->GetId();
2498     event.isInterpolated = true;
2499     std::chrono::microseconds microseconds(GetMicroTickCount());
2500     TimeStamp time(microseconds);
2501     event.time = time;
2502 }
2503 
TouchPointGenerator(const RefPtr<FrameNode> & actionNode,TouchPoint & point)2504 void MenuView::TouchPointGenerator(const RefPtr<FrameNode>& actionNode, TouchPoint& point)
2505 {
2506     CHECK_NULL_VOID(actionNode);
2507     auto childOffset = actionNode->GetPaintRectOffset(false, true);
2508     auto rectWithRender = actionNode->GetRectWithRender();
2509     auto globalOffset = actionNode->GetPaintRectGlobalOffsetWithTranslate();
2510     point.id = actionNode->GetId();
2511     point.originalId = actionNode->GetId();
2512     point.x = globalOffset.first.GetX() + static_cast<double>(rectWithRender.Width()) / HALF_DIVIDE;
2513     point.y = globalOffset.first.GetY() + static_cast<double>(rectWithRender.Height()) / HALF_DIVIDE;
2514     point.screenX = childOffset.GetX() + static_cast<double>(rectWithRender.Width()) / HALF_DIVIDE;
2515     point.screenY = childOffset.GetY() + static_cast<double>(rectWithRender.Height()) / HALF_DIVIDE;
2516     std::chrono::microseconds microseconds(GetMicroTickCount());
2517     TimeStamp time(microseconds);
2518     point.downTime = time;
2519 }
2520 
RegisterAccessibilityChildActionNotify(const RefPtr<FrameNode> & menuNode)2521 void MenuView::RegisterAccessibilityChildActionNotify(const RefPtr<FrameNode>& menuNode)
2522 {
2523     CHECK_NULL_VOID(menuNode);
2524     auto accessibilityProperty = menuNode->GetAccessibilityProperty<AccessibilityProperty>();
2525     CHECK_NULL_VOID(accessibilityProperty);
2526     accessibilityProperty->SetNotifyChildAction(
2527         [weak = WeakPtr<FrameNode>(menuNode)] (const RefPtr<FrameNode>& node, NotifyChildActionType childActionType) {
2528             auto result = AccessibilityActionResult::ACTION_ERROR;
2529             CHECK_NULL_RETURN(node, result);
2530             auto menu = weak.Upgrade();
2531             CHECK_NULL_RETURN(menu, result);
2532             auto eventHub = menu->GetEventHub<NG::EventHub>();
2533             CHECK_NULL_RETURN(eventHub, result);
2534             auto gesture = eventHub->GetGestureEventHub();
2535             CHECK_NULL_RETURN(gesture, result);
2536             TouchEvent event;
2537             event.type = TouchType::DOWN;
2538             MenuView::TouchEventGenerator(node, event);
2539             TouchPoint eventPoint;
2540             MenuView::TouchPointGenerator(node, eventPoint);
2541             event.pointers.push_back(eventPoint);
2542             auto actionResult = gesture->TriggerTouchEvent(event);
2543             event.type = TouchType::UP;
2544             result = gesture->TriggerTouchEvent(event) && actionResult ?
2545                 AccessibilityActionResult::ACTION_RISE : AccessibilityActionResult::ACTION_ERROR;
2546             return result;
2547     });
2548 }
2549 } // namespace OHOS::Ace::NG
2550