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