1 /*
2  * Copyright (c) 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_item/menu_item_pattern.h"
17 #include "core/components_ng/pattern/menu/menu_item/menu_item_model_ng.h"
18 
19 #include <memory>
20 #include <optional>
21 #include "menu_item_model.h"
22 
23 #include "base/geometry/ng/offset_t.h"
24 #include "base/log/log.h"
25 #include "base/memory/ace_type.h"
26 #include "base/utils/utils.h"
27 #include "core/common/recorder/event_recorder.h"
28 #include "core/common/recorder/node_data_cache.h"
29 #include "core/components/common/properties/shadow_config.h"
30 #include "core/components/select/select_theme.h"
31 #include "core/components/text/text_theme.h"
32 #include "core/components/theme/icon_theme.h"
33 #include "core/components_ng/base/frame_node.h"
34 #include "core/components_ng/base/view_stack_processor.h"
35 #include "core/components_ng/pattern/image/image_pattern.h"
36 #include "core/components_ng/pattern/menu/menu_item/menu_item_event_hub.h"
37 #include "core/components_ng/pattern/menu/menu_item/menu_item_model_ng.h"
38 #include "core/components_ng/pattern/menu/menu_layout_property.h"
39 #include "core/components_ng/pattern/menu/menu_pattern.h"
40 #include "core/components_ng/pattern/menu/menu_theme.h"
41 #include "core/components_ng/pattern/menu/menu_view.h"
42 #include "core/components_ng/pattern/menu/menu_theme.h"
43 #include "core/components_ng/pattern/menu/menu_pattern.h"
44 #include "core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.h"
45 #include "core/components_ng/pattern/text/text_layout_property.h"
46 #include "core/components_ng/pattern/text/text_pattern.h"
47 #include "core/components_ng/property/border_property.h"
48 #include "core/components_v2/inspector/inspector_constants.h"
49 #include "core/pipeline/pipeline_base.h"
50 
51 namespace OHOS::Ace::NG {
52 namespace {
53 const Color ITEM_FILL_COLOR = Color::TRANSPARENT;
54 constexpr double VELOCITY = 0.0f;
55 constexpr double MASS = 1.0f;
56 constexpr double STIFFNESS = 328.0f;
57 constexpr double DAMPING = 33.0f;
58 constexpr double SEMI_CIRCLE_ANGEL = 180.0f;
59 constexpr float OPACITY_EFFECT = 0.99;
60 const std::string SYSTEM_RESOURCE_PREFIX = std::string("resource:///");
61 // id of system resource start from 0x07000000
62 constexpr uint64_t MIN_SYSTEM_RESOURCE_ID = 0x07000000;
63 // id of system resource end to 0x07FFFFFF
64 constexpr uint64_t MAX_SYSTEM_RESOURCE_ID = 0x07FFFFFF;
65 
UpdateFontSize(RefPtr<TextLayoutProperty> & textProperty,RefPtr<MenuLayoutProperty> & menuProperty,const std::optional<Dimension> & fontSize,const Dimension & defaultFontSize)66 void UpdateFontSize(RefPtr<TextLayoutProperty>& textProperty, RefPtr<MenuLayoutProperty>& menuProperty,
67     const std::optional<Dimension>& fontSize, const Dimension& defaultFontSize)
68 {
69     if (fontSize.has_value()) {
70         textProperty->UpdateFontSize(fontSize.value());
71     } else if (menuProperty && menuProperty->GetFontSize().has_value()) {
72         textProperty->UpdateFontSize(menuProperty->GetFontSize().value());
73     } else {
74         textProperty->UpdateFontSize(defaultFontSize);
75     }
76 }
77 
UpdateFontWeight(RefPtr<TextLayoutProperty> & textProperty,RefPtr<MenuLayoutProperty> & menuProperty,const std::optional<FontWeight> & fontWeight)78 void UpdateFontWeight(RefPtr<TextLayoutProperty>& textProperty, RefPtr<MenuLayoutProperty>& menuProperty,
79     const std::optional<FontWeight>& fontWeight)
80 {
81     if (fontWeight.has_value()) {
82         textProperty->UpdateFontWeight(fontWeight.value());
83     } else if (menuProperty && menuProperty->GetFontWeight().has_value()) {
84         textProperty->UpdateFontWeight(menuProperty->GetFontWeight().value());
85     } else {
86         if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
87             textProperty->UpdateFontWeight(FontWeight::MEDIUM);
88         } else {
89             textProperty->UpdateFontWeight(FontWeight::REGULAR);
90         }
91     }
92 }
93 
UpdateFontStyle(RefPtr<TextLayoutProperty> & textProperty,RefPtr<MenuLayoutProperty> & menuProperty,const std::optional<Ace::FontStyle> & fontStyle)94 void UpdateFontStyle(RefPtr<TextLayoutProperty>& textProperty, RefPtr<MenuLayoutProperty>& menuProperty,
95     const std::optional<Ace::FontStyle>& fontStyle)
96 {
97     if (fontStyle.has_value()) {
98         textProperty->UpdateItalicFontStyle(fontStyle.value());
99     } else if (menuProperty && menuProperty->GetItalicFontStyle().has_value()) {
100         textProperty->UpdateItalicFontStyle(menuProperty->GetItalicFontStyle().value());
101     } else {
102         textProperty->UpdateItalicFontStyle(Ace::FontStyle::NORMAL);
103     }
104 }
105 
UpdateFontColor(const RefPtr<FrameNode> & textNode,RefPtr<MenuLayoutProperty> & menuProperty,const std::optional<Color> & fontColor,const Color & defaultFontColor)106 void UpdateFontColor(const RefPtr<FrameNode>& textNode, RefPtr<MenuLayoutProperty>& menuProperty,
107     const std::optional<Color>& fontColor, const Color& defaultFontColor)
108 {
109     auto textProperty = textNode ? textNode->GetLayoutProperty<TextLayoutProperty>() : nullptr;
110     CHECK_NULL_VOID(textProperty);
111     auto renderContext = textNode->GetRenderContext();
112     CHECK_NULL_VOID(renderContext);
113     if (fontColor.has_value()) {
114         textProperty->UpdateTextColor(fontColor.value());
115     } else if (menuProperty && menuProperty->GetFontColor().has_value()) {
116         textProperty->UpdateTextColor(menuProperty->GetFontColor().value());
117     } else {
118         textProperty->UpdateTextColor(defaultFontColor);
119     }
120 }
121 
UpdateFontFamily(RefPtr<TextLayoutProperty> & textProperty,RefPtr<MenuLayoutProperty> & menuProperty,const std::optional<std::vector<std::string>> & fontFamilies)122 void UpdateFontFamily(RefPtr<TextLayoutProperty>& textProperty, RefPtr<MenuLayoutProperty>& menuProperty,
123     const std::optional<std::vector<std::string>>& fontFamilies)
124 {
125     if (fontFamilies.has_value()) {
126         textProperty->UpdateFontFamily(fontFamilies.value());
127     } else if (menuProperty && menuProperty->GetFontFamily().has_value()) {
128         textProperty->UpdateFontFamily(menuProperty->GetFontFamily().value());
129     }
130 }
131 
UpdateIconSrc(RefPtr<FrameNode> & node,const Dimension & horizontalSize,const Dimension & verticalSize,const Color & color,const bool & useDefaultIcon)132 void UpdateIconSrc(RefPtr<FrameNode>& node, const Dimension& horizontalSize,
133     const Dimension& verticalSize, const Color& color, const bool& useDefaultIcon)
134 {
135     auto props = node->GetLayoutProperty<ImageLayoutProperty>();
136     CHECK_NULL_VOID(props);
137     props->UpdateAlignment(Alignment::CENTER);
138     CalcSize idealSize = { CalcLength(horizontalSize), CalcLength(verticalSize) };
139     MeasureProperty layoutConstraint;
140     layoutConstraint.selfIdealSize = idealSize;
141     props->UpdateCalcLayoutProperty(layoutConstraint);
142     if (useDefaultIcon) {
143         auto iconRenderProperty = node->GetPaintProperty<ImageRenderProperty>();
144         CHECK_NULL_VOID(iconRenderProperty);
145         iconRenderProperty->UpdateSvgFillColor(color);
146     }
147 }
148 } // namespace
149 
OnMountToParentDone()150 void MenuItemPattern::OnMountToParentDone()
151 {
152     UpdateTextNodes();
153 }
154 
OnAttachToFrameNode()155 void MenuItemPattern::OnAttachToFrameNode()
156 {
157     RegisterOnKeyEvent();
158     RegisterOnClick();
159     RegisterOnTouch();
160     RegisterOnHover();
161 }
162 
OnAttachToFrameNode()163 void CustomMenuItemPattern::OnAttachToFrameNode()
164 {
165     RegisterOnKeyEvent();
166     RegisterOnTouch();
167 }
168 
OnModifyDone()169 void MenuItemPattern::OnModifyDone()
170 {
171     Pattern::OnModifyDone();
172     auto host = GetHost();
173     CHECK_NULL_VOID(host);
174 
175     RefPtr<FrameNode> leftRow =
176         host->GetChildAtIndex(0) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(0)) : nullptr;
177     CHECK_NULL_VOID(leftRow);
178     AddSelectIcon(leftRow);
179     UpdateIcon(leftRow, true);
180     auto menuNode = GetMenu();
181     auto menuProperty = menuNode ? menuNode->GetLayoutProperty<MenuLayoutProperty>() : nullptr;
182     UpdateText(leftRow, menuProperty, false);
183 
184     if (menuProperty) {
185         expandingMode_ = menuProperty->GetExpandingMode().value_or(SubMenuExpandingMode::SIDE);
186         expandingModeSet_ = true;
187     }
188 
189     RefPtr<FrameNode> rightRow =
190         host->GetChildAtIndex(1) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(1)) : nullptr;
191     CHECK_NULL_VOID(rightRow);
192     UpdateText(rightRow, menuProperty, true);
193     UpdateIcon(rightRow, false);
194     AddExpandIcon(rightRow);
195     AddClickableArea();
196     if (IsDisabled()) {
197         UpdateDisabledStyle();
198     }
199     SetAccessibilityAction();
200 
201     host->GetRenderContext()->SetClipToBounds(true);
202     if (!longPressEvent_ && Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
203         InitLongPressEvent();
204     }
205     if (expandingModeSet_) {
206         RegisterOnKeyEvent();
207         RegisterOnTouch();
208         RegisterOnHover();
209         RegisterOnClick();
210     }
211 }
212 
OnAfterModifyDone()213 void MenuItemPattern::OnAfterModifyDone()
214 {
215     auto host = GetHost();
216     CHECK_NULL_VOID(host);
217     auto inspectorId = host->GetInspectorId().value_or("");
218     if (inspectorId.empty()) {
219         return;
220     }
221     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
222     CHECK_NULL_VOID(itemProperty);
223     auto content = itemProperty->GetContent().value_or("");
224     Recorder::NodeDataCache::Get().PutMultiple(host, inspectorId, content, isSelected_);
225 }
226 
RecordChangeEvent() const227 void MenuItemPattern::RecordChangeEvent() const
228 {
229     if (!Recorder::EventRecorder::Get().IsComponentRecordEnable()) {
230         return;
231     }
232     auto host = GetHost();
233     CHECK_NULL_VOID(host);
234     auto inspectorId = host->GetInspectorId().value_or("");
235     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
236     CHECK_NULL_VOID(itemProperty);
237     auto content = itemProperty->GetContent().value_or("");
238     Recorder::EventParamsBuilder builder;
239     builder.SetId(inspectorId)
240         .SetType(host->GetTag())
241         .SetChecked(isSelected_)
242         .SetText(content)
243         .SetDescription(host->GetAutoEventParamValue(""));
244     Recorder::EventRecorder::Get().OnChange(std::move(builder));
245     Recorder::NodeDataCache::Get().PutMultiple(host, inspectorId, content, isSelected_);
246 }
247 
GetMenuWrapper()248 RefPtr<FrameNode> MenuItemPattern::GetMenuWrapper()
249 {
250     auto host = GetHost();
251     CHECK_NULL_RETURN(host, nullptr);
252     auto parent = host->GetParent();
253     while (parent) {
254         if (parent->GetTag() == V2::MENU_WRAPPER_ETS_TAG || parent->GetTag() == V2::SELECT_OVERLAY_ETS_TAG) {
255             return AceType::DynamicCast<FrameNode>(parent);
256         }
257         parent = parent->GetParent();
258     }
259     return nullptr;
260 }
261 
GetMenu(bool needTopMenu)262 RefPtr<FrameNode> MenuItemPattern::GetMenu(bool needTopMenu)
263 {
264     auto host = GetHost();
265     CHECK_NULL_RETURN(host, nullptr);
266     auto parent = host->GetParent();
267     RefPtr<FrameNode> menuNode = nullptr;
268     while (parent) {
269         if (parent->GetTag() == V2::MENU_ETS_TAG) {
270             menuNode = AceType::DynamicCast<FrameNode>(parent);
271             if (!needTopMenu) {
272                 // innner menu
273                 return menuNode;
274             }
275         }
276         parent = parent->GetParent();
277     }
278     // outter menu
279     return menuNode;
280 }
281 
GetMenuPattern(bool needTopMenu)282 RefPtr<MenuPattern> MenuItemPattern::GetMenuPattern(bool needTopMenu)
283 {
284     auto menu = GetMenu(needTopMenu);
285     if (!menu) {
286         return nullptr;
287     }
288     return menu->GetPattern<MenuPattern>();
289 }
290 
ShowSubMenu()291 void MenuItemPattern::ShowSubMenu()
292 {
293     auto host = GetHost();
294     CHECK_NULL_VOID(host);
295     auto menuWrapper = GetMenuWrapper();
296     CHECK_NULL_VOID(menuWrapper);
297     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
298     CHECK_NULL_VOID(menuWrapperPattern);
299     auto hasSubMenu = menuWrapperPattern->HasStackSubMenu();
300     auto buildFunc = GetSubBuilder();
301     if (!buildFunc || isSubMenuShowed_ || IsEmbedded() ||
302         (expandingMode_ == SubMenuExpandingMode::STACK && hasSubMenu)) {
303         return;
304     }
305     // Hide SubMenu of parent Menu node
306     auto parentMenu = GetMenu();
307     CHECK_NULL_VOID(parentMenu);
308     // parentMenu no need focus
309     auto menuNode = GetMenu(true);
310     CHECK_NULL_VOID(menuNode);
311     auto layoutProps = parentMenu->GetLayoutProperty<MenuLayoutProperty>();
312     CHECK_NULL_VOID(layoutProps);
313     NG::ScopedViewStackProcessor builderViewStackProcessor;
314     buildFunc();
315     auto customNode = NG::ViewStackProcessor::GetInstance()->Finish();
316     CHECK_NULL_VOID(customNode);
317     UpdateSubmenuExpandingMode(customNode);
318     if (expandingMode_ == SubMenuExpandingMode::EMBEDDED) {
319         auto frameNode = AceType::DynamicCast<FrameNode>(customNode);
320         OnExpandChanged(frameNode);
321         return;
322     }
323 
324     auto menuPattern = menuNode->GetPattern<MenuPattern>();
325     CHECK_NULL_VOID(menuPattern);
326     menuPattern->FocusViewHide();
327     auto parentMenuPattern = parentMenu->GetPattern<MenuPattern>();
328     CHECK_NULL_VOID(parentMenuPattern);
329     HideSubMenu();
330     isSubMenuShowed_ = true;
331     bool isSelectOverlayMenu = IsSelectOverlayMenu();
332     MenuParam param;
333     auto outterMenuLayoutProps = menuNode->GetLayoutProperty<MenuLayoutProperty>();
334     CHECK_NULL_VOID(outterMenuLayoutProps);
335     param.isShowInSubWindow = outterMenuLayoutProps->GetShowInSubWindowValue(false);
336     auto focusMenuRenderContext = menuNode->GetRenderContext();
337     CHECK_NULL_VOID(focusMenuRenderContext);
338     if (focusMenuRenderContext->GetBackBlurStyle().has_value()) {
339         auto focusMenuBlurStyle = focusMenuRenderContext->GetBackBlurStyle();
340         param.backgroundBlurStyle = static_cast<int>(focusMenuBlurStyle->blurStyle);
341     }
342     param.type = isSelectOverlayMenu ? MenuType::SELECT_OVERLAY_SUB_MENU : MenuType::SUB_MENU;
343     ParseMenuRadius(param);
344     auto subMenu = MenuView::Create(customNode, host->GetId(), host->GetTag(), param);
345     CHECK_NULL_VOID(subMenu);
346     ShowSubMenuHelper(subMenu);
347     menuPattern->SetShowedSubMenu(subMenu);
348     auto accessibilityProperty = subMenu->GetAccessibilityProperty<MenuAccessibilityProperty>();
349     CHECK_NULL_VOID(accessibilityProperty);
350     accessibilityProperty->SetAccessibilityIsShow(true);
351 
352     subMenu->OnAccessibilityEvent(AccessibilityEventType::PAGE_OPEN);
353     TAG_LOGI(AceLogTag::ACE_OVERLAY, "Send event to %{public}d",
354         static_cast<int32_t>(AccessibilityEventType::PAGE_OPEN));
355 }
356 
GetSubMenu(RefPtr<UINode> & customNode)357 RefPtr<FrameNode> GetSubMenu(RefPtr<UINode>& customNode)
358 {
359     CHECK_NULL_RETURN(customNode, nullptr);
360     if (customNode->GetTag() == V2::MENU_ETS_TAG) {
361         auto frameNode = AceType::DynamicCast<FrameNode>(customNode);
362         return frameNode;
363     }
364     uint32_t depth = 0;
365     auto child = customNode->GetFrameChildByIndex(0, false);
366     while (child && depth < MAX_SEARCH_DEPTH) {
367         if (child->GetTag() == V2::JS_VIEW_ETS_TAG) {
368             child = child->GetFrameChildByIndex(0, false);
369             if (child && child->GetTag() == V2::JS_VIEW_ETS_TAG) {
370                 child = child->GetChildAtIndex(0);
371                 ++depth;
372             }
373             continue;
374         }
375         if (child->GetTag() == V2::MENU_ETS_TAG) {
376             return AceType::DynamicCast<FrameNode>(child);
377         }
378         child  = child->GetChildAtIndex(0);
379         ++depth;
380     }
381     return nullptr;
382 }
383 
UpdateSubmenuExpandingMode(RefPtr<UINode> & customNode)384 void MenuItemPattern::UpdateSubmenuExpandingMode(RefPtr<UINode>& customNode)
385 {
386     auto frameNode = GetSubMenu(customNode);
387     CHECK_NULL_VOID(frameNode);
388     if (frameNode->GetTag() == V2::MENU_ETS_TAG) {
389         auto props = frameNode->GetLayoutProperty<MenuLayoutProperty>();
390         CHECK_NULL_VOID(props);
391         auto pattern = frameNode->GetPattern<MenuPattern>();
392         CHECK_NULL_VOID(pattern);
393         props->UpdateExpandingMode(expandingMode_);
394         if (expandingMode_ == SubMenuExpandingMode::STACK) {
395             AddStackSubMenuHeader(frameNode);
396             pattern->SetIsStackSubmenu();
397         } else if (expandingMode_ == SubMenuExpandingMode::EMBEDDED) {
398             pattern->SetIsEmbedded();
399             return;
400         }
401         frameNode->MarkModifyDone();
402         frameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
403     }
404 }
405 
ShowSubMenuHelper(const RefPtr<FrameNode> & subMenu)406 void MenuItemPattern::ShowSubMenuHelper(const RefPtr<FrameNode>& subMenu)
407 {
408     auto host = GetHost();
409     CHECK_NULL_VOID(host);
410     bool isSelectOverlayMenu = IsSelectOverlayMenu();
411     auto menuPattern = subMenu->GetPattern<MenuPattern>();
412     CHECK_NULL_VOID(menuPattern);
413     menuPattern->SetParentMenuItem(host);
414     subMenuId_ = subMenu->GetId();
415     AddSelfHoverRegion(host);
416     auto menuWrapper = GetMenuWrapper();
417     CHECK_NULL_VOID(menuWrapper);
418     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) &&
419         expandingMode_ == SubMenuExpandingMode::STACK) {
420         SetClickMenuItemId(host->GetId());
421         subMenu->MountToParent(menuWrapper);
422         menuWrapper->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
423         menuPattern->SetSubMenuShow();
424         RegisterWrapperMouseEvent();
425     } else {
426         subMenu->MountToParent(menuWrapper);
427         OffsetF offset = GetSubMenuPosition(host);
428         auto menuProps = subMenu->GetLayoutProperty<MenuLayoutProperty>();
429         CHECK_NULL_VOID(menuProps);
430         menuProps->UpdateMenuOffset(offset);
431         menuWrapper->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
432         RegisterWrapperMouseEvent();
433     }
434     // select overlay menu no need focus
435     if (!isSelectOverlayMenu) {
436         menuPattern->FocusViewShow();
437     }
438 }
439 
HideSubMenu()440 void MenuItemPattern::HideSubMenu()
441 {
442     auto host = GetHost();
443     CHECK_NULL_VOID(host);
444     auto menuWrapper = GetMenuWrapper();
445     CHECK_NULL_VOID(menuWrapper);
446     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
447     CHECK_NULL_VOID(menuWrapperPattern);
448     auto showedSubMenu = menuWrapperPattern->GetShowedSubMenu();
449     if (showedSubMenu) {
450         auto showedSubMenuPattern = showedSubMenu->GetPattern<MenuPattern>();
451         CHECK_NULL_VOID(showedSubMenuPattern);
452         auto showedMenuItem = showedSubMenuPattern->GetParentMenuItem();
453         CHECK_NULL_VOID(showedMenuItem);
454         if (showedMenuItem->GetId() != host->GetId()) {
455             auto outterMenu = GetMenu(true);
456             CHECK_NULL_VOID(outterMenu);
457             auto outterMenuPattern = outterMenu->GetPattern<MenuPattern>();
458             CHECK_NULL_VOID(outterMenuPattern);
459             outterMenuPattern->HideSubMenu();
460         }
461     }
462 }
463 
OnExpandChanged(const RefPtr<FrameNode> & expandableNode)464 void MenuItemPattern::OnExpandChanged(const RefPtr<FrameNode>& expandableNode)
465 {
466     CHECK_NULL_VOID(expandableNode);
467     isExpanded_ = !isExpanded_;
468     if (isExpanded_) {
469         embeddedMenu_ = expandableNode;
470         ShowEmbeddedExpandMenu(embeddedMenu_);
471     } else {
472         HideEmbeddedExpandMenu(embeddedMenu_);
473         embeddedMenu_ = nullptr;
474     }
475 }
476 
ShowEmbeddedExpandMenu(const RefPtr<FrameNode> & expandableNode)477 void MenuItemPattern::ShowEmbeddedExpandMenu(const RefPtr<FrameNode>& expandableNode)
478 {
479     CHECK_NULL_VOID(expandableNode);
480     auto host = GetHost();
481     CHECK_NULL_VOID(host);
482     auto menuWrapper = GetMenuWrapper();
483     CHECK_NULL_VOID(menuWrapper);
484     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
485     CHECK_NULL_VOID(menuWrapperPattern);
486     menuWrapperPattern->IncreaseEmbeddedSubMenuCount();
487     auto rightRow = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(1));
488     CHECK_NULL_VOID(rightRow);
489     auto imageNode = AceType::DynamicCast<FrameNode>(rightRow->GetChildren().back());
490     CHECK_NULL_VOID(imageNode);
491     auto imageContext = imageNode->GetRenderContext();
492     CHECK_NULL_VOID(imageContext);
493     imageContext->UpdateTransformRotate(Vector5F(0.0f, 0.0f, 1.0f, 0.0f, 0.0f));
494 
495     auto expandableAreaContext = expandableNode->GetRenderContext();
496     CHECK_NULL_VOID(expandableAreaContext);
497     expandableAreaContext->UpdateBackShadow(ShadowConfig::NoneShadow);
498     auto itemSize = host->GetGeometryNode()->GetFrameSize();
499     expandableAreaContext->ClipWithRRect(RectF(0.0f, 0.0f, itemSize.Width(), 0.0f),
500         RadiusF(EdgeF(0.0f, 0.0f)));
501 
502     AnimationOption option = AnimationOption();
503     auto rotateOption = AceType::MakeRefPtr<InterpolatingSpring>(VELOCITY, MASS, STIFFNESS, DAMPING);
504     option.SetCurve(rotateOption);
505     AnimationUtils::Animate(option, [host, rightRow, expandableNode, expandableAreaContext, imageContext]() {
506         expandableNode->MountToParent(host, EXPANDABLE_AREA_VIEW_INDEX);
507         imageContext->UpdateTransformRotate(Vector5F(0.0f, 0.0f, 1.0f, SEMI_CIRCLE_ANGEL, 0.0f));
508         expandableNode->MarkModifyDone();
509         expandableNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
510 
511         auto pipeline = PipelineContext::GetCurrentContext();
512         CHECK_NULL_VOID(pipeline);
513         pipeline->FlushUITasks();
514         auto expandableAreaFrameSize = expandableNode->GetGeometryNode()->GetFrameSize();
515         expandableAreaContext->ClipWithRRect(RectF(0.0f, 0.0f, expandableAreaFrameSize.Width(),
516             expandableAreaFrameSize.Height()), RadiusF(EdgeF(0.0f, 0.0f)));
517     });
518 }
519 
HideEmbeddedExpandMenu(const RefPtr<FrameNode> & expandableNode)520 void MenuItemPattern::HideEmbeddedExpandMenu(const RefPtr<FrameNode>& expandableNode)
521 {
522     CHECK_NULL_VOID(expandableNode);
523     auto host = GetHost();
524     CHECK_NULL_VOID(host);
525     auto menuWrapper = GetMenuWrapper();
526     CHECK_NULL_VOID(menuWrapper);
527     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
528     CHECK_NULL_VOID(menuWrapperPattern);
529     menuWrapperPattern->DecreaseEmbeddedSubMenuCount();
530     auto expandableAreaContext = expandableNode->GetRenderContext();
531     CHECK_NULL_VOID(expandableAreaContext);
532 
533     AnimationOption option = AnimationOption();
534     auto rotateOption = AceType::MakeRefPtr<InterpolatingSpring>(VELOCITY, MASS, STIFFNESS, DAMPING);
535     option.SetCurve(rotateOption);
536     RefPtr<ChainedTransitionEffect> opacity = AceType::MakeRefPtr<ChainedOpacityEffect>(OPACITY_EFFECT);
537     expandableAreaContext->UpdateChainedTransition(opacity);
538 
539     AnimationUtils::Animate(option, [this, host, expandableNode]() {
540         host->RemoveChild(expandableNode, true);
541         host->MarkModifyDone();
542         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
543         auto rightRow = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(1));
544         CHECK_NULL_VOID(rightRow);
545         auto imageNode = AceType::DynamicCast<FrameNode>(rightRow->GetChildren().back());
546         CHECK_NULL_VOID(imageNode);
547         auto imageContext = imageNode->GetRenderContext();
548         CHECK_NULL_VOID(imageContext);
549         imageContext->UpdateTransformRotate(Vector5F(0.0f, 0.0f, 1.0f, 0.0f, 0.0f));
550         auto pipeline = PipelineContext::GetCurrentContext();
551         CHECK_NULL_VOID(pipeline);
552         pipeline->FlushUITasks();
553     });
554 }
555 
CloseMenu()556 void MenuItemPattern::CloseMenu()
557 {
558     // no need close for selection menu
559     if (IsSelectOverlayMenu()) {
560         return;
561     }
562     auto menuWrapper = GetMenuWrapper();
563     CHECK_NULL_VOID(menuWrapper);
564     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
565     CHECK_NULL_VOID(menuWrapperPattern);
566     menuWrapperPattern->UpdateMenuAnimation(menuWrapper);
567     menuWrapperPattern->HideMenu();
568 }
569 
RegisterOnClick()570 void MenuItemPattern::RegisterOnClick()
571 {
572     auto host = GetHost();
573     CHECK_NULL_VOID(host);
574     if (!onClickEvent_) {
575         auto event = [weak = WeakClaim(this)](GestureEvent& /* info */) {
576             auto pattern = weak.Upgrade();
577             CHECK_NULL_VOID(pattern);
578             pattern->OnClick();
579         };
580         onClickEvent_ = MakeRefPtr<ClickEvent>(std::move(event));
581     }
582     auto gestureHub = host->GetOrCreateGestureEventHub();
583     CHECK_NULL_VOID(gestureHub);
584     if (expandingMode_ == SubMenuExpandingMode::EMBEDDED && clickableArea_) {
585         auto clickableAreaGestureHub = clickableArea_->GetOrCreateGestureEventHub();
586         CHECK_NULL_VOID(clickableAreaGestureHub);
587         gestureHub->RemoveClickEvent(onClickEvent_);
588         clickableAreaGestureHub->AddClickEvent(onClickEvent_);
589     } else if (!onClickEventSet_) {
590         gestureHub->AddClickEvent(onClickEvent_);
591         onClickEventSet_ = true;
592     }
593 }
594 
RegisterOnTouch()595 void MenuItemPattern::RegisterOnTouch()
596 {
597     if (!onTouchEvent_) {
598         auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
599             auto pattern = weak.Upgrade();
600             CHECK_NULL_VOID(pattern);
601             pattern->OnTouch(info);
602         };
603         onTouchEvent_ = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
604     }
605     auto host = GetHost();
606     CHECK_NULL_VOID(host);
607     auto gestureHub = host->GetOrCreateGestureEventHub();
608     CHECK_NULL_VOID(gestureHub);
609     if (expandingMode_ == SubMenuExpandingMode::EMBEDDED && clickableArea_) {
610         auto clickableAreaGestureHub = clickableArea_->GetOrCreateGestureEventHub();
611         CHECK_NULL_VOID(clickableAreaGestureHub);
612         gestureHub->RemoveTouchEvent(onTouchEvent_);
613         clickableAreaGestureHub->AddTouchEvent(onTouchEvent_);
614     } else if (!onTouchEventSet_) {
615         gestureHub->AddTouchEvent(onTouchEvent_);
616         onTouchEventSet_ = true;
617     }
618 }
619 
RegisterOnHover()620 void MenuItemPattern::RegisterOnHover()
621 {
622     if (!onHoverEvent_) {
623         auto mouseTask = [weak = WeakClaim(this)](bool isHover) {
624             auto pattern = weak.Upgrade();
625             CHECK_NULL_VOID(pattern);
626             pattern->OnHover(isHover);
627         };
628         onHoverEvent_ = MakeRefPtr<InputEvent>(std::move(mouseTask));
629     }
630     auto host = GetHost();
631     CHECK_NULL_VOID(host);
632     auto inputHub = host->GetOrCreateInputEventHub();
633     CHECK_NULL_VOID(inputHub);
634     if (expandingMode_ == SubMenuExpandingMode::EMBEDDED && clickableArea_) {
635         auto clickableAreaInputHub = clickableArea_->GetOrCreateInputEventHub();
636         CHECK_NULL_VOID(clickableAreaInputHub);
637         inputHub->RemoveOnHoverEvent(onHoverEvent_);
638         clickableAreaInputHub->AddOnHoverEvent(onHoverEvent_);
639     } else if (!onHoverEventSet_) {
640         inputHub->AddOnHoverEvent(onHoverEvent_);
641         onHoverEventSet_ = true;
642     }
643 }
644 
RegisterOnKeyEvent()645 void MenuItemPattern::RegisterOnKeyEvent()
646 {
647     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
648         auto pattern = wp.Upgrade();
649         CHECK_NULL_RETURN(pattern, false);
650         return pattern->OnKeyEvent(event);
651     };
652     auto event = std::move(onKeyEvent);
653     auto host = GetHost();
654     CHECK_NULL_VOID(host);
655     auto focusHub = host->GetOrCreateFocusHub();
656     CHECK_NULL_VOID(focusHub);
657     if (expandingMode_ == SubMenuExpandingMode::EMBEDDED && clickableArea_) {
658         auto clickableAreaFocusHub = clickableArea_->GetOrCreateFocusHub();
659         CHECK_NULL_VOID(clickableAreaFocusHub);
660         focusHub->SetOnKeyEventInternal(nullptr);
661         clickableAreaFocusHub->SetOnKeyEventInternal(event);
662     } else if (!onKeyEventSet_) {
663         focusHub->SetOnKeyEventInternal(event);
664         onKeyEventSet_ = true;
665     }
666 }
667 
OnClick()668 void MenuItemPattern::OnClick()
669 {
670     auto host = GetHost();
671     CHECK_NULL_VOID(host);
672     if (onClickAIMenuItem_) {
673         onClickAIMenuItem_();
674     }
675     auto menuWrapper = GetMenuWrapper();
676     auto menuWrapperPattern = menuWrapper ? menuWrapper->GetPattern<MenuWrapperPattern>() : nullptr;
677     auto hasSubMenu = menuWrapperPattern ? menuWrapperPattern->HasStackSubMenu() : false;
678     if (expandingMode_ == SubMenuExpandingMode::STACK && !IsSubMenu() && hasSubMenu) {
679         return;
680     }
681     if (expandingMode_ == SubMenuExpandingMode::STACK && IsStackSubmenuHeader()) {
682         menuWrapperPattern->HideSubMenu();
683         return;
684     }
685     auto hub = host->GetEventHub<MenuItemEventHub>();
686     CHECK_NULL_VOID(hub);
687     auto onChange = hub->GetOnChange();
688     auto selectedChangeEvent = hub->GetSelectedChangeEvent();
689     SetChange();
690     if (selectedChangeEvent) {
691         selectedChangeEvent(IsSelected());
692     }
693     if (onChange) {
694         onChange(IsSelected());
695         RecordChangeEvent();
696     }
697     auto menuNode = GetMenu();
698     CHECK_NULL_VOID(menuNode);
699     auto menuPattern = menuNode->GetPattern<MenuPattern>();
700     CHECK_NULL_VOID(menuPattern);
701     auto lastSelectedItem = menuPattern->GetLastSelectedItem();
702     if (lastSelectedItem && lastSelectedItem != host) {
703         auto pattern = lastSelectedItem->GetPattern<MenuItemPattern>();
704         CHECK_NULL_VOID(pattern);
705         pattern->SetChange();
706     }
707     menuPattern->SetLastSelectedItem(host);
708     if (GetSubBuilder() != nullptr && (expandingMode_ == SubMenuExpandingMode::SIDE ||
709         (expandingMode_ == SubMenuExpandingMode::STACK && !IsSubMenu() && !hasSubMenu) ||
710         (expandingMode_ == SubMenuExpandingMode::EMBEDDED && !IsEmbedded()))) {
711         ShowSubMenu();
712         return;
713     }
714     // hide menu when menu item is clicked
715     CloseMenu();
716 }
717 
OnTouch(const TouchEventInfo & info)718 void MenuItemPattern::OnTouch(const TouchEventInfo& info)
719 {
720     auto menuWrapper = GetMenuWrapper();
721     // When menu wrapper exists, the pressed state is handed over to the menu wrapper
722     if (menuWrapper && menuWrapper->GetTag() == V2::MENU_WRAPPER_ETS_TAG) {
723         return;
724     }
725     // change menu item paint props on press
726     auto touchType = info.GetTouches().front().GetTouchType();
727     if (touchType == TouchType::DOWN) {
728         // change background color, update press status
729         NotifyPressStatus(true);
730     } else if (touchType == TouchType::UP) {
731         NotifyPressStatus(false);
732     }
733 }
734 
NotifyPressStatus(bool isPress)735 void MenuItemPattern::NotifyPressStatus(bool isPress)
736 {
737     auto host = GetHost();
738     CHECK_NULL_VOID(host);
739     auto pipeline = host->GetContext();
740     CHECK_NULL_VOID(pipeline);
741     auto theme = pipeline->GetTheme<SelectTheme>();
742     CHECK_NULL_VOID(theme);
743     auto props = GetPaintProperty<MenuItemPaintProperty>();
744     CHECK_NULL_VOID(props);
745     auto menu = GetMenu();
746     CHECK_NULL_VOID(menu);
747     auto menuPattern = menu->GetPattern<MenuPattern>();
748     CHECK_NULL_VOID(menuPattern);
749     auto parent = AceType::DynamicCast<UINode>(host->GetParent());
750     auto menuWrapper = GetMenuWrapper();
751     auto menuWrapperPattern = menuWrapper ? menuWrapper->GetPattern<MenuWrapperPattern>() : nullptr;
752 
753     // do not change color for stacked level-1 menu items if level-2 is shown
754     auto canChangeColor = !(expandingMode_ == SubMenuExpandingMode::STACK
755         && menuWrapperPattern && menuWrapperPattern->HasStackSubMenu() && !IsSubMenu());
756     if (!canChangeColor) return;
757     if (isPress) {
758         // change background color, update press status
759         SetBgBlendColor(GetSubBuilder() ? theme->GetHoverColor() : theme->GetClickedColor());
760         if (menuWrapperPattern) {
761             menuWrapperPattern->SetLastTouchItem(host);
762         }
763         props->UpdatePress(true);
764         menuPattern->OnItemPressed(parent, index_, true);
765     } else {
766         SetBgBlendColor(isHovered_ ? theme->GetHoverColor() : Color::TRANSPARENT);
767         props->UpdatePress(false);
768         menuPattern->OnItemPressed(parent, index_, false);
769     }
770     PlayBgColorAnimation(false);
771     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
772 }
773 
OnTouch(const TouchEventInfo & info)774 void CustomMenuItemPattern::OnTouch(const TouchEventInfo& info)
775 {
776     auto touchType = info.GetTouches().front().GetTouchType();
777 
778     // close menu when touch up
779     // can't use onClick because that conflicts with interactions developers might set to the customNode
780     // recognize gesture as click if touch up position is close to last touch down position
781     if (touchType == TouchType::DOWN) {
782         lastTouchOffset_ = std::make_unique<Offset>(info.GetTouches().front().GetLocalLocation());
783     } else if (touchType == TouchType::UP) {
784         auto touchUpOffset = info.GetTouches().front().GetLocalLocation();
785         if (lastTouchOffset_ && (touchUpOffset - *lastTouchOffset_).GetDistance() <= DEFAULT_CLICK_DISTANCE) {
786             if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
787                 HandleOnChange();
788             }
789             CloseMenu();
790         }
791         lastTouchOffset_.reset();
792     }
793 }
794 
HandleOnChange()795 void CustomMenuItemPattern::HandleOnChange()
796 {
797     auto host = GetHost();
798     CHECK_NULL_VOID(host);
799     auto hub = host->GetEventHub<MenuItemEventHub>();
800     CHECK_NULL_VOID(hub);
801     auto onChange = hub->GetOnChange();
802     auto selectedChangeEvent = hub->GetSelectedChangeEvent();
803     SetChange();
804     if (selectedChangeEvent) {
805         TAG_LOGI(AceLogTag::ACE_MENU, "trigger selectedChangeEvent");
806         selectedChangeEvent(IsSelected());
807     }
808     if (onChange) {
809         TAG_LOGI(AceLogTag::ACE_MENU, "trigger onChange");
810         onChange(IsSelected());
811     }
812 }
813 
OnHover(bool isHover)814 void MenuItemPattern::OnHover(bool isHover)
815 {
816     isHovered_ = isHover;
817     auto pipeline = PipelineBase::GetCurrentContext();
818     CHECK_NULL_VOID(pipeline);
819     auto theme = pipeline->GetTheme<SelectTheme>();
820     CHECK_NULL_VOID(theme);
821     auto props = GetPaintProperty<MenuItemPaintProperty>();
822     CHECK_NULL_VOID(props);
823     auto menu = GetMenu(false);
824     CHECK_NULL_VOID(menu);
825     auto menuPattern = menu->GetPattern<MenuPattern>();
826     CHECK_NULL_VOID(menuPattern);
827     auto host = GetHost();
828     CHECK_NULL_VOID(host);
829     auto parent = AceType::DynamicCast<UINode>(host->GetParent());
830 
831     if (isHover || isSubMenuShowed_) {
832         // keep hover color when subMenu showed
833         SetBgBlendColor(theme->GetHoverColor());
834         ShowSubMenu();
835         props->UpdateHover(true);
836         menuPattern->OnItemPressed(parent, index_, true, true);
837     } else {
838         SetBgBlendColor(Color::TRANSPARENT);
839         props->UpdateHover(false);
840         menuPattern->OnItemPressed(parent, index_, false, true);
841     }
842     PlayBgColorAnimation();
843     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
844 }
845 
OnVisibleChange(bool isVisible)846 void MenuItemPattern::OnVisibleChange(bool isVisible)
847 {
848     auto host = GetHost();
849     CHECK_NULL_VOID(host);
850     auto parentNode = host->GetParent();
851     CHECK_NULL_VOID(parentNode);
852     if (parentNode->GetTag() == V2::MENU_ITEM_GROUP_ETS_TAG) {
853         parentNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
854     }
855 }
856 
OnKeyEvent(const KeyEvent & event)857 bool MenuItemPattern::OnKeyEvent(const KeyEvent& event)
858 {
859     if (event.action != KeyAction::DOWN) {
860         return false;
861     }
862     auto host = GetHost();
863     CHECK_NULL_RETURN(host, false);
864     auto focusHub = host->GetOrCreateFocusHub();
865     CHECK_NULL_RETURN(focusHub, false);
866     if (event.code == KeyCode::KEY_ENTER) {
867         focusHub->OnClick(event);
868         return true;
869     }
870     if (event.code == KeyCode::KEY_DPAD_RIGHT && GetSubBuilder() && !isSubMenuShowed_) {
871         auto pipeline = PipelineBase::GetCurrentContext();
872         CHECK_NULL_RETURN(pipeline, false);
873         auto theme = pipeline->GetTheme<SelectTheme>();
874         CHECK_NULL_RETURN(theme, false);
875         SetBgBlendColor(theme->GetHoverColor());
876         PlayBgColorAnimation();
877         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
878         ShowSubMenu();
879         return true;
880     }
881     return false;
882 }
883 
OnKeyEvent(const KeyEvent & event)884 bool CustomMenuItemPattern::OnKeyEvent(const KeyEvent& event)
885 {
886     if (event.action != KeyAction::DOWN) {
887         return false;
888     }
889     auto host = GetHost();
890     CHECK_NULL_RETURN(host, false);
891     auto focusHub = host->GetOrCreateFocusHub();
892     CHECK_NULL_RETURN(focusHub, false);
893     if (event.code == KeyCode::KEY_ENTER || event.code == KeyCode::KEY_SPACE) {
894         focusHub->OnClick(event);
895         CloseMenu();
896         return true;
897     }
898     return false;
899 }
900 
InitLongPressEvent()901 void MenuItemPattern::InitLongPressEvent()
902 {
903     auto gesture = GetHost()->GetOrCreateGestureEventHub();
904     CHECK_NULL_VOID(gesture);
905     auto longPressCallback = [weak = WeakClaim(this)](GestureEvent& info) {
906         auto itemPattern = weak.Upgrade();
907         auto menuWrapper = itemPattern->GetMenuWrapper();
908         CHECK_NULL_VOID(menuWrapper);
909         auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
910         CHECK_NULL_VOID(menuWrapperPattern);
911         auto topLevelMenuPattern = itemPattern->GetMenuPattern(true);
912         CHECK_NULL_VOID(topLevelMenuPattern);
913         if (itemPattern && itemPattern->GetSubBuilder() != nullptr &&
914             menuWrapperPattern->GetPreviewMode() == MenuPreviewMode::NONE &&
915             !(topLevelMenuPattern->IsSelectOverlayCustomMenu())) {
916             itemPattern->ShowSubMenu();
917         }
918     };
919     longPressEvent_ = MakeRefPtr<LongPressEvent>(std::move(longPressCallback));
920     gesture->SetLongPressEvent(longPressEvent_);
921 }
922 
RegisterWrapperMouseEvent()923 void MenuItemPattern::RegisterWrapperMouseEvent()
924 {
925     auto menuWrapper = GetMenuWrapper();
926     if (menuWrapper && !wrapperMouseEvent_) {
927         auto inputHub = menuWrapper->GetOrCreateInputEventHub();
928         CHECK_NULL_VOID(inputHub);
929         auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
930         CHECK_NULL_VOID(menuWrapperPattern);
931 
932         auto mouseTask = [weak = WeakClaim(this), menuWrapperPattern](MouseInfo& info) {
933             auto pattern = weak.Upgrade();
934             CHECK_NULL_VOID(pattern);
935             if (menuWrapperPattern) {
936                 menuWrapperPattern->HandleMouseEvent(info, pattern);
937             }
938         };
939         wrapperMouseEvent_ = MakeRefPtr<InputEvent>(std::move(mouseTask));
940         inputHub->AddOnMouseEvent(wrapperMouseEvent_);
941     }
942 }
943 
AddSelfHoverRegion(const RefPtr<FrameNode> & targetNode)944 void MenuItemPattern::AddSelfHoverRegion(const RefPtr<FrameNode>& targetNode)
945 {
946     OffsetF topLeftPoint;
947     OffsetF bottomRightPoint;
948     auto frameSize = targetNode->GetGeometryNode()->GetMarginFrameSize();
949     topLeftPoint = targetNode->GetPaintRectOffset();
950     bottomRightPoint = targetNode->GetPaintRectOffset() + OffsetF(frameSize.Width(), frameSize.Height());
951     AddHoverRegions(topLeftPoint, bottomRightPoint);
952 }
953 
GetSubMenuPosition(const RefPtr<FrameNode> & targetNode)954 OffsetF MenuItemPattern::GetSubMenuPosition(const RefPtr<FrameNode>& targetNode)
955 {    // show menu at left top point of targetNode
956     auto frameSize = targetNode->GetGeometryNode()->GetMarginFrameSize();
957     OffsetF position = targetNode->GetPaintRectOffset() + OffsetF(frameSize.Width(), 0.0);
958     return position;
959 }
960 
961 
AddHoverRegions(const OffsetF & topLeftPoint,const OffsetF & bottomRightPoint)962 void MenuItemPattern::AddHoverRegions(const OffsetF& topLeftPoint, const OffsetF& bottomRightPoint)
963 {
964     TouchRegion hoverRegion = TouchRegion(
965         Offset(topLeftPoint.GetX(), topLeftPoint.GetY()), Offset(bottomRightPoint.GetX(), bottomRightPoint.GetY()));
966     hoverRegions_.emplace_back(hoverRegion);
967 }
968 
IsInHoverRegions(double x,double y)969 bool MenuItemPattern::IsInHoverRegions(double x, double y)
970 {
971     for (auto hoverRegion : hoverRegions_) {
972         if (hoverRegion.ContainsInRegion(x, y)) {
973             return true;
974         }
975     }
976     return false;
977 }
978 
PlayBgColorAnimation(bool isHoverChange)979 void MenuItemPattern::PlayBgColorAnimation(bool isHoverChange)
980 {
981     auto pipeline = PipelineBase::GetCurrentContext();
982     CHECK_NULL_VOID(pipeline);
983     auto theme = pipeline->GetTheme<SelectTheme>();
984     CHECK_NULL_VOID(theme);
985     AnimationOption option;
986     if (isHoverChange) {
987         option.SetDuration(theme->GetHoverAnimationDuration());
988         option.SetCurve(Curves::FRICTION);
989     } else {
990         option.SetDuration(theme->GetPressAnimationDuration());
991         option.SetCurve(Curves::SHARP);
992     }
993 
994     AnimationUtils::Animate(option, [weak = WeakClaim(this)]() {
995         auto pattern = weak.Upgrade();
996         CHECK_NULL_VOID(pattern);
997         auto clickableArea = pattern->GetClickableArea();
998         CHECK_NULL_VOID(clickableArea);
999         auto renderContext = clickableArea->GetRenderContext();
1000         CHECK_NULL_VOID(renderContext);
1001         renderContext->BlendBgColor(pattern->GetBgBlendColor());
1002     });
1003 }
1004 
UpdateImageNode(RefPtr<FrameNode> & row,RefPtr<FrameNode> & selectIcon)1005 void MenuItemPattern::UpdateImageNode(RefPtr<FrameNode>& row, RefPtr<FrameNode>& selectIcon)
1006 {
1007     auto pipeline = PipelineBase::GetCurrentContext();
1008     CHECK_NULL_VOID(pipeline);
1009     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1010     CHECK_NULL_VOID(itemProperty);
1011     auto symbol = itemProperty->GetSelectSymbol();
1012     if (itemProperty->GetSelectIconSrc().value_or("").empty() &&
1013         Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1014         // iamge -> symbol
1015         row->RemoveChild(selectIcon);
1016         selectIcon = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG,
1017             ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<TextPattern>(); });
1018         auto selectTheme = pipeline->GetTheme<SelectTheme>();
1019         CHECK_NULL_VOID(selectTheme);
1020         auto props = selectIcon->GetLayoutProperty<TextLayoutProperty>();
1021         CHECK_NULL_VOID(props);
1022         props->UpdateFontSize(selectTheme->GetEndIconWidth());
1023         props->UpdateSymbolColorList({ selectTheme->GetMenuIconColor() });
1024         symbol(AccessibilityManager::WeakClaim(AccessibilityManager::RawPtr(selectIcon)));
1025     } else {
1026         // image -> image
1027         auto iconTheme = pipeline->GetTheme<IconTheme>();
1028         CHECK_NULL_VOID(iconTheme);
1029         auto userIcon = itemProperty->GetSelectIconSrc().value_or("");
1030         auto iconPath = userIcon.empty() ? iconTheme->GetIconPath(InternalResource::ResourceId::MENU_OK_SVG) : userIcon;
1031         auto selectTheme = pipeline->GetTheme<SelectTheme>();
1032         CHECK_NULL_VOID(selectTheme);
1033         ImageSourceInfo imageSourceInfo;
1034         imageSourceInfo.SetSrc(iconPath);
1035         auto props = selectIcon->GetLayoutProperty<ImageLayoutProperty>();
1036         CHECK_NULL_VOID(props);
1037         props->UpdateImageSourceInfo(imageSourceInfo);
1038         UpdateIconSrc(selectIcon, selectTheme->GetIconSideLength(), selectTheme->GetIconSideLength(),
1039             selectTheme->GetMenuIconColor(), userIcon.empty());
1040     }
1041 }
1042 
UpdateSymbolNode(RefPtr<FrameNode> & row,RefPtr<FrameNode> & selectIcon)1043 void MenuItemPattern::UpdateSymbolNode(RefPtr<FrameNode>& row, RefPtr<FrameNode>& selectIcon)
1044 {
1045     auto pipeline = PipelineBase::GetCurrentContext();
1046     CHECK_NULL_VOID(pipeline);
1047     auto props = selectIcon->GetLayoutProperty<TextLayoutProperty>();
1048     CHECK_NULL_VOID(props);
1049     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1050     CHECK_NULL_VOID(itemProperty);
1051     auto selectTheme = pipeline->GetTheme<SelectTheme>();
1052     CHECK_NULL_VOID(selectTheme);
1053     auto symbol = itemProperty->GetSelectSymbol();
1054     if (itemProperty->GetSelectIconSrc().value_or("").empty()) {
1055         // symbol -> symbol
1056         props->UpdateFontSize(selectTheme->GetEndIconWidth());
1057         props->UpdateSymbolColorList({ selectTheme->GetMenuIconColor() });
1058         if (symbol) {
1059             symbol(AccessibilityManager::WeakClaim(AccessibilityManager::RawPtr(selectIcon)));
1060         } else {
1061             auto menuTheme = pipeline->GetTheme<MenuTheme>();
1062             CHECK_NULL_VOID(menuTheme);
1063             uint32_t symbolId = menuTheme->GetSymbolId();
1064             props->UpdateSymbolSourceInfo(SymbolSourceInfo(symbolId));
1065         }
1066     } else {
1067         // symbol -> image
1068         row->RemoveChild(selectIcon);
1069         selectIcon = FrameNode::CreateFrameNode(
1070             V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
1071         ImageSourceInfo imageSourceInfo;
1072         auto userIcon = itemProperty->GetSelectIconSrc().value_or("");
1073         imageSourceInfo.SetSrc(userIcon);
1074         auto props = selectIcon->GetLayoutProperty<ImageLayoutProperty>();
1075         CHECK_NULL_VOID(props);
1076         props->UpdateImageSourceInfo(imageSourceInfo);
1077         UpdateIconSrc(selectIcon, selectTheme->GetIconSideLength(), selectTheme->GetIconSideLength(),
1078             selectTheme->GetMenuIconColor(), userIcon.empty());
1079     }
1080 }
1081 
1082 
AddSelectIcon(RefPtr<FrameNode> & row)1083 void MenuItemPattern::AddSelectIcon(RefPtr<FrameNode>& row)
1084 {
1085     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1086     CHECK_NULL_VOID(itemProperty);
1087     if (!itemProperty->GetSelectIcon().value_or(false)) {
1088         if (selectIcon_) {
1089             row->RemoveChildAtIndex(0);
1090             selectIcon_ = nullptr;
1091             itemProperty->SetSelectSymbol(nullptr);
1092             row->MarkModifyDone();
1093             row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1094         }
1095         return;
1096     }
1097     if (!selectIcon_) {
1098         if (!itemProperty->GetSelectIconSrc().value_or("").empty() ||
1099             Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1100             selectIcon_ = FrameNode::CreateFrameNode(
1101                 V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
1102         } else {
1103             selectIcon_ = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG,
1104                 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<TextPattern>(); });
1105         }
1106     }
1107     if (selectIcon_->GetTag() == V2::IMAGE_ETS_TAG) {
1108         UpdateImageNode(row, selectIcon_);
1109     } else {
1110         UpdateSymbolNode(row, selectIcon_);
1111     }
1112     auto renderContext = selectIcon_->GetRenderContext();
1113     CHECK_NULL_VOID(renderContext);
1114     renderContext->SetVisible(isSelected_);
1115 
1116     selectIcon_->MountToParent(row, 0);
1117     selectIcon_->MarkModifyDone();
1118     selectIcon_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1119 }
1120 
AddExpandIcon(RefPtr<FrameNode> & row)1121 void MenuItemPattern::AddExpandIcon(RefPtr<FrameNode>& row)
1122 {
1123     auto host = GetHost();
1124     CHECK_NULL_VOID(host);
1125     auto menuNode = GetMenu();
1126     auto menuPattern = menuNode ? menuNode->GetPattern<MenuPattern>() : nullptr;
1127     auto menuProperty = menuNode ? menuNode->GetLayoutProperty<MenuLayoutProperty>() : nullptr;
1128     CHECK_NULL_VOID(menuProperty);
1129     auto canExpand = GetSubBuilder() != nullptr && menuPattern
1130         && !menuPattern->IsEmbedded() && !menuPattern->IsStackSubmenu()
1131         && (expandingMode_ == SubMenuExpandingMode::EMBEDDED || expandingMode_ == SubMenuExpandingMode::STACK);
1132     if (!canExpand) {
1133         if (expandIcon_) {
1134             row->RemoveChild(expandIcon_);
1135             expandIcon_ = nullptr;
1136             row->MarkModifyDone();
1137             row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1138         }
1139         return;
1140     }
1141     if (!expandIcon_) {
1142         expandIcon_ = FrameNode::CreateFrameNode(
1143             V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
1144         CHECK_NULL_VOID(expandIcon_);
1145     }
1146     auto pipeline = PipelineBase::GetCurrentContext();
1147     CHECK_NULL_VOID(pipeline);
1148     auto iconTheme = pipeline->GetTheme<IconTheme>();
1149     CHECK_NULL_VOID(iconTheme);
1150     auto iconPath = iconTheme->GetIconPath(
1151         expandingMode_ == SubMenuExpandingMode::STACK
1152             ? InternalResource::ResourceId::IC_PUBLIC_ARROW_RIGHT_SVG
1153             : InternalResource::ResourceId::IC_PUBLIC_ARROW_DOWN_SVG);
1154     auto selectTheme = pipeline->GetTheme<SelectTheme>();
1155     CHECK_NULL_VOID(selectTheme);
1156     ImageSourceInfo imageSourceInfo(iconPath);
1157     auto props = expandIcon_->GetLayoutProperty<ImageLayoutProperty>();
1158     CHECK_NULL_VOID(props);
1159     props->UpdateImageSourceInfo(imageSourceInfo);
1160     UpdateIconSrc(expandIcon_, selectTheme->GetIconSideLength(), selectTheme->GetIconSideLength(),
1161         selectTheme->GetMenuIconColor(), true);
1162 
1163     auto expandIconIndex = row->GetChildren().size();
1164     expandIcon_->MountToParent(row, expandIconIndex);
1165     expandIcon_->MarkModifyDone();
1166     expandIcon_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1167 }
1168 
AddClickableArea()1169 void MenuItemPattern::AddClickableArea()
1170 {
1171     if (expandingMode_ == SubMenuExpandingMode::EMBEDDED && GetSubBuilder() != nullptr && !IsEmbedded() &&
1172         !clickableArea_) {
1173         auto host = GetHost();
1174         CHECK_NULL_VOID(host);
1175         auto hostAccessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
1176         CHECK_NULL_VOID(hostAccessibilityProperty);
1177         hostAccessibilityProperty->SetAccessibilityLevel(AccessibilityProperty::Level::NO_STR);
1178         auto clickableArea = FrameNode::CreateFrameNode(V2::ROW_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
1179             AceType::MakeRefPtr<LinearLayoutPattern>(false));
1180         CHECK_NULL_VOID(clickableArea);
1181         auto pipeline = PipelineBase::GetCurrentContext();
1182         CHECK_NULL_VOID(pipeline);
1183         auto theme = pipeline->GetTheme<SelectTheme>();
1184         CHECK_NULL_VOID(theme);
1185         BorderRadiusProperty border;
1186         if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1187             border.SetRadius(theme->GetMenuDefaultInnerRadius());
1188         } else {
1189             border.SetRadius(theme->GetInnerBorderRadius());
1190         }
1191         auto clickableContext = clickableArea->GetRenderContext();
1192         CHECK_NULL_VOID(clickableContext);
1193         clickableContext->UpdateBorderRadius(border);
1194         auto menuProperty = host->GetLayoutProperty<MenuItemLayoutProperty>();
1195         CHECK_NULL_VOID(menuProperty);
1196         std::string content = menuProperty->GetContent().value_or("");
1197         std::string label = menuProperty->GetLabel().value_or("");
1198         auto accessibilityProperty = clickableArea->GetAccessibilityProperty<AccessibilityProperty>();
1199         CHECK_NULL_VOID(accessibilityProperty);
1200         accessibilityProperty->SetAccessibilityText(content + "," + label);
1201         clickableArea_ = clickableArea;
1202         clickableArea_->MountToParent(host, CLICKABLE_AREA_VIEW_INDEX);
1203     }
1204 }
1205 
AddStackSubMenuHeader(RefPtr<FrameNode> & menuNode)1206 void MenuItemPattern::AddStackSubMenuHeader(RefPtr<FrameNode>& menuNode)
1207 {
1208     auto host = GetHost();
1209     CHECK_NULL_VOID(host);
1210     auto layoutProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1211     CHECK_NULL_VOID(layoutProperty);
1212     auto pipeline = PipelineBase::GetCurrentContext();
1213     CHECK_NULL_VOID(pipeline);
1214     auto iconTheme = pipeline->GetTheme<IconTheme>();
1215     CHECK_NULL_VOID(iconTheme);
1216     auto selectTheme = pipeline->GetTheme<SelectTheme>();
1217     CHECK_NULL_VOID(selectTheme);
1218     auto iconPath = iconTheme->GetIconPath(InternalResource::ResourceId::IC_PUBLIC_ARROW_RIGHT_SVG);
1219     ImageSourceInfo imageSourceInfo;
1220     imageSourceInfo.SetSrc(iconPath);
1221     imageSourceInfo.SetFillColor(selectTheme->GetMenuIconColor());
1222     auto content = layoutProperty->GetContent().value_or(layoutProperty->GetLabel().value_or(""));
1223 
1224     MenuItemProperties menuItemProps;
1225     menuItemProps.content = content;
1226     menuItemProps.endIcon = imageSourceInfo;
1227     MenuItemModelNG menuItemModel;
1228     menuItemModel.Create(menuItemProps);
1229     auto stack = ViewStackProcessor::GetInstance();
1230 
1231     auto titleItem = AceType::DynamicCast<FrameNode>(stack->Finish());
1232     auto pattern = titleItem->GetPattern<MenuItemPattern>();
1233     CHECK_NULL_VOID(pattern);
1234     pattern->SetIsStackSubmenuHeader();
1235     titleItem->MountToParent(menuNode, 0);
1236 }
1237 
GetClickableArea()1238 RefPtr<FrameNode> MenuItemPattern::GetClickableArea()
1239 {
1240     auto host = GetHost();
1241     CHECK_NULL_RETURN(host, nullptr);
1242     auto clickableArea = host->GetChildAtIndex(CLICKABLE_AREA_VIEW_INDEX);
1243     CHECK_NULL_RETURN(clickableArea, host);
1244     auto clickableAreaNode = AceType::DynamicCast<FrameNode>(clickableArea);
1245     CHECK_NULL_RETURN(clickableAreaNode, host);
1246     return clickableAreaNode;
1247 }
1248 
UpdateIcon(RefPtr<FrameNode> & row,bool isStart)1249 void MenuItemPattern::UpdateIcon(RefPtr<FrameNode>& row, bool isStart)
1250 {
1251     auto pipeline = PipelineBase::GetCurrentContext();
1252     CHECK_NULL_VOID(pipeline);
1253     auto selectTheme = pipeline->GetTheme<SelectTheme>();
1254     CHECK_NULL_VOID(selectTheme);
1255     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1256     CHECK_NULL_VOID(itemProperty);
1257     ImageSourceInfo defaultImageSourceInfo;
1258     auto iconSrc = isStart ? itemProperty->GetStartIcon().value_or(defaultImageSourceInfo)
1259                            : itemProperty->GetEndIcon().value_or(defaultImageSourceInfo);
1260     auto symbol = isStart ? itemProperty->GetStartSymbol() : itemProperty->GetEndSymbol();
1261     auto& iconNode = isStart ? startIcon_ : endIcon_;
1262     if (iconSrc.GetSrc().empty() && symbol == nullptr) {
1263         row->RemoveChild(iconNode); // it's safe even if iconNode is nullptr
1264         iconNode = nullptr;
1265         if (isStart) {
1266             itemProperty->SetStartSymbol(nullptr);
1267         } else {
1268             itemProperty->SetEndSymbol(nullptr);
1269         }
1270         row->MarkModifyDone();
1271         row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1272         return;
1273     }
1274     if (!iconNode) {
1275         if (symbol) {
1276             iconNode = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG,
1277                 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<TextPattern>(); });
1278         } else {
1279             iconNode = FrameNode::CreateFrameNode(
1280                 V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
1281         }
1282         CHECK_NULL_VOID(iconNode);
1283     }
1284     if (iconNode->GetTag() == V2::IMAGE_ETS_TAG) {
1285         UpdateImageIcon(row, iconNode, iconSrc, symbol, isStart);
1286     } else {
1287         UpdateSymbolIcon(row, iconNode, iconSrc, symbol, isStart);
1288     }
1289     iconNode->MountToParent(row, ((isStart && selectIcon_) || (!isStart && label_)) ? 1 : 0);
1290     iconNode->MarkModifyDone();
1291     iconNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1292 }
1293 
UpdateImageIcon(RefPtr<FrameNode> & row,RefPtr<FrameNode> & iconNode,ImageSourceInfo & iconSrc,std::function<void (WeakPtr<NG::FrameNode>)> & symbol,bool isStart)1294 void MenuItemPattern::UpdateImageIcon(RefPtr<FrameNode>& row, RefPtr<FrameNode>& iconNode, ImageSourceInfo& iconSrc,
1295     std::function<void(WeakPtr<NG::FrameNode>)>& symbol, bool isStart)
1296 {
1297     auto pipeline = PipelineBase::GetCurrentContext();
1298     CHECK_NULL_VOID(pipeline);
1299     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1300     CHECK_NULL_VOID(itemProperty);
1301     auto selectTheme = pipeline->GetTheme<SelectTheme>();
1302     CHECK_NULL_VOID(selectTheme);
1303     if (symbol) {
1304         // iamge -> symbol
1305         row->RemoveChild(iconNode);
1306         iconNode = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
1307             []() { return AceType::MakeRefPtr<TextPattern>(); });
1308 
1309         auto props = iconNode->GetLayoutProperty<TextLayoutProperty>();
1310         CHECK_NULL_VOID(props);
1311         props->UpdateFontSize(selectTheme->GetEndIconWidth());
1312         props->UpdateSymbolColorList({ selectTheme->GetMenuIconColor() });
1313         symbol(AccessibilityManager::WeakClaim(AccessibilityManager::RawPtr(iconNode)));
1314     } else {
1315         // image -> image
1316         auto iconWidth = isStart ? selectTheme->GetIconSideLength() : selectTheme->GetEndIconWidth();
1317         auto iconHeight = isStart ? selectTheme->GetIconSideLength() : selectTheme->GetEndIconHeight();
1318         ImageSourceInfo imageSourceInfo(iconSrc);
1319         auto props = iconNode->GetLayoutProperty<ImageLayoutProperty>();
1320         CHECK_NULL_VOID(props);
1321         props->UpdateImageSourceInfo(imageSourceInfo);
1322         bool useDefaultThemeIcon = UseDefaultThemeIcon(imageSourceInfo);
1323         UpdateIconSrc(iconNode, iconWidth, iconHeight, selectTheme->GetMenuIconColor(), useDefaultThemeIcon);
1324     }
1325 }
1326 
UseDefaultThemeIcon(const ImageSourceInfo & imageSourceInfo)1327 bool MenuItemPattern::UseDefaultThemeIcon(const ImageSourceInfo& imageSourceInfo)
1328 {
1329     if (imageSourceInfo.IsSvg()) {
1330         auto src = imageSourceInfo.GetSrc();
1331         auto srcId = src.substr(SYSTEM_RESOURCE_PREFIX.size(),
1332             src.substr(0, src.rfind(".svg")).size() - SYSTEM_RESOURCE_PREFIX.size());
1333         if (srcId.find("ohos_") != std::string::npos) {
1334             return true;
1335         }
1336         uint64_t parsedSrcId = StringUtils::StringToLongUint(srcId);
1337         return (parsedSrcId != 0
1338             && (parsedSrcId >= MIN_SYSTEM_RESOURCE_ID)
1339             && (parsedSrcId <= MAX_SYSTEM_RESOURCE_ID));
1340     }
1341     return false;
1342 }
1343 
UpdateSymbolIcon(RefPtr<FrameNode> & row,RefPtr<FrameNode> & iconNode,ImageSourceInfo & iconSrc,std::function<void (WeakPtr<NG::FrameNode>)> & symbol,bool isStart)1344 void MenuItemPattern::UpdateSymbolIcon(RefPtr<FrameNode>& row, RefPtr<FrameNode>& iconNode, ImageSourceInfo& iconSrc,
1345     std::function<void(WeakPtr<NG::FrameNode>)>& symbol, bool isStart)
1346 {
1347     auto pipeline = PipelineBase::GetCurrentContext();
1348     CHECK_NULL_VOID(pipeline);
1349     auto props = iconNode->GetLayoutProperty<TextLayoutProperty>();
1350     CHECK_NULL_VOID(props);
1351     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1352     CHECK_NULL_VOID(itemProperty);
1353     auto selectTheme = pipeline->GetTheme<SelectTheme>();
1354     CHECK_NULL_VOID(selectTheme);
1355     if (symbol) {
1356         // symbol -> symbol
1357         props->UpdateFontSize(selectTheme->GetEndIconWidth());
1358         props->UpdateSymbolColorList({ selectTheme->GetMenuIconColor() });
1359         symbol(AccessibilityManager::WeakClaim(AccessibilityManager::RawPtr(iconNode)));
1360     } else {
1361         // symbol -> image
1362         row->RemoveChild(iconNode);
1363         iconNode = FrameNode::CreateFrameNode(
1364             V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
1365         auto iconWidth = isStart ? selectTheme->GetIconSideLength() : selectTheme->GetEndIconWidth();
1366         auto iconHeight = isStart ? selectTheme->GetIconSideLength() : selectTheme->GetEndIconHeight();
1367         ImageSourceInfo imageSourceInfo(iconSrc);
1368         auto props = iconNode->GetLayoutProperty<ImageLayoutProperty>();
1369         CHECK_NULL_VOID(props);
1370         props->UpdateImageSourceInfo(imageSourceInfo);
1371         UpdateIconSrc(iconNode, iconWidth, iconHeight, selectTheme->GetMenuIconColor(), false);
1372     }
1373 }
1374 
UpdateText(RefPtr<FrameNode> & row,RefPtr<MenuLayoutProperty> & menuProperty,bool isLabel)1375 void MenuItemPattern::UpdateText(RefPtr<FrameNode>& row, RefPtr<MenuLayoutProperty>& menuProperty, bool isLabel)
1376 {
1377     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1378     CHECK_NULL_VOID(itemProperty);
1379     auto content = isLabel ? itemProperty->GetLabel().value_or("") : itemProperty->GetContent().value_or("");
1380     auto& node = isLabel ? label_ : content_;
1381     if (content.empty()) {
1382         (void)row->RemoveChild(node); // it's safe even if node is nullptr
1383         node = nullptr;
1384         row->MarkModifyDone();
1385         row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1386         return;
1387     }
1388 
1389     if (!node) {
1390         node = FrameNode::CreateFrameNode(
1391             V2::TEXT_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<TextPattern>());
1392     }
1393     auto textProperty = node ? node->GetLayoutProperty<TextLayoutProperty>() : nullptr;
1394     CHECK_NULL_VOID(textProperty);
1395     auto renderContext = node->GetRenderContext();
1396     CHECK_NULL_VOID(renderContext);
1397     renderContext->UpdateClipEdge(false);
1398     auto context = PipelineBase::GetCurrentContext();
1399     auto theme = context ? context->GetTheme<SelectTheme>() : nullptr;
1400     CHECK_NULL_VOID(theme);
1401     auto layoutDirection = itemProperty->GetNonAutoLayoutDirection();
1402     TextAlign textAlign = static_cast<TextAlign>(theme->GetMenuItemContentAlign());
1403     if (layoutDirection == TextDirection::RTL) {
1404         if (textAlign == TextAlign::LEFT) {
1405             textAlign = TextAlign::RIGHT;
1406         } else if (textAlign ==TextAlign::RIGHT) {
1407             textAlign = TextAlign::LEFT;
1408         } else if (textAlign == TextAlign::START) {
1409             textAlign = TextAlign::END;
1410         } else if (textAlign == TextAlign::END) {
1411             textAlign = TextAlign::START;
1412         }
1413         textProperty->UpdateTextAlign(textAlign);
1414     }
1415     UpdateFont(menuProperty, theme, isLabel);
1416     textProperty->UpdateContent(content);
1417     UpdateTextOverflow(textProperty, theme);
1418     node->MountToParent(row, isLabel ? 0 : DEFAULT_NODE_SLOT);
1419     node->MarkModifyDone();
1420     node->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1421 }
1422 
UpdateTextOverflow(RefPtr<TextLayoutProperty> & textProperty,RefPtr<SelectTheme> & theme)1423 void MenuItemPattern::UpdateTextOverflow(RefPtr<TextLayoutProperty>& textProperty,
1424     RefPtr<SelectTheme>& theme)
1425 {
1426     if (theme && Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_THIRTEEN)) {
1427         if (theme->GetExpandDisplay()) {
1428             textProperty->UpdateTextOverflow(TextOverflow::ELLIPSIS);
1429             textProperty->UpdateMaxLines(1);
1430         } else {
1431             textProperty->UpdateMaxLines(std::numeric_limits<int32_t>::max());
1432         }
1433     } else {
1434         textProperty->UpdateTextOverflow(TextOverflow::ELLIPSIS);
1435         textProperty->UpdateMaxLines(1);
1436     }
1437     UpdateMaxLinesFromTheme(textProperty);
1438 }
1439 
UpdateFont(RefPtr<MenuLayoutProperty> & menuProperty,RefPtr<SelectTheme> & theme,bool isLabel)1440 void MenuItemPattern::UpdateFont(RefPtr<MenuLayoutProperty>& menuProperty, RefPtr<SelectTheme>& theme, bool isLabel)
1441 {
1442     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1443     CHECK_NULL_VOID(itemProperty);
1444     auto& node = isLabel ? label_ : content_;
1445     auto textProperty = node ? node->GetLayoutProperty<TextLayoutProperty>() : nullptr;
1446     CHECK_NULL_VOID(textProperty);
1447 
1448     auto fontSize = isLabel ? itemProperty->GetLabelFontSize() : itemProperty->GetFontSize();
1449     UpdateFontSize(textProperty, menuProperty, fontSize, theme->GetMenuFontSize());
1450     auto fontWeight = isLabel ? itemProperty->GetLabelFontWeight() : itemProperty->GetFontWeight();
1451     UpdateFontWeight(textProperty, menuProperty, fontWeight);
1452     auto fontStyle = isLabel ? itemProperty->GetLabelItalicFontStyle() : itemProperty->GetItalicFontStyle();
1453     UpdateFontStyle(textProperty, menuProperty, fontStyle);
1454     auto fontColor = isLabel ? itemProperty->GetLabelFontColor() : itemProperty->GetFontColor();
1455     auto menuItemNode = GetHost();
1456     UpdateFontColor(
1457         node, menuProperty, fontColor, isLabel ? theme->GetSecondaryFontColor() : theme->GetMenuFontColor());
1458     if (!isLabel) {
1459         auto menuItemRenderContext = menuItemNode->GetRenderContext();
1460         CHECK_NULL_VOID(menuItemRenderContext);
1461         auto renderContext = node->GetRenderContext();
1462         CHECK_NULL_VOID(renderContext);
1463         if (menuItemRenderContext->HasForegroundColor()) {
1464             textProperty->UpdateTextColor(menuItemRenderContext->GetForegroundColorValue());
1465             renderContext->UpdateForegroundColor(menuItemRenderContext->GetForegroundColorValue());
1466         }
1467     }
1468     auto fontFamily = isLabel ? itemProperty->GetLabelFontFamily() : itemProperty->GetFontFamily();
1469     UpdateFontFamily(textProperty, menuProperty, fontFamily);
1470 }
1471 
UpdateMaxLinesFromTheme(RefPtr<TextLayoutProperty> & textProperty)1472 void MenuItemPattern::UpdateMaxLinesFromTheme(RefPtr<TextLayoutProperty>& textProperty)
1473 {
1474     auto host = GetHost();
1475     CHECK_NULL_VOID(host);
1476     auto pipeline = host->GetContext();
1477     CHECK_NULL_VOID(pipeline);
1478     auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
1479     CHECK_NULL_VOID(menuTheme);
1480     auto fontScale = pipeline->GetFontScale();
1481     if (NearEqual(fontScale, menuTheme->GetBigFontSizeScale()) ||
1482         NearEqual(fontScale, menuTheme->GetLargeFontSizeScale()) ||
1483         NearEqual(fontScale, menuTheme->GetMaxFontSizeScale())) {
1484         textProperty->UpdateMaxLines(menuTheme->GetTextMaxLines());
1485     }
1486 }
1487 
UpdateTextNodes()1488 void MenuItemPattern::UpdateTextNodes()
1489 {
1490     auto host = GetHost();
1491     CHECK_NULL_VOID(host);
1492     auto menuNode = GetMenu();
1493     CHECK_NULL_VOID(menuNode);
1494     auto menuProperty = menuNode->GetLayoutProperty<MenuLayoutProperty>();
1495     RefPtr<FrameNode> leftRow =
1496         host->GetChildAtIndex(0) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(0)) : nullptr;
1497     CHECK_NULL_VOID(leftRow);
1498     UpdateText(leftRow, menuProperty, false);
1499     RefPtr<FrameNode> rightRow =
1500         host->GetChildAtIndex(1) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(1)) : nullptr;
1501     CHECK_NULL_VOID(rightRow);
1502     UpdateText(rightRow, menuProperty, true);
1503     if (IsDisabled()) {
1504         UpdateDisabledStyle();
1505     }
1506     host->MarkModifyDone();
1507     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1508 }
1509 
IsDisabled()1510 bool MenuItemPattern::IsDisabled()
1511 {
1512     auto eventHub = GetHost()->GetEventHub<MenuItemEventHub>();
1513     CHECK_NULL_RETURN(eventHub, true);
1514     return !eventHub->IsEnabled();
1515 }
1516 
UpdateDisabledStyle()1517 void MenuItemPattern::UpdateDisabledStyle()
1518 {
1519     auto context = PipelineBase::GetCurrentContext();
1520     CHECK_NULL_VOID(context);
1521     auto theme = context->GetTheme<SelectTheme>();
1522     CHECK_NULL_VOID(theme);
1523     if (content_) {
1524         content_->GetRenderContext()->UpdateOpacity(theme->GetDisabledFontColorAlpha());
1525         content_->MarkModifyDone();
1526     }
1527     if (label_) {
1528         label_->GetRenderContext()->UpdateOpacity(theme->GetDisabledFontColorAlpha());
1529         label_->MarkModifyDone();
1530     }
1531     if (startIcon_) {
1532         startIcon_->GetRenderContext()->UpdateOpacity(theme->GetDisabledFontColorAlpha());
1533         startIcon_->MarkModifyDone();
1534     }
1535     if (endIcon_) {
1536         endIcon_->GetRenderContext()->UpdateOpacity(theme->GetDisabledFontColorAlpha());
1537         endIcon_->MarkModifyDone();
1538     }
1539 }
1540 
SetAccessibilityAction()1541 void MenuItemPattern::SetAccessibilityAction()
1542 {
1543     auto host = GetHost();
1544     CHECK_NULL_VOID(host);
1545     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
1546     CHECK_NULL_VOID(accessibilityProperty);
1547     accessibilityProperty->SetActionSelect([weakPtr = WeakClaim(this)]() {
1548         const auto& pattern = weakPtr.Upgrade();
1549         CHECK_NULL_VOID(pattern);
1550         auto host = pattern->GetHost();
1551         CHECK_NULL_VOID(host);
1552         auto hub = host->GetEventHub<MenuItemEventHub>();
1553         CHECK_NULL_VOID(hub);
1554         auto onChange = hub->GetOnChange();
1555         auto selectedChangeEvent = hub->GetSelectedChangeEvent();
1556         pattern->SetChange();
1557         if (selectedChangeEvent) {
1558             selectedChangeEvent(pattern->IsSelected());
1559         }
1560         if (onChange) {
1561             onChange(pattern->IsSelected());
1562             pattern->RecordChangeEvent();
1563         }
1564         auto context = host->GetRenderContext();
1565         CHECK_NULL_VOID(context);
1566         pattern->MarkIsSelected(pattern->IsSelected());
1567         context->OnMouseSelectUpdate(pattern->IsSelected(), ITEM_FILL_COLOR, ITEM_FILL_COLOR);
1568         if (pattern->GetSubBuilder() != nullptr) {
1569             pattern->ShowSubMenu();
1570             return;
1571         }
1572 
1573         pattern->CloseMenu();
1574     });
1575 }
1576 
MarkIsSelected(bool isSelected)1577 void MenuItemPattern::MarkIsSelected(bool isSelected)
1578 {
1579     if (isSelected_ == isSelected) {
1580         return;
1581     }
1582     isSelected_ = isSelected;
1583     auto eventHub = GetEventHub<MenuItemEventHub>();
1584     CHECK_NULL_VOID(eventHub);
1585     auto onChange = eventHub->GetOnChange();
1586     auto selectedChangeEvent = eventHub->GetSelectedChangeEvent();
1587     if (selectedChangeEvent) {
1588         selectedChangeEvent(isSelected);
1589     }
1590     if (onChange) {
1591         onChange(isSelected);
1592     }
1593     auto host = GetHost();
1594     CHECK_NULL_VOID(host);
1595     if (isSelected) {
1596         eventHub->SetCurrentUIState(UI_STATE_SELECTED, isSelected);
1597     } else {
1598         eventHub->SetCurrentUIState(UI_STATE_SELECTED, isSelected);
1599     }
1600 }
1601 
IsSelectOverlayMenu()1602 bool MenuItemPattern::IsSelectOverlayMenu()
1603 {
1604     auto topLevelMenuPattern = GetMenuPattern(true);
1605     if (!topLevelMenuPattern) {
1606         return false;
1607     }
1608     return topLevelMenuPattern->IsSelectOverlayExtensionMenu() || topLevelMenuPattern->IsSelectOverlayCustomMenu() ||
1609            topLevelMenuPattern->IsSelectOverlaySubMenu();
1610 }
1611 
ParseMenuRadius(MenuParam & param)1612 void MenuItemPattern::ParseMenuRadius(MenuParam& param)
1613 {
1614     auto menuWrapperNode = GetMenuWrapper();
1615     CHECK_NULL_VOID(menuWrapperNode);
1616     auto menuWrapperPattern = menuWrapperNode->GetPattern<MenuWrapperPattern>();
1617     CHECK_NULL_VOID(menuWrapperPattern);
1618 
1619     if (menuWrapperPattern->GetHasCustomRadius()) {
1620         auto outterMenuNode = GetMenu(true);
1621         CHECK_NULL_VOID(outterMenuNode);
1622         auto menuLayoutProp = outterMenuNode->GetLayoutProperty<MenuLayoutProperty>();
1623         CHECK_NULL_VOID(menuLayoutProp);
1624         if (menuLayoutProp->GetBorderRadius().has_value()) {
1625             BorderRadiusProperty borderRadius = menuLayoutProp->GetBorderRadiusValue();
1626             param.borderRadius = std::make_optional(borderRadius);
1627         }
1628     }
1629 }
1630 
IsSubMenu()1631 bool MenuItemPattern::IsSubMenu()
1632 {
1633     auto topLevelMenuPattern = GetMenuPattern(true);
1634     if (!topLevelMenuPattern) {
1635         return false;
1636     }
1637     return topLevelMenuPattern->IsSubMenu();
1638 }
1639 
IsEmbedded()1640 bool MenuItemPattern::IsEmbedded()
1641 {
1642     auto parentMenuPattern = GetMenuPattern();
1643     return parentMenuPattern ? parentMenuPattern->IsEmbedded() : false;
1644 }
1645 
ModifyDivider()1646 void MenuItemPattern::ModifyDivider()
1647 {
1648     auto menu = GetMenu();
1649     CHECK_NULL_VOID(menu);
1650     auto menuProperty = menu->GetLayoutProperty<MenuLayoutProperty>();
1651     CHECK_NULL_VOID(menuProperty);
1652     auto divider = menuProperty->GetItemDivider();
1653     if (divider.has_value()) {
1654         auto host = GetHost();
1655         CHECK_NULL_VOID(host);
1656         auto paintProperty = host->GetPaintProperty<MenuItemPaintProperty>();
1657         CHECK_NULL_VOID(paintProperty);
1658         paintProperty->UpdateStrokeWidth(divider->strokeWidth);
1659         paintProperty->UpdateStartMargin(divider->startMargin);
1660         paintProperty->UpdateEndMargin(divider->endMargin);
1661         paintProperty->UpdateDividerColor(divider->color);
1662         host->MarkModifyDone();
1663         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1664     }
1665 }
1666 
UpdateNeedDivider(bool need)1667 void MenuItemPattern::UpdateNeedDivider(bool need)
1668 {
1669     auto host = GetHost();
1670     CHECK_NULL_VOID(host);
1671     auto paintProperty = host->GetPaintProperty<MenuItemPaintProperty>();
1672     CHECK_NULL_VOID(paintProperty);
1673     paintProperty->UpdateNeedDivider(need);
1674     if (need) {
1675         ModifyDivider();
1676     }
1677 }
1678 
GetDividerStroke()1679 float MenuItemPattern::GetDividerStroke()
1680 {
1681     auto host = GetHost();
1682     CHECK_NULL_RETURN(host, 0.0f);
1683     auto props = host->GetPaintProperty<MenuItemPaintProperty>();
1684     CHECK_NULL_RETURN(props, 0.0f);
1685     return props->GetStrokeWidth().value_or(Dimension(0.0f, DimensionUnit::PX)).ConvertToPx();
1686 }
1687 
FindTouchedEmbeddedMenuItem(const OffsetF & position)1688 RefPtr<FrameNode> MenuItemPattern::FindTouchedEmbeddedMenuItem(const OffsetF& position)
1689 {
1690     auto host = GetHost();
1691     CHECK_NULL_RETURN(host, nullptr);
1692     if (expandingMode_ != SubMenuExpandingMode::EMBEDDED || !isExpanded_
1693         || embeddedMenu_ == nullptr || embeddedMenu_->GetTag() != V2::MENU_ETS_TAG) {
1694         return host;
1695     }
1696     CHECK_NULL_RETURN(clickableArea_, host);
1697     auto clickableAreaOffset = clickableArea_->GetPaintRectOffset();
1698     auto clickableAreaSize = clickableArea_->GetGeometryNode()->GetFrameSize();
1699     auto clickableAreaZone = RectF(clickableAreaOffset.GetX(), clickableAreaOffset.GetY(),
1700         clickableAreaSize.Width(), clickableAreaSize.Height());
1701     if (clickableAreaZone.IsInRegion(PointF(position.GetX(), position.GetY()))) {
1702         return host;
1703     }
1704     RefPtr<FrameNode> menuItem = nullptr;
1705     for (const auto& child : embeddedMenu_->GetChildren()) {
1706         if (child->GetTag() == V2::MENU_ITEM_ETS_TAG) {
1707             menuItem = AceType::DynamicCast<FrameNode>(child);
1708         }
1709         if (menuItem) {
1710             auto menuItemOffset = menuItem->GetPaintRectOffset();
1711             auto menuItemSize = menuItem->GetGeometryNode()->GetFrameSize();
1712             auto menuItemZone = RectF(menuItemOffset.GetX(), menuItemOffset.GetY(),
1713                 menuItemSize.Width(), menuItemSize.Height());
1714             if (menuItemZone.IsInRegion(PointF(position.GetX(), position.GetY()))) {
1715                 break;
1716             } else {
1717                 menuItem = nullptr;
1718             }
1719         }
1720     }
1721     return menuItem;
1722 }
1723 } // namespace OHOS::Ace::NG
1724