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