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/i18n/localization.h"
20 #include "base/memory/ace_type.h"
21 #include "base/memory/referenced.h"
22 #include "base/utils/utils.h"
23 #include "core/common/container.h"
24 #include "core/components/common/properties/placement.h"
25 #include "core/components_ng/manager/drag_drop/utils/drag_animation_helper.h"
26 #include "core/components_ng/base/frame_node.h"
27 #include "core/components_ng/base/view_abstract.h"
28 #include "core/components_ng/base/view_stack_processor.h"
29 #include "core/components_ng/manager/drag_drop/drag_drop_func_wrapper.h"
30 #include "core/components_ng/pattern/flex/flex_layout_pattern.h"
31 #include "core/components_ng/pattern/image/image_layout_property.h"
32 #include "core/components_ng/pattern/image/image_pattern.h"
33 #include "core/components_ng/pattern/linear_layout/linear_layout_pattern.h"
34 #include "core/components_ng/pattern/menu/menu_layout_property.h"
35 #include "core/components_ng/pattern/menu/menu_pattern.h"
36 #include "core/components_ng/pattern/menu/menu_theme.h"
37 #include "core/components_ng/pattern/menu/preview/menu_preview_pattern.h"
38 #include "core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.h"
39 #include "core/components_ng/pattern/option/option_paint_property.h"
40 #include "core/components_ng/pattern/option/option_view.h"
41 #include "core/components_ng/pattern/overlay/overlay_manager.h"
42 #include "core/components_ng/pattern/scroll/scroll_pattern.h"
43 #include "core/components_ng/pattern/stack/stack_pattern.h"
44 #include "core/components_ng/pattern/text/text_pattern.h"
45 #include "core/components_v2/inspector/inspector_constants.h"
46 
47 namespace OHOS::Ace::NG {
48 
49 /**
50  * The structure of menu is designed as follows :
51  * |--menuWrapper(size is same as root)
52  *   |--menu
53  *      |--scroll
54  *          |--column(for bindMenu/select)
55  *            |--options
56  *          |--customNode(for custom builder)
57  */
58 
59 namespace {
60 constexpr float PAN_MAX_VELOCITY = 2000.0f;
61 constexpr float HALF_DIVIDE = 2.0f;
62 constexpr float PREVIEW_ORIGIN_SCALE = 1.0f;
63 const RefPtr<Curve> CUSTOM_PREVIEW_ANIMATION_CURVE =
64     AceType::MakeRefPtr<InterpolatingSpring>(0.0f, 1.0f, 328.0f, 34.0f);
65 const std::string HOVER_IMAGE_CLIP_PROPERTY_NAME = "hoverImageClip";
66 
MountTextNode(const RefPtr<FrameNode> & wrapperNode,const RefPtr<UINode> & previewCustomNode=nullptr)67 void MountTextNode(const RefPtr<FrameNode>& wrapperNode, const RefPtr<UINode>& previewCustomNode = nullptr)
68 {
69     CHECK_NULL_VOID(previewCustomNode);
70     auto pipeline = PipelineContext::GetMainPipelineContext();
71     CHECK_NULL_VOID(pipeline);
72     auto manager = pipeline->GetOverlayManager();
73     CHECK_NULL_VOID(manager);
74     auto gatherNode = manager->GetGatherNode();
75     CHECK_NULL_VOID(gatherNode);
76     auto textNode = FrameNode::GetOrCreateFrameNode(V2::TEXT_ETS_TAG,
77         ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<TextPattern>(); });
78     CHECK_NULL_VOID(textNode);
79     textNode->MountToParent(wrapperNode);
80     textNode->MarkModifyDone();
81 }
82 
CustomPreviewNodeProc(const RefPtr<FrameNode> & previewNode,const MenuParam & menuParam,const RefPtr<UINode> & previewCustomNode=nullptr)83 void CustomPreviewNodeProc(const RefPtr<FrameNode>& previewNode, const MenuParam& menuParam,
84     const RefPtr<UINode>& previewCustomNode = nullptr)
85 {
86     CHECK_NULL_VOID(previewCustomNode);
87     CHECK_NULL_VOID(previewNode);
88     auto previewPattern = previewNode->GetPattern<MenuPreviewPattern>();
89     CHECK_NULL_VOID(previewPattern);
90     previewPattern->SetHasPreviewTransitionEffect(menuParam.hasPreviewTransitionEffect);
91     auto layoutProperty = previewNode->GetLayoutProperty();
92     CHECK_NULL_VOID(layoutProperty);
93     layoutProperty->UpdateVisibility(VisibleType::VISIBLE, true);
94     previewNode->AddChild(previewCustomNode);
95 
96     CHECK_NULL_VOID(menuParam.isShowHoverImage);
97     auto pipeline = previewNode->GetContextWithCheck();
98     CHECK_NULL_VOID(pipeline);
99     ScopedLayout scope(pipeline);
100     previewNode->Measure(layoutProperty->GetLayoutConstraint());
101     auto previewSize = previewNode->GetGeometryNode()->GetFrameSize();
102     previewPattern->SetIsShowHoverImage(true);
103     previewPattern->SetCustomPreviewWidth(previewSize.Width());
104     previewPattern->SetCustomPreviewHeight(previewSize.Height());
105 
106     auto previewScaleTo = menuParam.previewAnimationOptions.scaleTo;
107     CHECK_NULL_VOID(previewScaleTo);
108     auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
109     CHECK_NULL_VOID(menuTheme);
110     previewScaleTo = LessOrEqual(previewScaleTo, 0.0) ? menuTheme->GetPreviewAfterAnimationScale() : previewScaleTo;
111     previewPattern->SetCustomPreviewScaleTo(previewScaleTo);
112 }
113 
114 // create menuWrapper and menu node, update menu props
CreateMenu(int32_t targetId,const std::string & targetTag="",MenuType type=MenuType::MENU)115 std::pair<RefPtr<FrameNode>, RefPtr<FrameNode>> CreateMenu(int32_t targetId, const std::string& targetTag = "",
116     MenuType type = MenuType::MENU)
117 {
118     // use wrapper to detect click events outside menu
119     auto wrapperNode = FrameNode::CreateFrameNode(V2::MENU_WRAPPER_ETS_TAG,
120         ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<MenuWrapperPattern>(targetId));
121 
122     auto nodeId = ElementRegister::GetInstance()->MakeUniqueId();
123     auto menuNode = FrameNode::CreateFrameNode(
124         V2::MENU_ETS_TAG, nodeId, AceType::MakeRefPtr<MenuPattern>(targetId, targetTag, type));
125 
126     auto renderContext = menuNode->GetRenderContext();
127     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) && renderContext->IsUniRenderEnabled()) {
128         BlurStyleOption styleOption;
129         styleOption.blurStyle = BlurStyle::COMPONENT_ULTRA_THICK;
130         renderContext->UpdateBackgroundColor(Color::TRANSPARENT);
131         renderContext->UpdateBackBlurStyle(styleOption);
132     }
133 
134     menuNode->MountToParent(wrapperNode);
135 
136     return { wrapperNode, menuNode };
137 }
138 
CreateTitleNode(const std::string & title,RefPtr<FrameNode> & column)139 void CreateTitleNode(const std::string& title, RefPtr<FrameNode>& column)
140 {
141     auto textNode = FrameNode::CreateFrameNode(
142         V2::TEXT_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<TextPattern>());
143     CHECK_NULL_VOID(textNode);
144     auto textProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
145     CHECK_NULL_VOID(textProperty);
146 
147     auto pipeline = textNode->GetContextWithCheck();
148     CHECK_NULL_VOID(pipeline);
149     auto theme = pipeline->GetTheme<SelectTheme>();
150     CHECK_NULL_VOID(theme);
151     auto padding = static_cast<float>(theme->GetMenuIconPadding().ConvertToPx()) -
152                    static_cast<float>(theme->GetOutPadding().ConvertToPx());
153     PaddingProperty textPadding;
154     textPadding.left = CalcLength(padding);
155     textPadding.right = CalcLength(padding);
156     textProperty->UpdatePadding(textPadding);
157     textProperty->UpdateFontSize(theme->GetMenuTitleFontSize());
158     textProperty->UpdateFontWeight(FontWeight::MEDIUM);
159     textProperty->UpdateItalicFontStyle(Ace::FontStyle::NORMAL);
160     textProperty->UpdateTextColor(theme->GetMenuTitleFontColor());
161     textProperty->UpdateContent(title);
162     textProperty->UpdateMaxLines(1);
163     textProperty->UpdateTextOverflow(TextOverflow::ELLIPSIS);
164 
165     CalcSize idealSize;
166     idealSize.SetHeight(CalcLength(theme->GetMenuTitleHeight()));
167     MeasureProperty layoutConstraint;
168     layoutConstraint.selfIdealSize = idealSize;
169     textProperty->UpdateCalcLayoutProperty(layoutConstraint);
170 
171     textNode->MountToParent(column);
172     textNode->MarkModifyDone();
173 }
174 
CreateMenuScroll(const RefPtr<UINode> & node)175 RefPtr<FrameNode> CreateMenuScroll(const RefPtr<UINode>& node)
176 {
177     auto scroll = FrameNode::CreateFrameNode(
178         V2::SCROLL_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ScrollPattern>());
179     CHECK_NULL_RETURN(scroll, nullptr);
180     auto props = scroll->GetLayoutProperty<ScrollLayoutProperty>();
181     props->UpdateAxis(Axis::VERTICAL);
182     props->UpdateAlignment(Alignment::CENTER_LEFT);
183     auto pipeline = scroll->GetContextWithCheck();
184     CHECK_NULL_RETURN(pipeline, nullptr);
185     auto theme = pipeline->GetTheme<SelectTheme>();
186     float contentPadding = 0.0f;
187     if (theme) {
188         contentPadding = static_cast<float>(theme->GetOutPadding().ConvertToPx());
189     }
190     PaddingProperty padding;
191     padding.left = padding.right = padding.top = padding.bottom = CalcLength(contentPadding);
192     props->UpdatePadding(padding);
193     if (node) {
194         node->MountToParent(scroll);
195     }
196     auto renderContext = scroll->GetRenderContext();
197     CHECK_NULL_RETURN(renderContext, nullptr);
198     BorderRadiusProperty borderRadius;
199     if (theme) {
200         auto defaultRadius = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) ?
201             theme->GetMenuDefaultRadius() : theme->GetMenuBorderRadius();
202         borderRadius.SetRadius(defaultRadius);
203     }
204     renderContext->UpdateBorderRadius(borderRadius);
205     return scroll;
206 }
207 
MountScrollToMenu(const RefPtr<UINode> & customNode,RefPtr<FrameNode> scroll,RefPtr<FrameNode> menuNode)208 void MountScrollToMenu(
209     const RefPtr<UINode>& customNode, RefPtr<FrameNode> scroll, RefPtr<FrameNode> menuNode)
210 {
211     auto customMenuNode = AceType::DynamicCast<FrameNode>(customNode);
212     if (customMenuNode) {
213         customMenuNode->SetDraggable(false);
214     }
215     scroll->MountToParent(menuNode);
216     scroll->MarkModifyDone();
217 }
218 
OptionKeepMenu(RefPtr<FrameNode> & option,WeakPtr<FrameNode> & menuWeak)219 void OptionKeepMenu(RefPtr<FrameNode>& option, WeakPtr<FrameNode>& menuWeak)
220 {
221     auto pattern = option->GetPattern<OptionPattern>();
222     CHECK_NULL_VOID(pattern);
223     pattern->SetMenu(menuWeak);
224 }
225 
GetHasIcon(const std::vector<OptionParam> & params)226 bool GetHasIcon(const std::vector<OptionParam>& params)
227 {
228     for (size_t i = 0; i < params.size(); ++i) {
229         if (!params[i].icon.empty() || params[i].isPasteOption) {
230             return true;
231         }
232     }
233     return false;
234 }
235 
GetHasSymbol(const std::vector<OptionParam> & params)236 bool GetHasSymbol(const std::vector<OptionParam>& params)
237 {
238     for (size_t i = 0; i < params.size(); ++i) {
239         if (params[i].symbol != nullptr) {
240             return true;
241         }
242     }
243     return false;
244 }
245 
GetFloatImageOffset(const RefPtr<FrameNode> & frameNode)246 OffsetF GetFloatImageOffset(const RefPtr<FrameNode>& frameNode)
247 {
248     auto offsetToWindow = frameNode->GetPaintRectOffset();
249     auto offsetX = offsetToWindow.GetX();
250     auto offsetY = offsetToWindow.GetY();
251     return OffsetF(offsetX, offsetY);
252 }
253 
UpdateContainerIdealSizeConstraint(const RefPtr<FrameNode> & node,const CalcSize & idealSize)254 void UpdateContainerIdealSizeConstraint(const RefPtr<FrameNode>& node, const CalcSize& idealSize)
255 {
256     CHECK_NULL_VOID(node);
257     MeasureProperty layoutConstraint;
258     layoutConstraint.selfIdealSize = idealSize;
259     auto nodeLayoutProperty = node->GetLayoutProperty<LayoutProperty>();
260     CHECK_NULL_VOID(nodeLayoutProperty);
261     nodeLayoutProperty->UpdateCalcLayoutProperty(layoutConstraint);
262 }
263 
ShowBorderRadiusAndShadowAnimation(const RefPtr<MenuTheme> & menuTheme,const RefPtr<FrameNode> & imageNode,bool isShowHoverImage)264 void ShowBorderRadiusAndShadowAnimation(
265     const RefPtr<MenuTheme>& menuTheme, const RefPtr<FrameNode>& imageNode, bool isShowHoverImage)
266 {
267     CHECK_NULL_VOID(menuTheme && imageNode);
268     auto imageContext = imageNode->GetRenderContext();
269     CHECK_NULL_VOID(imageContext);
270     auto shadow = imageContext->GetBackShadow();
271     if (!shadow.has_value()) {
272         shadow = Shadow::CreateShadow(ShadowStyle::None);
273     }
274     AnimationOption option;
275     option.SetDuration(menuTheme->GetPreviewAnimationDuration());
276     option.SetCurve(Curves::SHARP);
277     auto previewBorderRadius = BorderRadiusProperty(menuTheme->GetPreviewBorderRadius());
278     if (auto presetRad = imageContext->GetBorderRadius(); presetRad) {
279         previewBorderRadius = presetRad.value();
280         imageContext->ResetBorderRadius();
281     }
282 
283     imageContext->UpdateBorderRadius(imageContext->GetBorderRadius().value_or(BorderRadiusProperty()));
284     auto pipelineContext = imageNode->GetContext();
285     CHECK_NULL_VOID(pipelineContext);
286     pipelineContext->AddAfterLayoutTask([option, imageContext, previewBorderRadius, shadow, isShowHoverImage]() {
287         AnimationUtils::Animate(
288             option,
289             [imageContext, previewBorderRadius, shadow, isShowHoverImage]() mutable {
290                 CHECK_NULL_VOID(imageContext && shadow);
291                 auto color = shadow->GetColor();
292                 auto newColor = Color::FromARGB(100, color.GetRed(), color.GetGreen(), color.GetBlue());
293                 shadow->SetColor(newColor);
294                 imageContext->UpdateBackShadow(shadow.value());
295 
296                 CHECK_NULL_VOID(!isShowHoverImage);
297                 imageContext->UpdateBorderRadius(previewBorderRadius);
298             },
299             option.GetOnFinishEvent());
300     });
301 }
302 
UpdateOpacityInFinishEvent(const RefPtr<FrameNode> & previewNode,const RefPtr<RenderContext> & imageContext,const RefPtr<MenuTheme> & menuTheme,bool isScaleNearEqual=true)303 void UpdateOpacityInFinishEvent(const RefPtr<FrameNode>& previewNode, const RefPtr<RenderContext>& imageContext,
304     const RefPtr<MenuTheme>& menuTheme, bool isScaleNearEqual = true)
305 {
306     CHECK_NULL_VOID(imageContext && menuTheme);
307     // hover image disappear opacity animation
308     AnimationOption option;
309     if (isScaleNearEqual) {
310         option.SetDuration(menuTheme->GetPreviewAnimationDuration());
311         option.SetCurve(Curves::SHARP);
312         option.SetDelay(menuTheme->GetHoverImageDelayDuration());
313     } else {
314         option.SetDuration(menuTheme->GetHoverImageSwitchToPreviewOpacityDuration());
315         option.SetCurve(Curves::FRICTION);
316     }
317 
318     CHECK_NULL_VOID(previewNode);
319     auto previewContext = previewNode->GetRenderContext();
320     CHECK_NULL_VOID(previewContext);
321 
322     imageContext->UpdateOpacity(1.0);
323     previewContext->UpdateOpacity(0.0);
324     AnimationUtils::Animate(
325         option, [previewContext]() {
326             CHECK_NULL_VOID(previewContext);
327             previewContext->UpdateOpacity(1.0);
328         });
329 }
330 
SetHoverImageStackBorderRadius(const RefPtr<FrameNode> & hoverImageStackNode,const RefPtr<MenuTheme> & menuTheme,const RefPtr<RenderContext> & imageContext,bool isScaleNearEqual)331 void SetHoverImageStackBorderRadius(const RefPtr<FrameNode>& hoverImageStackNode, const RefPtr<MenuTheme>& menuTheme,
332     const RefPtr<RenderContext>& imageContext, bool isScaleNearEqual)
333 {
334     CHECK_NULL_VOID(hoverImageStackNode);
335     auto stackContext = hoverImageStackNode->GetRenderContext();
336     CHECK_NULL_VOID(stackContext);
337 
338     auto stackLayoutProps = hoverImageStackNode->GetLayoutProperty<StackLayoutProperty>();
339     CHECK_NULL_VOID(stackLayoutProps);
340     BorderWidthProperty widthProp;
341     widthProp.SetBorderWidth(Dimension(0.0f));
342     stackLayoutProps->UpdateBorderWidth(widthProp);
343     stackContext->UpdateBorderWidth(widthProp);
344 
345     AnimationOption option;
346     option.SetCurve(CUSTOM_PREVIEW_ANIMATION_CURVE);
347     if (isScaleNearEqual) {
348         option.SetDelay(menuTheme->GetHoverImageDelayDuration());
349     }
350     auto previewBorderRadius = menuTheme->GetPreviewBorderRadius();
351     AnimationUtils::Animate(
352         option, [stackContext, imageContext, previewBorderRadius]() {
353             CHECK_NULL_VOID(stackContext);
354             BorderRadiusProperty borderRadius;
355             borderRadius.SetRadius(previewBorderRadius);
356             stackContext->UpdateBorderRadius(borderRadius);
357 
358             CHECK_NULL_VOID(imageContext);
359             imageContext->UpdateBorderRadius(borderRadius);
360         });
361 }
362 
UpdatePreviewVisibleAreaByFrame(const RefPtr<RenderContext> & clipContext,const RefPtr<MenuPreviewPattern> & previewPattern,float value,float radius,float distVal)363 void UpdatePreviewVisibleAreaByFrame(const RefPtr<RenderContext>& clipContext,
364     const RefPtr<MenuPreviewPattern>& previewPattern, float value, float radius, float distVal)
365 {
366     CHECK_NULL_VOID(previewPattern);
367     CHECK_NULL_VOID(!NearZero(distVal));
368     auto rate = (value - previewPattern->GetClipStartValue()) / distVal;
369     // clip area start by actual image size after contain stack container and squeezed by parent proc
370     auto clipStartWidth = previewPattern->GetHoverImageAfterScaleWidth();
371     auto clipStartHeight = previewPattern->GetHoverImageAfterScaleHeight();
372     // child stack node may be squeezed by parent flex node
373     auto clipEndWidth = previewPattern->GetStackAfterScaleActualWidth();
374     auto clipEndHeight = previewPattern->GetStackAfterScaleActualHeight();
375 
376     auto curentClipAreaWidth = rate * (clipEndWidth - clipStartWidth) + clipStartWidth;
377     auto curentClipAreaHeight = rate * (clipEndHeight - clipStartHeight) + clipStartHeight;
378 
379     auto clipOffset = previewPattern->GetHoverImageAfterScaleOffset();
380     RoundRect roundRectInstance;
381     roundRectInstance.SetRect(RectF(OffsetF((1 - rate) * clipOffset.GetX(), (1 - rate) * clipOffset.GetY()),
382         SizeF(curentClipAreaWidth, curentClipAreaHeight)));
383     roundRectInstance.SetCornerRadius(rate * radius);
384     CHECK_NULL_VOID(clipContext);
385     clipContext->ClipWithRoundRect(roundRectInstance);
386 }
387 
UpdatePreivewVisibleArea(const RefPtr<FrameNode> & hoverImageStackNode,const RefPtr<FrameNode> & previewNode,const RefPtr<MenuTheme> & menuTheme,bool isScaleNearEqual)388 void UpdatePreivewVisibleArea(const RefPtr<FrameNode>& hoverImageStackNode, const RefPtr<FrameNode>& previewNode,
389     const RefPtr<MenuTheme>& menuTheme, bool isScaleNearEqual)
390 {
391     CHECK_NULL_VOID(previewNode && menuTheme);
392     auto previewPattern = previewNode->GetPattern<MenuPreviewPattern>();
393     CHECK_NULL_VOID(previewPattern);
394 
395     auto clipStartValue = previewPattern->GetClipStartValue();
396     auto clipEndValue = previewPattern->GetClipEndValue();
397     clipEndValue += NearEqual(clipStartValue, clipEndValue) ? 1.0f : 0;
398     auto dist = clipEndValue - clipStartValue;
399 
400     CHECK_NULL_VOID(hoverImageStackNode);
401     hoverImageStackNode->CreateAnimatablePropertyFloat(HOVER_IMAGE_CLIP_PROPERTY_NAME, 0,
402         [weak = AceType::WeakClaim(AceType::RawPtr(hoverImageStackNode)),
403             previewWeak = AceType::WeakClaim(AceType::RawPtr(previewNode)),
404             radius = menuTheme->GetPreviewBorderRadius().ConvertToPx(), distVal = dist](float value) {
405             auto clipNode = weak.Upgrade();
406             CHECK_NULL_VOID(clipNode);
407             auto clipContext = clipNode->GetRenderContext();
408             CHECK_NULL_VOID(clipContext);
409 
410             auto preview = previewWeak.Upgrade();
411             CHECK_NULL_VOID(preview);
412             auto previewPattern = preview->GetPattern<MenuPreviewPattern>();
413 
414             UpdatePreviewVisibleAreaByFrame(clipContext, previewPattern, value, radius, distVal);
415         });
416     AnimationOption option;
417     option.SetCurve(CUSTOM_PREVIEW_ANIMATION_CURVE);
418     if (isScaleNearEqual) { option.SetDelay(menuTheme->GetHoverImageDelayDuration()); }
419     hoverImageStackNode->UpdateAnimatablePropertyFloat(HOVER_IMAGE_CLIP_PROPERTY_NAME, clipStartValue);
420     auto clipAnimation_ = AnimationUtils::StartAnimation(option, [hoverImageStackNode, clipEndValue]() {
421             CHECK_NULL_VOID(hoverImageStackNode);
422             hoverImageStackNode->UpdateAnimatablePropertyFloat(HOVER_IMAGE_CLIP_PROPERTY_NAME, clipEndValue);
423         });
424 }
425 
UpdateHoverImagePreviewScale(const RefPtr<FrameNode> & hoverImageStackNode,const RefPtr<MenuPreviewPattern> & previewPattern,const RefPtr<MenuTheme> & menuTheme,bool isScaleNearEqual)426 void UpdateHoverImagePreviewScale(const RefPtr<FrameNode>& hoverImageStackNode,
427     const RefPtr<MenuPreviewPattern>& previewPattern, const RefPtr<MenuTheme>& menuTheme, bool isScaleNearEqual)
428 {
429     CHECK_NULL_VOID(hoverImageStackNode);
430     auto stackContext = hoverImageStackNode->GetRenderContext();
431     CHECK_NULL_VOID(stackContext);
432 
433     auto scaleBefore = previewPattern->GetHoverImageScaleTo();
434     auto scaleFrom =
435         LessOrEqual(scaleBefore, 0.0) ? menuTheme->GetPreviewBeforeAnimationScale() : scaleBefore;
436     stackContext->UpdateTransformScale(VectorF(scaleFrom, scaleFrom));
437 
438     auto scaleAfter = previewPattern->GetCustomPreviewScaleTo();
439     auto scaleTo =
440         LessOrEqual(scaleAfter, 0.0) ? menuTheme->GetPreviewAfterAnimationScale() : scaleAfter;
441 
442     AnimationOption scaleOption = AnimationOption();
443     scaleOption.SetCurve(CUSTOM_PREVIEW_ANIMATION_CURVE);
444     if (isScaleNearEqual) {
445         scaleOption.SetDelay(menuTheme->GetHoverImageDelayDuration());
446     }
447     previewPattern->SetIsHoverImageScalePlaying(true);
448     scaleOption.SetOnFinishEvent([weak = WeakPtr<MenuPreviewPattern>(previewPattern)] {
449         auto previewPattern = weak.Upgrade();
450         CHECK_NULL_VOID(previewPattern);
451         previewPattern->SetIsHoverImageScalePlaying(false);
452     });
453 
454     auto menuWrapper = previewPattern->GetMenuWrapper();
455     CHECK_NULL_VOID(menuWrapper);
456     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
457     CHECK_NULL_VOID(menuWrapperPattern);
458     auto callback = [weak = AceType::WeakClaim(AceType::RawPtr(menuWrapperPattern)), scaleFrom, scaleTo](float rate) {
459         auto menuWrapperPattern = weak.Upgrade();
460         CHECK_NULL_VOID(menuWrapperPattern);
461         menuWrapperPattern->SetHoverImageToPreviewRate(rate);
462         menuWrapperPattern->SetHoverImageToPreviewScale((scaleTo - scaleFrom) * rate + scaleFrom);
463     };
464     auto animateProperty = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(-1.0, std::move(callback));
465     CHECK_NULL_VOID(animateProperty);
466     stackContext->AttachNodeAnimatableProperty(animateProperty);
467     animateProperty->Set(0.0);
468     AnimationUtils::Animate(
469         scaleOption,
470         [stackContext, scaleTo, animateProperty]() {
471             CHECK_NULL_VOID(stackContext);
472             stackContext->UpdateTransformScale(VectorF(scaleTo, scaleTo));
473             CHECK_NULL_VOID(animateProperty);
474             animateProperty->Set(1.0);
475         },
476         scaleOption.GetOnFinishEvent());
477 }
478 
SetHoverImageFinishEvent(const RefPtr<FrameNode> & hoverImageStackNode,const RefPtr<FrameNode> & previewNode,const RefPtr<RenderContext> & imageContext,const RefPtr<MenuTheme> & menuTheme,const RefPtr<MenuWrapperPattern> & wrapperPattern)479 void SetHoverImageFinishEvent(const RefPtr<FrameNode>& hoverImageStackNode, const RefPtr<FrameNode>& previewNode,
480     const RefPtr<RenderContext>& imageContext, const RefPtr<MenuTheme>& menuTheme,
481     const RefPtr<MenuWrapperPattern>& wrapperPattern)
482 {
483     CHECK_NULL_VOID(previewNode);
484     auto previewPattern = previewNode->GetPattern<MenuPreviewPattern>();
485     CHECK_NULL_VOID(previewPattern);
486     previewPattern->SetIsHoverImageAnimationPlaying(false);
487 
488     CHECK_NULL_VOID(wrapperPattern);
489     // if the animation is interrupted during the image hover phase, the next dynamic effects are not processed.
490     CHECK_NULL_VOID(!wrapperPattern->IsStopHoverImageAnimation());
491 
492     bool isScaleNearEqual = previewPattern->IsHoverImageScaleNearEqual();
493 
494     SetHoverImageStackBorderRadius(hoverImageStackNode, menuTheme, imageContext, isScaleNearEqual);
495 
496     UpdateOpacityInFinishEvent(previewNode, imageContext, menuTheme, isScaleNearEqual);
497 
498     UpdatePreivewVisibleArea(hoverImageStackNode, previewNode, menuTheme, isScaleNearEqual);
499 
500     UpdateHoverImagePreviewScale(hoverImageStackNode, previewPattern, menuTheme, isScaleNearEqual);
501 }
502 
ShowHoverImageAnimationProc(const RefPtr<FrameNode> & hoverImageStackNode,const RefPtr<FrameNode> & previewNode,const RefPtr<RenderContext> & imageContext,const RefPtr<MenuWrapperPattern> & wrapperPattern)503 void ShowHoverImageAnimationProc(const RefPtr<FrameNode>& hoverImageStackNode, const RefPtr<FrameNode>& previewNode,
504     const RefPtr<RenderContext>& imageContext, const RefPtr<MenuWrapperPattern>& wrapperPattern)
505 {
506     CHECK_NULL_VOID(wrapperPattern && wrapperPattern->GetIsShowHoverImage());
507     CHECK_NULL_VOID(hoverImageStackNode && previewNode);
508     auto stackContext = hoverImageStackNode->GetRenderContext();
509     CHECK_NULL_VOID(stackContext);
510     stackContext->UpdateClipEdge(true);
511     auto previewPattern = previewNode->GetPattern<MenuPreviewPattern>();
512     CHECK_NULL_VOID(previewPattern);
513     auto pipeline = previewNode->GetContextWithCheck();
514     CHECK_NULL_VOID(pipeline);
515     auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
516     CHECK_NULL_VOID(menuTheme);
517 
518     DragEventActuator::ExecutePreDragAction(PreDragStatus::PREVIEW_LIFT_STARTED);
519     auto scaleBefore = previewPattern->GetHoverImageScaleFrom();
520     auto scaleFrom =
521         LessOrEqual(scaleBefore, 0.0) ? menuTheme->GetPreviewBeforeAnimationScale() : scaleBefore;
522     stackContext->UpdateTransformScale(VectorF(scaleFrom, scaleFrom));
523 
524     auto scaleAfter = previewPattern->GetHoverImageScaleTo();
525     auto scaleTo =
526         LessOrEqual(scaleAfter, 0.0) ? menuTheme->GetPreviewAfterAnimationScale() : scaleAfter;
527 
528     previewPattern->SetIsHoverImageAnimationPlaying(true);
529     // when the scaling start and end sizes are the same, the end callback method should not be relied on
530     AnimationOption scaleOption = AnimationOption();
531     if (previewPattern->IsHoverImageScaleNearEqual()) {
532         SetHoverImageFinishEvent(hoverImageStackNode, previewNode, imageContext, menuTheme, wrapperPattern);
533         DragEventActuator::ExecutePreDragAction(PreDragStatus::PREVIEW_LIFT_FINISHED);
534         return;
535     } else {
536         scaleOption.SetOnFinishEvent(
537             [hoverImageStackNode, previewNode, imageContext, menuTheme, wrapperPattern]() {
538                 DragEventActuator::ExecutePreDragAction(PreDragStatus::PREVIEW_LIFT_FINISHED);
539                 SetHoverImageFinishEvent(hoverImageStackNode, previewNode, imageContext, menuTheme, wrapperPattern);
540             });
541     }
542     scaleOption.SetDuration(menuTheme->GetHoverImageDelayDuration());
543     scaleOption.SetCurve(Curves::SHARP);
544     AnimationUtils::Animate(
545         scaleOption, [stackContext, scaleTo]() {
546             CHECK_NULL_VOID(stackContext);
547             stackContext->UpdateTransformScale(VectorF(scaleTo, scaleTo));
548         },
549         scaleOption.GetOnFinishEvent());
550 }
551 
ShowPixelMapScaleAnimationProc(const RefPtr<MenuTheme> & menuTheme,const RefPtr<FrameNode> & imageNode,const RefPtr<MenuPattern> & menuPattern)552 void ShowPixelMapScaleAnimationProc(
553     const RefPtr<MenuTheme>& menuTheme, const RefPtr<FrameNode>& imageNode, const RefPtr<MenuPattern>& menuPattern)
554 {
555     CHECK_NULL_VOID(menuPattern && menuTheme);
556     auto scaleBefore = menuPattern->GetPreviewBeforeAnimationScale();
557     auto scaleAfter = menuPattern->GetPreviewAfterAnimationScale();
558     DragEventActuator::ExecutePreDragAction(PreDragStatus::PREVIEW_LIFT_STARTED);
559     auto previewBeforeAnimationScale =
560         LessNotEqual(scaleBefore, 0.0) ? menuTheme->GetPreviewBeforeAnimationScale() : scaleBefore;
561     auto previewAfterAnimationScale =
562         LessNotEqual(scaleAfter, 0.0) ? menuTheme->GetPreviewAfterAnimationScale() : scaleAfter;
563 
564     CHECK_NULL_VOID(imageNode);
565     auto imagePattern = imageNode->GetPattern<ImagePattern>();
566     CHECK_NULL_VOID(imagePattern);
567     auto imageRawSize = imagePattern->GetRawImageSize();
568     auto geometryNode = imageNode->GetGeometryNode();
569     CHECK_NULL_VOID(geometryNode);
570     auto geometrySize = geometryNode->GetFrameSize();
571     if (geometrySize.IsPositive() && imageRawSize.IsPositive() && imageRawSize > geometrySize) {
572         previewBeforeAnimationScale *= imageRawSize.Width() / geometrySize.Width();
573     }
574 
575     auto imageContext = imageNode->GetRenderContext();
576     CHECK_NULL_VOID(imageContext);
577     imageContext->UpdateTransformScale(VectorF(previewBeforeAnimationScale, previewBeforeAnimationScale));
578 
579     AnimationOption scaleOption = AnimationOption();
580     auto motion = AceType::MakeRefPtr<ResponsiveSpringMotion>(
581         menuTheme->GetSpringMotionResponse(), menuTheme->GetSpringMotionDampingFraction());
582     scaleOption.SetCurve(motion);
583     scaleOption.SetOnFinishEvent(
584         []() { DragEventActuator::ExecutePreDragAction(PreDragStatus::PREVIEW_LIFT_FINISHED); });
585     AnimationUtils::Animate(
586         scaleOption, [imageContext, previewAfterAnimationScale]() {
587             CHECK_NULL_VOID(imageContext);
588             imageContext->UpdateTransformScale(VectorF(previewAfterAnimationScale, previewAfterAnimationScale));
589         },
590         scaleOption.GetOnFinishEvent());
591 }
592 
HandleDragEnd(float offsetX,float offsetY,float velocity,const RefPtr<FrameNode> & menuWrapper)593 void HandleDragEnd(float offsetX, float offsetY, float velocity, const RefPtr<FrameNode>& menuWrapper)
594 {
595     if ((LessOrEqual(std::abs(offsetY), std::abs(offsetX)) || LessOrEqual(offsetY, 0.0f)) &&
596         LessOrEqual(velocity, PAN_MAX_VELOCITY)) {
597         return;
598     }
599     CHECK_NULL_VOID(menuWrapper);
600     auto wrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
601     CHECK_NULL_VOID(wrapperPattern);
602     wrapperPattern->HideMenu();
603 }
604 
InitPanEvent(const RefPtr<GestureEventHub> & targetGestureHub,const RefPtr<GestureEventHub> & gestureHub,const RefPtr<FrameNode> & menuWrapper)605 void InitPanEvent(const RefPtr<GestureEventHub>& targetGestureHub, const RefPtr<GestureEventHub>& gestureHub,
606     const RefPtr<FrameNode>& menuWrapper)
607 {
608     auto dragEventActuator = targetGestureHub->GetDragEventActuator();
609     auto actionStartTask = [actuator = AceType::WeakClaim(AceType::RawPtr(dragEventActuator))](
610                                const GestureEvent& info) {
611         auto dragEventActuator = actuator.Upgrade();
612         CHECK_NULL_VOID(dragEventActuator);
613         dragEventActuator->RestartDragTask(info);
614     };
615     auto actionEndTask = [menuWrapper](const GestureEvent& info) {
616         auto offsetX = static_cast<float>(info.GetOffsetX());
617         auto offsetY = static_cast<float>(info.GetOffsetY());
618         auto offsetPerSecondX = info.GetVelocity().GetOffsetPerSecond().GetX();
619         auto offsetPerSecondY = info.GetVelocity().GetOffsetPerSecond().GetY();
620         auto velocity =
621             static_cast<float>(std::sqrt(offsetPerSecondX * offsetPerSecondX + offsetPerSecondY * offsetPerSecondY));
622         HandleDragEnd(offsetX, offsetY, velocity, menuWrapper);
623     };
624     PanDirection panDirection;
625     panDirection.type = PanDirection::ALL;
626     auto panEvent =
627         AceType::MakeRefPtr<PanEvent>(std::move(actionStartTask), nullptr, std::move(actionEndTask), nullptr);
628     auto distance = SystemProperties::GetDragStartPanDistanceThreshold();
629     gestureHub->AddPanEvent(panEvent, panDirection, 1, Dimension(distance, DimensionUnit::VP));
630 
631     // add TouchEvent for Menu dragStart Move
632     auto touchTask = [actuator = AceType::WeakClaim(AceType::RawPtr(dragEventActuator))](const TouchEventInfo& info) {
633         auto dragEventActuator = actuator.Upgrade();
634         CHECK_NULL_VOID(dragEventActuator);
635         auto touchPoint = Point(
636             info.GetTouches().front().GetGlobalLocation().GetX(), info.GetTouches().front().GetGlobalLocation().GetY());
637         if (info.GetTouches().front().GetTouchType() == TouchType::DOWN) {
638             dragEventActuator->SetDragDampStartPointInfo(touchPoint, info.GetTouches().front().GetFingerId());
639         } else if (info.GetTouches().front().GetTouchType() == TouchType::MOVE) {
640             dragEventActuator->HandleDragDampingMove(touchPoint, info.GetTouches().front().GetFingerId(), true);
641         }
642     };
643     auto touchListener = AceType::MakeRefPtr<TouchEventImpl>(std::move(touchTask));
644     gestureHub->AddTouchEvent(touchListener);
645 }
646 
GetHoverImageCustomPreviewBaseScaleInfo(const MenuParam & menuParam,int32_t width,int32_t height,const RefPtr<MenuPreviewPattern> & previewPattern)647 float GetHoverImageCustomPreviewBaseScaleInfo(const MenuParam& menuParam, int32_t width, int32_t height,
648     const RefPtr<MenuPreviewPattern>& previewPattern)
649 {
650     float scaleRet = PREVIEW_ORIGIN_SCALE;
651     CHECK_NULL_RETURN(menuParam.isShowHoverImage, scaleRet);
652     CHECK_NULL_RETURN(previewPattern, scaleRet);
653     // if the parent container is smaller than the child component, the child container will be squeezed
654     auto previewWidth = previewPattern->GetStackAfterScaleActualWidth();
655     auto previewHeight = previewPattern->GetStackAfterScaleActualHeight();
656     CHECK_NULL_RETURN(width > 0 && height > 0, scaleRet);
657     if (LessOrEqual(previewWidth / width, previewHeight / height)) {
658         CHECK_EQUAL_RETURN(previewWidth, 0, scaleRet);
659         scaleRet = width / previewWidth;
660         previewPattern->SetIsWidthDistLarger(false);
661     } else {
662         CHECK_EQUAL_RETURN(previewHeight, 0, scaleRet);
663         scaleRet = height / previewHeight;
664         previewPattern->SetIsWidthDistLarger(true);
665     }
666     return scaleRet;
667 }
668 
SetHoverImageCustomPreviewInfo(const RefPtr<FrameNode> & previewNode,const MenuParam & menuParam,int32_t width,int32_t height)669 void SetHoverImageCustomPreviewInfo(const RefPtr<FrameNode>& previewNode, const MenuParam& menuParam,
670     int32_t width, int32_t height)
671 {
672     CHECK_NULL_VOID(previewNode);
673     auto previewPattern = previewNode->GetPattern<MenuPreviewPattern>();
674     CHECK_NULL_VOID(previewPattern);
675     auto baseScale = GetHoverImageCustomPreviewBaseScaleInfo(menuParam, width, height, previewPattern);
676     CHECK_NULL_VOID(!NearZero(baseScale));
677 
678     auto hoverImageScaleFrom = menuParam.hoverImageAnimationOptions.scaleFrom;
679     hoverImageScaleFrom = LessOrEqual(hoverImageScaleFrom, 0.0) ? PREVIEW_ORIGIN_SCALE : hoverImageScaleFrom;
680     previewPattern->SetHoverImageScaleFrom(baseScale * hoverImageScaleFrom);
681 
682     auto hoverImageScaleTo = menuParam.hoverImageAnimationOptions.scaleTo;
683     hoverImageScaleTo = LessOrEqual(hoverImageScaleTo, 0.0) ? PREVIEW_ORIGIN_SCALE : hoverImageScaleTo;
684     auto hoverImageScaleToNew = baseScale * hoverImageScaleTo;
685     previewPattern->SetHoverImageScaleTo(hoverImageScaleToNew);
686     previewPattern->SetIsHoverImageScaleNearEqual(NearEqual(hoverImageScaleFrom, hoverImageScaleTo));
687 
688     // get actual area size for clip visible area
689     previewPattern->SetHoverImageAfterScaleWidth(width / baseScale);
690     previewPattern->SetHoverImageAfterScaleHeight(height  / baseScale);
691 
692     // stack attr will not changed by the scale animation, but a start cooradinate is required to calc the clip diff
693     previewPattern->SetClipStartWidth(previewPattern->GetStackAfterScaleActualWidth() * hoverImageScaleToNew);
694     previewPattern->SetClipStartHeight(previewPattern->GetStackAfterScaleActualHeight() * hoverImageScaleToNew);
695     previewPattern->SetClipStartValue(previewPattern->GetIsWidthDistLarger() ? previewPattern->GetClipStartWidth() :
696         previewPattern->GetClipStartHeight());
697     previewPattern->SetClipEndValue(previewPattern->GetIsWidthDistLarger() ?
698         previewPattern->GetStackAfterScaleActualWidth() : previewPattern->GetStackAfterScaleActualHeight());
699 
700     previewPattern->SetHoverImageAfterScaleOffset(OffsetF((previewPattern->GetStackAfterScaleActualWidth() -
701         previewPattern->GetHoverImageAfterScaleWidth()) / HALF_DIVIDE,
702         (previewPattern->GetStackAfterScaleActualHeight() -
703         previewPattern->GetHoverImageAfterScaleHeight()) / HALF_DIVIDE));
704 }
705 
SetAccessibilityPixelMap(const RefPtr<FrameNode> & targetNode,RefPtr<FrameNode> & imageNode)706 void SetAccessibilityPixelMap(const RefPtr<FrameNode>& targetNode, RefPtr<FrameNode>& imageNode)
707 {
708     auto targetProps = targetNode->GetAccessibilityProperty<AccessibilityProperty>();
709     CHECK_NULL_VOID(targetProps);
710     targetProps->SetOnAccessibilityFocusCallback([targetWK = AceType::WeakClaim(AceType::RawPtr(targetNode)),
711         imageWK = AceType::WeakClaim(AceType::RawPtr(imageNode))](bool focus) {
712         if (!focus) {
713             auto targetNode = targetWK.Upgrade();
714             CHECK_NULL_VOID(targetNode);
715             auto context = targetNode->GetRenderContext();
716             CHECK_NULL_VOID(context);
717             auto pixelMap = context->GetThumbnailPixelMap();
718             auto imageNode = imageWK.Upgrade();
719             CHECK_NULL_VOID(imageNode);
720             auto props = imageNode->GetLayoutProperty<ImageLayoutProperty>();
721             CHECK_NULL_VOID(props);
722             props->UpdateAutoResize(false);
723             props->UpdateImageSourceInfo(ImageSourceInfo(pixelMap));
724             imageNode->MarkModifyDone();
725         }
726     });
727 }
728 
SetPixelMap(const RefPtr<FrameNode> & target,const RefPtr<FrameNode> & wrapperNode,const RefPtr<FrameNode> & hoverImageStackNode,const RefPtr<FrameNode> & previewNode,const MenuParam & menuParam)729 void SetPixelMap(const RefPtr<FrameNode>& target, const RefPtr<FrameNode>& wrapperNode,
730     const RefPtr<FrameNode>& hoverImageStackNode, const RefPtr<FrameNode>& previewNode, const MenuParam& menuParam)
731 {
732     CHECK_NULL_VOID(target);
733     auto eventHub = target->GetEventHub<NG::EventHub>();
734     CHECK_NULL_VOID(eventHub);
735     auto gestureHub = eventHub->GetGestureEventHub();
736     CHECK_NULL_VOID(gestureHub);
737     RefPtr<PixelMap> pixelMap = gestureHub->GetPixelMap();
738     CHECK_NULL_VOID(pixelMap);
739     auto width = pixelMap->GetWidth();
740     auto height = pixelMap->GetHeight();
741     auto imageOffset = GetFloatImageOffset(target);
742     auto imageNode = FrameNode::GetOrCreateFrameNode(V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
743         []() { return AceType::MakeRefPtr<ImagePattern>(); });
744     auto renderProps = imageNode->GetPaintProperty<ImageRenderProperty>();
745     renderProps->UpdateImageInterpolation(ImageInterpolation::HIGH);
746     auto props = imageNode->GetLayoutProperty<ImageLayoutProperty>();
747     props->UpdateAutoResize(false);
748     props->UpdateImageSourceInfo(ImageSourceInfo(pixelMap));
749     auto imagePattern = imageNode->GetPattern<ImagePattern>();
750     CHECK_NULL_VOID(imagePattern);
751     imagePattern->SetSyncLoad(true);
752     SetAccessibilityPixelMap(target, imageNode);
753     auto hub = imageNode->GetEventHub<EventHub>();
754     CHECK_NULL_VOID(hub);
755     auto imageGestureHub = hub->GetOrCreateGestureEventHub();
756     CHECK_NULL_VOID(imageGestureHub);
757     InitPanEvent(gestureHub, imageGestureHub, wrapperNode);
758 
759     if (menuParam.isShowHoverImage) {
760         props->UpdateImageFit(ImageFit::CONTAIN);
761 
762         imageNode->GetLayoutProperty()->UpdateMeasureType(MeasureType::MATCH_PARENT);
763         imageNode->MarkModifyDone();
764         imageNode->MountToParent(hoverImageStackNode);
765     } else {
766         auto targetSize = CalcSize(NG::CalcLength(width), NG::CalcLength(height));
767         props->UpdateUserDefinedIdealSize(targetSize);
768         props->UpdateImageFit(ImageFit::FILL);
769 
770         auto imageContext = imageNode->GetRenderContext();
771         CHECK_NULL_VOID(imageContext);
772         if (menuParam.previewBorderRadius) {
773             imageContext->UpdateBorderRadius(menuParam.previewBorderRadius.value());
774         }
775         imageNode->MarkModifyDone();
776         imageNode->MountToParent(wrapperNode);
777         DragAnimationHelper::UpdateGatherNodeToTop();
778         DragDropFuncWrapper::UpdatePositionFromFrameNode(imageNode, target, width, height);
779         imageOffset = DragDropFuncWrapper::GetPaintRectCenterToScreen(target) -
780             OffsetF(width / HALF_DIVIDE, height / HALF_DIVIDE);
781         imageOffset -= DragDropFuncWrapper::GetCurrentWindowOffset(imageNode->GetContextRefPtr());
782         MountTextNode(wrapperNode, previewNode);
783     }
784 
785     auto geometryNode = imageNode->GetGeometryNode();
786     CHECK_NULL_VOID(geometryNode);
787     geometryNode->SetFrameOffset(imageOffset);
788 }
789 
SetFilter(const RefPtr<FrameNode> & targetNode,const RefPtr<FrameNode> & menuWrapperNode)790 void SetFilter(const RefPtr<FrameNode>& targetNode, const RefPtr<FrameNode>& menuWrapperNode)
791 {
792     auto parent = targetNode->GetParent();
793     CHECK_NULL_VOID(parent);
794     while (parent->GetDepth() != 1) {
795         parent = parent->GetParent();
796         CHECK_NULL_VOID(parent);
797     }
798     auto containerId = Container::CurrentId();
799     if (containerId >= MIN_SUBCONTAINER_ID) {
800         containerId = SubwindowManager::GetInstance()->GetParentContainerId(containerId);
801     }
802     ContainerScope scope(containerId);
803     auto container = Container::Current();
804     CHECK_NULL_VOID(container);
805     auto pipelineContext = AceType::DynamicCast<PipelineContext>(container->GetPipelineContext());
806     CHECK_NULL_VOID(pipelineContext);
807     auto manager = pipelineContext->GetOverlayManager();
808     CHECK_NULL_VOID(manager);
809     auto menuTheme = pipelineContext->GetTheme<NG::MenuTheme>();
810     CHECK_NULL_VOID(menuTheme);
811     if (!manager->GetHasFilter() && !manager->GetIsOnAnimation()) {
812         bool isBindOverlayValue = targetNode->GetLayoutProperty()->GetIsBindOverlayValue(false);
813         CHECK_NULL_VOID(isBindOverlayValue && menuTheme->GetHasFilter());
814         // insert columnNode to rootNode
815         auto columnNode = FrameNode::CreateFrameNode(V2::COLUMN_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
816             AceType::MakeRefPtr<LinearLayoutPattern>(true));
817         columnNode->GetLayoutProperty()->UpdateMeasureType(MeasureType::MATCH_PARENT);
818         auto accessibilityProperty = columnNode->GetAccessibilityProperty<NG::AccessibilityProperty>();
819         if (accessibilityProperty) {
820             accessibilityProperty->SetAccessibilityHoverPriority(true); // consume barrierfree hover event
821         }
822         // set filter
823         if (container->IsScenceBoardWindow()) {
824             auto windowScene = manager->FindWindowScene(targetNode);
825             manager->MountFilterToWindowScene(columnNode, windowScene);
826             manager->ShowFilterAnimation(columnNode);
827         } else if (container->IsUIExtensionWindow()) {
828             // mount filter node on subwindow to ensure filter node's size equals to host window's size
829             CHECK_NULL_VOID(menuWrapperNode);
830             auto menuWrapperPattern = menuWrapperNode->GetPattern<MenuWrapperPattern>();
831             CHECK_NULL_VOID(menuWrapperPattern);
832             menuWrapperPattern->SetFilterColumnNode(columnNode);
833         } else {
834             columnNode->MountToParent(parent);
835             columnNode->OnMountToParentDone();
836             manager->SetHasFilter(true);
837             manager->SetFilterActive(true);
838             manager->SetFilterColumnNode(columnNode);
839             parent->MarkDirtyNode(NG::PROPERTY_UPDATE_BY_CHILD_REQUEST);
840             manager->ShowFilterAnimation(columnNode);
841         }
842     }
843 }
844 
SetPreviewInfoToMenu(const RefPtr<FrameNode> & targetNode,const RefPtr<FrameNode> & wrapperNode,const RefPtr<FrameNode> & hoverImageStackNode,const RefPtr<FrameNode> & previewNode,const MenuParam & menuParam)845 void SetPreviewInfoToMenu(const RefPtr<FrameNode>& targetNode, const RefPtr<FrameNode>& wrapperNode,
846     const RefPtr<FrameNode>& hoverImageStackNode, const RefPtr<FrameNode>& previewNode, const MenuParam& menuParam)
847 {
848     CHECK_NULL_VOID(targetNode);
849     auto eventHub = targetNode->GetEventHub<EventHub>();
850     CHECK_NULL_VOID(eventHub);
851     auto gestureEventHub = eventHub->GetGestureEventHub();
852     CHECK_NULL_VOID(gestureEventHub);
853     auto isAllowedDrag = gestureEventHub->IsAllowedDrag(eventHub) && !gestureEventHub->GetTextDraggable();
854     if (targetNode->GetTag() == V2::TEXT_ETS_TAG && targetNode->IsDraggable() && !targetNode->IsCustomerSet()) {
855         auto textPattern = targetNode->GetPattern<TextPattern>();
856         if (textPattern && textPattern->GetCopyOptions() == CopyOptions::None) {
857             isAllowedDrag = false;
858         }
859     }
860     if (menuParam.previewMode != MenuPreviewMode::NONE || isAllowedDrag) {
861         SetFilter(targetNode, wrapperNode);
862     }
863     if (menuParam.previewMode == MenuPreviewMode::IMAGE ||
864         (menuParam.previewMode == MenuPreviewMode::NONE && menuParam.menuBindType == MenuBindingType::LONG_PRESS &&
865             isAllowedDrag) ||
866         menuParam.isShowHoverImage) {
867         SetPixelMap(targetNode, wrapperNode, hoverImageStackNode, previewNode, menuParam);
868     }
869     if (menuParam.previewMode == MenuPreviewMode::NONE && isAllowedDrag) {
870         CHECK_NULL_VOID(wrapperNode);
871         auto pixelMapNode = AceType::DynamicCast<FrameNode>(wrapperNode->GetChildAtIndex(1));
872         CHECK_NULL_VOID(pixelMapNode);
873         auto renderContext = pixelMapNode->GetRenderContext();
874         CHECK_NULL_VOID(renderContext);
875         renderContext->UpdateZIndex(-1);
876         auto menuNode = AceType::DynamicCast<FrameNode>(wrapperNode->GetChildAtIndex(0));
877         if (menuNode) {
878             MenuView::ShowPixelMapAnimation(menuNode);
879         }
880         // if filter set in subwindow, need to adjust zOrder to show in back.
881         auto menuWrapperPattern = wrapperNode->GetPattern<MenuWrapperPattern>();
882         CHECK_NULL_VOID(menuWrapperPattern);
883         auto columnNode = menuWrapperPattern->GetFilterColumnNode();
884         CHECK_NULL_VOID(columnNode);
885         auto columnRenderContext = columnNode->GetRenderContext();
886         CHECK_NULL_VOID(columnRenderContext);
887         columnRenderContext->UpdateZIndex(-1);
888     }
889 }
890 
SetHasCustomRadius(const RefPtr<FrameNode> & menuWrapperNode,const RefPtr<FrameNode> & menuNode,const MenuParam & menuParam)891 void SetHasCustomRadius(
892     const RefPtr<FrameNode>& menuWrapperNode, const RefPtr<FrameNode>& menuNode, const MenuParam& menuParam)
893 {
894     CHECK_NULL_VOID(menuWrapperNode);
895     CHECK_NULL_VOID(menuNode);
896     auto menuWrapperPattern = menuWrapperNode->GetPattern<MenuWrapperPattern>();
897     CHECK_NULL_VOID(menuWrapperPattern);
898     if (menuParam.borderRadius.has_value()) {
899         menuWrapperPattern->SetHasCustomRadius(true);
900         auto menuProperty = menuNode->GetLayoutProperty<MenuLayoutProperty>();
901         CHECK_NULL_VOID(menuProperty);
902         menuProperty->UpdateBorderRadius(menuParam.borderRadius.value());
903     } else {
904         menuWrapperPattern->SetHasCustomRadius(false);
905     }
906 }
907 } // namespace
908 
CalcHoverScaleInfo(const RefPtr<FrameNode> & menuNode)909 void MenuView::CalcHoverScaleInfo(const RefPtr<FrameNode>& menuNode)
910 {
911     CHECK_NULL_VOID(menuNode);
912     auto menuPattern = menuNode->GetPattern<MenuPattern>();
913     CHECK_NULL_VOID(menuPattern);
914     CHECK_NULL_VOID(menuPattern->GetIsShowHoverImage());
915 
916     auto wrapperNode = menuPattern->GetMenuWrapper();
917     CHECK_NULL_VOID(wrapperNode);
918     auto menuWrapperPattern = wrapperNode->GetPattern<MenuWrapperPattern>();
919     CHECK_NULL_VOID(menuWrapperPattern);
920     auto imageNode = menuWrapperPattern->GetHoverImagePreview();
921     CHECK_NULL_VOID(imageNode);
922     auto imagePattern = imageNode->GetPattern<ImagePattern>();
923     CHECK_NULL_VOID(imagePattern);
924 
925     auto preview = menuWrapperPattern->GetPreview();
926     CHECK_NULL_VOID(preview);
927     auto previewGeometryNode = preview->GetGeometryNode();
928     CHECK_NULL_VOID(previewGeometryNode);
929     auto previewPattern = preview->GetPattern<MenuPreviewPattern>();
930     CHECK_NULL_VOID(previewPattern);
931     auto previewSize = previewGeometryNode->GetMarginFrameSize();
932     previewPattern->SetStackAfterScaleActualWidth(previewSize.Width());
933     previewPattern->SetStackAfterScaleActualHeight(previewSize.Height());
934 
935     auto menuParam = menuWrapperPattern->GetMenuParam();
936     auto imageRawSize = imagePattern->GetRawImageSize();
937     SetHoverImageCustomPreviewInfo(preview, menuParam, imageRawSize.Width(), imageRawSize.Height());
938 }
939 
ShowPixelMapAnimation(const RefPtr<FrameNode> & menuNode)940 void MenuView::ShowPixelMapAnimation(const RefPtr<FrameNode>& menuNode)
941 {
942     CHECK_NULL_VOID(menuNode);
943     auto menuPattern = menuNode->GetPattern<MenuPattern>();
944     CHECK_NULL_VOID(menuPattern);
945     auto wrapperNode = menuPattern->GetMenuWrapper();
946     CHECK_NULL_VOID(wrapperNode);
947     auto menuWrapperPattern = wrapperNode->GetPattern<MenuWrapperPattern>();
948     CHECK_NULL_VOID(menuWrapperPattern);
949 
950     auto preview = AceType::DynamicCast<FrameNode>(wrapperNode->GetChildAtIndex(1));
951     CHECK_NULL_VOID(preview);
952     auto imageNode = preview->GetTag() == V2::FLEX_ETS_TAG ? menuWrapperPattern->GetHoverImagePreview() : preview;
953     CHECK_NULL_VOID(imageNode);
954     auto imageContext = imageNode->GetRenderContext();
955     CHECK_NULL_VOID(imageContext);
956     imageContext->SetClipToBounds(true);
957 
958     auto pipelineContext = menuNode->GetContext();
959     CHECK_NULL_VOID(pipelineContext);
960     auto menuTheme = pipelineContext->GetTheme<NG::MenuTheme>();
961     CHECK_NULL_VOID(menuTheme);
962 
963     auto isShowHoverImage = menuPattern->GetIsShowHoverImage();
964     if (menuWrapperPattern->HasPreviewTransitionEffect()) {
965         auto layoutProperty = imageNode->GetLayoutProperty();
966         layoutProperty->UpdateVisibility(VisibleType::VISIBLE, true);
967     } else {
968         if (isShowHoverImage) {
969             auto hoverImageStackNode = menuWrapperPattern->GetHoverImageStackNode();
970             auto previewNode = menuWrapperPattern->GetHoverImageCustomPreview();
971             ShowHoverImageAnimationProc(hoverImageStackNode, previewNode, imageContext, menuWrapperPattern);
972         } else {
973             ShowPixelMapScaleAnimationProc(menuTheme, imageNode, menuPattern);
974         }
975     }
976     ShowBorderRadiusAndShadowAnimation(menuTheme, imageNode, isShowHoverImage);
977 }
978 
979 // create menu with MenuElement array
Create(std::vector<OptionParam> && params,int32_t targetId,const std::string & targetTag,MenuType type,const MenuParam & menuParam)980 RefPtr<FrameNode> MenuView::Create(std::vector<OptionParam>&& params, int32_t targetId, const std::string& targetTag,
981     MenuType type, const MenuParam& menuParam)
982 {
983     auto [wrapperNode, menuNode] = CreateMenu(targetId, targetTag, type);
984     UpdateMenuBackgroundStyle(menuNode, menuParam);
985     auto column = FrameNode::CreateFrameNode(V2::COLUMN_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
986         AceType::MakeRefPtr<LinearLayoutPattern>(true));
987     if (!menuParam.title.empty()) {
988         CreateTitleNode(menuParam.title, column);
989     }
990     SetHasCustomRadius(wrapperNode, menuNode, menuParam);
991     auto menuPattern = menuNode->GetPattern<MenuPattern>();
992     CHECK_NULL_RETURN(menuPattern, nullptr);
993     bool optionsHasIcon = GetHasIcon(params);
994     bool optionsHasSymbol = GetHasSymbol(params);
995     RefPtr<FrameNode> optionNode = nullptr;
996     // append options to menu
997     for (size_t i = 0; i < params.size(); ++i) {
998         if (params[i].symbol != nullptr) {
999             optionNode = OptionView::CreateMenuOption(optionsHasSymbol, params, i);
1000         } else {
1001             optionNode = OptionView::CreateMenuOption(
1002                 optionsHasIcon, { params[i].value, params[i].isPasteOption }, params[i].action, i, params[i].icon);
1003         }
1004         if (!optionNode) {
1005             continue;
1006         }
1007         NeedAgingUpdateNode(optionNode);
1008         menuPattern->AddOptionNode(optionNode);
1009         auto menuWeak = AceType::WeakClaim(AceType::RawPtr(menuNode));
1010         auto eventHub = optionNode->GetEventHub<EventHub>();
1011         CHECK_NULL_RETURN(eventHub, nullptr);
1012         eventHub->SetEnabled(params[i].enabled);
1013         auto focusHub = optionNode->GetFocusHub();
1014         CHECK_NULL_RETURN(focusHub, nullptr);
1015         focusHub->SetEnabled(params[i].enabled);
1016 
1017         OptionKeepMenu(optionNode, menuWeak);
1018         // first node never paints divider
1019         auto props = optionNode->GetPaintProperty<OptionPaintProperty>();
1020         if (i == 0 && menuParam.title.empty()) {
1021             props->UpdateNeedDivider(false);
1022         }
1023         if (optionsHasIcon) {
1024             props->UpdateHasIcon(true);
1025         }
1026         optionNode->MountToParent(column);
1027         optionNode->MarkModifyDone();
1028     }
1029     auto menuProperty = menuNode->GetLayoutProperty<MenuLayoutProperty>();
1030     if (menuProperty) {
1031         menuProperty->UpdateTitle(menuParam.title);
1032         menuProperty->UpdatePositionOffset(menuParam.positionOffset);
1033         if (menuParam.placement.has_value()) {
1034             menuProperty->UpdateMenuPlacement(menuParam.placement.value_or(OHOS::Ace::Placement::BOTTOM));
1035         }
1036         menuProperty->UpdateShowInSubWindow(menuParam.isShowInSubWindow);
1037     }
1038     UpdateMenuPaintProperty(menuNode, menuParam, type);
1039     auto scroll = CreateMenuScroll(column);
1040     CHECK_NULL_RETURN(scroll, nullptr);
1041     scroll->MountToParent(menuNode);
1042     scroll->MarkModifyDone();
1043     menuNode->MarkModifyDone();
1044     return wrapperNode;
1045 }
1046 
SetPreviewTransitionEffect(const RefPtr<FrameNode> & menuWrapperNode,const MenuParam & menuParam)1047 void SetPreviewTransitionEffect(const RefPtr<FrameNode> &menuWrapperNode, const MenuParam &menuParam)
1048 {
1049     TAG_LOGD(AceLogTag::ACE_DIALOG, "set menu transition effect");
1050     CHECK_NULL_VOID(menuWrapperNode);
1051     auto pattern = menuWrapperNode->GetPattern<MenuWrapperPattern>();
1052     CHECK_NULL_VOID(pattern);
1053     pattern->SetHasPreviewTransitionEffect(menuParam.hasPreviewTransitionEffect);
1054 }
1055 
SetPreviewScaleAndHoverImageScale(const RefPtr<FrameNode> & menuNode,const MenuParam & menuParam)1056 void SetPreviewScaleAndHoverImageScale(const RefPtr<FrameNode>& menuNode, const MenuParam& menuParam)
1057 {
1058     auto pattern = menuNode->GetPattern<MenuPattern>();
1059     CHECK_NULL_VOID(pattern);
1060     pattern->SetPreviewMode(menuParam.previewMode);
1061     pattern->SetPreviewBeforeAnimationScale(menuParam.previewAnimationOptions.scaleFrom);
1062     pattern->SetPreviewAfterAnimationScale(menuParam.previewAnimationOptions.scaleTo);
1063     pattern->SetIsShowHoverImage(menuParam.isShowHoverImage);
1064 }
1065 
CustomPreviewParentNodeCreate(const RefPtr<FrameNode> & stackNode,const RefPtr<FrameNode> & posNode,const RefPtr<FrameNode> & wrapperNode,const RefPtr<FrameNode> & previewNode)1066 void MenuView::CustomPreviewParentNodeCreate(const RefPtr<FrameNode>& stackNode, const RefPtr<FrameNode>& posNode,
1067     const RefPtr<FrameNode>& wrapperNode, const RefPtr<FrameNode>& previewNode)
1068 {
1069     CHECK_NULL_VOID(previewNode);
1070     auto previewPattern = previewNode->GetPattern<MenuPreviewPattern>();
1071     CHECK_NULL_VOID(previewPattern);
1072     auto previewWidth = previewPattern->GetCustomPreviewWidth();
1073     auto previewHeight = previewPattern->GetCustomPreviewHeight();
1074     CHECK_NULL_VOID(stackNode);
1075     CalcSize maxSize = { CalcLength(previewWidth), CalcLength(previewHeight) };
1076 
1077     CHECK_NULL_VOID(posNode);
1078     UpdateContainerIdealSizeConstraint(posNode, maxSize);
1079     auto posProps = posNode->GetLayoutProperty<FlexLayoutProperty>();
1080     CHECK_NULL_VOID(posProps);
1081     posProps->UpdateMainAxisAlign(FlexAlign::CENTER);
1082     posProps->UpdateCrossAxisAlign(FlexAlign::CENTER);
1083     stackNode->MountToParent(posNode);
1084     stackNode->MarkModifyDone();
1085 
1086     CHECK_NULL_VOID(wrapperNode);
1087     posNode->MountToParent(wrapperNode);
1088     posNode->MarkModifyDone();
1089 }
1090 
1091 // contextmenu mount info proc
ContextMenuChildMountProc(const RefPtr<FrameNode> & targetNode,const RefPtr<FrameNode> & wrapperNode,const RefPtr<FrameNode> & previewNode,const RefPtr<FrameNode> & menuNode,const MenuParam & menuParam)1092 void MenuView::ContextMenuChildMountProc(const RefPtr<FrameNode>& targetNode, const RefPtr<FrameNode>& wrapperNode,
1093     const RefPtr<FrameNode>& previewNode, const RefPtr<FrameNode>& menuNode, const MenuParam& menuParam)
1094 {
1095     // stack to put image and custom preview and control visible area when hoverImage api is using
1096     auto hoverImageStackNode = FrameNode::GetOrCreateFrameNode(V2::STACK_ETS_TAG,
1097         ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<StackPattern>(); });
1098     CHECK_NULL_VOID(hoverImageStackNode);
1099 
1100     // flex  to control visible area position
1101     auto hoverImagePosNode = FrameNode::CreateFrameNode(V2::FLEX_ETS_TAG,
1102         ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<FlexLayoutPattern>(false));
1103     CHECK_NULL_VOID(hoverImagePosNode);
1104 
1105     if (menuParam.isShowHoverImage) {
1106         CustomPreviewParentNodeCreate(hoverImageStackNode, hoverImagePosNode, wrapperNode, previewNode);
1107         CHECK_NULL_VOID(wrapperNode);
1108         auto menuWrapperPattern = wrapperNode->GetPattern<MenuWrapperPattern>();
1109         CHECK_NULL_VOID(menuWrapperPattern);
1110         menuWrapperPattern->SetIsShowHoverImage(menuParam.isShowHoverImage);
1111         auto previewRenderContext = previewNode->GetRenderContext();
1112         CHECK_NULL_VOID(previewRenderContext);
1113         previewRenderContext->UpdateOpacity(0.0);
1114     }
1115 
1116     if (menuNode) {
1117         SetPreviewInfoToMenu(targetNode, wrapperNode, hoverImageStackNode, previewNode, menuParam);
1118     }
1119 
1120     if (menuParam.previewMode == MenuPreviewMode::CUSTOM) {
1121         previewNode->MountToParent(menuParam.isShowHoverImage ? hoverImageStackNode : wrapperNode);
1122         previewNode->MarkModifyDone();
1123     }
1124 }
1125 
1126 // 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)1127 RefPtr<FrameNode> MenuView::Create(const RefPtr<UINode>& customNode, int32_t targetId, const std::string& targetTag,
1128     const MenuParam& menuParam, bool withWrapper, const RefPtr<UINode>& previewCustomNode)
1129 {
1130     auto type = menuParam.type;
1131     auto [wrapperNode, menuNode] = CreateMenu(targetId, targetTag, type);
1132     // create previewNode
1133     auto previewNode = FrameNode::CreateFrameNode(V2::MENU_PREVIEW_ETS_TAG,
1134         ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<MenuPreviewPattern>());
1135     CHECK_NULL_RETURN(previewNode, nullptr);
1136     auto menuWrapperPattern = wrapperNode->GetPattern<MenuWrapperPattern>();
1137     CHECK_NULL_RETURN(menuWrapperPattern, nullptr);
1138     menuWrapperPattern->SetMenuParam(menuParam);
1139 
1140     CustomPreviewNodeProc(previewNode, menuParam, previewCustomNode);
1141 
1142     UpdateMenuBackgroundStyle(menuNode, menuParam);
1143     SetPreviewTransitionEffect(wrapperNode, menuParam);
1144     SetHasCustomRadius(wrapperNode, menuNode, menuParam);
1145 
1146     SetPreviewScaleAndHoverImageScale(menuNode, menuParam);
1147     // put custom node in a scroll to limit its height
1148     auto scroll = CreateMenuScroll(customNode);
1149     CHECK_NULL_RETURN(scroll, nullptr);
1150     MountScrollToMenu(customNode, scroll, menuNode);
1151 
1152     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
1153         UpdateMenuBorderEffect(menuNode);
1154     }
1155     menuNode->MarkModifyDone();
1156 
1157     auto menuProperty = menuNode->GetLayoutProperty<MenuLayoutProperty>();
1158     if (menuProperty) {
1159         menuProperty->UpdateTitle(menuParam.title);
1160         menuProperty->UpdatePositionOffset(menuParam.positionOffset);
1161         if (menuParam.placement.has_value()) {
1162             menuProperty->UpdateMenuPlacement(menuParam.placement.value());
1163         }
1164         menuProperty->UpdateShowInSubWindow(menuParam.isShowInSubWindow);
1165     }
1166     UpdateMenuPaintProperty(menuNode, menuParam, type);
1167     if (type == MenuType::SUB_MENU || type == MenuType::SELECT_OVERLAY_SUB_MENU || !withWrapper) {
1168         wrapperNode->RemoveChild(menuNode);
1169         wrapperNode.Reset();
1170         return menuNode;
1171     }
1172     if (type == MenuType::CONTEXT_MENU) {
1173         auto targetNode = FrameNode::GetFrameNode(targetTag, targetId);
1174         ContextMenuChildMountProc(targetNode, wrapperNode, previewNode, menuNode, menuParam);
1175         MountTextNode(wrapperNode, previewCustomNode);
1176     }
1177     return wrapperNode;
1178 }
1179 
UpdateMenuPaintProperty(const RefPtr<FrameNode> & menuNode,const MenuParam & menuParam,const MenuType & type)1180 void MenuView::UpdateMenuPaintProperty(
1181     const RefPtr<FrameNode>& menuNode, const MenuParam& menuParam, const MenuType& type)
1182 {
1183     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1184         if (!(type == MenuType::CONTEXT_MENU || type == MenuType::MENU)) {
1185             return;
1186         }
1187     } else {
1188         if (!(type == MenuType::CONTEXT_MENU)) {
1189             return;
1190         }
1191     }
1192 
1193     auto paintProperty = menuNode->GetPaintProperty<MenuPaintProperty>();
1194     CHECK_NULL_VOID(paintProperty);
1195     paintProperty->UpdateEnableArrow(menuParam.enableArrow.value_or(false));
1196     paintProperty->UpdateArrowOffset(menuParam.arrowOffset.value_or(Dimension(0)));
1197 }
1198 
Create(const std::vector<SelectParam> & params,int32_t targetId,const std::string & targetTag)1199 RefPtr<FrameNode> MenuView::Create(
1200     const std::vector<SelectParam>& params, int32_t targetId, const std::string& targetTag)
1201 {
1202     auto [wrapperNode, menuNode] = CreateMenu(targetId, targetTag);
1203     auto column = FrameNode::CreateFrameNode(V2::COLUMN_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
1204         AceType::MakeRefPtr<LinearLayoutPattern>(true));
1205     auto menuPattern = menuNode->GetPattern<MenuPattern>();
1206     CHECK_NULL_RETURN(menuPattern, nullptr);
1207     auto menuProperty = menuNode->GetLayoutProperty<MenuLayoutProperty>();
1208     CHECK_NULL_RETURN(menuProperty, nullptr);
1209     menuProperty->UpdateShowInSubWindow(false);
1210     for (size_t i = 0; i < params.size(); ++i) {
1211         auto optionNode = OptionView::CreateSelectOption(params[i], i);
1212         auto optionPattern = optionNode->GetPattern<OptionPattern>();
1213         CHECK_NULL_RETURN(optionPattern, nullptr);
1214         optionPattern->SetIsSelectOption(true);
1215         menuPattern->AddOptionNode(optionNode);
1216         auto menuWeak = AceType::WeakClaim(AceType::RawPtr(menuNode));
1217         OptionKeepMenu(optionNode, menuWeak);
1218         // first node never paints divider
1219         if (i == 0) {
1220             auto props = optionNode->GetPaintProperty<OptionPaintProperty>();
1221             props->UpdateNeedDivider(false);
1222             auto focusHub = optionNode->GetOrCreateFocusHub();
1223             CHECK_NULL_RETURN(focusHub, nullptr);
1224             focusHub->SetIsDefaultFocus(true);
1225         }
1226         optionNode->MarkModifyDone();
1227         optionNode->MountToParent(column);
1228     }
1229     auto scroll = CreateMenuScroll(column);
1230     CHECK_NULL_RETURN(scroll, nullptr);
1231     auto scrollPattern = scroll->GetPattern<ScrollPattern>();
1232     CHECK_NULL_RETURN(scrollPattern, nullptr);
1233     scrollPattern->SetIsSelectScroll(true);
1234     scroll->MountToParent(menuNode);
1235     scroll->MarkModifyDone();
1236     menuNode->MarkModifyDone();
1237 
1238     menuPattern->SetIsSelectMenu(true);
1239     return wrapperNode;
1240 }
1241 
UpdateMenuBackgroundEffect(const RefPtr<FrameNode> & menuNode)1242 void MenuView::UpdateMenuBackgroundEffect(const RefPtr<FrameNode>& menuNode)
1243 {
1244     CHECK_NULL_VOID(menuNode);
1245     auto pipeLineContext = menuNode->GetContextWithCheck();
1246     CHECK_NULL_VOID(pipeLineContext);
1247     auto menuTheme = pipeLineContext->GetTheme<NG::MenuTheme>();
1248     CHECK_NULL_VOID(menuTheme);
1249     if (menuTheme->GetBgBlurEffectEnable()) {
1250         auto renderContext = menuNode->GetRenderContext();
1251         CHECK_NULL_VOID(renderContext);
1252         auto saturation = menuTheme->GetBgEffectSaturation();
1253         auto brightness = menuTheme->GetBgEffectBrightness();
1254         auto radius = menuTheme->GetBgEffectRadius();
1255         auto color = menuTheme->GetBgEffectColor();
1256         EffectOption option = { radius, saturation, brightness, color };
1257         renderContext->UpdateBackgroundColor(Color::TRANSPARENT);
1258         renderContext->UpdateBackgroundEffect(option);
1259     }
1260 }
1261 
UpdateMenuBorderEffect(const RefPtr<FrameNode> & menuNode)1262 void MenuView::UpdateMenuBorderEffect(const RefPtr<FrameNode>& menuNode)
1263 {
1264     CHECK_NULL_VOID(menuNode);
1265     auto pipeLineContext = menuNode->GetContextWithCheck();
1266     CHECK_NULL_VOID(pipeLineContext);
1267     auto menuTheme = pipeLineContext->GetTheme<NG::MenuTheme>();
1268     CHECK_NULL_VOID(menuTheme);
1269     if (menuTheme->GetDoubleBorderEnable()) {
1270         auto renderContext = menuNode->GetRenderContext();
1271         CHECK_NULL_VOID(renderContext);
1272         BorderStyleProperty styleProp;
1273         styleProp.SetBorderStyle(BorderStyle::SOLID);
1274         BorderColorProperty outerColorProp;
1275         outerColorProp.SetColor(menuTheme->GetOuterBorderColor());
1276         auto theme = pipeLineContext->GetTheme<SelectTheme>();
1277         CHECK_NULL_VOID(theme);
1278         BorderRadiusProperty outerRadiusProp;
1279         outerRadiusProp.SetRadius(Dimension(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) ?
1280             theme->GetMenuDefaultRadius() : theme->GetMenuBorderRadius()));
1281         BorderWidthProperty outerWidthProp;
1282         outerWidthProp.SetBorderWidth(Dimension(menuTheme->GetOuterBorderWidth()));
1283         renderContext->SetOuterBorderStyle(styleProp);
1284         renderContext->SetOuterBorderColor(outerColorProp);
1285         renderContext->UpdateOuterBorderRadius(outerRadiusProp);
1286         renderContext->SetOuterBorderWidth(outerWidthProp);
1287         BorderColorProperty innerColorProp;
1288         innerColorProp.SetColor(menuTheme->GetInnerBorderColor());
1289         BorderRadiusProperty innerRadiusProp;
1290         innerRadiusProp.SetRadius(Dimension(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) ?
1291             theme->GetMenuDefaultRadius() : theme->GetMenuBorderRadius()));
1292         BorderWidthProperty innerWidthProp;
1293         innerWidthProp.SetBorderWidth(Dimension(menuTheme->GetInnerBorderWidth()));
1294         renderContext->SetBorderStyle(styleProp);
1295         renderContext->SetBorderColor(innerColorProp);
1296         renderContext->UpdateBorderRadius(innerRadiusProp);
1297         renderContext->SetBorderWidth(innerWidthProp);
1298     }
1299 }
UpdateMenuBackgroundStyle(const RefPtr<FrameNode> & menuNode,const MenuParam & menuParam)1300 void MenuView::UpdateMenuBackgroundStyle(const RefPtr<FrameNode>& menuNode, const MenuParam& menuParam)
1301 {
1302     auto menuNodeRenderContext = menuNode->GetRenderContext();
1303     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) &&
1304         menuNodeRenderContext->IsUniRenderEnabled()) {
1305         BlurStyleOption styleOption;
1306         styleOption.blurStyle = static_cast<BlurStyle>(
1307             menuParam.backgroundBlurStyle.value_or(static_cast<int>(BlurStyle::COMPONENT_ULTRA_THICK)));
1308         menuNodeRenderContext->UpdateBackBlurStyle(styleOption);
1309         menuNodeRenderContext->UpdateBackgroundColor(menuParam.backgroundColor.value_or(Color::TRANSPARENT));
1310     }
1311 }
1312 
NeedAgingUpdateNode(const RefPtr<FrameNode> & optionNode)1313 void MenuView::NeedAgingUpdateNode(const RefPtr<FrameNode>& optionNode)
1314 {
1315     CHECK_NULL_VOID(optionNode);
1316     auto pipeline = optionNode->GetContextWithCheck();
1317     CHECK_NULL_VOID(pipeline);
1318     auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
1319     CHECK_NULL_VOID(menuTheme);
1320     auto fontScale = pipeline->GetFontScale();
1321     if (NearEqual(fontScale, menuTheme->GetBigFontSizeScale()) ||
1322         NearEqual(fontScale, menuTheme->GetLargeFontSizeScale()) ||
1323         NearEqual(fontScale, menuTheme->GetMaxFontSizeScale())) {
1324         auto optionPattern = optionNode->GetPattern<OptionPattern>();
1325         CHECK_NULL_VOID(optionPattern);
1326         auto textNode = AceType::DynamicCast<FrameNode>(optionPattern->GetTextNode());
1327         CHECK_NULL_VOID(textNode);
1328         auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
1329         CHECK_NULL_VOID(textLayoutProperty);
1330         textLayoutProperty->UpdateMaxLines(menuTheme->GetTextMaxLines());
1331     }
1332 }
1333 } // namespace OHOS::Ace::NG
1334