• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     subMenu->OnAccessibilityEvent(AccessibilityEventType::PAGE_OPEN);
352     TAG_LOGI(AceLogTag::ACE_OVERLAY, "Send event to %{public}d",
353         static_cast<int32_t>(AccessibilityEventType::PAGE_OPEN));
354 }
355 
GetSubMenu(RefPtr<UINode> & customNode)356 RefPtr<FrameNode> GetSubMenu(RefPtr<UINode>& customNode)
357 {
358     CHECK_NULL_RETURN(customNode, nullptr);
359     if (customNode->GetTag() == V2::MENU_ETS_TAG) {
360         auto frameNode = AceType::DynamicCast<FrameNode>(customNode);
361         CHECK_NULL_RETURN(frameNode, nullptr);
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     SetClickMenuItemId(host->GetId());
411     bool isSelectOverlayMenu = IsSelectOverlayMenu();
412     auto menuPattern = subMenu->GetPattern<MenuPattern>();
413     CHECK_NULL_VOID(menuPattern);
414     menuPattern->SetParentMenuItem(host);
415     subMenuId_ = subMenu->GetId();
416     AddSelfHoverRegion(host);
417     auto menuWrapper = GetMenuWrapper();
418     CHECK_NULL_VOID(menuWrapper);
419     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) &&
420         expandingMode_ == SubMenuExpandingMode::STACK) {
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     host->OnAccessibilityEvent(AccessibilityEventType::SELECTED);
698     auto menuNode = GetMenu();
699     CHECK_NULL_VOID(menuNode);
700     auto menuPattern = menuNode->GetPattern<MenuPattern>();
701     CHECK_NULL_VOID(menuPattern);
702     auto lastSelectedItem = menuPattern->GetLastSelectedItem();
703     if (lastSelectedItem && lastSelectedItem != host) {
704         auto pattern = lastSelectedItem->GetPattern<MenuItemPattern>();
705         CHECK_NULL_VOID(pattern);
706         pattern->SetChange();
707     }
708     menuPattern->SetLastSelectedItem(host);
709     if (GetSubBuilder() != nullptr && (expandingMode_ == SubMenuExpandingMode::SIDE ||
710         (expandingMode_ == SubMenuExpandingMode::STACK && !IsSubMenu() && !hasSubMenu) ||
711         (expandingMode_ == SubMenuExpandingMode::EMBEDDED && !IsEmbedded()))) {
712         ShowSubMenu();
713         return;
714     }
715     // hide menu when menu item is clicked
716     CloseMenu();
717 }
718 
OnTouch(const TouchEventInfo & info)719 void MenuItemPattern::OnTouch(const TouchEventInfo& info)
720 {
721     auto menuWrapper = GetMenuWrapper();
722     // When menu wrapper exists, the pressed state is handed over to the menu wrapper
723     if (menuWrapper && menuWrapper->GetTag() == V2::MENU_WRAPPER_ETS_TAG) {
724         return;
725     }
726     // change menu item paint props on press
727     auto touchType = info.GetTouches().front().GetTouchType();
728     if (touchType == TouchType::DOWN) {
729         // change background color, update press status
730         NotifyPressStatus(true);
731     } else if (touchType == TouchType::UP) {
732         NotifyPressStatus(false);
733     }
734 }
735 
NotifyPressStatus(bool isPress)736 void MenuItemPattern::NotifyPressStatus(bool isPress)
737 {
738     auto host = GetHost();
739     CHECK_NULL_VOID(host);
740     auto pipeline = host->GetContext();
741     CHECK_NULL_VOID(pipeline);
742     auto theme = pipeline->GetTheme<SelectTheme>();
743     CHECK_NULL_VOID(theme);
744     auto props = GetPaintProperty<MenuItemPaintProperty>();
745     CHECK_NULL_VOID(props);
746     auto menu = GetMenu();
747     CHECK_NULL_VOID(menu);
748     auto menuPattern = menu->GetPattern<MenuPattern>();
749     CHECK_NULL_VOID(menuPattern);
750     auto parent = AceType::DynamicCast<UINode>(host->GetParent());
751     auto menuWrapper = GetMenuWrapper();
752     auto menuWrapperPattern = menuWrapper ? menuWrapper->GetPattern<MenuWrapperPattern>() : nullptr;
753 
754     // do not change color for stacked level-1 menu items if level-2 is shown
755     auto canChangeColor = !(expandingMode_ == SubMenuExpandingMode::STACK
756         && menuWrapperPattern && menuWrapperPattern->HasStackSubMenu() && !IsSubMenu());
757     if (!canChangeColor) return;
758     if (isPress) {
759         // change background color, update press status
760         SetBgBlendColor(GetSubBuilder() ? theme->GetHoverColor() : theme->GetClickedColor());
761         if (menuWrapperPattern) {
762             menuWrapperPattern->SetLastTouchItem(host);
763         }
764         props->UpdatePress(true);
765         menuPattern->OnItemPressed(parent, index_, true);
766     } else {
767         SetBgBlendColor(isHovered_ ? theme->GetHoverColor() : Color::TRANSPARENT);
768         props->UpdatePress(false);
769         menuPattern->OnItemPressed(parent, index_, false);
770     }
771     PlayBgColorAnimation(false);
772     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
773 }
774 
OnTouch(const TouchEventInfo & info)775 void CustomMenuItemPattern::OnTouch(const TouchEventInfo& info)
776 {
777     auto touchType = info.GetTouches().front().GetTouchType();
778 
779     // close menu when touch up
780     // can't use onClick because that conflicts with interactions developers might set to the customNode
781     // recognize gesture as click if touch up position is close to last touch down position
782     if (touchType == TouchType::DOWN) {
783         lastTouchOffset_ = std::make_unique<Offset>(info.GetTouches().front().GetLocalLocation());
784     } else if (touchType == TouchType::UP) {
785         auto touchUpOffset = info.GetTouches().front().GetLocalLocation();
786         if (lastTouchOffset_ && (touchUpOffset - *lastTouchOffset_).GetDistance() <= DEFAULT_CLICK_DISTANCE) {
787             if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
788                 HandleOnChange();
789             }
790             CloseMenu();
791         }
792         lastTouchOffset_.reset();
793     }
794 }
795 
HandleOnChange()796 void CustomMenuItemPattern::HandleOnChange()
797 {
798     auto host = GetHost();
799     CHECK_NULL_VOID(host);
800     auto hub = host->GetEventHub<MenuItemEventHub>();
801     CHECK_NULL_VOID(hub);
802     auto onChange = hub->GetOnChange();
803     auto selectedChangeEvent = hub->GetSelectedChangeEvent();
804     SetChange();
805     if (selectedChangeEvent) {
806         TAG_LOGI(AceLogTag::ACE_MENU, "trigger selectedChangeEvent");
807         selectedChangeEvent(IsSelected());
808     }
809     if (onChange) {
810         TAG_LOGI(AceLogTag::ACE_MENU, "trigger onChange");
811         onChange(IsSelected());
812     }
813 }
814 
OnHover(bool isHover)815 void MenuItemPattern::OnHover(bool isHover)
816 {
817     isHovered_ = isHover;
818     auto pipeline = PipelineBase::GetCurrentContext();
819     CHECK_NULL_VOID(pipeline);
820     auto theme = pipeline->GetTheme<SelectTheme>();
821     CHECK_NULL_VOID(theme);
822     auto props = GetPaintProperty<MenuItemPaintProperty>();
823     CHECK_NULL_VOID(props);
824     auto menu = GetMenu(false);
825     CHECK_NULL_VOID(menu);
826     auto menuPattern = menu->GetPattern<MenuPattern>();
827     CHECK_NULL_VOID(menuPattern);
828     auto host = GetHost();
829     CHECK_NULL_VOID(host);
830     auto parent = AceType::DynamicCast<UINode>(host->GetParent());
831 
832     if (isHover || isSubMenuShowed_) {
833         // keep hover color when subMenu showed
834         SetBgBlendColor(theme->GetHoverColor());
835         ShowSubMenu();
836         props->UpdateHover(true);
837         menuPattern->OnItemPressed(parent, index_, true, true);
838     } else {
839         SetBgBlendColor(Color::TRANSPARENT);
840         props->UpdateHover(false);
841         menuPattern->OnItemPressed(parent, index_, false, true);
842     }
843     PlayBgColorAnimation();
844     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
845 }
846 
OnVisibleChange(bool isVisible)847 void MenuItemPattern::OnVisibleChange(bool isVisible)
848 {
849     auto host = GetHost();
850     CHECK_NULL_VOID(host);
851     auto parentNode = host->GetParent();
852     CHECK_NULL_VOID(parentNode);
853     if (parentNode->GetTag() == V2::MENU_ITEM_GROUP_ETS_TAG) {
854         parentNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
855     }
856 }
857 
OnKeyEvent(const KeyEvent & event)858 bool MenuItemPattern::OnKeyEvent(const KeyEvent& event)
859 {
860     if (event.action != KeyAction::DOWN) {
861         return false;
862     }
863     auto host = GetHost();
864     CHECK_NULL_RETURN(host, false);
865     auto focusHub = host->GetOrCreateFocusHub();
866     CHECK_NULL_RETURN(focusHub, false);
867     if (event.code == KeyCode::KEY_ENTER) {
868         focusHub->OnClick(event);
869         return true;
870     }
871     if (event.code == KeyCode::KEY_DPAD_RIGHT && GetSubBuilder() && !isSubMenuShowed_) {
872         auto pipeline = PipelineBase::GetCurrentContext();
873         CHECK_NULL_RETURN(pipeline, false);
874         auto theme = pipeline->GetTheme<SelectTheme>();
875         CHECK_NULL_RETURN(theme, false);
876         SetBgBlendColor(theme->GetHoverColor());
877         PlayBgColorAnimation();
878         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
879         ShowSubMenu();
880         return true;
881     }
882     return false;
883 }
884 
OnKeyEvent(const KeyEvent & event)885 bool CustomMenuItemPattern::OnKeyEvent(const KeyEvent& event)
886 {
887     if (event.action != KeyAction::DOWN) {
888         return false;
889     }
890     auto host = GetHost();
891     CHECK_NULL_RETURN(host, false);
892     auto focusHub = host->GetOrCreateFocusHub();
893     CHECK_NULL_RETURN(focusHub, false);
894     if (event.code == KeyCode::KEY_ENTER || event.code == KeyCode::KEY_SPACE) {
895         focusHub->OnClick(event);
896         CloseMenu();
897         return true;
898     }
899     return false;
900 }
901 
InitLongPressEvent()902 void MenuItemPattern::InitLongPressEvent()
903 {
904     auto gesture = GetHost()->GetOrCreateGestureEventHub();
905     CHECK_NULL_VOID(gesture);
906     auto longPressCallback = [weak = WeakClaim(this)](GestureEvent& info) {
907         auto itemPattern = weak.Upgrade();
908         auto menuWrapper = itemPattern->GetMenuWrapper();
909         CHECK_NULL_VOID(menuWrapper);
910         auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
911         CHECK_NULL_VOID(menuWrapperPattern);
912         auto topLevelMenuPattern = itemPattern->GetMenuPattern(true);
913         CHECK_NULL_VOID(topLevelMenuPattern);
914         if (itemPattern && itemPattern->GetSubBuilder() != nullptr &&
915             menuWrapperPattern->GetPreviewMode() == MenuPreviewMode::NONE &&
916             !(topLevelMenuPattern->IsSelectOverlayCustomMenu())) {
917             itemPattern->ShowSubMenu();
918         }
919     };
920     longPressEvent_ = MakeRefPtr<LongPressEvent>(std::move(longPressCallback));
921     gesture->SetLongPressEvent(longPressEvent_);
922 }
923 
RegisterWrapperMouseEvent()924 void MenuItemPattern::RegisterWrapperMouseEvent()
925 {
926     auto menuWrapper = GetMenuWrapper();
927     if (menuWrapper && !wrapperMouseEvent_) {
928         auto inputHub = menuWrapper->GetOrCreateInputEventHub();
929         CHECK_NULL_VOID(inputHub);
930         auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
931         CHECK_NULL_VOID(menuWrapperPattern);
932 
933         auto mouseTask = [weak = WeakClaim(this), menuWrapperPattern](MouseInfo& info) {
934             auto pattern = weak.Upgrade();
935             CHECK_NULL_VOID(pattern);
936             if (menuWrapperPattern) {
937                 menuWrapperPattern->HandleMouseEvent(info, pattern);
938             }
939         };
940         wrapperMouseEvent_ = MakeRefPtr<InputEvent>(std::move(mouseTask));
941         inputHub->AddOnMouseEvent(wrapperMouseEvent_);
942     }
943 }
944 
AddSelfHoverRegion(const RefPtr<FrameNode> & targetNode)945 void MenuItemPattern::AddSelfHoverRegion(const RefPtr<FrameNode>& targetNode)
946 {
947     OffsetF topLeftPoint;
948     OffsetF bottomRightPoint;
949     auto frameSize = targetNode->GetGeometryNode()->GetMarginFrameSize();
950     topLeftPoint = targetNode->GetPaintRectOffset();
951     bottomRightPoint = targetNode->GetPaintRectOffset() + OffsetF(frameSize.Width(), frameSize.Height());
952     AddHoverRegions(topLeftPoint, bottomRightPoint);
953 }
954 
GetSubMenuPosition(const RefPtr<FrameNode> & targetNode)955 OffsetF MenuItemPattern::GetSubMenuPosition(const RefPtr<FrameNode>& targetNode)
956 {    // show menu at left top point of targetNode
957     auto frameSize = targetNode->GetGeometryNode()->GetMarginFrameSize();
958     OffsetF position = targetNode->GetPaintRectOffset() + OffsetF(frameSize.Width(), 0.0);
959     return position;
960 }
961 
962 
AddHoverRegions(const OffsetF & topLeftPoint,const OffsetF & bottomRightPoint)963 void MenuItemPattern::AddHoverRegions(const OffsetF& topLeftPoint, const OffsetF& bottomRightPoint)
964 {
965     TouchRegion hoverRegion = TouchRegion(
966         Offset(topLeftPoint.GetX(), topLeftPoint.GetY()), Offset(bottomRightPoint.GetX(), bottomRightPoint.GetY()));
967     hoverRegions_.emplace_back(hoverRegion);
968 }
969 
IsInHoverRegions(double x,double y)970 bool MenuItemPattern::IsInHoverRegions(double x, double y)
971 {
972     for (auto hoverRegion : hoverRegions_) {
973         if (hoverRegion.ContainsInRegion(x, y)) {
974             return true;
975         }
976     }
977     return false;
978 }
979 
PlayBgColorAnimation(bool isHoverChange)980 void MenuItemPattern::PlayBgColorAnimation(bool isHoverChange)
981 {
982     auto pipeline = PipelineBase::GetCurrentContext();
983     CHECK_NULL_VOID(pipeline);
984     auto theme = pipeline->GetTheme<SelectTheme>();
985     CHECK_NULL_VOID(theme);
986     AnimationOption option;
987     if (isHoverChange) {
988         option.SetDuration(theme->GetHoverAnimationDuration());
989         option.SetCurve(Curves::FRICTION);
990     } else {
991         option.SetDuration(theme->GetPressAnimationDuration());
992         option.SetCurve(Curves::SHARP);
993     }
994 
995     AnimationUtils::Animate(option, [weak = WeakClaim(this)]() {
996         auto pattern = weak.Upgrade();
997         CHECK_NULL_VOID(pattern);
998         auto clickableArea = pattern->GetClickableArea();
999         CHECK_NULL_VOID(clickableArea);
1000         auto renderContext = clickableArea->GetRenderContext();
1001         CHECK_NULL_VOID(renderContext);
1002         renderContext->BlendBgColor(pattern->GetBgBlendColor());
1003     });
1004 }
1005 
UpdateImageNode(RefPtr<FrameNode> & row,RefPtr<FrameNode> & selectIcon)1006 void MenuItemPattern::UpdateImageNode(RefPtr<FrameNode>& row, RefPtr<FrameNode>& selectIcon)
1007 {
1008     auto pipeline = PipelineBase::GetCurrentContext();
1009     CHECK_NULL_VOID(pipeline);
1010     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1011     CHECK_NULL_VOID(itemProperty);
1012     auto symbol = itemProperty->GetSelectSymbol();
1013     if (itemProperty->GetSelectIconSrc().value_or("").empty() &&
1014         Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1015         // iamge -> symbol
1016         row->RemoveChild(selectIcon);
1017         selectIcon = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG,
1018             ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<TextPattern>(); });
1019         auto selectTheme = pipeline->GetTheme<SelectTheme>();
1020         CHECK_NULL_VOID(selectTheme);
1021         auto props = selectIcon->GetLayoutProperty<TextLayoutProperty>();
1022         CHECK_NULL_VOID(props);
1023         props->UpdateFontSize(selectTheme->GetEndIconWidth());
1024         props->UpdateSymbolColorList({ selectTheme->GetMenuIconColor() });
1025         symbol(AccessibilityManager::WeakClaim(AccessibilityManager::RawPtr(selectIcon)));
1026     } else {
1027         // image -> image
1028         auto iconTheme = pipeline->GetTheme<IconTheme>();
1029         CHECK_NULL_VOID(iconTheme);
1030         auto userIcon = itemProperty->GetSelectIconSrc().value_or("");
1031         auto iconPath = userIcon.empty() ? iconTheme->GetIconPath(InternalResource::ResourceId::MENU_OK_SVG) : userIcon;
1032         auto selectTheme = pipeline->GetTheme<SelectTheme>();
1033         CHECK_NULL_VOID(selectTheme);
1034         ImageSourceInfo imageSourceInfo;
1035         imageSourceInfo.SetSrc(iconPath);
1036         auto props = selectIcon->GetLayoutProperty<ImageLayoutProperty>();
1037         CHECK_NULL_VOID(props);
1038         props->UpdateImageSourceInfo(imageSourceInfo);
1039         UpdateIconSrc(selectIcon, selectTheme->GetIconSideLength(), selectTheme->GetIconSideLength(),
1040             selectTheme->GetMenuIconColor(), userIcon.empty());
1041     }
1042 }
1043 
UpdateSymbolNode(RefPtr<FrameNode> & row,RefPtr<FrameNode> & selectIcon)1044 void MenuItemPattern::UpdateSymbolNode(RefPtr<FrameNode>& row, RefPtr<FrameNode>& selectIcon)
1045 {
1046     auto pipeline = PipelineBase::GetCurrentContext();
1047     CHECK_NULL_VOID(pipeline);
1048     auto props = selectIcon->GetLayoutProperty<TextLayoutProperty>();
1049     CHECK_NULL_VOID(props);
1050     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1051     CHECK_NULL_VOID(itemProperty);
1052     auto selectTheme = pipeline->GetTheme<SelectTheme>();
1053     CHECK_NULL_VOID(selectTheme);
1054     auto symbol = itemProperty->GetSelectSymbol();
1055     if (itemProperty->GetSelectIconSrc().value_or("").empty()) {
1056         // symbol -> symbol
1057         props->UpdateFontSize(selectTheme->GetEndIconWidth());
1058         props->UpdateSymbolColorList({ selectTheme->GetMenuIconColor() });
1059         if (symbol) {
1060             symbol(AccessibilityManager::WeakClaim(AccessibilityManager::RawPtr(selectIcon)));
1061         } else {
1062             auto menuTheme = pipeline->GetTheme<MenuTheme>();
1063             CHECK_NULL_VOID(menuTheme);
1064             uint32_t symbolId = menuTheme->GetSymbolId();
1065             props->UpdateSymbolSourceInfo(SymbolSourceInfo(symbolId));
1066         }
1067     } else {
1068         // symbol -> image
1069         row->RemoveChild(selectIcon);
1070         selectIcon = FrameNode::CreateFrameNode(
1071             V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
1072         ImageSourceInfo imageSourceInfo;
1073         auto userIcon = itemProperty->GetSelectIconSrc().value_or("");
1074         imageSourceInfo.SetSrc(userIcon);
1075         auto props = selectIcon->GetLayoutProperty<ImageLayoutProperty>();
1076         CHECK_NULL_VOID(props);
1077         props->UpdateImageSourceInfo(imageSourceInfo);
1078         UpdateIconSrc(selectIcon, selectTheme->GetIconSideLength(), selectTheme->GetIconSideLength(),
1079             selectTheme->GetMenuIconColor(), userIcon.empty());
1080     }
1081 }
1082 
1083 
AddSelectIcon(RefPtr<FrameNode> & row)1084 void MenuItemPattern::AddSelectIcon(RefPtr<FrameNode>& row)
1085 {
1086     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1087     CHECK_NULL_VOID(itemProperty);
1088     if (!itemProperty->GetSelectIcon().value_or(false)) {
1089         if (selectIcon_) {
1090             row->RemoveChildAtIndex(0);
1091             selectIcon_ = nullptr;
1092             itemProperty->SetSelectSymbol(nullptr);
1093             row->MarkModifyDone();
1094             row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1095         }
1096         return;
1097     }
1098     if (!selectIcon_) {
1099         if (!itemProperty->GetSelectIconSrc().value_or("").empty() ||
1100             Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1101             selectIcon_ = FrameNode::CreateFrameNode(
1102                 V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
1103         } else {
1104             selectIcon_ = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG,
1105                 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<TextPattern>(); });
1106         }
1107     }
1108     if (selectIcon_->GetTag() == V2::IMAGE_ETS_TAG) {
1109         UpdateImageNode(row, selectIcon_);
1110     } else {
1111         UpdateSymbolNode(row, selectIcon_);
1112     }
1113     auto renderContext = selectIcon_->GetRenderContext();
1114     CHECK_NULL_VOID(renderContext);
1115     renderContext->SetVisible(isSelected_);
1116 
1117     selectIcon_->MountToParent(row, 0);
1118     selectIcon_->MarkModifyDone();
1119     selectIcon_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1120 }
1121 
AddExpandIcon(RefPtr<FrameNode> & row)1122 void MenuItemPattern::AddExpandIcon(RefPtr<FrameNode>& row)
1123 {
1124     auto host = GetHost();
1125     CHECK_NULL_VOID(host);
1126     auto menuNode = GetMenu();
1127     auto menuPattern = menuNode ? menuNode->GetPattern<MenuPattern>() : nullptr;
1128     auto menuProperty = menuNode ? menuNode->GetLayoutProperty<MenuLayoutProperty>() : nullptr;
1129     CHECK_NULL_VOID(menuProperty);
1130     auto canExpand = GetSubBuilder() != nullptr && menuPattern
1131         && !menuPattern->IsEmbedded() && !menuPattern->IsStackSubmenu()
1132         && (expandingMode_ == SubMenuExpandingMode::EMBEDDED || expandingMode_ == SubMenuExpandingMode::STACK);
1133     if (!canExpand) {
1134         if (expandIcon_) {
1135             row->RemoveChild(expandIcon_);
1136             expandIcon_ = nullptr;
1137             row->MarkModifyDone();
1138             row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1139         }
1140         return;
1141     }
1142     if (!expandIcon_) {
1143         expandIcon_ = FrameNode::CreateFrameNode(
1144             V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
1145         CHECK_NULL_VOID(expandIcon_);
1146     }
1147     auto pipeline = PipelineBase::GetCurrentContext();
1148     CHECK_NULL_VOID(pipeline);
1149     auto iconTheme = pipeline->GetTheme<IconTheme>();
1150     CHECK_NULL_VOID(iconTheme);
1151     auto iconPath = iconTheme->GetIconPath(
1152         expandingMode_ == SubMenuExpandingMode::STACK
1153             ? InternalResource::ResourceId::IC_PUBLIC_ARROW_RIGHT_SVG
1154             : InternalResource::ResourceId::IC_PUBLIC_ARROW_DOWN_SVG);
1155     auto selectTheme = pipeline->GetTheme<SelectTheme>();
1156     CHECK_NULL_VOID(selectTheme);
1157     ImageSourceInfo imageSourceInfo(iconPath);
1158     auto props = expandIcon_->GetLayoutProperty<ImageLayoutProperty>();
1159     CHECK_NULL_VOID(props);
1160     props->UpdateImageSourceInfo(imageSourceInfo);
1161     UpdateIconSrc(expandIcon_, selectTheme->GetIconSideLength(), selectTheme->GetIconSideLength(),
1162         selectTheme->GetMenuIconColor(), true);
1163 
1164     auto expandIconIndex = row->GetChildren().size();
1165     expandIcon_->MountToParent(row, expandIconIndex);
1166     expandIcon_->MarkModifyDone();
1167     expandIcon_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1168 }
1169 
AddClickableArea()1170 void MenuItemPattern::AddClickableArea()
1171 {
1172     if (expandingMode_ == SubMenuExpandingMode::EMBEDDED && GetSubBuilder() != nullptr && !IsEmbedded() &&
1173         !clickableArea_) {
1174         auto host = GetHost();
1175         CHECK_NULL_VOID(host);
1176         auto hostAccessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
1177         CHECK_NULL_VOID(hostAccessibilityProperty);
1178         hostAccessibilityProperty->SetAccessibilityLevel(AccessibilityProperty::Level::NO_STR);
1179         auto clickableArea = FrameNode::CreateFrameNode(V2::ROW_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
1180             AceType::MakeRefPtr<LinearLayoutPattern>(false));
1181         CHECK_NULL_VOID(clickableArea);
1182         auto pipeline = PipelineBase::GetCurrentContext();
1183         CHECK_NULL_VOID(pipeline);
1184         auto theme = pipeline->GetTheme<SelectTheme>();
1185         CHECK_NULL_VOID(theme);
1186         BorderRadiusProperty border;
1187         if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1188             border.SetRadius(theme->GetMenuDefaultInnerRadius());
1189         } else {
1190             border.SetRadius(theme->GetInnerBorderRadius());
1191         }
1192         auto clickableContext = clickableArea->GetRenderContext();
1193         CHECK_NULL_VOID(clickableContext);
1194         clickableContext->UpdateBorderRadius(border);
1195         auto menuProperty = host->GetLayoutProperty<MenuItemLayoutProperty>();
1196         CHECK_NULL_VOID(menuProperty);
1197         std::string content = menuProperty->GetContent().value_or("");
1198         std::string label = menuProperty->GetLabel().value_or("");
1199         auto accessibilityProperty = clickableArea->GetAccessibilityProperty<AccessibilityProperty>();
1200         CHECK_NULL_VOID(accessibilityProperty);
1201         accessibilityProperty->SetAccessibilityText(content + "," + label);
1202         clickableArea_ = clickableArea;
1203         clickableArea_->MountToParent(host, CLICKABLE_AREA_VIEW_INDEX);
1204     }
1205 }
1206 
AddStackSubMenuHeader(RefPtr<FrameNode> & menuNode)1207 void MenuItemPattern::AddStackSubMenuHeader(RefPtr<FrameNode>& menuNode)
1208 {
1209     auto host = GetHost();
1210     CHECK_NULL_VOID(host);
1211     auto layoutProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1212     CHECK_NULL_VOID(layoutProperty);
1213     auto pipeline = PipelineBase::GetCurrentContext();
1214     CHECK_NULL_VOID(pipeline);
1215     auto iconTheme = pipeline->GetTheme<IconTheme>();
1216     CHECK_NULL_VOID(iconTheme);
1217     auto selectTheme = pipeline->GetTheme<SelectTheme>();
1218     CHECK_NULL_VOID(selectTheme);
1219     auto iconPath = iconTheme->GetIconPath(InternalResource::ResourceId::IC_PUBLIC_ARROW_RIGHT_SVG);
1220     ImageSourceInfo imageSourceInfo;
1221     imageSourceInfo.SetSrc(iconPath);
1222     imageSourceInfo.SetFillColor(selectTheme->GetMenuIconColor());
1223     auto content = layoutProperty->GetContent().value_or(layoutProperty->GetLabel().value_or(""));
1224 
1225     MenuItemProperties menuItemProps;
1226     menuItemProps.content = content;
1227     menuItemProps.endIcon = imageSourceInfo;
1228     MenuItemModelNG menuItemModel;
1229     menuItemModel.Create(menuItemProps);
1230     auto stack = ViewStackProcessor::GetInstance();
1231 
1232     auto titleItem = AceType::DynamicCast<FrameNode>(stack->Finish());
1233     auto pattern = titleItem->GetPattern<MenuItemPattern>();
1234     CHECK_NULL_VOID(pattern);
1235     pattern->SetIsStackSubmenuHeader();
1236     titleItem->MountToParent(menuNode, 0);
1237 }
1238 
GetClickableArea()1239 RefPtr<FrameNode> MenuItemPattern::GetClickableArea()
1240 {
1241     auto host = GetHost();
1242     CHECK_NULL_RETURN(host, nullptr);
1243     auto clickableArea = host->GetChildAtIndex(CLICKABLE_AREA_VIEW_INDEX);
1244     CHECK_NULL_RETURN(clickableArea, host);
1245     auto clickableAreaNode = AceType::DynamicCast<FrameNode>(clickableArea);
1246     CHECK_NULL_RETURN(clickableAreaNode, host);
1247     return clickableAreaNode;
1248 }
1249 
UpdateIcon(RefPtr<FrameNode> & row,bool isStart)1250 void MenuItemPattern::UpdateIcon(RefPtr<FrameNode>& row, bool isStart)
1251 {
1252     auto pipeline = PipelineBase::GetCurrentContext();
1253     CHECK_NULL_VOID(pipeline);
1254     auto selectTheme = pipeline->GetTheme<SelectTheme>();
1255     CHECK_NULL_VOID(selectTheme);
1256     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1257     CHECK_NULL_VOID(itemProperty);
1258     ImageSourceInfo defaultImageSourceInfo;
1259     auto iconSrc = isStart ? itemProperty->GetStartIcon().value_or(defaultImageSourceInfo)
1260                            : itemProperty->GetEndIcon().value_or(defaultImageSourceInfo);
1261     auto symbol = isStart ? itemProperty->GetStartSymbol() : itemProperty->GetEndSymbol();
1262     auto& iconNode = isStart ? startIcon_ : endIcon_;
1263     if (iconSrc.GetSrc().empty() && symbol == nullptr) {
1264         row->RemoveChild(iconNode); // it's safe even if iconNode is nullptr
1265         iconNode = nullptr;
1266         if (isStart) {
1267             itemProperty->SetStartSymbol(nullptr);
1268         } else {
1269             itemProperty->SetEndSymbol(nullptr);
1270         }
1271         row->MarkModifyDone();
1272         row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1273         return;
1274     }
1275     if (!iconNode) {
1276         if (symbol) {
1277             iconNode = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG,
1278                 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<TextPattern>(); });
1279         } else {
1280             iconNode = FrameNode::CreateFrameNode(
1281                 V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
1282         }
1283         CHECK_NULL_VOID(iconNode);
1284     }
1285     if (iconNode->GetTag() == V2::IMAGE_ETS_TAG) {
1286         UpdateImageIcon(row, iconNode, iconSrc, symbol, isStart);
1287     } else {
1288         UpdateSymbolIcon(row, iconNode, iconSrc, symbol, isStart);
1289     }
1290     iconNode->MountToParent(row, ((isStart && selectIcon_) || (!isStart && label_)) ? 1 : 0);
1291     iconNode->MarkModifyDone();
1292     iconNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1293 }
1294 
UpdateImageIcon(RefPtr<FrameNode> & row,RefPtr<FrameNode> & iconNode,ImageSourceInfo & iconSrc,std::function<void (WeakPtr<NG::FrameNode>)> & symbol,bool isStart)1295 void MenuItemPattern::UpdateImageIcon(RefPtr<FrameNode>& row, RefPtr<FrameNode>& iconNode, ImageSourceInfo& iconSrc,
1296     std::function<void(WeakPtr<NG::FrameNode>)>& symbol, bool isStart)
1297 {
1298     auto pipeline = PipelineBase::GetCurrentContext();
1299     CHECK_NULL_VOID(pipeline);
1300     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1301     CHECK_NULL_VOID(itemProperty);
1302     auto selectTheme = pipeline->GetTheme<SelectTheme>();
1303     CHECK_NULL_VOID(selectTheme);
1304     if (symbol) {
1305         // iamge -> symbol
1306         row->RemoveChild(iconNode);
1307         iconNode = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
1308             []() { return AceType::MakeRefPtr<TextPattern>(); });
1309 
1310         auto props = iconNode->GetLayoutProperty<TextLayoutProperty>();
1311         CHECK_NULL_VOID(props);
1312         props->UpdateFontSize(selectTheme->GetEndIconWidth());
1313         props->UpdateSymbolColorList({ selectTheme->GetMenuIconColor() });
1314         symbol(AccessibilityManager::WeakClaim(AccessibilityManager::RawPtr(iconNode)));
1315     } else {
1316         // image -> image
1317         auto iconWidth = isStart ? selectTheme->GetIconSideLength() : selectTheme->GetEndIconWidth();
1318         auto iconHeight = isStart ? selectTheme->GetIconSideLength() : selectTheme->GetEndIconHeight();
1319         ImageSourceInfo imageSourceInfo(iconSrc);
1320         auto props = iconNode->GetLayoutProperty<ImageLayoutProperty>();
1321         CHECK_NULL_VOID(props);
1322         props->UpdateImageSourceInfo(imageSourceInfo);
1323         bool useDefaultThemeIcon = UseDefaultThemeIcon(imageSourceInfo);
1324         UpdateIconSrc(iconNode, iconWidth, iconHeight, selectTheme->GetMenuIconColor(), useDefaultThemeIcon);
1325     }
1326 }
1327 
UseDefaultThemeIcon(const ImageSourceInfo & imageSourceInfo)1328 bool MenuItemPattern::UseDefaultThemeIcon(const ImageSourceInfo& imageSourceInfo)
1329 {
1330     if (imageSourceInfo.IsSvg()) {
1331         auto src = imageSourceInfo.GetSrc();
1332         auto srcId = src.substr(SYSTEM_RESOURCE_PREFIX.size(),
1333             src.substr(0, src.rfind(".svg")).size() - SYSTEM_RESOURCE_PREFIX.size());
1334         if (srcId.find("ohos_") != std::string::npos) {
1335             return true;
1336         }
1337         uint64_t parsedSrcId = StringUtils::StringToLongUint(srcId);
1338         return (parsedSrcId != 0
1339             && (parsedSrcId >= MIN_SYSTEM_RESOURCE_ID)
1340             && (parsedSrcId <= MAX_SYSTEM_RESOURCE_ID));
1341     }
1342     return false;
1343 }
1344 
UpdateSymbolIcon(RefPtr<FrameNode> & row,RefPtr<FrameNode> & iconNode,ImageSourceInfo & iconSrc,std::function<void (WeakPtr<NG::FrameNode>)> & symbol,bool isStart)1345 void MenuItemPattern::UpdateSymbolIcon(RefPtr<FrameNode>& row, RefPtr<FrameNode>& iconNode, ImageSourceInfo& iconSrc,
1346     std::function<void(WeakPtr<NG::FrameNode>)>& symbol, bool isStart)
1347 {
1348     auto pipeline = PipelineBase::GetCurrentContext();
1349     CHECK_NULL_VOID(pipeline);
1350     auto props = iconNode->GetLayoutProperty<TextLayoutProperty>();
1351     CHECK_NULL_VOID(props);
1352     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1353     CHECK_NULL_VOID(itemProperty);
1354     auto selectTheme = pipeline->GetTheme<SelectTheme>();
1355     CHECK_NULL_VOID(selectTheme);
1356     if (symbol) {
1357         // symbol -> symbol
1358         props->UpdateFontSize(selectTheme->GetEndIconWidth());
1359         props->UpdateSymbolColorList({ selectTheme->GetMenuIconColor() });
1360         symbol(AccessibilityManager::WeakClaim(AccessibilityManager::RawPtr(iconNode)));
1361     } else {
1362         // symbol -> image
1363         row->RemoveChild(iconNode);
1364         iconNode = FrameNode::CreateFrameNode(
1365             V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
1366         auto iconWidth = isStart ? selectTheme->GetIconSideLength() : selectTheme->GetEndIconWidth();
1367         auto iconHeight = isStart ? selectTheme->GetIconSideLength() : selectTheme->GetEndIconHeight();
1368         ImageSourceInfo imageSourceInfo(iconSrc);
1369         auto props = iconNode->GetLayoutProperty<ImageLayoutProperty>();
1370         CHECK_NULL_VOID(props);
1371         props->UpdateImageSourceInfo(imageSourceInfo);
1372         UpdateIconSrc(iconNode, iconWidth, iconHeight, selectTheme->GetMenuIconColor(), false);
1373     }
1374 }
1375 
UpdateText(RefPtr<FrameNode> & row,RefPtr<MenuLayoutProperty> & menuProperty,bool isLabel)1376 void MenuItemPattern::UpdateText(RefPtr<FrameNode>& row, RefPtr<MenuLayoutProperty>& menuProperty, bool isLabel)
1377 {
1378     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1379     CHECK_NULL_VOID(itemProperty);
1380     auto content = isLabel ? itemProperty->GetLabel().value_or("") : itemProperty->GetContent().value_or("");
1381     auto& node = isLabel ? label_ : content_;
1382     if (content.empty()) {
1383         (void)row->RemoveChild(node); // it's safe even if node is nullptr
1384         node = nullptr;
1385         row->MarkModifyDone();
1386         row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1387         return;
1388     }
1389 
1390     if (!node) {
1391         node = FrameNode::CreateFrameNode(
1392             V2::TEXT_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<TextPattern>());
1393     }
1394     auto textProperty = node ? node->GetLayoutProperty<TextLayoutProperty>() : nullptr;
1395     CHECK_NULL_VOID(textProperty);
1396     auto renderContext = node->GetRenderContext();
1397     CHECK_NULL_VOID(renderContext);
1398     renderContext->UpdateClipEdge(false);
1399     auto context = PipelineBase::GetCurrentContext();
1400     auto theme = context ? context->GetTheme<SelectTheme>() : nullptr;
1401     CHECK_NULL_VOID(theme);
1402     auto layoutDirection = itemProperty->GetNonAutoLayoutDirection();
1403     TextAlign textAlign = static_cast<TextAlign>(theme->GetMenuItemContentAlign());
1404     if (layoutDirection == TextDirection::RTL) {
1405         if (textAlign == TextAlign::LEFT) {
1406             textAlign = TextAlign::RIGHT;
1407         } else if (textAlign ==TextAlign::RIGHT) {
1408             textAlign = TextAlign::LEFT;
1409         } else if (textAlign == TextAlign::START) {
1410             textAlign = TextAlign::END;
1411         } else if (textAlign == TextAlign::END) {
1412             textAlign = TextAlign::START;
1413         }
1414     }
1415 
1416     UpdateFont(menuProperty, theme, isLabel);
1417     textProperty->UpdateContent(content);
1418     UpdateTextOverflow(textProperty, theme);
1419     node->MountToParent(row, isLabel ? 0 : DEFAULT_NODE_SLOT);
1420     node->MarkModifyDone();
1421     node->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1422 }
1423 
UpdateTextOverflow(RefPtr<TextLayoutProperty> & textProperty,RefPtr<SelectTheme> & theme)1424 void MenuItemPattern::UpdateTextOverflow(RefPtr<TextLayoutProperty>& textProperty,
1425     RefPtr<SelectTheme>& theme)
1426 {
1427     if (theme && Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_THIRTEEN)) {
1428         if (theme->GetExpandDisplay()) {
1429             textProperty->UpdateTextOverflow(TextOverflow::ELLIPSIS);
1430             textProperty->UpdateMaxLines(1);
1431         } else {
1432             textProperty->UpdateMaxLines(std::numeric_limits<int32_t>::max());
1433         }
1434     } else {
1435         textProperty->UpdateTextOverflow(TextOverflow::ELLIPSIS);
1436         textProperty->UpdateMaxLines(1);
1437     }
1438     UpdateMaxLinesFromTheme(textProperty);
1439 }
1440 
UpdateFont(RefPtr<MenuLayoutProperty> & menuProperty,RefPtr<SelectTheme> & theme,bool isLabel)1441 void MenuItemPattern::UpdateFont(RefPtr<MenuLayoutProperty>& menuProperty, RefPtr<SelectTheme>& theme, bool isLabel)
1442 {
1443     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1444     CHECK_NULL_VOID(itemProperty);
1445     auto& node = isLabel ? label_ : content_;
1446     auto textProperty = node ? node->GetLayoutProperty<TextLayoutProperty>() : nullptr;
1447     CHECK_NULL_VOID(textProperty);
1448 
1449     auto fontSize = isLabel ? itemProperty->GetLabelFontSize() : itemProperty->GetFontSize();
1450     UpdateFontSize(textProperty, menuProperty, fontSize, theme->GetMenuFontSize());
1451     auto fontWeight = isLabel ? itemProperty->GetLabelFontWeight() : itemProperty->GetFontWeight();
1452     UpdateFontWeight(textProperty, menuProperty, fontWeight);
1453     auto fontStyle = isLabel ? itemProperty->GetLabelItalicFontStyle() : itemProperty->GetItalicFontStyle();
1454     UpdateFontStyle(textProperty, menuProperty, fontStyle);
1455     auto fontColor = isLabel ? itemProperty->GetLabelFontColor() : itemProperty->GetFontColor();
1456     auto menuItemNode = GetHost();
1457     UpdateFontColor(
1458         node, menuProperty, fontColor, isLabel ? theme->GetSecondaryFontColor() : theme->GetMenuFontColor());
1459     if (!isLabel) {
1460         auto menuItemRenderContext = menuItemNode->GetRenderContext();
1461         CHECK_NULL_VOID(menuItemRenderContext);
1462         auto renderContext = node->GetRenderContext();
1463         CHECK_NULL_VOID(renderContext);
1464         if (menuItemRenderContext->HasForegroundColor()) {
1465             textProperty->UpdateTextColor(menuItemRenderContext->GetForegroundColorValue());
1466             renderContext->UpdateForegroundColor(menuItemRenderContext->GetForegroundColorValue());
1467         }
1468     }
1469     auto fontFamily = isLabel ? itemProperty->GetLabelFontFamily() : itemProperty->GetFontFamily();
1470     UpdateFontFamily(textProperty, menuProperty, fontFamily);
1471 }
1472 
UpdateMaxLinesFromTheme(RefPtr<TextLayoutProperty> & textProperty)1473 void MenuItemPattern::UpdateMaxLinesFromTheme(RefPtr<TextLayoutProperty>& textProperty)
1474 {
1475     auto host = GetHost();
1476     CHECK_NULL_VOID(host);
1477     auto pipeline = host->GetContext();
1478     CHECK_NULL_VOID(pipeline);
1479     auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
1480     CHECK_NULL_VOID(menuTheme);
1481     auto fontScale = pipeline->GetFontScale();
1482     if (NearEqual(fontScale, menuTheme->GetBigFontSizeScale()) ||
1483         NearEqual(fontScale, menuTheme->GetLargeFontSizeScale()) ||
1484         NearEqual(fontScale, menuTheme->GetMaxFontSizeScale())) {
1485         textProperty->UpdateMaxLines(menuTheme->GetTextMaxLines());
1486     }
1487 }
1488 
UpdateTextNodes()1489 void MenuItemPattern::UpdateTextNodes()
1490 {
1491     auto host = GetHost();
1492     CHECK_NULL_VOID(host);
1493     auto menuNode = GetMenu();
1494     CHECK_NULL_VOID(menuNode);
1495     auto menuProperty = menuNode->GetLayoutProperty<MenuLayoutProperty>();
1496     RefPtr<FrameNode> leftRow =
1497         host->GetChildAtIndex(0) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(0)) : nullptr;
1498     CHECK_NULL_VOID(leftRow);
1499     UpdateText(leftRow, menuProperty, false);
1500     RefPtr<FrameNode> rightRow =
1501         host->GetChildAtIndex(1) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(1)) : nullptr;
1502     CHECK_NULL_VOID(rightRow);
1503     UpdateText(rightRow, menuProperty, true);
1504     if (IsDisabled()) {
1505         UpdateDisabledStyle();
1506     }
1507     host->MarkModifyDone();
1508     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1509 }
1510 
IsDisabled()1511 bool MenuItemPattern::IsDisabled()
1512 {
1513     auto eventHub = GetHost()->GetEventHub<MenuItemEventHub>();
1514     CHECK_NULL_RETURN(eventHub, true);
1515     return !eventHub->IsEnabled();
1516 }
1517 
UpdateDisabledStyle()1518 void MenuItemPattern::UpdateDisabledStyle()
1519 {
1520     auto context = PipelineBase::GetCurrentContext();
1521     CHECK_NULL_VOID(context);
1522     auto theme = context->GetTheme<SelectTheme>();
1523     CHECK_NULL_VOID(theme);
1524     if (content_) {
1525         content_->GetRenderContext()->UpdateOpacity(theme->GetDisabledFontColorAlpha());
1526         content_->MarkModifyDone();
1527     }
1528     if (label_) {
1529         label_->GetRenderContext()->UpdateOpacity(theme->GetDisabledFontColorAlpha());
1530         label_->MarkModifyDone();
1531     }
1532     if (startIcon_) {
1533         startIcon_->GetRenderContext()->UpdateOpacity(theme->GetDisabledFontColorAlpha());
1534         startIcon_->MarkModifyDone();
1535     }
1536     if (endIcon_) {
1537         endIcon_->GetRenderContext()->UpdateOpacity(theme->GetDisabledFontColorAlpha());
1538         endIcon_->MarkModifyDone();
1539     }
1540 }
1541 
SetAccessibilityAction()1542 void MenuItemPattern::SetAccessibilityAction()
1543 {
1544     auto host = GetHost();
1545     CHECK_NULL_VOID(host);
1546     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
1547     CHECK_NULL_VOID(accessibilityProperty);
1548     accessibilityProperty->SetActionSelect([weakPtr = WeakClaim(this)]() {
1549         const auto& pattern = weakPtr.Upgrade();
1550         CHECK_NULL_VOID(pattern);
1551         auto host = pattern->GetHost();
1552         CHECK_NULL_VOID(host);
1553         auto hub = host->GetEventHub<MenuItemEventHub>();
1554         CHECK_NULL_VOID(hub);
1555         auto onChange = hub->GetOnChange();
1556         auto selectedChangeEvent = hub->GetSelectedChangeEvent();
1557         pattern->SetChange();
1558         if (selectedChangeEvent) {
1559             selectedChangeEvent(pattern->IsSelected());
1560         }
1561         if (onChange) {
1562             onChange(pattern->IsSelected());
1563             pattern->RecordChangeEvent();
1564         }
1565         auto context = host->GetRenderContext();
1566         CHECK_NULL_VOID(context);
1567         pattern->MarkIsSelected(pattern->IsSelected());
1568         context->OnMouseSelectUpdate(pattern->IsSelected(), ITEM_FILL_COLOR, ITEM_FILL_COLOR);
1569         if (pattern->GetSubBuilder() != nullptr) {
1570             pattern->ShowSubMenu();
1571             return;
1572         }
1573 
1574         pattern->CloseMenu();
1575     });
1576 }
1577 
MarkIsSelected(bool isSelected)1578 void MenuItemPattern::MarkIsSelected(bool isSelected)
1579 {
1580     if (isSelected_ == isSelected) {
1581         return;
1582     }
1583     isSelected_ = isSelected;
1584     auto eventHub = GetEventHub<MenuItemEventHub>();
1585     CHECK_NULL_VOID(eventHub);
1586     auto onChange = eventHub->GetOnChange();
1587     auto selectedChangeEvent = eventHub->GetSelectedChangeEvent();
1588     if (selectedChangeEvent) {
1589         selectedChangeEvent(isSelected);
1590     }
1591     if (onChange) {
1592         onChange(isSelected);
1593     }
1594     auto host = GetHost();
1595     CHECK_NULL_VOID(host);
1596     if (isSelected) {
1597         eventHub->SetCurrentUIState(UI_STATE_SELECTED, isSelected);
1598         host->OnAccessibilityEvent(AccessibilityEventType::SELECTED);
1599     } else {
1600         eventHub->SetCurrentUIState(UI_STATE_SELECTED, isSelected);
1601         host->OnAccessibilityEvent(AccessibilityEventType::CHANGE);
1602     }
1603 }
1604 
IsSelectOverlayMenu()1605 bool MenuItemPattern::IsSelectOverlayMenu()
1606 {
1607     auto topLevelMenuPattern = GetMenuPattern(true);
1608     if (!topLevelMenuPattern) {
1609         return false;
1610     }
1611     return topLevelMenuPattern->IsSelectOverlayExtensionMenu() || topLevelMenuPattern->IsSelectOverlayCustomMenu() ||
1612            topLevelMenuPattern->IsSelectOverlaySubMenu();
1613 }
1614 
ParseMenuRadius(MenuParam & param)1615 void MenuItemPattern::ParseMenuRadius(MenuParam& param)
1616 {
1617     auto menuWrapperNode = GetMenuWrapper();
1618     CHECK_NULL_VOID(menuWrapperNode);
1619     auto menuWrapperPattern = menuWrapperNode->GetPattern<MenuWrapperPattern>();
1620     CHECK_NULL_VOID(menuWrapperPattern);
1621 
1622     if (menuWrapperPattern->GetHasCustomRadius()) {
1623         auto outterMenuNode = GetMenu(true);
1624         CHECK_NULL_VOID(outterMenuNode);
1625         auto menuLayoutProp = outterMenuNode->GetLayoutProperty<MenuLayoutProperty>();
1626         CHECK_NULL_VOID(menuLayoutProp);
1627         if (menuLayoutProp->GetBorderRadius().has_value()) {
1628             BorderRadiusProperty borderRadius = menuLayoutProp->GetBorderRadiusValue();
1629             param.borderRadius = std::make_optional(borderRadius);
1630         }
1631     }
1632 }
1633 
IsSubMenu()1634 bool MenuItemPattern::IsSubMenu()
1635 {
1636     auto topLevelMenuPattern = GetMenuPattern(true);
1637     if (!topLevelMenuPattern) {
1638         return false;
1639     }
1640     return topLevelMenuPattern->IsSubMenu();
1641 }
1642 
IsEmbedded()1643 bool MenuItemPattern::IsEmbedded()
1644 {
1645     auto parentMenuPattern = GetMenuPattern();
1646     return parentMenuPattern ? parentMenuPattern->IsEmbedded() : false;
1647 }
1648 
ModifyDivider()1649 void MenuItemPattern::ModifyDivider()
1650 {
1651     auto menu = GetMenu();
1652     CHECK_NULL_VOID(menu);
1653     auto menuProperty = menu->GetLayoutProperty<MenuLayoutProperty>();
1654     CHECK_NULL_VOID(menuProperty);
1655     auto divider = menuProperty->GetItemDivider();
1656     if (divider.has_value()) {
1657         auto host = GetHost();
1658         CHECK_NULL_VOID(host);
1659         auto paintProperty = host->GetPaintProperty<MenuItemPaintProperty>();
1660         CHECK_NULL_VOID(paintProperty);
1661         paintProperty->UpdateStrokeWidth(divider->strokeWidth);
1662         paintProperty->UpdateStartMargin(divider->startMargin);
1663         paintProperty->UpdateEndMargin(divider->endMargin);
1664         paintProperty->UpdateDividerColor(divider->color);
1665         host->MarkModifyDone();
1666         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1667     }
1668 }
1669 
UpdateNeedDivider(bool need)1670 void MenuItemPattern::UpdateNeedDivider(bool need)
1671 {
1672     auto host = GetHost();
1673     CHECK_NULL_VOID(host);
1674     auto paintProperty = host->GetPaintProperty<MenuItemPaintProperty>();
1675     CHECK_NULL_VOID(paintProperty);
1676     paintProperty->UpdateNeedDivider(need);
1677     if (need) {
1678         ModifyDivider();
1679     }
1680 }
1681 
GetDividerStroke()1682 float MenuItemPattern::GetDividerStroke()
1683 {
1684     auto host = GetHost();
1685     CHECK_NULL_RETURN(host, 0.0f);
1686     auto props = host->GetPaintProperty<MenuItemPaintProperty>();
1687     CHECK_NULL_RETURN(props, 0.0f);
1688     return props->GetStrokeWidth().value_or(Dimension(0.0f, DimensionUnit::PX)).ConvertToPx();
1689 }
1690 
FindTouchedEmbeddedMenuItem(const OffsetF & position)1691 RefPtr<FrameNode> MenuItemPattern::FindTouchedEmbeddedMenuItem(const OffsetF& position)
1692 {
1693     auto host = GetHost();
1694     CHECK_NULL_RETURN(host, nullptr);
1695     if (expandingMode_ != SubMenuExpandingMode::EMBEDDED || !isExpanded_
1696         || embeddedMenu_ == nullptr || embeddedMenu_->GetTag() != V2::MENU_ETS_TAG) {
1697         return host;
1698     }
1699     CHECK_NULL_RETURN(clickableArea_, host);
1700     auto clickableAreaOffset = clickableArea_->GetPaintRectOffset();
1701     auto clickableAreaSize = clickableArea_->GetGeometryNode()->GetFrameSize();
1702     auto clickableAreaZone = RectF(clickableAreaOffset.GetX(), clickableAreaOffset.GetY(),
1703         clickableAreaSize.Width(), clickableAreaSize.Height());
1704     if (clickableAreaZone.IsInRegion(PointF(position.GetX(), position.GetY()))) {
1705         return host;
1706     }
1707     RefPtr<FrameNode> menuItem = nullptr;
1708     for (const auto& child : embeddedMenu_->GetChildren()) {
1709         if (child->GetTag() == V2::MENU_ITEM_ETS_TAG) {
1710             menuItem = AceType::DynamicCast<FrameNode>(child);
1711         }
1712         if (menuItem) {
1713             auto menuItemOffset = menuItem->GetPaintRectOffset();
1714             auto menuItemSize = menuItem->GetGeometryNode()->GetFrameSize();
1715             auto menuItemZone = RectF(menuItemOffset.GetX(), menuItemOffset.GetY(),
1716                 menuItemSize.Width(), menuItemSize.Height());
1717             if (menuItemZone.IsInRegion(PointF(position.GetX(), position.GetY()))) {
1718                 break;
1719             } else {
1720                 menuItem = nullptr;
1721             }
1722         }
1723     }
1724     return menuItem;
1725 }
1726 } // namespace OHOS::Ace::NG
1727