• 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 
18 #include "menu_item_model.h"
19 
20 #include "core/common/recorder/node_data_cache.h"
21 #include "core/components/common/layout/grid_system_manager.h"
22 #include "core/components/common/properties/shadow_config.h"
23 #include "core/components/select/select_theme.h"
24 #include "core/components/theme/shadow_theme.h"
25 #include "core/components/text/text_theme.h"
26 #include "core/components/theme/icon_theme.h"
27 #include "core/components_ng/base/frame_node.h"
28 #include "core/components_ng/base/view_stack_processor.h"
29 #include "core/components_ng/pattern/image/image_pattern.h"
30 #include "core/components_ng/pattern/menu/menu_divider/menu_divider_pattern.h"
31 #include "core/components_ng/pattern/menu/menu_item/menu_item_event_hub.h"
32 #include "core/components_ng/pattern/menu/menu_item/menu_item_model_ng.h"
33 #include "core/components_ng/pattern/menu/menu_item/menu_item_row_pattern.h"
34 #include "core/components_ng/pattern/menu/menu_layout_property.h"
35 #include "core/components_ng/pattern/menu/menu_pattern.h"
36 #include "core/components_ng/pattern/menu/menu_theme.h"
37 #include "core/components_ng/pattern/menu/menu_view.h"
38 #include "core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.h"
39 #include "core/components_ng/pattern/security_component/security_component_pattern.h"
40 #include "core/components_ng/pattern/text/text_layout_property.h"
41 #include "core/components_ng/pattern/text/text_pattern.h"
42 #include "core/components_ng/property/border_property.h"
43 #include "core/components_v2/inspector/inspector_constants.h"
44 #include "core/pipeline/pipeline_base.h"
45 #if defined(OHOS_STANDARD_SYSTEM) and !defined(ACE_UNITTEST)
46 #include "accessibility_element_info.h"
47 #endif
48 
49 namespace OHOS::Ace::NG {
50 namespace {
51 const Color ITEM_FILL_COLOR = Color::TRANSPARENT;
52 // default clicked & hover color for background blend when theme is null(value from SelectTheme)
53 const Color DEFAULT_CLICKED_COLOR(0x19000000);
54 const Color DEFAULT_HOVER_COLOR(0x0c000000);
55 constexpr double VELOCITY = 0.0f;
56 constexpr double MASS = 1.0f;
57 constexpr double STIFFNESS = 328.0f;
58 constexpr double DAMPING = 33.0f;
59 constexpr double SEMI_CIRCLE_ANGEL = 180.0f;
60 constexpr double MENU_FOCUS_TYPE = 1.0;
61 constexpr float OPACITY_EFFECT = 0.99;
62 const std::string SYSTEM_RESOURCE_PREFIX = std::string("resource:///");
63 // id of system resource start from 0x07000000
64 constexpr uint64_t MIN_SYSTEM_RESOURCE_ID = 0x07000000;
65 // id of system resource end to 0x07FFFFFF
66 constexpr uint64_t MAX_SYSTEM_RESOURCE_ID = 0x07FFFFFF;
67 constexpr Dimension MIN_OPTION_WIDTH = 56.0_vp;
68 constexpr Dimension OPTION_MARGIN = 8.0_vp;
69 constexpr int32_t COLUMN_NUM = 2;
70 constexpr Dimension BORDER_DEFAULT_WIDTH = 0.0_vp;
71 constexpr Dimension STACK_EXPAND_ICON_PADDING = 2.0_vp;
72 #if defined(OHOS_STANDARD_SYSTEM) and !defined(ACE_UNITTEST)
73 constexpr const char* MENU_STATE_COLLAPSED = "collapsed";
74 constexpr const char* MENU_STATE_EXPANDED = "expanded";
75 constexpr const char* MENU_ITEM = "MenuItem";
76 constexpr const char* MENU_EXPANDED_STATE = "expandedState";
77 #endif
78 
UpdateFontSize(RefPtr<TextLayoutProperty> & textProperty,RefPtr<MenuLayoutProperty> & menuProperty,const std::optional<Dimension> & fontSize,const Dimension & defaultFontSize)79 void UpdateFontSize(RefPtr<TextLayoutProperty>& textProperty, RefPtr<MenuLayoutProperty>& menuProperty,
80     const std::optional<Dimension>& fontSize, const Dimension& defaultFontSize)
81 {
82     if (fontSize.has_value()) {
83         textProperty->UpdateFontSize(fontSize.value());
84     } else if (menuProperty && menuProperty->GetFontSize().has_value()) {
85         textProperty->UpdateFontSize(menuProperty->GetFontSize().value());
86     } else {
87         textProperty->UpdateFontSize(defaultFontSize);
88     }
89 }
90 
UpdateFontWeight(RefPtr<TextLayoutProperty> & textProperty,RefPtr<MenuLayoutProperty> & menuProperty,const std::optional<FontWeight> & fontWeight)91 void UpdateFontWeight(RefPtr<TextLayoutProperty>& textProperty, RefPtr<MenuLayoutProperty>& menuProperty,
92     const std::optional<FontWeight>& fontWeight)
93 {
94     if (fontWeight.has_value()) {
95         textProperty->UpdateFontWeight(fontWeight.value());
96     } else if (menuProperty && menuProperty->GetFontWeight().has_value()) {
97         textProperty->UpdateFontWeight(menuProperty->GetFontWeight().value());
98     } else {
99         if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
100             textProperty->UpdateFontWeight(FontWeight::MEDIUM);
101         } else {
102             textProperty->UpdateFontWeight(FontWeight::REGULAR);
103         }
104     }
105 }
106 
UpdateFontStyle(RefPtr<TextLayoutProperty> & textProperty,RefPtr<MenuLayoutProperty> & menuProperty,const std::optional<Ace::FontStyle> & fontStyle)107 void UpdateFontStyle(RefPtr<TextLayoutProperty>& textProperty, RefPtr<MenuLayoutProperty>& menuProperty,
108     const std::optional<Ace::FontStyle>& fontStyle)
109 {
110     if (fontStyle.has_value()) {
111         textProperty->UpdateItalicFontStyle(fontStyle.value());
112     } else if (menuProperty && menuProperty->GetItalicFontStyle().has_value()) {
113         textProperty->UpdateItalicFontStyle(menuProperty->GetItalicFontStyle().value());
114     } else {
115         textProperty->UpdateItalicFontStyle(Ace::FontStyle::NORMAL);
116     }
117 }
118 
UpdateFontColor(const RefPtr<FrameNode> & textNode,RefPtr<MenuLayoutProperty> & menuProperty,const std::optional<Color> & fontColor,const Color & defaultFontColor)119 void UpdateFontColor(const RefPtr<FrameNode>& textNode, RefPtr<MenuLayoutProperty>& menuProperty,
120     const std::optional<Color>& fontColor, const Color& defaultFontColor)
121 {
122     auto textProperty = textNode ? textNode->GetLayoutProperty<TextLayoutProperty>() : nullptr;
123     CHECK_NULL_VOID(textProperty);
124     auto renderContext = textNode->GetRenderContext();
125     CHECK_NULL_VOID(renderContext);
126     if (fontColor.has_value()) {
127         textProperty->UpdateTextColor(fontColor.value());
128     } else if (menuProperty && menuProperty->GetFontColor().has_value()) {
129         textProperty->UpdateTextColor(menuProperty->GetFontColor().value());
130     } else {
131         textProperty->UpdateTextColor(defaultFontColor);
132     }
133 }
134 
UpdateFontFamily(RefPtr<TextLayoutProperty> & textProperty,RefPtr<MenuLayoutProperty> & menuProperty,const std::optional<std::vector<std::string>> & fontFamilies)135 void UpdateFontFamily(RefPtr<TextLayoutProperty>& textProperty, RefPtr<MenuLayoutProperty>& menuProperty,
136     const std::optional<std::vector<std::string>>& fontFamilies)
137 {
138     if (fontFamilies.has_value()) {
139         textProperty->UpdateFontFamily(fontFamilies.value());
140     } else if (menuProperty && menuProperty->GetFontFamily().has_value()) {
141         textProperty->UpdateFontFamily(menuProperty->GetFontFamily().value());
142     }
143 }
144 
UpdateIconSrc(RefPtr<FrameNode> & node,const Dimension & horizontalSize,const Dimension & verticalSize,const Color & color,const bool & useDefaultIcon)145 void UpdateIconSrc(RefPtr<FrameNode>& node, const Dimension& horizontalSize,
146     const Dimension& verticalSize, const Color& color, const bool& useDefaultIcon)
147 {
148     auto props = node->GetLayoutProperty<ImageLayoutProperty>();
149     CHECK_NULL_VOID(props);
150     props->UpdateAlignment(Alignment::CENTER);
151     CalcSize idealSize = { CalcLength(horizontalSize), CalcLength(verticalSize) };
152     MeasureProperty layoutConstraint;
153     layoutConstraint.selfIdealSize = idealSize;
154     props->UpdateCalcLayoutProperty(layoutConstraint);
155     if (useDefaultIcon) {
156         auto iconRenderProperty = node->GetPaintProperty<ImageRenderProperty>();
157         CHECK_NULL_VOID(iconRenderProperty);
158         iconRenderProperty->UpdateSvgFillColor(color);
159     }
160 }
161 } // namespace
162 
OnMountToParentDone()163 void MenuItemPattern::OnMountToParentDone()
164 {
165     UpdateTextNodes();
166 }
167 
AttachBottomDivider()168 void MenuItemPattern::AttachBottomDivider()
169 {
170     CreateBottomDivider();
171     CHECK_NULL_VOID(bottomDivider_);
172     auto host = GetHost();
173     CHECK_NULL_VOID(host);
174     auto parent = host->GetParent();
175     CHECK_NULL_VOID(parent);
176     RemoveBottomDivider();
177     auto index = parent->GetChildIndex(host);
178     if (index >= 0) {
179         bottomDivider_->MountToParent(parent, ++index);
180     }
181 }
182 
RemoveBottomDivider()183 void MenuItemPattern::RemoveBottomDivider()
184 {
185     CHECK_NULL_VOID(bottomDivider_);
186     auto dividerParent = bottomDivider_->GetParent();
187     if (dividerParent) {
188         dividerParent->RemoveChild(bottomDivider_);
189     }
190 }
191 
OnAttachToFrameNode()192 void MenuItemPattern::OnAttachToFrameNode()
193 {
194     InitFocusPadding();
195     RegisterOnKeyEvent();
196     RegisterOnClick();
197     RegisterOnTouch();
198     RegisterOnPress();
199     RegisterOnHover();
200     auto host = GetHost();
201     CHECK_NULL_VOID(host);
202     MenuView::RegisterAccessibilityChildActionNotify(host);
203 }
204 
OnAttachToFrameNode()205 void CustomMenuItemPattern::OnAttachToFrameNode()
206 {
207     InitFocusPadding();
208     RegisterOnKeyEvent();
209     RegisterOnTouch();
210     auto host = GetHost();
211     CHECK_NULL_VOID(host);
212     MenuView::RegisterAccessibilityChildActionNotify(host);
213 }
214 
CreateBottomDivider()215 void MenuItemPattern::CreateBottomDivider()
216 {
217     if (bottomDivider_) {
218         return;
219     }
220     auto host = GetHost();
221     CHECK_NULL_VOID(host);
222     bottomDivider_ = FrameNode::GetOrCreateFrameNode(V2::MENU_DIVIDER_TAG,
223         ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<MenuDividerPattern>(); });
224     auto dividerPattern = bottomDivider_->GetPattern<MenuDividerPattern>();
225     dividerPattern->BindMenuItem(host);
226 }
227 
InitFocusPadding()228 void MenuItemPattern::InitFocusPadding()
229 {
230     auto host = GetHost();
231     CHECK_NULL_VOID(host);
232     auto context = host->GetContextRefPtr();
233     CHECK_NULL_VOID(context);
234     auto selectTheme = context->GetTheme<SelectTheme>();
235     CHECK_NULL_VOID(selectTheme);
236     focusPadding_ = selectTheme->GetOptionFocusedBoxPadding();
237     auto menuTheme = context->GetTheme<MenuTheme>();
238     CHECK_NULL_VOID(menuTheme);
239     menuFocusType_ = menuTheme->GetFocusStyleType();
240 }
241 
OnModifyDone()242 void MenuItemPattern::OnModifyDone()
243 {
244     Pattern::OnModifyDone();
245     auto host = GetHost();
246     CHECK_NULL_VOID(host);
247     if (isOptionPattern_) {
248         OptionOnModifyDone(host);
249     } else {
250         NeedFocusEvent();
251         SetThemeProps(host);
252         InitTextFadeOut();
253         RefPtr<FrameNode> leftRow =
254             host->GetChildAtIndex(0) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(0)) : nullptr;
255         CHECK_NULL_VOID(leftRow);
256         AddSelectIcon(leftRow);
257         UpdateIcon(leftRow, true);
258         auto menuNode = GetMenu();
259         auto menuProperty = menuNode ? menuNode->GetLayoutProperty<MenuLayoutProperty>() : nullptr;
260         UpdateText(leftRow, menuProperty, false);
261 
262         if (menuProperty) {
263             expandingMode_ = menuProperty->GetExpandingMode().value_or(SubMenuExpandingMode::SIDE);
264             expandingModeSet_ = true;
265         }
266 
267         RefPtr<FrameNode> rightRow =
268             host->GetChildAtIndex(1) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(1)) : nullptr;
269         CHECK_NULL_VOID(rightRow);
270         UpdateText(rightRow, menuProperty, true);
271         UpdateIcon(rightRow, false);
272         AddExpandIcon(rightRow);
273         AddClickableArea();
274         UpdateDisabledStyle();
275         SetAccessibilityAction();
276 
277         auto renderContext = host->GetRenderContext();
278         if (renderContext) {
279             renderContext->SetClipToBounds(true);
280         }
281         if (!longPressEvent_ && Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
282             InitLongPressEvent();
283         }
284         if (expandingModeSet_) {
285             RegisterOnKeyEvent();
286             RegisterOnTouch();
287             RegisterOnPress();
288             RegisterOnHover();
289             RegisterOnClick();
290         }
291     }
292 }
293 
SetThemeProps(const RefPtr<FrameNode> & host)294 void MenuItemPattern::SetThemeProps(const RefPtr<FrameNode>& host)
295 {
296     CHECK_NULL_VOID(host);
297     auto layoutProp = host->GetLayoutProperty();
298     CHECK_NULL_VOID(layoutProp);
299     if (!layoutProp->GetMarginProperty()) {
300         auto context = host->GetContextRefPtr();
301         CHECK_NULL_VOID(context);
302         auto selectTheme = context->GetTheme<SelectTheme>();
303         CHECK_NULL_VOID(selectTheme);
304         MarginProperty margin;
305         auto horizontalMargin = CalcLength(selectTheme->GetMenuItemLeftRightMargin());
306         auto verticalMargin = CalcLength(selectTheme->GetMenuItemTopBottomMargin());
307         margin.SetEdges(horizontalMargin, horizontalMargin, verticalMargin, verticalMargin);
308         layoutProp->UpdateMargin(margin);
309     }
310 }
311 
InitTextFadeOut()312 void MenuItemPattern::InitTextFadeOut()
313 {
314     auto host = GetHost();
315     CHECK_NULL_VOID(host);
316     auto context = host->GetContextRefPtr();
317     CHECK_NULL_VOID(context);
318     auto textTheme = context->GetTheme<TextTheme>();
319     CHECK_NULL_VOID(textTheme);
320     isTextFadeOut_ = textTheme->GetIsTextFadeout();
321 }
322 
NeedFocusEvent()323 void MenuItemPattern::NeedFocusEvent()
324 {
325     auto host = GetHost();
326     CHECK_NULL_VOID(host);
327     auto context = host->GetContextRefPtr();
328     CHECK_NULL_VOID(context);
329     auto selectTheme = context->GetTheme<SelectTheme>();
330     CHECK_NULL_VOID(selectTheme);
331     auto menuItemNeedFocus = selectTheme->GetMenuItemNeedFocus();
332     if (menuItemNeedFocus) {
333         InitFocusEvent();
334     }
335 }
336 
InitFocusEvent()337 void MenuItemPattern::InitFocusEvent()
338 {
339     auto host = GetHost();
340     CHECK_NULL_VOID(host);
341     auto focusHub = host->GetOrCreateFocusHub();
342     CHECK_NULL_VOID(focusHub);
343     auto focusTask = [weak = WeakClaim(this)](FocusReason reason) {
344         auto pattern = weak.Upgrade();
345         CHECK_NULL_VOID(pattern);
346         if (pattern->IsOptionPattern()) {
347             pattern->OptionHandleFocusEvent();
348         } else {
349             pattern->HandleFocusEvent();
350             pattern->UpdateTextMarquee(pattern->isFocused_);
351         }
352     };
353     focusHub->SetOnFocusInternal(focusTask);
354 
355     auto blurTask = [weak = WeakClaim(this)]() {
356         auto pattern = weak.Upgrade();
357         CHECK_NULL_VOID(pattern);
358         if (pattern->IsOptionPattern()) {
359             pattern->OptionHandleBlurEvent();
360         } else {
361             pattern->HandleBlurEvent();
362             pattern->UpdateTextMarquee(pattern->isHovered_);
363         }
364     };
365     focusHub->SetOnBlurInternal(blurTask);
366 }
367 
GetShadowFromTheme(ShadowStyle shadowStyle,Shadow & shadow)368 bool MenuItemPattern::GetShadowFromTheme(ShadowStyle shadowStyle, Shadow& shadow)
369 {
370     auto host = GetHost();
371     CHECK_NULL_RETURN(host, false);
372     auto context = host->GetContextRefPtr();
373     CHECK_NULL_RETURN(context, false);
374     auto shadowTheme = context->GetTheme<ShadowTheme>();
375     CHECK_NULL_RETURN(shadowTheme, false);
376     auto colorMode = context->GetColorMode();
377     shadow = shadowTheme->GetShadow(shadowStyle, colorMode);
378     return true;
379 }
380 
OptionHandleFocusEvent()381 void MenuItemPattern::OptionHandleFocusEvent()
382 {
383     auto host = GetHost();
384     CHECK_NULL_VOID(host);
385     auto pipeline = host->GetContextRefPtr();
386     CHECK_NULL_VOID(pipeline);
387     if (pipeline->GetIsFocusActive()) {
388         SetFocusStyle();
389     }
390 
391     if (!isFocusActiveUpdateEvent_) {
392         isFocusActiveUpdateEvent_ = [weak = WeakClaim(this)](bool isFocusActive) {
393             auto pattern = weak.Upgrade();
394             CHECK_NULL_VOID(pattern);
395             isFocusActive ? pattern->SetFocusStyle() : pattern->ClearFocusStyle();
396         };
397     }
398 
399     pipeline->AddIsFocusActiveUpdateEvent(GetHost(), isFocusActiveUpdateEvent_);
400 }
401 
OptionHandleBlurEvent()402 void MenuItemPattern::OptionHandleBlurEvent()
403 {
404     ClearFocusStyle();
405     auto host = GetHost();
406     CHECK_NULL_VOID(host);
407     auto pipeline = host->GetContextRefPtr();
408     CHECK_NULL_VOID(pipeline);
409     pipeline->RemoveIsFocusActiveUpdateEvent(GetHost());
410 }
411 
SetFocusStyle()412 void MenuItemPattern::SetFocusStyle()
413 {
414     CHECK_NULL_VOID(selectTheme_);
415     auto host = GetHost();
416     CHECK_NULL_VOID(host);
417     auto renderContext = host->GetRenderContext();
418     CHECK_NULL_VOID(renderContext);
419     auto&& graphics = renderContext->GetOrCreateGraphics();
420     CHECK_NULL_VOID(graphics);
421     if (!selectTheme_->GetoptionApplyFocusedStyle()) {
422         return;
423     }
424 
425     Shadow shadow = Shadow::CreateShadow(static_cast<ShadowStyle>(NONE_SHADOW_VALUE));
426     if (!graphics->HasBackShadow() || graphics->GetBackShadowValue() == shadow) {
427         ShadowStyle shadowStyle = static_cast<ShadowStyle>(selectTheme_->GetOptionFocusedShadow());
428         renderContext->UpdateBackShadow(Shadow::CreateShadow(shadowStyle));
429         isFocusShadowSet_ = true;
430     }
431 
432     auto paintProperty = GetPaintProperty<MenuItemPaintProperty>();
433     CHECK_NULL_VOID(paintProperty);
434     if (!paintProperty->HasOptionBgColor() && !paintProperty->HasSelectedOptionBgColor() && !showDefaultSelectedIcon_) {
435         renderContext->UpdateBackgroundColor(selectTheme_->GetOptionFocusedBackgroundColor());
436     }
437     if (!paintProperty->HasOptionFontColor() && !paintProperty->HasSelectedOptionFontColor()) {
438         auto color = selectTheme_->GetOptionFocusedFontColor();
439         SetFontColor(color, false);
440     }
441 }
442 
ClearFocusStyle()443 void MenuItemPattern::ClearFocusStyle()
444 {
445     CHECK_NULL_VOID(selectTheme_);
446     auto host = GetHost();
447     CHECK_NULL_VOID(host);
448     auto renderContext = host->GetRenderContext();
449     CHECK_NULL_VOID(renderContext);
450     if (!selectTheme_->GetoptionApplyFocusedStyle()) {
451         return;
452     }
453     if (isFocusShadowSet_) {
454         renderContext->ResetBackShadow();
455         renderContext->SetShadowRadius(0.0f);
456         isFocusShadowSet_ = false;
457     }
458     auto paintProperty = GetPaintProperty<MenuItemPaintProperty>();
459     CHECK_NULL_VOID(paintProperty);
460     if (!paintProperty->HasOptionBgColor() && !paintProperty->HasSelectedOptionBgColor()) {
461         renderContext->UpdateBackgroundColor(bgColor_.value_or(selectTheme_->GetBackgroundColor()));
462     }
463     if (!paintProperty->HasOptionFontColor() && !paintProperty->HasSelectedOptionFontColor()) {
464         SetFontColor(fontColor_.value_or(selectTheme_->GetMenuFontColor()));
465     }
466 }
467 
HandleFocusEvent()468 void MenuItemPattern::HandleFocusEvent()
469 {
470     isFocused_ = true;
471     auto host = GetHost();
472     CHECK_NULL_VOID(host);
473     auto renderContext = host->GetRenderContext();
474     CHECK_NULL_VOID(renderContext);
475     renderContext->SetClipToBounds(focusPadding_ == 0.0_vp);
476     auto context = host->GetContextRefPtr();
477     CHECK_NULL_VOID(context);
478     auto selectTheme = context->GetTheme<SelectTheme>();
479     CHECK_NULL_VOID(selectTheme);
480 
481     if (!renderContext->HasBackgroundColor()) {
482         renderContext->UpdateBackgroundColor(selectTheme->GetMenuItemFocusedBgColor());
483         isFocusBGColorSet_ = true;
484     }
485     if (!renderContext->HasBackShadow()) {
486         ShadowStyle shadowStyle = static_cast<ShadowStyle>(selectTheme->GetMenuItemFocusedShadowStyle());
487         if (shadowStyle != ShadowStyle::None) {
488             Shadow shadow;
489             if (!GetShadowFromTheme(shadowStyle, shadow)) {
490                 shadow = Shadow::CreateShadow(shadowStyle);
491             }
492             renderContext->UpdateBackShadow(shadow);
493             isFocusShadowSet_ = true;
494         }
495     }
496 
497     RefPtr<FrameNode> leftRow =
498         host->GetChildAtIndex(0) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(0)) : nullptr;
499     CHECK_NULL_VOID(leftRow);
500     auto menuNode = GetMenu();
501     auto menuProperty = menuNode ? menuNode->GetLayoutProperty<MenuLayoutProperty>() : nullptr;
502     UpdateText(leftRow, menuProperty, false);
503 }
504 
HandleBlurEvent()505 void MenuItemPattern::HandleBlurEvent()
506 {
507     isFocused_ = false;
508     auto host = GetHost();
509     CHECK_NULL_VOID(host);
510     auto renderContext = host->GetRenderContext();
511     CHECK_NULL_VOID(renderContext);
512     renderContext->SetClipToBounds(true);
513     if (isFocusBGColorSet_) {
514         renderContext->ResetBackgroundColor();
515         renderContext->SetBackgroundColor(Color::TRANSPARENT.GetValue());
516         isFocusBGColorSet_ = false;
517     }
518     if (isFocusShadowSet_) {
519         renderContext->ResetBackShadow();
520         renderContext->SetShadowRadius(0.0f);
521         isFocusShadowSet_ = false;
522     }
523 
524     RefPtr<FrameNode> leftRow =
525         host->GetChildAtIndex(0) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(0)) : nullptr;
526     CHECK_NULL_VOID(leftRow);
527     auto menuNode = GetMenu();
528     auto menuProperty = menuNode ? menuNode->GetLayoutProperty<MenuLayoutProperty>() : nullptr;
529     UpdateText(leftRow, menuProperty, false);
530 }
531 
OnAfterModifyDone()532 void MenuItemPattern::OnAfterModifyDone()
533 {
534     auto host = GetHost();
535     CHECK_NULL_VOID(host);
536     auto inspectorId = host->GetInspectorId().value_or("");
537     if (inspectorId.empty()) {
538         return;
539     }
540     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
541     CHECK_NULL_VOID(itemProperty);
542     auto content = itemProperty->GetContent().value_or("");
543     Recorder::NodeDataCache::Get().PutMultiple(host, inspectorId, content, isSelected_);
544 }
545 
RecordChangeEvent() const546 void MenuItemPattern::RecordChangeEvent() const
547 {
548     if (!Recorder::EventRecorder::Get().IsComponentRecordEnable()) {
549         return;
550     }
551     auto host = GetHost();
552     CHECK_NULL_VOID(host);
553     auto inspectorId = host->GetInspectorId().value_or("");
554     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
555     CHECK_NULL_VOID(itemProperty);
556     auto content = itemProperty->GetContent().value_or("");
557     Recorder::EventParamsBuilder builder;
558     builder.SetId(inspectorId)
559         .SetType(host->GetTag())
560         .SetChecked(isSelected_)
561         .SetText(content)
562         .SetDescription(host->GetAutoEventParamValue(""))
563         .SetHost(host);
564     Recorder::EventRecorder::Get().OnChange(std::move(builder));
565     Recorder::NodeDataCache::Get().PutMultiple(host, inspectorId, content, isSelected_);
566 }
567 
GetMenuWrapper()568 RefPtr<FrameNode> MenuItemPattern::GetMenuWrapper()
569 {
570     auto host = GetHost();
571     CHECK_NULL_RETURN(host, nullptr);
572     auto parent = host->GetParent();
573     while (parent) {
574         if (parent->GetTag() == V2::MENU_WRAPPER_ETS_TAG || parent->GetTag() == V2::SELECT_OVERLAY_ETS_TAG) {
575             return AceType::DynamicCast<FrameNode>(parent);
576         }
577         parent = parent->GetParent();
578     }
579     return nullptr;
580 }
581 
GetMenu(bool needTopMenu)582 RefPtr<FrameNode> MenuItemPattern::GetMenu(bool needTopMenu)
583 {
584     auto host = GetHost();
585     CHECK_NULL_RETURN(host, nullptr);
586     auto parent = host->GetParent();
587     RefPtr<FrameNode> menuNode = nullptr;
588     while (parent) {
589         if (parent->GetTag() == V2::MENU_ETS_TAG) {
590             menuNode = AceType::DynamicCast<FrameNode>(parent);
591             if (!needTopMenu) {
592                 // innner menu
593                 return menuNode;
594             }
595         }
596         parent = parent->GetParent();
597     }
598     // outter menu
599     return menuNode;
600 }
601 
GetMenuPattern(bool needTopMenu)602 RefPtr<MenuPattern> MenuItemPattern::GetMenuPattern(bool needTopMenu)
603 {
604     auto menu = GetMenu(needTopMenu);
605     if (!menu) {
606         return nullptr;
607     }
608     return menu->GetPattern<MenuPattern>();
609 }
610 
CleanParentMenuItemBgColor()611 void MenuItemPattern::CleanParentMenuItemBgColor()
612 {
613     auto host = GetHost();
614     CHECK_NULL_VOID(host);
615     auto menu = GetMenu(true);
616     CHECK_NULL_VOID(menu);
617     auto menuPattern = menu->GetPattern<MenuPattern>();
618     CHECK_NULL_VOID(menuPattern);
619     SetBgBlendColor(Color::TRANSPARENT);
620     auto props = GetPaintProperty<MenuItemPaintProperty>();
621     CHECK_NULL_VOID(props);
622     props->UpdateHover(false);
623     props->UpdatePress(false);
624     auto parentNode = host->GetParent();
625     CHECK_NULL_VOID(parentNode);
626     auto parent = AceType::DynamicCast<UINode>(parentNode);
627     CHECK_NULL_VOID(parent);
628     menuPattern->OnItemPressed(parent, index_, false, false);
629     PlayBgColorAnimation();
630     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
631 }
632 
ShowSubMenu(ShowSubMenuType type)633 void MenuItemPattern::ShowSubMenu(ShowSubMenuType type)
634 {
635     auto host = GetHost();
636     CHECK_NULL_VOID(host);
637     auto menuNode = GetMenu(true);
638     CHECK_NULL_VOID(menuNode);
639     auto menuPattern = menuNode->GetPattern<MenuPattern>();
640     CHECK_NULL_VOID(menuPattern);
641     auto customNode = BuildSubMenuCustomNode();
642     CHECK_NULL_VOID(customNode);
643     UpdateSubmenuExpandingMode(customNode);
644     if (expandingMode_ == SubMenuExpandingMode::EMBEDDED) {
645         auto frameNode = GetSubMenu(customNode);
646         if (!frameNode) {
647             frameNode = AceType::DynamicCast<FrameNode>(customNode);
648         }
649         CHECK_NULL_VOID(frameNode);
650         OnExpandChanged(frameNode);
651         return;
652     }
653 
654     menuPattern->FocusViewHide();
655     HideSubMenu();
656     isSubMenuShowed_ = true;
657     bool isSelectOverlayMenu = IsSelectOverlayMenu();
658     MenuParam param;
659     auto outterMenuLayoutProps = menuNode->GetLayoutProperty<MenuLayoutProperty>();
660     CHECK_NULL_VOID(outterMenuLayoutProps);
661     param.isShowInSubWindow = outterMenuLayoutProps->GetShowInSubWindowValue(false);
662     if (!ParseMenuBlurStyleEffect(param, menuNode->GetRenderContext())) {
663         return;
664     }
665     param.type = isSelectOverlayMenu ? MenuType::SELECT_OVERLAY_SUB_MENU : MenuType::SUB_MENU;
666     ParseMenuRadius(param);
667     auto subMenu = MenuView::Create(customNode, host->GetId(), host->GetTag(), param);
668     CHECK_NULL_VOID(subMenu);
669     ShowSubMenuHelper(subMenu);
670     menuPattern->SetShowedSubMenu(subMenu);
671     auto subMenuPattern = subMenu->GetPattern<MenuPattern>();
672     CHECK_NULL_VOID(subMenuPattern);
673     if (expandingMode_ == SubMenuExpandingMode::SIDE) {
674         subMenuPattern->SetSubMenuDepth(menuPattern->GetSubMenuDepth() + 1);
675     }
676     if (type == ShowSubMenuType::KEY_DPAD_RIGHT) {
677         subMenuPattern->SetIsViewRootScopeFocused(false);
678     }
679     if (type == ShowSubMenuType::LONG_PRESS && expandingMode_ == SubMenuExpandingMode::STACK) {
680         CleanParentMenuItemBgColor();
681     }
682     ShowSubMenuWithAnimation(subMenu);
683     SendSubMenuOpenToAccessibility(subMenu, type);
684 }
685 
ShowSubMenuWithAnimation(const RefPtr<FrameNode> & subMenu)686 void MenuItemPattern::ShowSubMenuWithAnimation(const RefPtr<FrameNode>& subMenu)
687 {
688     CHECK_NULL_VOID(subMenu);
689     auto pipeline = subMenu->GetContext();
690     CHECK_NULL_VOID(pipeline);
691     auto menuTheme = pipeline->GetTheme<SelectTheme>();
692     CHECK_NULL_VOID(menuTheme);
693     if (menuTheme->GetMenuAnimationDuration()) {
694         auto renderContext = subMenu->GetRenderContext();
695         CHECK_NULL_VOID(renderContext);
696         renderContext->UpdateTransformCenter(DimensionOffset(Offset()));
697         renderContext->UpdateOpacity(MENU_ANIMATION_MIN_OPACITY);
698         renderContext->UpdateTransformScale(
699             VectorF(menuTheme->GetMenuAnimationScale(), menuTheme->GetMenuAnimationScale()));
700         auto animationOption = AnimationOption();
701         animationOption.SetDuration(menuTheme->GetMenuAnimationDuration());
702         animationOption.SetCurve(menuTheme->GetMenuAnimationCurve());
703         AnimationUtils::Animate(
704             animationOption,
705             [weak = WeakClaim(RawPtr(subMenu))]() {
706                 auto subMenuNode = weak.Upgrade();
707                 CHECK_NULL_VOID(subMenuNode);
708                 auto renderContext = subMenuNode->GetRenderContext();
709                 CHECK_NULL_VOID(renderContext);
710                 renderContext->UpdateTransformScale(VectorF(MENU_ANIMATION_MAX_SCALE, MENU_ANIMATION_MAX_SCALE));
711                 renderContext->UpdateOpacity(MENU_ANIMATION_MAX_OPACITY);
712             },
713             animationOption.GetOnFinishEvent(), nullptr, subMenu->GetContextRefPtr());
714     }
715 }
716 
SendSubMenuOpenToAccessibility(RefPtr<FrameNode> & subMenu,ShowSubMenuType type)717 void MenuItemPattern::SendSubMenuOpenToAccessibility(RefPtr<FrameNode>& subMenu, ShowSubMenuType type)
718 {
719     CHECK_NULL_VOID(subMenu);
720     auto accessibilityProperty = subMenu->GetAccessibilityProperty<MenuAccessibilityProperty>();
721     CHECK_NULL_VOID(accessibilityProperty);
722     accessibilityProperty->SetAccessibilityIsShow(true);
723     subMenu->OnAccessibilityEvent(AccessibilityEventType::PAGE_OPEN);
724     TAG_LOGI(AceLogTag::ACE_OVERLAY, "show sub menu, open type %{public}d", type);
725 }
726 
ParseMenuBlurStyleEffect(MenuParam & param,const RefPtr<RenderContext> & focusMenuRenderContext)727 bool MenuItemPattern::ParseMenuBlurStyleEffect(MenuParam& param, const RefPtr<RenderContext>& focusMenuRenderContext)
728 {
729     CHECK_NULL_RETURN(focusMenuRenderContext, false);
730     if (focusMenuRenderContext->GetBackBlurStyle().has_value()) {
731         auto focusMenuBlurStyle = focusMenuRenderContext->GetBackBlurStyle();
732         CHECK_NULL_RETURN(focusMenuBlurStyle, false);
733         param.backgroundBlurStyle = static_cast<int>(focusMenuBlurStyle->blurStyle);
734         param.blurStyleOption = focusMenuBlurStyle;
735     }
736     if (focusMenuRenderContext->GetBackgroundEffect().has_value()) {
737         auto focusMenuEffect = focusMenuRenderContext->GetBackgroundEffect();
738         CHECK_NULL_RETURN(focusMenuEffect, false);
739         param.effectOption = focusMenuEffect;
740     }
741     return true;
742 }
743 
BuildSubMenuCustomNode()744 RefPtr<UINode> MenuItemPattern::BuildSubMenuCustomNode()
745 {
746     auto menuWrapper = GetMenuWrapper();
747     CHECK_NULL_RETURN(menuWrapper, nullptr);
748     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
749     CHECK_NULL_RETURN(menuWrapperPattern, nullptr);
750     auto hasSubMenu = menuWrapperPattern->HasStackSubMenu();
751     auto buildFunc = GetSubBuilder();
752     if (!buildFunc || isSubMenuShowed_ || IsEmbedded() ||
753         (expandingMode_ == SubMenuExpandingMode::STACK && hasSubMenu)) {
754         return nullptr;
755     }
756 
757     NG::ScopedViewStackProcessor builderViewStackProcessor;
758     buildFunc();
759     return NG::ViewStackProcessor::GetInstance()->Finish();
760 }
761 
GetSubMenu(RefPtr<UINode> & customNode)762 RefPtr<FrameNode> MenuItemPattern::GetSubMenu(RefPtr<UINode>& customNode)
763 {
764     CHECK_NULL_RETURN(customNode, nullptr);
765     if (customNode->GetTag() == V2::MENU_ETS_TAG) {
766         auto frameNode = AceType::DynamicCast<FrameNode>(customNode);
767         CHECK_NULL_RETURN(frameNode, nullptr);
768         return frameNode;
769     }
770     uint32_t depth = 0;
771     auto child = customNode->GetFrameChildByIndex(0, false);
772     while (child && depth < MAX_SEARCH_DEPTH) {
773         if (child->GetTag() == V2::JS_VIEW_ETS_TAG) {
774             child = child->GetFrameChildByIndex(0, false);
775             if (child && child->GetTag() == V2::JS_VIEW_ETS_TAG) {
776                 child  = child->GetChildAtIndex(0);
777                 ++depth;
778             }
779             continue;
780         }
781         if (child->GetTag() == V2::MENU_ETS_TAG) {
782             return AceType::DynamicCast<FrameNode>(child);
783         }
784         child  = child->GetChildAtIndex(0);
785         ++depth;
786     }
787     return nullptr;
788 }
789 
UpdateSubmenuExpandingMode(RefPtr<UINode> & customNode)790 void MenuItemPattern::UpdateSubmenuExpandingMode(RefPtr<UINode>& customNode)
791 {
792     auto frameNode = GetSubMenu(customNode);
793     if (!frameNode) {
794         TAG_LOGW(AceLogTag::ACE_MENU, "subMenu has no Menu node");
795     }
796     CHECK_NULL_VOID(frameNode);
797     if (frameNode->GetTag() == V2::MENU_ETS_TAG) {
798         auto props = frameNode->GetLayoutProperty<MenuLayoutProperty>();
799         CHECK_NULL_VOID(props);
800         auto pattern = frameNode->GetPattern<MenuPattern>();
801         CHECK_NULL_VOID(pattern);
802         props->UpdateExpandingMode(expandingMode_);
803         if (expandingMode_ == SubMenuExpandingMode::STACK) {
804             AddStackSubMenuHeader(frameNode);
805             pattern->SetIsStackSubmenu();
806         } else if (expandingMode_ == SubMenuExpandingMode::EMBEDDED) {
807             pattern->SetIsEmbedded();
808             return;
809         }
810         frameNode->MarkModifyDone();
811         frameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
812     }
813 }
814 
ShowSubMenuHelper(const RefPtr<FrameNode> & subMenu)815 void MenuItemPattern::ShowSubMenuHelper(const RefPtr<FrameNode>& subMenu)
816 {
817     CHECK_NULL_VOID(subMenu);
818     auto host = GetHost();
819     CHECK_NULL_VOID(host);
820     bool isSelectOverlayMenu = IsSelectOverlayMenu();
821     auto menuPattern = subMenu->GetPattern<MenuPattern>();
822     CHECK_NULL_VOID(menuPattern);
823     menuPattern->SetParentMenuItem(host);
824     subMenuId_ = subMenu->GetId();
825     AddSelfHoverRegion(host);
826     auto menuWrapper = GetMenuWrapper();
827     CHECK_NULL_VOID(menuWrapper);
828     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) &&
829         expandingMode_ == SubMenuExpandingMode::STACK) {
830         SetClickMenuItemId(host->GetId());
831         subMenu->MountToParent(menuWrapper);
832         menuWrapper->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
833         menuPattern->SetSubMenuShow(true);
834         RegisterWrapperMouseEvent();
835     } else {
836         subMenu->MountToParent(menuWrapper);
837         OffsetF offset = GetSubMenuPosition(host);
838         auto menuProps = subMenu->GetLayoutProperty<MenuLayoutProperty>();
839         CHECK_NULL_VOID(menuProps);
840         menuProps->UpdateMenuOffset(offset);
841         menuWrapper->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
842         RegisterWrapperMouseEvent();
843     }
844     // select overlay menu no need focus
845     if (!isSelectOverlayMenu) {
846         menuPattern->FocusViewShow();
847     }
848 }
849 
HideSubMenu()850 void MenuItemPattern::HideSubMenu()
851 {
852     auto host = GetHost();
853     CHECK_NULL_VOID(host);
854     auto menuWrapper = GetMenuWrapper();
855     CHECK_NULL_VOID(menuWrapper);
856     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
857     CHECK_NULL_VOID(menuWrapperPattern);
858     auto showedSubMenu = menuWrapperPattern->GetShowedSubMenu();
859     if (showedSubMenu) {
860         auto showedSubMenuPattern = showedSubMenu->GetPattern<MenuPattern>();
861         CHECK_NULL_VOID(showedSubMenuPattern);
862         auto showedMenuItem = showedSubMenuPattern->GetParentMenuItem();
863         CHECK_NULL_VOID(showedMenuItem);
864         if (showedMenuItem->GetId() != host->GetId()) {
865             auto outterMenu = GetMenu(true);
866             CHECK_NULL_VOID(outterMenu);
867             auto outterMenuPattern = outterMenu->GetPattern<MenuPattern>();
868             CHECK_NULL_VOID(outterMenuPattern);
869             outterMenuPattern->HideSubMenu();
870         }
871     }
872 }
873 
OnExpandChanged(const RefPtr<FrameNode> & expandableNode)874 void MenuItemPattern::OnExpandChanged(const RefPtr<FrameNode>& expandableNode)
875 {
876     CHECK_NULL_VOID(expandableNode);
877     auto host = GetHost();
878     CHECK_NULL_VOID(host);
879     auto menuNode = GetMenu(true);
880     CHECK_NULL_VOID(menuNode);
881     auto menuPattern = menuNode->GetPattern<MenuPattern>();
882     CHECK_NULL_VOID(menuPattern);
883     isExpanded_ = !isExpanded_;
884     if (isExpanded_) {
885         embeddedMenu_ = expandableNode;
886         ShowEmbeddedExpandMenu(embeddedMenu_);
887         menuPattern->SetShowedSubMenu(embeddedMenu_);
888         menuPattern->AddEmbeddedMenuItem(host);
889     } else {
890         HideEmbedded();
891     }
892 }
893 
HideEmbedded(bool isNeedAnimation)894 void MenuItemPattern::HideEmbedded(bool isNeedAnimation)
895 {
896     auto host = GetHost();
897     CHECK_NULL_VOID(host);
898     isExpanded_ = false;
899     auto menuNode = GetMenu(true);
900     CHECK_NULL_VOID(menuNode);
901     auto menuPattern = menuNode->GetPattern<MenuPattern>();
902     CHECK_NULL_VOID(menuPattern);
903     CHECK_NULL_VOID(embeddedMenu_);
904     HideEmbeddedExpandMenu(embeddedMenu_, isNeedAnimation);
905     embeddedMenu_ = nullptr;
906     menuPattern->SetShowedSubMenu(nullptr);
907     menuPattern->RemoveEmbeddedMenuItem(host);
908 }
909 
GetTransformCenter(SizeF size,Placement placement)910 Offset GetTransformCenter(SizeF size, Placement placement)
911 {
912     if (placement == Placement::TOP) {
913         return Offset(size.Width() / 2.0f, size.Height());
914     }
915     if (placement == Placement::TOP_LEFT) {
916         return Offset(0.0f, size.Height());
917     }
918     if (placement == Placement::TOP_RIGHT) {
919         return Offset(size.Width(), size.Height());
920     }
921     return Offset();
922 }
923 
UpdatePreviewPosition(SizeF oldMenuSize,SizeF menuSize)924 void MenuItemPattern::UpdatePreviewPosition(SizeF oldMenuSize, SizeF menuSize)
925 {
926     auto menuWrapper = GetMenuWrapper();
927     CHECK_NULL_VOID(menuWrapper);
928     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
929     CHECK_NULL_VOID(menuWrapperPattern);
930 
931     auto topMenu = menuWrapperPattern->GetMenu();
932     CHECK_NULL_VOID(topMenu);
933     auto menuPattern = topMenu->GetPattern<MenuPattern>();
934     CHECK_NULL_VOID(menuPattern && menuPattern->GetPreviewMode() != MenuPreviewMode::NONE);
935 
936     auto placement = menuPattern->GetLastPlacement().value_or(Placement::NONE);
937     auto isTopMenuBottomPreview =
938         placement == Placement::TOP || placement == Placement::TOP_LEFT || placement == Placement::TOP_RIGHT;
939 
940     auto offsetY = menuSize.Height() - oldMenuSize.Height();
941     CHECK_NULL_VOID(isTopMenuBottomPreview && !NearZero(offsetY));
942 
943     auto preview = AceType::DynamicCast<FrameNode>(menuWrapper->GetChildAtIndex(1));
944     CHECK_NULL_VOID(preview);
945     auto tag = preview->GetTag();
946     auto isPreview = tag == V2::IMAGE_ETS_TAG || tag == V2::MENU_PREVIEW_ETS_TAG || tag == V2::FLEX_ETS_TAG;
947     CHECK_NULL_VOID(isPreview);
948 
949     auto renderContext = preview->GetRenderContext();
950     CHECK_NULL_VOID(renderContext);
951     auto previewGeometryNode = preview->GetGeometryNode();
952     CHECK_NULL_VOID(previewGeometryNode);
953 
954     auto offset = previewGeometryNode->GetFrameOffset();
955     offset.AddY(offsetY);
956 
957     previewGeometryNode->SetFrameOffset(offset);
958     renderContext->UpdatePosition(OffsetT<Dimension>(Dimension(offset.GetX()), Dimension(offset.GetY())));
959     menuWrapperPattern->SetPreviewDisappearStartOffset(offset);
960 
961     auto pipeline = topMenu->GetContext();
962     CHECK_NULL_VOID(pipeline);
963     auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
964     CHECK_NULL_VOID(menuTheme);
965     menuPattern->InitPreviewMenuAnimationInfo(menuTheme);
966     auto menuRenderContext = topMenu->GetRenderContext();
967     CHECK_NULL_VOID(menuRenderContext);
968     menuRenderContext->UpdateTransformCenter(DimensionOffset(GetTransformCenter(menuSize, placement)));
969 }
970 
ShowEmbeddedExpandMenu(const RefPtr<FrameNode> & expandableNode)971 void MenuItemPattern::ShowEmbeddedExpandMenu(const RefPtr<FrameNode>& expandableNode)
972 {
973     CHECK_NULL_VOID(expandableNode);
974     auto host = GetHost();
975     CHECK_NULL_VOID(host);
976     auto menuWrapper = GetMenuWrapper();
977     CHECK_NULL_VOID(menuWrapper);
978     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
979     CHECK_NULL_VOID(menuWrapperPattern);
980     menuWrapperPattern->IncreaseEmbeddedSubMenuCount();
981     auto rightRow = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(1));
982     CHECK_NULL_VOID(rightRow);
983     auto imageNode = AceType::DynamicCast<FrameNode>(rightRow->GetChildren().back());
984     CHECK_NULL_VOID(imageNode);
985     auto imageContext = imageNode->GetRenderContext();
986     CHECK_NULL_VOID(imageContext);
987     imageContext->UpdateTransformRotate(Vector5F(0.0f, 0.0f, 1.0f, 0.0f, 0.0f));
988 
989     auto expandableAreaContext = expandableNode->GetRenderContext();
990     CHECK_NULL_VOID(expandableAreaContext);
991     expandableAreaContext->UpdateBackShadow(ShadowConfig::NoneShadow);
992     auto itemSize = host->GetGeometryNode()->GetFrameSize();
993     expandableAreaContext->ClipWithRRect(RectF(0.0f, 0.0f, itemSize.Width(), 0.0f),
994         RadiusF(EdgeF(0.0f, 0.0f)));
995 
996     AnimationOption option = AnimationOption();
997     auto rotateOption = AceType::MakeRefPtr<InterpolatingSpring>(VELOCITY, MASS, STIFFNESS, DAMPING);
998     option.SetCurve(rotateOption);
999     AnimationUtils::Animate(option, [weak = WeakClaim(this), expandableNodeWk = WeakClaim(RawPtr(expandableNode))]() {
1000         auto pattern = weak.Upgrade();
1001         CHECK_NULL_VOID(pattern);
1002         auto expandableNode = expandableNodeWk.Upgrade();
1003         CHECK_NULL_VOID(expandableNode);
1004         pattern->SetShowEmbeddedMenuParams(expandableNode);
1005     }, nullptr, nullptr, host->GetContextRefPtr());
1006 }
1007 
SetShowEmbeddedMenuParams(const RefPtr<FrameNode> & expandableNode)1008 void MenuItemPattern::SetShowEmbeddedMenuParams(const RefPtr<FrameNode>& expandableNode)
1009 {
1010     CHECK_NULL_VOID(expandableNode);
1011     auto host = GetHost();
1012     CHECK_NULL_VOID(host);
1013     auto rightRow = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(1));
1014     CHECK_NULL_VOID(rightRow);
1015     auto imageNode = AceType::DynamicCast<FrameNode>(rightRow->GetChildren().back());
1016     CHECK_NULL_VOID(imageNode);
1017     expandableNode->MountToParent(host, EXPANDABLE_AREA_VIEW_INDEX);
1018     auto imageContext = imageNode->GetRenderContext();
1019     CHECK_NULL_VOID(imageContext);
1020     imageContext->UpdateTransformRotate(Vector5F(0.0f, 0.0f, 1.0f, SEMI_CIRCLE_ANGEL, 0.0f));
1021     expandableNode->MarkModifyDone();
1022     expandableNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1023 
1024     auto menuItemPattern = host->GetPattern<MenuItemPattern>();
1025     CHECK_NULL_VOID(menuItemPattern);
1026     auto menuWrapper = menuItemPattern->GetMenuWrapper();
1027     CHECK_NULL_VOID(menuWrapper);
1028     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
1029     CHECK_NULL_VOID(menuWrapperPattern);
1030 
1031     auto topMenu = menuWrapperPattern->GetMenu();
1032     CHECK_NULL_VOID(topMenu);
1033     auto menuGeometryNode = topMenu->GetGeometryNode();
1034     CHECK_NULL_VOID(menuGeometryNode);
1035     auto oldMenuSize = menuGeometryNode->GetFrameSize();
1036 
1037     auto pipeline = host->GetContextWithCheck();
1038     CHECK_NULL_VOID(pipeline);
1039     pipeline->FlushUITasks();
1040     auto expandableAreaFrameSize = expandableNode->GetGeometryNode()->GetFrameSize();
1041     auto expandableAreaContext = expandableNode->GetRenderContext();
1042     CHECK_NULL_VOID(expandableAreaContext);
1043     expandableAreaContext->ClipWithRRect(
1044         RectF(0.0f, 0.0f, expandableAreaFrameSize.Width(), expandableAreaFrameSize.Height()),
1045         RadiusF(EdgeF(0.0f, 0.0f)));
1046     menuItemPattern->UpdatePreviewPosition(oldMenuSize, menuGeometryNode->GetFrameSize());
1047 }
1048 
HideEmbeddedExpandMenu(const RefPtr<FrameNode> & expandableNode,bool isNeedAnimation)1049 void MenuItemPattern::HideEmbeddedExpandMenu(const RefPtr<FrameNode>& expandableNode, bool isNeedAnimation)
1050 {
1051     CHECK_NULL_VOID(expandableNode);
1052     auto host = GetHost();
1053     CHECK_NULL_VOID(host);
1054     auto menuWrapper = GetMenuWrapper();
1055     CHECK_NULL_VOID(menuWrapper);
1056     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
1057     CHECK_NULL_VOID(menuWrapperPattern);
1058     menuWrapperPattern->DecreaseEmbeddedSubMenuCount();
1059     auto expandableAreaContext = expandableNode->GetRenderContext();
1060     CHECK_NULL_VOID(expandableAreaContext);
1061 
1062     AnimationOption option = AnimationOption();
1063     if (isNeedAnimation) {
1064         auto rotateOption = AceType::MakeRefPtr<InterpolatingSpring>(VELOCITY, MASS, STIFFNESS, DAMPING);
1065         option.SetCurve(rotateOption);
1066         RefPtr<ChainedTransitionEffect> opacity = AceType::MakeRefPtr<ChainedOpacityEffect>(OPACITY_EFFECT);
1067         expandableAreaContext->UpdateChainedTransition(opacity);
1068     }
1069     MenuRemoveChild(expandableNode, menuFocusType_ == MENU_FOCUS_TYPE);
1070 
1071     AnimationUtils::Animate(option, [host, expandableNode, menuWrapperPattern]() {
1072         auto menuItemPattern = host->GetPattern<MenuItemPattern>();
1073         CHECK_NULL_VOID(menuItemPattern);
1074         menuItemPattern->MenuRemoveChild(expandableNode, menuItemPattern->menuFocusType_ != MENU_FOCUS_TYPE);
1075         auto rightRow = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(1));
1076         CHECK_NULL_VOID(rightRow);
1077         auto imageNode = AceType::DynamicCast<FrameNode>(rightRow->GetChildren().back());
1078         CHECK_NULL_VOID(imageNode);
1079         auto imageContext = imageNode->GetRenderContext();
1080         CHECK_NULL_VOID(imageContext);
1081         imageContext->UpdateTransformRotate(Vector5F(0.0f, 0.0f, 1.0f, 0.0f, 0.0f));
1082 
1083         CHECK_NULL_VOID(menuWrapperPattern);
1084         auto topMenu = menuWrapperPattern->GetMenu();
1085         CHECK_NULL_VOID(topMenu);
1086         auto menuGeometryNode = topMenu->GetGeometryNode();
1087         CHECK_NULL_VOID(menuGeometryNode);
1088         auto oldMenuSize = menuGeometryNode->GetFrameSize();
1089 
1090         auto pipeline = host->GetContext();
1091         CHECK_NULL_VOID(pipeline);
1092         pipeline->FlushUITasks();
1093 
1094         menuItemPattern->UpdatePreviewPosition(oldMenuSize, menuGeometryNode->GetFrameSize());
1095     }, nullptr, nullptr, host->GetContextRefPtr());
1096 }
1097 
MenuRemoveChild(const RefPtr<FrameNode> & expandableNode,bool isOutFocus)1098 void MenuItemPattern::MenuRemoveChild(const RefPtr<FrameNode>& expandableNode, bool isOutFocus)
1099 {
1100     if (!isOutFocus) {
1101         return;
1102     }
1103     CHECK_NULL_VOID(expandableNode);
1104     auto host = GetHost();
1105     CHECK_NULL_VOID(host);
1106     host->RemoveChild(expandableNode, true);
1107     host->MarkModifyDone();
1108     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1109 }
1110 
CloseMenu()1111 void MenuItemPattern::CloseMenu()
1112 {
1113     // no need close for selection menu
1114     if (IsSelectOverlayMenu()) {
1115         return;
1116     }
1117     auto menuWrapper = GetMenuWrapper();
1118     CHECK_NULL_VOID(menuWrapper);
1119     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
1120     CHECK_NULL_VOID(menuWrapperPattern);
1121     menuWrapperPattern->UpdateMenuAnimation(menuWrapper);
1122     TAG_LOGI(AceLogTag::ACE_MENU, "will hide menu.");
1123     menuWrapperPattern->HideMenu();
1124 }
1125 
RegisterOnClick()1126 void MenuItemPattern::RegisterOnClick()
1127 {
1128     auto host = GetHost();
1129     CHECK_NULL_VOID(host);
1130     if (!onClickEvent_) {
1131         auto event = [weak = WeakClaim(this)](GestureEvent& /* info */) {
1132             auto pattern = weak.Upgrade();
1133             CHECK_NULL_VOID(pattern);
1134             pattern->IsOptionPattern() ? pattern->OnSelectProcess() : pattern->OnClick();
1135         };
1136         onClickEvent_ = MakeRefPtr<ClickEvent>(std::move(event));
1137     }
1138     auto gestureHub = host->GetOrCreateGestureEventHub();
1139     CHECK_NULL_VOID(gestureHub);
1140     if (!isOptionPattern_ && expandingMode_ == SubMenuExpandingMode::EMBEDDED && clickableArea_) {
1141         auto clickableAreaGestureHub = clickableArea_->GetOrCreateGestureEventHub();
1142         CHECK_NULL_VOID(clickableAreaGestureHub);
1143         gestureHub->RemoveClickEvent(onClickEvent_);
1144         clickableAreaGestureHub->AddClickEvent(onClickEvent_);
1145     } else if (!onClickEventSet_) {
1146         gestureHub->AddClickEvent(onClickEvent_);
1147         onClickEventSet_ = true;
1148     }
1149 }
1150 
RegisterOnTouch()1151 void MenuItemPattern::RegisterOnTouch()
1152 {
1153     if (!onTouchEvent_) {
1154         auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
1155             auto pattern = weak.Upgrade();
1156             CHECK_NULL_VOID(pattern);
1157             if (!pattern->IsOptionPattern()) {
1158                 pattern->OnTouch(info);
1159             }
1160         };
1161         onTouchEvent_ = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
1162     }
1163     auto host = GetHost();
1164     CHECK_NULL_VOID(host);
1165     auto gestureHub = host->GetOrCreateGestureEventHub();
1166     CHECK_NULL_VOID(gestureHub);
1167     if (!isOptionPattern_ && expandingMode_ == SubMenuExpandingMode::EMBEDDED && clickableArea_) {
1168         auto clickableAreaGestureHub = clickableArea_->GetOrCreateGestureEventHub();
1169         CHECK_NULL_VOID(clickableAreaGestureHub);
1170         gestureHub->RemoveTouchEvent(onTouchEvent_);
1171         clickableAreaGestureHub->AddTouchEvent(onTouchEvent_);
1172     } else if (!onTouchEventSet_) {
1173         gestureHub->AddTouchEvent(onTouchEvent_);
1174         onTouchEventSet_ = true;
1175     }
1176 }
1177 
RegisterOnPress()1178 void MenuItemPattern::RegisterOnPress()
1179 {
1180     if (!onPressEvent_) {
1181         auto touchCallback = [weak = WeakClaim(this)](const UIState& state) {
1182             auto pattern = weak.Upgrade();
1183             CHECK_NULL_VOID(pattern);
1184             if (pattern->IsOptionPattern()) {
1185                 pattern->OnPress(state);
1186             }
1187         };
1188         onPressEvent_ = std::move(touchCallback);
1189     }
1190     auto host = GetHost();
1191     CHECK_NULL_VOID(host);
1192     auto eventHub = host->GetOrCreateEventHub<MenuItemEventHub>();
1193     CHECK_NULL_VOID(eventHub);
1194     if (!onPressEventSet_) {
1195         eventHub->AddSupportedUIStateWithCallback(UI_STATE_PRESSED | UI_STATE_NORMAL, onPressEvent_, true);
1196         onPressEventSet_ = true;
1197     }
1198 }
1199 
UpdateTextMarquee(bool isMarqueeStart)1200 void MenuItemPattern::UpdateTextMarquee(bool isMarqueeStart)
1201 {
1202     CHECK_NULL_VOID(content_ && isTextFadeOut_);
1203     auto textLayoutProperty = content_->GetLayoutProperty<TextLayoutProperty>();
1204     CHECK_NULL_VOID(textLayoutProperty);
1205     if (textLayoutProperty) {
1206         textLayoutProperty->UpdateTextOverflow(TextOverflow::MARQUEE);
1207         textLayoutProperty->UpdateTextMarqueeFadeout(true);
1208         textLayoutProperty->UpdateTextMarqueeStart(isMarqueeStart);
1209         textLayoutProperty->UpdateTextMarqueeStartPolicy(MarqueeStartPolicy::DEFAULT);
1210         content_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1211     }
1212 }
1213 
RegisterOnHover()1214 void MenuItemPattern::RegisterOnHover()
1215 {
1216     if (!onHoverEvent_) {
1217         auto mouseTask = [weak = WeakClaim(this)](bool isHover) {
1218             auto pattern = weak.Upgrade();
1219             CHECK_NULL_VOID(pattern);
1220             pattern->OnHover(isHover);
1221             pattern->UpdateTextMarquee(isHover || pattern->isFocused_);
1222         };
1223         onHoverEvent_ = MakeRefPtr<InputEvent>(std::move(mouseTask));
1224     }
1225     auto host = GetHost();
1226     CHECK_NULL_VOID(host);
1227     auto inputHub = host->GetOrCreateInputEventHub();
1228     CHECK_NULL_VOID(inputHub);
1229     if (!isOptionPattern_ && expandingMode_ == SubMenuExpandingMode::EMBEDDED && clickableArea_) {
1230         auto clickableAreaInputHub = clickableArea_->GetOrCreateInputEventHub();
1231         CHECK_NULL_VOID(clickableAreaInputHub);
1232         inputHub->RemoveOnHoverEvent(onHoverEvent_);
1233         clickableAreaInputHub->AddOnHoverEvent(onHoverEvent_);
1234     } else if (!onHoverEventSet_) {
1235         inputHub->AddOnHoverEvent(onHoverEvent_);
1236         onHoverEventSet_ = true;
1237     }
1238 }
1239 
RegisterOnKeyEvent()1240 void MenuItemPattern::RegisterOnKeyEvent()
1241 {
1242     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
1243         auto pattern = wp.Upgrade();
1244         CHECK_NULL_RETURN(pattern, false);
1245         return pattern->OnKeyEvent(event);
1246     };
1247     auto event = std::move(onKeyEvent);
1248     auto host = GetHost();
1249     CHECK_NULL_VOID(host);
1250     auto focusHub = host->GetOrCreateFocusHub();
1251     CHECK_NULL_VOID(focusHub);
1252     if (!isOptionPattern_ && expandingMode_ == SubMenuExpandingMode::EMBEDDED && clickableArea_) {
1253         auto clickableAreaFocusHub = clickableArea_->GetOrCreateFocusHub();
1254         CHECK_NULL_VOID(clickableAreaFocusHub);
1255         focusHub->SetOnKeyEventInternal(nullptr);
1256         clickableAreaFocusHub->SetOnKeyEventInternal(event);
1257     } else if (!onKeyEventSet_) {
1258         focusHub->SetOnKeyEventInternal(event);
1259         onKeyEventSet_ = true;
1260     }
1261 }
1262 
OnClick()1263 bool MenuItemPattern::OnClick()
1264 {
1265     TAG_LOGD(AceLogTag::ACE_MENU, "MenuItem index:%{public}d receive click event", index_);
1266     auto host = GetHost();
1267     CHECK_NULL_RETURN(host, false);
1268     if (onClickAIMenuItem_) {
1269         onClickAIMenuItem_();
1270     }
1271     auto menuWrapper = GetMenuWrapper();
1272     auto menuWrapperPattern = menuWrapper ? menuWrapper->GetPattern<MenuWrapperPattern>() : nullptr;
1273     auto hasSubMenu = menuWrapperPattern ? menuWrapperPattern->HasStackSubMenu() : false;
1274     CHECK_EQUAL_RETURN(expandingMode_ == SubMenuExpandingMode::STACK && !IsSubMenu() && hasSubMenu, true, true);
1275     if (expandingMode_ == SubMenuExpandingMode::STACK && IsStackSubmenuHeader()) {
1276         menuWrapperPattern->HideSubMenu();
1277         return true;
1278     }
1279     auto hub = host->GetOrCreateEventHub<MenuItemEventHub>();
1280     CHECK_NULL_RETURN(hub, false);
1281     auto onChange = hub->GetOnChange();
1282     auto selectedChangeEvent = hub->GetSelectedChangeEvent();
1283     SetChange();
1284     if (selectedChangeEvent) {
1285         selectedChangeEvent(IsSelected());
1286     }
1287     if (onChange) {
1288         onChange(IsSelected());
1289         RecordChangeEvent();
1290     }
1291     auto menuNode = GetMenu();
1292     CHECK_NULL_RETURN(menuNode, false);
1293     auto menuPattern = menuNode->GetPattern<MenuPattern>();
1294     CHECK_NULL_RETURN(menuPattern, false);
1295     auto lastSelectedItem = menuPattern->GetLastSelectedItem();
1296     if (lastSelectedItem && lastSelectedItem != host) {
1297         auto pattern = lastSelectedItem->GetPattern<MenuItemPattern>();
1298         CHECK_NULL_RETURN(pattern, false);
1299         pattern->SetChange();
1300     }
1301     menuPattern->SetLastSelectedItem(host);
1302     if (GetSubBuilder() != nullptr && (expandingMode_ == SubMenuExpandingMode::SIDE ||
1303         (expandingMode_ == SubMenuExpandingMode::STACK && !IsSubMenu() && !hasSubMenu) ||
1304         (expandingMode_ == SubMenuExpandingMode::EMBEDDED && !IsEmbedded()))) {
1305         ShowSubMenu(ShowSubMenuType::CLICK);
1306         return true;
1307     }
1308     // hide menu when menu item is clicked, unless the click is intercepted
1309     if (!blockClick_) {
1310         CloseMenu();
1311     }
1312     return true;
1313 }
1314 
OnTouch(const TouchEventInfo & info)1315 void MenuItemPattern::OnTouch(const TouchEventInfo& info)
1316 {
1317     TAG_LOGD(AceLogTag::ACE_MENU, "MenuItem index:%{public}d receive touch event", index_);
1318     auto menuWrapper = GetMenuWrapper();
1319     // When menu wrapper exists, the pressed state is handed over to the menu wrapper
1320     if (menuWrapper && menuWrapper->GetTag() == V2::MENU_WRAPPER_ETS_TAG) {
1321         return;
1322     }
1323     // change menu item paint props on press
1324     const auto& touches = info.GetTouches();
1325     CHECK_EQUAL_VOID(touches.empty(), true);
1326     auto touchType = touches.front().GetTouchType();
1327     if (touchType == TouchType::DOWN) {
1328         // change background color, update press status
1329         NotifyPressStatus(true);
1330         UpdateTextMarquee(false);
1331     } else if (touchType == TouchType::UP) {
1332         NotifyPressStatus(false);
1333         UpdateTextMarquee(true);
1334     }
1335 }
1336 
NotifyPressStatus(bool isPress)1337 void MenuItemPattern::NotifyPressStatus(bool isPress)
1338 {
1339     auto host = GetHost();
1340     CHECK_NULL_VOID(host);
1341     auto pipeline = host->GetContext();
1342     CHECK_NULL_VOID(pipeline);
1343     auto theme = pipeline->GetTheme<SelectTheme>();
1344     CHECK_NULL_VOID(theme);
1345     auto props = GetPaintProperty<MenuItemPaintProperty>();
1346     CHECK_NULL_VOID(props);
1347     auto menu = GetMenu();
1348     CHECK_NULL_VOID(menu);
1349     auto menuPattern = menu->GetPattern<MenuPattern>();
1350     CHECK_NULL_VOID(menuPattern);
1351     auto parent = AceType::DynamicCast<UINode>(host->GetParent());
1352     auto menuWrapper = GetMenuWrapper();
1353     auto menuWrapperPattern = menuWrapper ? menuWrapper->GetPattern<MenuWrapperPattern>() : nullptr;
1354 
1355     // do not change color for stacked level-1 menu items if level-2 is shown
1356     auto canChangeColor = !(expandingMode_ == SubMenuExpandingMode::STACK
1357         && menuWrapperPattern && menuWrapperPattern->HasStackSubMenu() && !IsSubMenu());
1358     if (!canChangeColor) return;
1359     if (isPress) {
1360         // change background color, update press status
1361         SetBgBlendColor(GetSubBuilder() ? theme->GetHoverColor() : theme->GetClickedColor());
1362         if (menuWrapperPattern) {
1363             menuWrapperPattern->SetLastTouchItem(host);
1364         }
1365         props->UpdatePress(true);
1366         UpdateDividerPressStatus(true);
1367         menuPattern->OnItemPressed(parent, index_, true);
1368     } else {
1369         SetBgBlendColor(isHovered_ ? theme->GetHoverColor() : Color::TRANSPARENT);
1370         props->UpdatePress(false);
1371         UpdateDividerPressStatus(false);
1372         menuPattern->OnItemPressed(parent, index_, false);
1373     }
1374     PlayBgColorAnimation(false);
1375     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1376 }
1377 
OnTouch(const TouchEventInfo & info)1378 void CustomMenuItemPattern::OnTouch(const TouchEventInfo& info)
1379 {
1380     TAG_LOGD(AceLogTag::ACE_MENU, "Custom MenuItem receive touch event");
1381     const auto& touches = info.GetTouches();
1382     CHECK_EQUAL_VOID(touches.empty(), true);
1383     auto touchType = touches.front().GetTouchType();
1384 
1385     // close menu when touch up
1386     // can't use onClick because that conflicts with interactions developers might set to the customNode
1387     // recognize gesture as click if touch up position is close to last touch down position
1388     if (touchType == TouchType::DOWN) {
1389         lastTouchOffset_ = std::make_unique<Offset>(touches.front().GetLocalLocation());
1390     } else if (touchType == TouchType::UP) {
1391         auto touchUpOffset = touches.front().GetLocalLocation();
1392         if (lastTouchOffset_ && (touchUpOffset - *lastTouchOffset_).GetDistance() <= DEFAULT_CLICK_DISTANCE) {
1393             if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1394                 HandleOnChange();
1395             }
1396             CloseMenu();
1397         }
1398         lastTouchOffset_.reset();
1399     }
1400 }
1401 
HandleOnChange()1402 void CustomMenuItemPattern::HandleOnChange()
1403 {
1404     auto host = GetHost();
1405     CHECK_NULL_VOID(host);
1406     auto hub = host->GetOrCreateEventHub<MenuItemEventHub>();
1407     CHECK_NULL_VOID(hub);
1408     auto onChange = hub->GetOnChange();
1409     auto selectedChangeEvent = hub->GetSelectedChangeEvent();
1410     SetChange();
1411     if (selectedChangeEvent) {
1412         TAG_LOGI(AceLogTag::ACE_MENU, "trigger selectedChangeEvent");
1413         selectedChangeEvent(IsSelected());
1414     }
1415     if (onChange) {
1416         TAG_LOGI(AceLogTag::ACE_MENU, "trigger onChange");
1417         onChange(IsSelected());
1418     }
1419 }
1420 
OnHover(bool isHover)1421 void MenuItemPattern::OnHover(bool isHover)
1422 {
1423     SetIsHover(isHover);
1424     auto host = GetHost();
1425     CHECK_NULL_VOID(host);
1426     auto theme = GetCurrentSelectTheme();
1427     CHECK_NULL_VOID(theme);
1428     if (isOptionPattern_) {
1429         SetBgBlendColor(isHover ? theme->GetHoverColor() : Color::TRANSPARENT);
1430         auto props = GetPaintProperty<MenuItemPaintProperty>();
1431         CHECK_NULL_VOID(props);
1432         props->UpdateHover(isHover);
1433         UpdateDividerHoverStatus(isHover);
1434         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1435         if ((isHover || !IsSelected()) && !showDefaultSelectedIcon_) {
1436             UpdateNextNodeDivider(!isHover);
1437         }
1438         PlayBgColorAnimation();
1439     } else {
1440         auto menu = GetMenu(false);
1441         CHECK_NULL_VOID(menu);
1442         auto menuPattern = menu->GetPattern<MenuPattern>();
1443         CHECK_NULL_VOID(menuPattern);
1444         auto props = GetPaintProperty<MenuItemPaintProperty>();
1445         CHECK_NULL_VOID(props);
1446         auto parent = AceType::DynamicCast<UINode>(host->GetParent());
1447         SetBgBlendColor(isHover || isSubMenuShowed_ ? theme->GetHoverColor() : Color::TRANSPARENT);
1448         props->UpdateHover(isHover || isSubMenuShowed_);
1449         UpdateDividerHoverStatus(isHover || isSubMenuShowed_);
1450         if (isHover && (expandingMode_ == SubMenuExpandingMode::SIDE)) {
1451             auto menuWrapper = GetMenuWrapper();
1452             CHECK_NULL_VOID(menuWrapper);
1453             auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
1454             CHECK_NULL_VOID(menuWrapperPattern);
1455             menuWrapperPattern->HideSubMenuByDepth(host);
1456         }
1457         PostHoverSubMenuTask();
1458         menuPattern->OnItemPressed(parent, index_, isHover || isSubMenuShowed_, true);
1459         PlayBgColorAnimation();
1460         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1461     }
1462 }
1463 
PostHoverSubMenuTask()1464 void MenuItemPattern::PostHoverSubMenuTask()
1465 {
1466     if (GetSubBuilder() == nullptr) {
1467         return;
1468     }
1469     if (showTask_) {
1470         TAG_LOGD(AceLogTag::ACE_MENU, "Submenu show task cancel");
1471         showTask_.Cancel();
1472     }
1473     if (!isHover_ && !isSubMenuShowed_) {
1474         return;
1475     }
1476     if (expandingMode_ != SubMenuExpandingMode::SIDE) {
1477         ShowSubMenu(ShowSubMenuType::HOVER);
1478         return;
1479     }
1480     auto host = GetHost();
1481     CHECK_NULL_VOID(host);
1482     auto context = host->GetContext();
1483     CHECK_NULL_VOID(context);
1484     auto menuTheme = context->GetTheme<MenuTheme>();
1485     CHECK_NULL_VOID(menuTheme);
1486     auto delayTime = menuTheme->GetSubMenuShowDelayDuration();
1487     if (delayTime > 0) {
1488         showTask_.Reset([weak = WeakClaim(this)] {
1489             TAG_LOGD(AceLogTag::ACE_MENU, "Execute show submenu task");
1490             auto pattern = weak.Upgrade();
1491             CHECK_NULL_VOID(pattern);
1492             if (pattern->IsHover() || pattern->IsSubMenuShowed()) {
1493                 pattern->ShowSubMenu(ShowSubMenuType::HOVER);
1494             }
1495         });
1496         auto executor = context->GetTaskExecutor();
1497         CHECK_NULL_VOID(executor);
1498         TAG_LOGD(AceLogTag::ACE_MENU, "Post show submenu task");
1499         executor->PostDelayedTask(showTask_, TaskExecutor::TaskType::UI, delayTime, "ArkUIShowSubMenuTask");
1500     } else {
1501         ShowSubMenu(ShowSubMenuType::HOVER);
1502     }
1503 }
1504 
CheckHideSubMenu(std::function<void ()> callback,const PointF & mousePoint,const RectF & menuZone)1505 void MenuItemPattern::CheckHideSubMenu(std::function<void()> callback, const PointF& mousePoint, const RectF& menuZone)
1506 {
1507     if (expandingMode_ != SubMenuExpandingMode::SIDE) {
1508         PerformHideSubMenu(callback);
1509         return;
1510     }
1511     auto host = GetHost();
1512     CHECK_NULL_VOID(host);
1513     auto context = host->GetContext();
1514     CHECK_NULL_VOID(context);
1515     auto menuTheme = context->GetTheme<MenuTheme>();
1516     CHECK_NULL_VOID(menuTheme);
1517     if (!lastInnerPosition_.has_value()) {
1518         TAG_LOGW(AceLogTag::ACE_MENU, "Last inner position not has value");
1519         PerformHideSubMenu(callback);
1520         return;
1521     }
1522     if (hideTask_) {
1523         if (NeedPerformHideSubMenuImmediately(mousePoint, menuZone)) {
1524             PerformHideSubMenu(callback);
1525             return;
1526         }
1527     } else {
1528         if (!NeedStartHideMenuTask(mousePoint, menuZone)) {
1529             PerformHideSubMenu(callback);
1530             return;
1531         }
1532         auto delayTime = menuTheme->GetSubMenuHideDelayDuration();
1533         if (delayTime > 0) {
1534             hideTask_.Reset([weak = WeakClaim(this), callback = std::move(callback)] {
1535                 TAG_LOGD(AceLogTag::ACE_MENU, "Execute hide submenu task");
1536                 auto pattern = weak.Upgrade();
1537                 CHECK_NULL_VOID(pattern);
1538                 pattern->PerformHideSubMenu(callback);
1539             });
1540             auto executor = context->GetTaskExecutor();
1541             CHECK_NULL_VOID(executor);
1542             TAG_LOGD(AceLogTag::ACE_MENU, "Post hide submenu task");
1543             executor->PostDelayedTask(hideTask_, TaskExecutor::TaskType::UI, delayTime, "ArkUIHideSubMenuTask");
1544         } else {
1545             PerformHideSubMenu(callback);
1546             return;
1547         }
1548     }
1549     lastOutterPosition_ = mousePoint;
1550 }
1551 
NeedPerformHideSubMenuImmediately(const PointF & mousePoint,const RectF & menuZone)1552 bool MenuItemPattern::NeedPerformHideSubMenuImmediately(const PointF& mousePoint, const RectF& menuZone)
1553 {
1554     if (!lastInnerPosition_.has_value()) {
1555         return false;
1556     }
1557     if (!lastOutterPosition_.has_value()) {
1558         return false;
1559     }
1560     auto lastInnerPoint = lastInnerPosition_.value();
1561     auto lastOutterPoint = lastOutterPosition_.value();
1562     if (LessNotEqual(lastInnerPoint.GetX(), menuZone.Left())) {
1563         if (leaveFromBottom_) {
1564             if (GreatNotEqual(lastOutterPoint.GetX(), mousePoint.GetX()) &&
1565                 LessNotEqual(lastOutterPoint.GetY(), mousePoint.GetY())) {
1566                 return true;
1567             }
1568         } else {
1569             if (GreatNotEqual(lastOutterPoint.GetX(), mousePoint.GetX()) &&
1570                 GreatNotEqual(lastOutterPoint.GetY(), mousePoint.GetY())) {
1571                 return true;
1572             }
1573         }
1574     } else if (GreatNotEqual(lastInnerPoint.GetX(), menuZone.Right())) {
1575         if (leaveFromBottom_) {
1576             if (LessNotEqual(lastOutterPoint.GetX(), mousePoint.GetX()) &&
1577                 GreatNotEqual(lastOutterPoint.GetY(), mousePoint.GetY())) {
1578                 return true;
1579             }
1580         } else {
1581             if (LessNotEqual(lastOutterPoint.GetX(), mousePoint.GetX()) &&
1582                 LessNotEqual(lastOutterPoint.GetY(), mousePoint.GetY())) {
1583                 return true;
1584             }
1585         }
1586     }
1587     return false;
1588 }
1589 
NeedStartHideMenuTask(const PointF & mousePoint,const RectF & menuZone)1590 bool MenuItemPattern::NeedStartHideMenuTask(const PointF& mousePoint, const RectF& menuZone)
1591 {
1592     if (!lastInnerPosition_.has_value()) {
1593         return false;
1594     }
1595     auto lastInnerPoint = lastInnerPosition_.value();
1596     leaveFromBottom_ = GreatNotEqual(mousePoint.GetY(), lastInnerPoint.GetY());
1597     if (LessNotEqual(lastInnerPoint.GetX(), menuZone.Left())) {
1598         return NeedStartHideRightSubMenuTask(lastInnerPoint, mousePoint, menuZone);
1599     } else if (GreatNotEqual(lastInnerPoint.GetX(), menuZone.Right())) {
1600         return NeedStartHideLeftSubMenuTask(lastInnerPoint, mousePoint, menuZone);
1601     } else {
1602         return false;
1603     }
1604 }
1605 
NeedStartHideRightSubMenuTask(const PointF & lastInnerPoint,const PointF & mousePoint,const RectF & menuZone)1606 bool MenuItemPattern::NeedStartHideRightSubMenuTask(
1607     const PointF& lastInnerPoint, const PointF& mousePoint, const RectF& menuZone)
1608 {
1609     if (leaveFromBottom_) {
1610         if (NearEqual(menuZone.Bottom(), lastInnerPoint.GetY()) ||
1611             NearEqual(mousePoint.GetY(), lastInnerPoint.GetY())) {
1612             return false;
1613         }
1614         auto compareRatio = (menuZone.Left() - lastInnerPoint.GetX()) / (menuZone.Bottom() - lastInnerPoint.GetY());
1615         auto currentRadio = (mousePoint.GetX() - lastInnerPoint.GetX()) / (mousePoint.GetY() - lastInnerPoint.GetY());
1616         if (GreatNotEqual(compareRatio, currentRadio)) {
1617             return false;
1618         }
1619     } else {
1620         if (NearEqual(menuZone.Top(), lastInnerPoint.GetY()) || NearEqual(mousePoint.GetY(), lastInnerPoint.GetY())) {
1621             return false;
1622         }
1623         auto compareRatio = (menuZone.Left() - lastInnerPoint.GetX()) / (lastInnerPoint.GetY() - menuZone.Top());
1624         auto currentRadio = (mousePoint.GetX() - lastInnerPoint.GetX()) / (lastInnerPoint.GetY() - mousePoint.GetY());
1625         if (GreatNotEqual(compareRatio, currentRadio)) {
1626             return false;
1627         }
1628     }
1629     return true;
1630 }
1631 
NeedStartHideLeftSubMenuTask(const PointF & lastInnerPoint,const PointF & mousePoint,const RectF & menuZone)1632 bool MenuItemPattern::NeedStartHideLeftSubMenuTask(
1633     const PointF& lastInnerPoint, const PointF& mousePoint, const RectF& menuZone)
1634 {
1635     if (leaveFromBottom_) {
1636         if (NearEqual(menuZone.Bottom(), lastInnerPoint.GetY()) ||
1637             NearEqual(mousePoint.GetY(), lastInnerPoint.GetY())) {
1638             return false;
1639         }
1640         auto compareRatio = (lastInnerPoint.GetX() - menuZone.Right()) / (menuZone.Bottom() - lastInnerPoint.GetY());
1641         auto currentRadio = (lastInnerPoint.GetX() - mousePoint.GetX()) / (mousePoint.GetY() - lastInnerPoint.GetY());
1642         if (GreatNotEqual(compareRatio, currentRadio)) {
1643             return false;
1644         }
1645     } else {
1646         if (NearEqual(menuZone.Top(), lastInnerPoint.GetY()) || NearEqual(mousePoint.GetY(), lastInnerPoint.GetY())) {
1647             return false;
1648         }
1649         auto compareRatio = (lastInnerPoint.GetX() - menuZone.Right()) / (lastInnerPoint.GetY() - menuZone.Top());
1650         auto currentRadio = (lastInnerPoint.GetX() - mousePoint.GetX()) / (lastInnerPoint.GetY() - mousePoint.GetY());
1651         if (GreatNotEqual(compareRatio, currentRadio)) {
1652             return false;
1653         }
1654     }
1655     return true;
1656 }
1657 
PerformHideSubMenu(std::function<void ()> callback)1658 void MenuItemPattern::PerformHideSubMenu(std::function<void()> callback)
1659 {
1660     if (callback) {
1661         callback();
1662     }
1663     OnHover(false);
1664     SetIsSubMenuShowed(false);
1665     ClearHoverRegions();
1666     ResetWrapperMouseEvent();
1667     if (hideTask_) {
1668         hideTask_.Cancel();
1669     }
1670     lastOutterPosition_.reset();
1671     lastInnerPosition_.reset();
1672     leaveFromBottom_ = false;
1673 }
1674 
CancelHideSubMenuTask(const PointF & mousePoint)1675 void MenuItemPattern::CancelHideSubMenuTask(const PointF& mousePoint)
1676 {
1677     if (hideTask_) {
1678         hideTask_.Cancel();
1679     }
1680     lastInnerPosition_ = mousePoint;
1681 }
1682 
OnVisibleChange(bool isVisible)1683 void MenuItemPattern::OnVisibleChange(bool isVisible)
1684 {
1685     auto host = GetHost();
1686     CHECK_NULL_VOID(host);
1687     auto parentNode = host->GetParent();
1688     CHECK_NULL_VOID(parentNode);
1689     if (parentNode->GetTag() == V2::MENU_ITEM_GROUP_ETS_TAG) {
1690         parentNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1691     }
1692 }
1693 
OnKeyEvent(const KeyEvent & event)1694 bool MenuItemPattern::OnKeyEvent(const KeyEvent& event)
1695 {
1696     if (event.action != KeyAction::DOWN) {
1697         return false;
1698     }
1699     auto host = GetHost();
1700     CHECK_NULL_RETURN(host, false);
1701     auto focusHub = host->GetOrCreateFocusHub();
1702     CHECK_NULL_RETURN(focusHub, false);
1703     if (event.code == KeyCode::KEY_ENTER) {
1704         isOptionPattern_ ? OnSelectProcess() : focusHub->OnClick(event);
1705         return true;
1706     }
1707     if (!isOptionPattern_ && event.code == KeyCode::KEY_DPAD_RIGHT && GetSubBuilder() && !isSubMenuShowed_) {
1708         auto theme = GetCurrentSelectTheme();
1709         CHECK_NULL_RETURN(theme, false);
1710         SetBgBlendColor(theme->GetHoverColor());
1711         PlayBgColorAnimation();
1712         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1713         ShowSubMenu(ShowSubMenuType::KEY_DPAD_RIGHT);
1714         return true;
1715     }
1716     if (isOptionPattern_
1717         && (event.code == KeyCode::KEY_MOVE_HOME || event.code == KeyCode::KEY_MOVE_END) && IsSelectOption()) {
1718         return UpdateOptionFocus(event.code);
1719     }
1720     return false;
1721 }
1722 
OnKeyEvent(const KeyEvent & event)1723 bool CustomMenuItemPattern::OnKeyEvent(const KeyEvent& event)
1724 {
1725     if (event.action != KeyAction::DOWN) {
1726         return false;
1727     }
1728     auto host = GetHost();
1729     CHECK_NULL_RETURN(host, false);
1730     auto focusHub = host->GetOrCreateFocusHub();
1731     CHECK_NULL_RETURN(focusHub, false);
1732     if (event.code == KeyCode::KEY_ENTER || event.code == KeyCode::KEY_SPACE) {
1733         focusHub->OnClick(event);
1734         CloseMenu();
1735         return true;
1736     }
1737     return false;
1738 }
1739 
InitLongPressEvent()1740 void MenuItemPattern::InitLongPressEvent()
1741 {
1742     auto gesture = GetHost()->GetOrCreateGestureEventHub();
1743     CHECK_NULL_VOID(gesture);
1744     auto longPressCallback = [weak = WeakClaim(this)](GestureEvent& info) {
1745         auto itemPattern = weak.Upgrade();
1746         auto menuWrapper = itemPattern->GetMenuWrapper();
1747         CHECK_NULL_VOID(menuWrapper);
1748         auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
1749         CHECK_NULL_VOID(menuWrapperPattern);
1750         auto topLevelMenuPattern = itemPattern->GetMenuPattern(true);
1751         CHECK_NULL_VOID(topLevelMenuPattern);
1752         if (itemPattern && itemPattern->GetSubBuilder() != nullptr &&
1753             menuWrapperPattern->GetPreviewMode() == MenuPreviewMode::NONE &&
1754             !(topLevelMenuPattern->IsSelectOverlayCustomMenu())) {
1755             itemPattern->ShowSubMenu(ShowSubMenuType::LONG_PRESS);
1756         }
1757     };
1758     longPressEvent_ = MakeRefPtr<LongPressEvent>(std::move(longPressCallback));
1759     gesture->SetLongPressEvent(longPressEvent_);
1760 }
1761 
RegisterWrapperMouseEvent()1762 void MenuItemPattern::RegisterWrapperMouseEvent()
1763 {
1764     auto menuWrapper = GetMenuWrapper();
1765     if (menuWrapper && !wrapperMouseEvent_) {
1766         auto inputHub = menuWrapper->GetOrCreateInputEventHub();
1767         CHECK_NULL_VOID(inputHub);
1768         auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
1769         CHECK_NULL_VOID(menuWrapperPattern);
1770 
1771         auto mouseTask = [weak = WeakClaim(this), menuWrapperPattern](MouseInfo& info) {
1772             auto pattern = weak.Upgrade();
1773             CHECK_NULL_VOID(pattern);
1774             if (menuWrapperPattern) {
1775                 menuWrapperPattern->HandleMouseEvent(info, pattern);
1776             }
1777         };
1778         wrapperMouseEvent_ = MakeRefPtr<InputEvent>(std::move(mouseTask));
1779         inputHub->AddOnMouseEvent(wrapperMouseEvent_);
1780     }
1781 }
1782 
AddSelfHoverRegion(const RefPtr<FrameNode> & targetNode)1783 void MenuItemPattern::AddSelfHoverRegion(const RefPtr<FrameNode>& targetNode)
1784 {
1785     OffsetF topLeftPoint;
1786     OffsetF bottomRightPoint;
1787     auto frameSize = targetNode->GetGeometryNode()->GetMarginFrameSize();
1788     topLeftPoint = targetNode->GetPaintRectOffset(false, true);
1789     bottomRightPoint = targetNode->GetPaintRectOffset(false, true) + OffsetF(frameSize.Width(), frameSize.Height());
1790     AddHoverRegions(topLeftPoint, bottomRightPoint);
1791 }
1792 
GetSubMenuPosition(const RefPtr<FrameNode> & targetNode)1793 OffsetF MenuItemPattern::GetSubMenuPosition(const RefPtr<FrameNode>& targetNode)
1794 { // show menu at left top point of targetNode
1795     auto frameSize = targetNode->GetGeometryNode()->GetMarginFrameSize();
1796     OffsetF position = targetNode->GetPaintRectOffset(false, true) + OffsetF(frameSize.Width(), 0.0);
1797     return position;
1798 }
1799 
AddHoverRegions(const OffsetF & topLeftPoint,const OffsetF & bottomRightPoint)1800 void MenuItemPattern::AddHoverRegions(const OffsetF& topLeftPoint, const OffsetF& bottomRightPoint)
1801 {
1802     TouchRegion hoverRegion = TouchRegion(
1803         Offset(topLeftPoint.GetX(), topLeftPoint.GetY()), Offset(bottomRightPoint.GetX(), bottomRightPoint.GetY()));
1804     hoverRegions_.emplace_back(hoverRegion);
1805 }
1806 
IsInHoverRegions(double x,double y)1807 bool MenuItemPattern::IsInHoverRegions(double x, double y)
1808 {
1809     for (auto hoverRegion : hoverRegions_) {
1810         if (hoverRegion.ContainsInRegion(x, y)) {
1811             return true;
1812         }
1813     }
1814     return false;
1815 }
1816 
PlayBgColorAnimation(bool isHoverChange)1817 void MenuItemPattern::PlayBgColorAnimation(bool isHoverChange)
1818 {
1819     auto host = GetHost();
1820     CHECK_NULL_VOID(host);
1821     auto theme = GetCurrentSelectTheme();
1822     CHECK_NULL_VOID(theme);
1823     AnimationOption option;
1824     if (isHoverChange) {
1825         option.SetDuration(theme->GetHoverAnimationDuration());
1826         option.SetCurve(Curves::FRICTION);
1827     } else {
1828         option.SetDuration(theme->GetPressAnimationDuration());
1829         option.SetCurve(Curves::SHARP);
1830     }
1831 
1832     AnimationUtils::Animate(option, [weak = WeakClaim(this)]() {
1833         auto pattern = weak.Upgrade();
1834         CHECK_NULL_VOID(pattern);
1835         if (pattern->IsOptionPattern()) {
1836             auto host = pattern->GetHost();
1837             CHECK_NULL_VOID(host);
1838             auto renderContext = host->GetRenderContext();
1839             CHECK_NULL_VOID(renderContext);
1840             renderContext->BlendBgColor(pattern->GetBgBlendColor());
1841         } else {
1842             auto clickableArea = pattern->GetClickableArea();
1843             CHECK_NULL_VOID(clickableArea);
1844             auto renderContext = clickableArea->GetRenderContext();
1845             CHECK_NULL_VOID(renderContext);
1846             renderContext->BlendBgColor(pattern->GetBgBlendColor());
1847         }
1848     }, nullptr, nullptr, host->GetContextRefPtr());
1849 }
1850 
UpdateImageNode(RefPtr<FrameNode> & row,RefPtr<FrameNode> & selectIcon)1851 void MenuItemPattern::UpdateImageNode(RefPtr<FrameNode>& row, RefPtr<FrameNode>& selectIcon)
1852 {
1853     auto pipeline = GetContext();
1854     CHECK_NULL_VOID(pipeline);
1855     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1856     CHECK_NULL_VOID(itemProperty);
1857     auto symbol = itemProperty->GetSelectSymbol();
1858     if (itemProperty->GetSelectIconSrc().value_or("").empty() &&
1859         Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) && SystemProperties::IsNeedSymbol()) {
1860         // iamge -> symbol
1861         row->RemoveChild(selectIcon);
1862         selectIcon = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG,
1863             ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<TextPattern>(); });
1864         auto selectTheme = pipeline->GetTheme<SelectTheme>();
1865         CHECK_NULL_VOID(selectTheme);
1866         auto props = selectIcon->GetLayoutProperty<TextLayoutProperty>();
1867         CHECK_NULL_VOID(props);
1868         props->UpdateFontSize(selectTheme->GetEndIconWidth());
1869         props->UpdateSymbolColorList({ selectTheme->GetMenuIconColor() });
1870         symbol(AccessibilityManager::WeakClaim(AccessibilityManager::RawPtr(selectIcon)));
1871     } else {
1872         // image -> image
1873         auto iconTheme = pipeline->GetTheme<IconTheme>();
1874         CHECK_NULL_VOID(iconTheme);
1875         auto userIcon = itemProperty->GetSelectIconSrc().value_or("");
1876         auto iconPath = userIcon.empty() ? iconTheme->GetIconPath(InternalResource::ResourceId::MENU_OK_SVG) : userIcon;
1877         auto selectTheme = pipeline->GetTheme<SelectTheme>();
1878         CHECK_NULL_VOID(selectTheme);
1879         ImageSourceInfo imageSourceInfo;
1880         imageSourceInfo.SetSrc(iconPath);
1881         auto props = selectIcon->GetLayoutProperty<ImageLayoutProperty>();
1882         CHECK_NULL_VOID(props);
1883         props->UpdateImageSourceInfo(imageSourceInfo);
1884         Ace::NG::UpdateIconSrc(selectIcon, selectTheme->GetIconSideLength(), selectTheme->GetIconSideLength(),
1885             selectTheme->GetMenuIconColor(), userIcon.empty());
1886     }
1887 }
1888 
UpdateSymbolNode(RefPtr<FrameNode> & row,RefPtr<FrameNode> & selectIcon)1889 void MenuItemPattern::UpdateSymbolNode(RefPtr<FrameNode>& row, RefPtr<FrameNode>& selectIcon)
1890 {
1891     auto pipeline = GetContext();
1892     CHECK_NULL_VOID(pipeline);
1893     auto props = selectIcon->GetLayoutProperty<TextLayoutProperty>();
1894     CHECK_NULL_VOID(props);
1895     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1896     CHECK_NULL_VOID(itemProperty);
1897     auto selectTheme = pipeline->GetTheme<SelectTheme>();
1898     CHECK_NULL_VOID(selectTheme);
1899     auto symbol = itemProperty->GetSelectSymbol();
1900     if (itemProperty->GetSelectIconSrc().value_or("").empty()) {
1901         // symbol -> symbol
1902         props->UpdateFontSize(selectTheme->GetEndIconWidth());
1903         props->UpdateSymbolColorList({ selectTheme->GetMenuIconColor() });
1904         if (symbol) {
1905             symbol(AccessibilityManager::WeakClaim(AccessibilityManager::RawPtr(selectIcon)));
1906         } else {
1907             auto menuTheme = pipeline->GetTheme<MenuTheme>();
1908             CHECK_NULL_VOID(menuTheme);
1909             uint32_t symbolId = menuTheme->GetSymbolId();
1910             props->UpdateSymbolSourceInfo(SymbolSourceInfo(symbolId));
1911         }
1912     } else {
1913         // symbol -> image
1914         row->RemoveChild(selectIcon);
1915         selectIcon = FrameNode::CreateFrameNode(
1916             V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
1917         ImageSourceInfo imageSourceInfo;
1918         auto userIcon = itemProperty->GetSelectIconSrc().value_or("");
1919         imageSourceInfo.SetSrc(userIcon);
1920         auto props = selectIcon->GetLayoutProperty<ImageLayoutProperty>();
1921         CHECK_NULL_VOID(props);
1922         props->UpdateImageSourceInfo(imageSourceInfo);
1923         Ace::NG::UpdateIconSrc(selectIcon, selectTheme->GetIconSideLength(), selectTheme->GetIconSideLength(),
1924             selectTheme->GetMenuIconColor(), userIcon.empty());
1925     }
1926 }
1927 
AddSelectIcon(RefPtr<FrameNode> & row)1928 void MenuItemPattern::AddSelectIcon(RefPtr<FrameNode>& row)
1929 {
1930     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1931     CHECK_NULL_VOID(itemProperty);
1932     if (!itemProperty->GetSelectIcon().value_or(false)) {
1933         if (selectIcon_) {
1934             row->RemoveChildAtIndex(0);
1935             selectIcon_ = nullptr;
1936             itemProperty->SetSelectSymbol(nullptr);
1937             row->MarkModifyDone();
1938             row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1939         }
1940         return;
1941     }
1942     if (!selectIcon_) {
1943         if (!itemProperty->GetSelectIconSrc().value_or("").empty() ||
1944             Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE) || !SystemProperties::IsNeedSymbol()) {
1945             selectIcon_ = FrameNode::CreateFrameNode(
1946                 V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
1947         } else {
1948             selectIcon_ = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG,
1949                 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<TextPattern>(); });
1950         }
1951     }
1952     if (selectIcon_->GetTag() == V2::IMAGE_ETS_TAG) {
1953         UpdateImageNode(row, selectIcon_);
1954     } else {
1955         UpdateSymbolNode(row, selectIcon_);
1956     }
1957     auto renderContext = selectIcon_->GetRenderContext();
1958     CHECK_NULL_VOID(renderContext);
1959     renderContext->SetVisible(isSelected_);
1960 
1961     selectIcon_->MountToParent(row, 0);
1962     selectIcon_->MarkModifyDone();
1963     selectIcon_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1964 }
1965 
AddExpandIcon(RefPtr<FrameNode> & row)1966 void MenuItemPattern::AddExpandIcon(RefPtr<FrameNode>& row)
1967 {
1968     CHECK_EQUAL_VOID(ISNeedAddExpandIcon(row), false);
1969     if (!expandIcon_) {
1970         expandIcon_ = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG,
1971             ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<TextPattern>(); });
1972         CHECK_NULL_VOID(expandIcon_);
1973     }
1974     auto menuNode = GetMenu();
1975     auto menuProperty = menuNode ? menuNode->GetLayoutProperty<MenuLayoutProperty>() : nullptr;
1976     CHECK_NULL_VOID(menuProperty);
1977     auto symbol = menuProperty->GetExpandSymbol();
1978     if (symbol) {
1979         symbol(AccessibilityManager::WeakClaim(AccessibilityManager::RawPtr(expandIcon_)));
1980     } else {
1981         auto host = GetHost();
1982         CHECK_NULL_VOID(host);
1983         auto pipeline = host->GetContext();
1984         CHECK_NULL_VOID(pipeline);
1985         auto selectTheme = pipeline->GetTheme<SelectTheme>();
1986         CHECK_NULL_VOID(selectTheme);
1987         auto menuTheme = pipeline->GetTheme<MenuTheme>();
1988         CHECK_NULL_VOID(menuTheme);
1989         auto props = expandIcon_->GetLayoutProperty<TextLayoutProperty>();
1990         CHECK_NULL_VOID(props);
1991         uint32_t symbolId = 0;
1992         if (expandingMode_ == SubMenuExpandingMode::STACK) {
1993             symbolId = menuTheme->GetStackExpandIconId();
1994             PaddingProperty padding;
1995             padding.SetEdges(CalcLength(STACK_EXPAND_ICON_PADDING));
1996             props->UpdatePadding(padding);
1997             props->UpdateFontSize(
1998                 selectTheme->GetIconSideLength() - STACK_EXPAND_ICON_PADDING - STACK_EXPAND_ICON_PADDING);
1999         } else {
2000             symbolId = menuTheme->GetEmbeddedExpandIconId();
2001             props->UpdateFontSize(selectTheme->GetIconSideLength());
2002         }
2003         props->UpdateSymbolColorList({ selectTheme->GetMenuIconColor() });
2004         props->UpdateSymbolSourceInfo(SymbolSourceInfo(symbolId));
2005     }
2006     auto expandIconIndex = row->GetChildren().size();
2007     expandIcon_->MountToParent(row, expandIconIndex);
2008     expandIcon_->MarkModifyDone();
2009     expandIcon_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2010 }
2011 
ISNeedAddExpandIcon(RefPtr<FrameNode> & row)2012 bool MenuItemPattern::ISNeedAddExpandIcon(RefPtr<FrameNode>& row)
2013 {
2014     auto host = GetHost();
2015     CHECK_NULL_RETURN(host, false);
2016     auto menuNode = GetMenu();
2017     CHECK_NULL_RETURN(menuNode, false);
2018     auto menuPattern = menuNode->GetPattern<MenuPattern>();
2019     CHECK_NULL_RETURN(menuPattern, false);
2020     auto menuProperty = menuNode->GetLayoutProperty<MenuLayoutProperty>();
2021     CHECK_NULL_RETURN(menuProperty, false);
2022     auto pattern = host->GetPattern<MenuItemPattern>();
2023     auto isStackSubmenuHeader = pattern ? pattern->IsStackSubmenuHeader() : false;
2024     if (isStackSubmenuHeader) {
2025         return true;
2026     } else {
2027         auto canExpand =
2028             GetSubBuilder() != nullptr && menuPattern && !menuPattern->IsEmbedded() && !menuPattern->IsStackSubmenu() &&
2029             (expandingMode_ == SubMenuExpandingMode::EMBEDDED || expandingMode_ == SubMenuExpandingMode::STACK);
2030         if (canExpand) {
2031             return true;
2032         } else {
2033             if (expandIcon_) {
2034                 row->RemoveChild(expandIcon_);
2035                 expandIcon_ = nullptr;
2036                 row->MarkModifyDone();
2037                 row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2038             }
2039             return false;
2040         }
2041     }
2042 }
2043 
AddClickableArea()2044 void MenuItemPattern::AddClickableArea()
2045 {
2046     if (expandingMode_ == SubMenuExpandingMode::EMBEDDED && GetSubBuilder() != nullptr && !IsEmbedded() &&
2047         !clickableArea_) {
2048         auto host = GetHost();
2049         CHECK_NULL_VOID(host);
2050         auto hostAccessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
2051         CHECK_NULL_VOID(hostAccessibilityProperty);
2052         hostAccessibilityProperty->SetAccessibilityLevel(AccessibilityProperty::Level::NO_STR);
2053         auto clickableArea = FrameNode::CreateFrameNode(V2::ROW_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
2054             AceType::MakeRefPtr<LinearLayoutPattern>(false));
2055         CHECK_NULL_VOID(clickableArea);
2056         auto theme = GetCurrentSelectTheme();
2057         CHECK_NULL_VOID(theme);
2058         BorderRadiusProperty border;
2059         if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
2060             border.SetRadius(theme->GetMenuDefaultInnerRadius());
2061         } else {
2062             border.SetRadius(theme->GetInnerBorderRadius());
2063         }
2064         auto clickableContext = clickableArea->GetRenderContext();
2065         CHECK_NULL_VOID(clickableContext);
2066         clickableContext->UpdateBorderRadius(border);
2067         auto menuProperty = host->GetLayoutProperty<MenuItemLayoutProperty>();
2068         CHECK_NULL_VOID(menuProperty);
2069         std::string content = menuProperty->GetContent().value_or("");
2070         std::string label = menuProperty->GetLabel().value_or("");
2071         auto accessibilityProperty = clickableArea->GetAccessibilityProperty<AccessibilityProperty>();
2072         CHECK_NULL_VOID(accessibilityProperty);
2073         accessibilityProperty->SetAccessibilityText(content + "," + label);
2074 #if defined(OHOS_STANDARD_SYSTEM) and !defined(ACE_UNITTEST)
2075         accessibilityProperty->SetAccessibilityCustomRole(MENU_ITEM);
2076         accessibilityProperty->SetRelatedElementInfoCallback(
2077             [weak = WeakClaim(this)] (Accessibility::ExtraElementInfo& extraInfo) {
2078                 auto pattern = weak.Upgrade();
2079                 CHECK_NULL_VOID(pattern);
2080                 std::string expandedState = pattern->IsExpanded() ? MENU_STATE_EXPANDED : MENU_STATE_COLLAPSED;
2081                 extraInfo.SetExtraElementInfo(MENU_EXPANDED_STATE, expandedState);
2082                 TAG_LOGI(AceLogTag::ACE_MENU, "Get embeeded menu expanded stated: %{public}s.", expandedState.c_str());
2083         });
2084 #endif
2085         clickableArea_ = clickableArea;
2086         clickableArea_->MountToParent(host, CLICKABLE_AREA_VIEW_INDEX);
2087 
2088         SetRowAccessibilityLevel();
2089     }
2090 }
2091 
SetRowAccessibilityLevel()2092 void MenuItemPattern::SetRowAccessibilityLevel()
2093 {
2094     auto host = GetHost();
2095     CHECK_NULL_VOID(host);
2096     RefPtr<FrameNode> leftRow =
2097         host->GetChildAtIndex(0) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(0)) : nullptr;
2098     if (leftRow) {
2099         auto nodeAccessibilityProps = leftRow->GetAccessibilityProperty<AccessibilityProperty>();
2100         CHECK_NULL_VOID(nodeAccessibilityProps);
2101         nodeAccessibilityProps->SetAccessibilityLevel(AccessibilityProperty::Level::NO_STR);
2102         nodeAccessibilityProps->SetAccessibilityGroup(true);
2103     }
2104     RefPtr<FrameNode> rightRow =
2105         host->GetChildAtIndex(1) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(1)) : nullptr;
2106     if (rightRow) {
2107         auto nodeAccessibilityProps = rightRow->GetAccessibilityProperty<AccessibilityProperty>();
2108         CHECK_NULL_VOID(nodeAccessibilityProps);
2109         nodeAccessibilityProps->SetAccessibilityLevel(AccessibilityProperty::Level::NO_STR);
2110         nodeAccessibilityProps->SetAccessibilityGroup(true);
2111     }
2112 }
2113 
AddStackSubMenuHeader(RefPtr<FrameNode> & menuNode)2114 void MenuItemPattern::AddStackSubMenuHeader(RefPtr<FrameNode>& menuNode)
2115 {
2116     auto host = GetHost();
2117     CHECK_NULL_VOID(host);
2118     auto layoutProperty = GetLayoutProperty<MenuItemLayoutProperty>();
2119     CHECK_NULL_VOID(layoutProperty);
2120     auto content = layoutProperty->GetContent().value_or(layoutProperty->GetLabel().value_or(""));
2121     MenuItemProperties menuItemProps;
2122     menuItemProps.content = content;
2123     auto menu = GetMenu();
2124     CHECK_NULL_VOID(menu);
2125     auto menuProperty = menu->GetLayoutProperty<MenuLayoutProperty>();
2126     CHECK_NULL_VOID(menuProperty);
2127     auto symbol = menuProperty->GetExpandSymbol();
2128     if (symbol) {
2129         auto menuNodeProperty = menuNode ? menuNode->GetLayoutProperty<MenuLayoutProperty>() : nullptr;
2130         if (menuNodeProperty) {
2131             menuNodeProperty->SetExpandSymbol(symbol);
2132         }
2133     }
2134     MenuItemModelNG menuItemModel;
2135     menuItemModel.Create(menuItemProps);
2136     auto stack = ViewStackProcessor::GetInstance();
2137 
2138     auto titleItem = AceType::DynamicCast<FrameNode>(stack->Finish());
2139     auto pattern = titleItem->GetPattern<MenuItemPattern>();
2140     CHECK_NULL_VOID(pattern);
2141     pattern->SetIsStackSubmenuHeader();
2142     titleItem->MountToParent(menuNode, 0);
2143 }
2144 
GetClickableArea()2145 RefPtr<FrameNode> MenuItemPattern::GetClickableArea()
2146 {
2147     auto host = GetHost();
2148     CHECK_NULL_RETURN(host, nullptr);
2149     auto clickableArea = host->GetChildAtIndex(CLICKABLE_AREA_VIEW_INDEX);
2150     CHECK_NULL_RETURN(clickableArea, host);
2151     auto clickableAreaNode = AceType::DynamicCast<FrameNode>(clickableArea);
2152     CHECK_NULL_RETURN(clickableAreaNode, host);
2153     return clickableAreaNode;
2154 }
2155 
UpdateIcon(RefPtr<FrameNode> & row,bool isStart)2156 void MenuItemPattern::UpdateIcon(RefPtr<FrameNode>& row, bool isStart)
2157 {
2158     auto selectTheme = GetCurrentSelectTheme();
2159     CHECK_NULL_VOID(selectTheme);
2160     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
2161     CHECK_NULL_VOID(itemProperty);
2162     ImageSourceInfo defaultImageSourceInfo;
2163     auto iconSrc = isStart ? itemProperty->GetStartIcon().value_or(defaultImageSourceInfo)
2164                            : itemProperty->GetEndIcon().value_or(defaultImageSourceInfo);
2165     auto symbol = isStart ? itemProperty->GetStartSymbol() : itemProperty->GetEndSymbol();
2166     auto& iconNode = isStart ? startIcon_ : endIcon_;
2167     if (iconSrc.GetSrc().empty() && symbol == nullptr) {
2168         row->RemoveChild(iconNode); // it's safe even if iconNode is nullptr
2169         iconNode = nullptr;
2170         if (isStart) {
2171             itemProperty->SetStartSymbol(nullptr);
2172         } else {
2173             itemProperty->SetEndSymbol(nullptr);
2174         }
2175         row->MarkModifyDone();
2176         row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2177         return;
2178     }
2179     if (!iconNode) {
2180         if (symbol) {
2181             iconNode = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG,
2182                 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<TextPattern>(); });
2183         } else {
2184             iconNode = FrameNode::CreateFrameNode(
2185                 V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
2186         }
2187         CHECK_NULL_VOID(iconNode);
2188     }
2189     if (iconNode->GetTag() == V2::IMAGE_ETS_TAG) {
2190         UpdateImageIcon(row, iconNode, iconSrc, symbol, isStart);
2191     } else {
2192         UpdateSymbolIcon(row, iconNode, iconSrc, symbol, isStart);
2193     }
2194     iconNode->MountToParent(row, ((isStart && selectIcon_) || (!isStart && label_)) ? 1 : 0);
2195     iconNode->MarkModifyDone();
2196     iconNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2197 }
2198 
UpdateImageIcon(RefPtr<FrameNode> & row,RefPtr<FrameNode> & iconNode,ImageSourceInfo & iconSrc,std::function<void (WeakPtr<NG::FrameNode>)> & symbol,bool isStart)2199 void MenuItemPattern::UpdateImageIcon(RefPtr<FrameNode>& row, RefPtr<FrameNode>& iconNode, ImageSourceInfo& iconSrc,
2200     std::function<void(WeakPtr<NG::FrameNode>)>& symbol, bool isStart)
2201 {
2202     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
2203     CHECK_NULL_VOID(itemProperty);
2204     auto selectTheme = GetCurrentSelectTheme();
2205     CHECK_NULL_VOID(selectTheme);
2206     if (symbol) {
2207         // iamge -> symbol
2208         row->RemoveChild(iconNode);
2209         iconNode = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
2210             []() { return AceType::MakeRefPtr<TextPattern>(); });
2211 
2212         auto props = iconNode->GetLayoutProperty<TextLayoutProperty>();
2213         CHECK_NULL_VOID(props);
2214         props->UpdateFontSize(selectTheme->GetEndIconWidth());
2215         props->UpdateSymbolColorList({ selectTheme->GetMenuIconColor() });
2216         symbol(AccessibilityManager::WeakClaim(AccessibilityManager::RawPtr(iconNode)));
2217     } else {
2218         // image -> image
2219         auto iconWidth = isStart ? selectTheme->GetIconSideLength() : selectTheme->GetEndIconWidth();
2220         auto iconHeight = isStart ? selectTheme->GetIconSideLength() : selectTheme->GetEndIconHeight();
2221         ImageSourceInfo imageSourceInfo(iconSrc);
2222         auto props = iconNode->GetLayoutProperty<ImageLayoutProperty>();
2223         CHECK_NULL_VOID(props);
2224         props->UpdateImageSourceInfo(imageSourceInfo);
2225         bool useDefaultThemeIcon = UseDefaultThemeIcon(imageSourceInfo);
2226         Ace::NG::UpdateIconSrc(iconNode, iconWidth, iconHeight, selectTheme->GetMenuIconColor(), useDefaultThemeIcon);
2227     }
2228 }
2229 
UseDefaultThemeIcon(const ImageSourceInfo & imageSourceInfo)2230 bool MenuItemPattern::UseDefaultThemeIcon(const ImageSourceInfo& imageSourceInfo)
2231 {
2232     if (imageSourceInfo.IsSvg()) {
2233         auto src = imageSourceInfo.GetSrc();
2234         auto srcId = src.substr(SYSTEM_RESOURCE_PREFIX.size(),
2235             src.substr(0, src.rfind(".svg")).size() - SYSTEM_RESOURCE_PREFIX.size());
2236         if ((srcId.find("ohos_") != std::string::npos) || (srcId.find("public_") != std::string::npos)) {
2237             return true;
2238         }
2239         uint64_t parsedSrcId = StringUtils::StringToLongUint(srcId);
2240         return (parsedSrcId != 0
2241             && (parsedSrcId >= MIN_SYSTEM_RESOURCE_ID)
2242             && (parsedSrcId <= MAX_SYSTEM_RESOURCE_ID));
2243     }
2244     return false;
2245 }
2246 
UpdateSymbolIcon(RefPtr<FrameNode> & row,RefPtr<FrameNode> & iconNode,ImageSourceInfo & iconSrc,std::function<void (WeakPtr<NG::FrameNode>)> & symbol,bool isStart)2247 void MenuItemPattern::UpdateSymbolIcon(RefPtr<FrameNode>& row, RefPtr<FrameNode>& iconNode, ImageSourceInfo& iconSrc,
2248     std::function<void(WeakPtr<NG::FrameNode>)>& symbol, bool isStart)
2249 {
2250     auto props = iconNode->GetLayoutProperty<TextLayoutProperty>();
2251     CHECK_NULL_VOID(props);
2252     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
2253     CHECK_NULL_VOID(itemProperty);
2254     auto selectTheme = GetCurrentSelectTheme();
2255     CHECK_NULL_VOID(selectTheme);
2256     if (symbol) {
2257         // symbol -> symbol
2258         props->UpdateFontSize(selectTheme->GetEndIconWidth());
2259         props->UpdateSymbolColorList({ selectTheme->GetMenuIconColor() });
2260         symbol(AccessibilityManager::WeakClaim(AccessibilityManager::RawPtr(iconNode)));
2261     } else {
2262         // symbol -> image
2263         row->RemoveChild(iconNode);
2264         iconNode = FrameNode::CreateFrameNode(
2265             V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
2266         auto iconWidth = isStart ? selectTheme->GetIconSideLength() : selectTheme->GetEndIconWidth();
2267         auto iconHeight = isStart ? selectTheme->GetIconSideLength() : selectTheme->GetEndIconHeight();
2268         ImageSourceInfo imageSourceInfo(iconSrc);
2269         auto props = iconNode->GetLayoutProperty<ImageLayoutProperty>();
2270         CHECK_NULL_VOID(props);
2271         props->UpdateImageSourceInfo(imageSourceInfo);
2272         Ace::NG::UpdateIconSrc(iconNode, iconWidth, iconHeight, selectTheme->GetMenuIconColor(), false);
2273     }
2274 }
2275 
UpdateText(RefPtr<FrameNode> & row,RefPtr<MenuLayoutProperty> & menuProperty,bool isLabel)2276 void MenuItemPattern::UpdateText(RefPtr<FrameNode>& row, RefPtr<MenuLayoutProperty>& menuProperty, bool isLabel)
2277 {
2278     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
2279     CHECK_NULL_VOID(itemProperty);
2280     auto content = isLabel ? itemProperty->GetLabel().value_or("") : itemProperty->GetContent().value_or("");
2281     auto& node = isLabel ? label_ : content_;
2282     if (content.empty()) {
2283         (void)row->RemoveChild(node); // it's safe even if node is nullptr
2284         node = nullptr;
2285         row->MarkModifyDone();
2286         row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2287         return;
2288     }
2289 
2290     if (!node) {
2291         node = FrameNode::CreateFrameNode(
2292             V2::TEXT_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<TextPattern>());
2293     }
2294     CHECK_NULL_VOID(node);
2295     auto textProperty = node->GetLayoutProperty<TextLayoutProperty>();
2296     CHECK_NULL_VOID(textProperty);
2297     auto renderContext = node->GetRenderContext();
2298     CHECK_NULL_VOID(renderContext);
2299     renderContext->UpdateClipEdge(isTextFadeOut_);
2300     auto theme = GetCurrentSelectTheme();
2301     CHECK_NULL_VOID(theme);
2302     auto layoutDirection = textProperty->GetNonAutoLayoutDirection();
2303     TextAlign textAlign = static_cast<TextAlign>(theme->GetMenuItemContentAlign());
2304     if (layoutDirection == TextDirection::RTL) {
2305         if (textAlign == TextAlign::LEFT) {
2306             textAlign = TextAlign::RIGHT;
2307         } else if (textAlign == TextAlign::RIGHT) {
2308             textAlign = TextAlign::LEFT;
2309         } else if (textAlign == TextAlign::START) {
2310             textAlign = TextAlign::END;
2311         } else if (textAlign == TextAlign::END) {
2312             textAlign = TextAlign::START;
2313         }
2314     }
2315     textProperty->UpdateTextAlign(textAlign);
2316     UpdateFont(menuProperty, theme, isLabel);
2317     textProperty->UpdateContent(content);
2318     UpdateTextOverflow(textProperty, theme);
2319     node->MountToParent(row, isLabel ? 0 : DEFAULT_NODE_SLOT);
2320     node->MarkModifyDone();
2321     node->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2322     if (isLabel) {
2323         label_ = node;
2324     } else {
2325         content_ = node;
2326     }
2327 }
2328 
UpdateTextOverflow(RefPtr<TextLayoutProperty> & textProperty,RefPtr<SelectTheme> & theme)2329 void MenuItemPattern::UpdateTextOverflow(RefPtr<TextLayoutProperty>& textProperty,
2330     RefPtr<SelectTheme>& theme)
2331 {
2332     if (theme && Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_THIRTEEN)) {
2333         if (theme->GetExpandDisplay()) {
2334             textProperty->UpdateTextOverflow(TextOverflow::ELLIPSIS);
2335             textProperty->UpdateMaxLines(1);
2336         } else {
2337             textProperty->UpdateMaxLines(std::numeric_limits<int32_t>::max());
2338         }
2339     } else {
2340         textProperty->UpdateTextOverflow(TextOverflow::ELLIPSIS);
2341         textProperty->UpdateMaxLines(1);
2342     }
2343     UpdateMaxLinesFromTheme(textProperty);
2344     if (isTextFadeOut_) {
2345         textProperty->UpdateMaxLines(1);
2346         textProperty->UpdateTextOverflow(TextOverflow::MARQUEE);
2347         textProperty->UpdateTextMarqueeStartPolicy(MarqueeStartPolicy::DEFAULT);
2348         textProperty->UpdateTextMarqueeFadeout(true);
2349         textProperty->UpdateTextMarqueeStart(false);
2350     }
2351     textProperty->UpdateWordBreak(theme->GetWordBreak());
2352 }
2353 
UpdateFont(RefPtr<MenuLayoutProperty> & menuProperty,RefPtr<SelectTheme> & theme,bool isLabel)2354 void MenuItemPattern::UpdateFont(RefPtr<MenuLayoutProperty>& menuProperty, RefPtr<SelectTheme>& theme, bool isLabel)
2355 {
2356     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
2357     CHECK_NULL_VOID(itemProperty);
2358     auto& node = isLabel ? label_ : content_;
2359     auto textProperty = node ? node->GetLayoutProperty<TextLayoutProperty>() : nullptr;
2360     CHECK_NULL_VOID(textProperty);
2361 
2362     auto fontSize = isLabel ? itemProperty->GetLabelFontSize() : itemProperty->GetFontSize();
2363     UpdateFontSize(textProperty, menuProperty, fontSize, theme->GetMenuFontSize());
2364     auto fontWeight = isLabel ? itemProperty->GetLabelFontWeight() : itemProperty->GetFontWeight();
2365     UpdateFontWeight(textProperty, menuProperty, fontWeight);
2366     auto fontStyle = isLabel ? itemProperty->GetLabelItalicFontStyle() : itemProperty->GetItalicFontStyle();
2367     UpdateFontStyle(textProperty, menuProperty, fontStyle);
2368     auto fontColor = isLabel ? itemProperty->GetLabelFontColor() : itemProperty->GetFontColor();
2369     if (!isLabel && !itemProperty->HasFontColor() && isFocused_) {
2370         fontColor = theme->GetMenuItemFocusedTextColor();
2371     }
2372     auto menuItemNode = GetHost();
2373     UpdateFontColor(
2374         node, menuProperty, fontColor, isLabel ? theme->GetSecondaryFontColor() : theme->GetMenuFontColor());
2375     if (!isLabel) {
2376         auto menuItemRenderContext = menuItemNode->GetRenderContext();
2377         CHECK_NULL_VOID(menuItemRenderContext);
2378         auto renderContext = node->GetRenderContext();
2379         CHECK_NULL_VOID(renderContext);
2380         if (menuItemRenderContext->HasForegroundColor()) {
2381             textProperty->UpdateTextColor(menuItemRenderContext->GetForegroundColorValue());
2382             renderContext->UpdateForegroundColor(menuItemRenderContext->GetForegroundColorValue());
2383         }
2384     }
2385     auto fontFamily = isLabel ? itemProperty->GetLabelFontFamily() : itemProperty->GetFontFamily();
2386     UpdateFontFamily(textProperty, menuProperty, fontFamily);
2387 }
2388 
UpdateMaxLinesFromTheme(RefPtr<TextLayoutProperty> & textProperty)2389 void MenuItemPattern::UpdateMaxLinesFromTheme(RefPtr<TextLayoutProperty>& textProperty)
2390 {
2391     auto host = GetHost();
2392     CHECK_NULL_VOID(host);
2393     auto pipeline = host->GetContext();
2394     CHECK_NULL_VOID(pipeline);
2395     auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
2396     CHECK_NULL_VOID(menuTheme);
2397     auto fontScale = pipeline->GetFontScale();
2398     if (NearEqual(fontScale, menuTheme->GetBigFontSizeScale()) ||
2399         NearEqual(fontScale, menuTheme->GetLargeFontSizeScale()) ||
2400         NearEqual(fontScale, menuTheme->GetMaxFontSizeScale())) {
2401         textProperty->UpdateMaxLines(menuTheme->GetTextMaxLines());
2402     }
2403 }
2404 
UpdateTextNodes()2405 void MenuItemPattern::UpdateTextNodes()
2406 {
2407     auto host = GetHost();
2408     CHECK_NULL_VOID(host);
2409     auto menuNode = GetMenu();
2410     CHECK_NULL_VOID(menuNode);
2411     auto menuProperty = menuNode->GetLayoutProperty<MenuLayoutProperty>();
2412     RefPtr<FrameNode> leftRow =
2413         host->GetChildAtIndex(0) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(0)) : nullptr;
2414     CHECK_NULL_VOID(leftRow);
2415     UpdateText(leftRow, menuProperty, false);
2416     RefPtr<FrameNode> rightRow =
2417         host->GetChildAtIndex(1) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(1)) : nullptr;
2418     CHECK_NULL_VOID(rightRow);
2419     UpdateText(rightRow, menuProperty, true);
2420     UpdateDisabledStyle();
2421     host->MarkModifyDone();
2422     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2423 }
2424 
IsDisabled()2425 bool MenuItemPattern::IsDisabled()
2426 {
2427     auto eventHub = GetHost()->GetOrCreateEventHub<MenuItemEventHub>();
2428     CHECK_NULL_RETURN(eventHub, true);
2429     return !eventHub->IsEnabled();
2430 }
2431 
UpdateDisabledStyle()2432 void MenuItemPattern::UpdateDisabledStyle()
2433 {
2434     auto host = GetHost();
2435     CHECK_NULL_VOID(host);
2436     auto eventHub = host->GetOrCreateEventHub<MenuItemEventHub>();
2437     CHECK_NULL_VOID(eventHub);
2438     auto context = host->GetContext();
2439     CHECK_NULL_VOID(context);
2440     auto theme = context->GetTheme<SelectTheme>();
2441     CHECK_NULL_VOID(theme);
2442     auto enabled = eventHub->IsEnabled();
2443     auto alpha = theme->GetDisabledFontColorAlpha();
2444     if (content_) {
2445         auto renderContext = content_->GetRenderContext();
2446         CHECK_NULL_VOID(renderContext);
2447         auto originalOpacity = renderContext->GetOpacityValue(1.0);
2448         renderContext->OnOpacityUpdate(enabled ? originalOpacity : alpha * originalOpacity);
2449         content_->MarkModifyDone();
2450     }
2451     if (label_) {
2452         auto renderContext = label_->GetRenderContext();
2453         CHECK_NULL_VOID(renderContext);
2454         auto originalOpacity = renderContext->GetOpacityValue(1.0);
2455         renderContext->OnOpacityUpdate(enabled ? originalOpacity : alpha * originalOpacity);
2456         label_->MarkModifyDone();
2457     }
2458     if (startIcon_) {
2459         auto renderContext = startIcon_->GetRenderContext();
2460         CHECK_NULL_VOID(renderContext);
2461         auto originalOpacity = renderContext->GetOpacityValue(1.0);
2462         renderContext->OnOpacityUpdate(enabled ? originalOpacity : alpha * originalOpacity);
2463         startIcon_->MarkModifyDone();
2464     }
2465     if (endIcon_) {
2466         auto renderContext = endIcon_->GetRenderContext();
2467         CHECK_NULL_VOID(renderContext);
2468         auto originalOpacity = renderContext->GetOpacityValue(1.0);
2469         renderContext->OnOpacityUpdate(enabled ? originalOpacity : alpha * originalOpacity);
2470         endIcon_->MarkModifyDone();
2471     }
2472 }
2473 
SetAccessibilityAction()2474 void MenuItemPattern::SetAccessibilityAction()
2475 {
2476     auto host = GetHost();
2477     CHECK_NULL_VOID(host);
2478     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
2479     CHECK_NULL_VOID(accessibilityProperty);
2480     accessibilityProperty->SetActionSelect([weakPtr = WeakClaim(this)]() {
2481         const auto& pattern = weakPtr.Upgrade();
2482         CHECK_NULL_VOID(pattern);
2483         if (pattern->IsOptionPattern()) {
2484             pattern->OnSelectProcess();
2485         } else {
2486             auto host = pattern->GetHost();
2487             CHECK_NULL_VOID(host);
2488             auto hub = host->GetOrCreateEventHub<MenuItemEventHub>();
2489             CHECK_NULL_VOID(hub);
2490             auto onChange = hub->GetOnChange();
2491             auto selectedChangeEvent = hub->GetSelectedChangeEvent();
2492             pattern->SetChange();
2493             if (selectedChangeEvent) {
2494                 selectedChangeEvent(pattern->IsSelected());
2495             }
2496             if (onChange) {
2497                 onChange(pattern->IsSelected());
2498                 pattern->RecordChangeEvent();
2499             }
2500             auto context = host->GetRenderContext();
2501             CHECK_NULL_VOID(context);
2502             pattern->MarkIsSelected(pattern->IsSelected());
2503             context->OnMouseSelectUpdate(pattern->IsSelected(), ITEM_FILL_COLOR, ITEM_FILL_COLOR);
2504             if (pattern->GetSubBuilder() != nullptr) {
2505                 pattern->ShowSubMenu(ShowSubMenuType::ACTION);
2506                 return;
2507             }
2508 
2509             pattern->CloseMenu();
2510         }
2511     });
2512 }
2513 
MarkIsSelected(bool isSelected)2514 void MenuItemPattern::MarkIsSelected(bool isSelected)
2515 {
2516     if (isSelected_ == isSelected) {
2517         return;
2518     }
2519     isSelected_ = isSelected;
2520     auto eventHub = GetOrCreateEventHub<MenuItemEventHub>();
2521     CHECK_NULL_VOID(eventHub);
2522     auto onChange = eventHub->GetOnChange();
2523     auto selectedChangeEvent = eventHub->GetSelectedChangeEvent();
2524     if (selectedChangeEvent) {
2525         selectedChangeEvent(isSelected);
2526     }
2527     if (onChange) {
2528         onChange(isSelected);
2529     }
2530     auto host = GetHost();
2531     CHECK_NULL_VOID(host);
2532     if (isSelected) {
2533         eventHub->SetCurrentUIState(UI_STATE_SELECTED, isSelected);
2534     } else {
2535         eventHub->SetCurrentUIState(UI_STATE_SELECTED, isSelected);
2536     }
2537 }
2538 
IsSelectOverlayMenu()2539 bool MenuItemPattern::IsSelectOverlayMenu()
2540 {
2541     auto topLevelMenuPattern = GetMenuPattern(true);
2542     if (!topLevelMenuPattern) {
2543         return false;
2544     }
2545     return topLevelMenuPattern->IsSelectOverlayExtensionMenu() || topLevelMenuPattern->IsSelectOverlayCustomMenu() ||
2546            topLevelMenuPattern->IsSelectOverlaySubMenu();
2547 }
2548 
ParseMenuRadius(MenuParam & param)2549 void MenuItemPattern::ParseMenuRadius(MenuParam& param)
2550 {
2551     auto menuWrapperNode = GetMenuWrapper();
2552     CHECK_NULL_VOID(menuWrapperNode);
2553     auto menuWrapperPattern = menuWrapperNode->GetPattern<MenuWrapperPattern>();
2554     CHECK_NULL_VOID(menuWrapperPattern);
2555 
2556     if (menuWrapperPattern->GetHasCustomRadius()) {
2557         auto outterMenuNode = GetMenu(true);
2558         CHECK_NULL_VOID(outterMenuNode);
2559         auto menuLayoutProp = outterMenuNode->GetLayoutProperty<MenuLayoutProperty>();
2560         CHECK_NULL_VOID(menuLayoutProp);
2561         if (menuLayoutProp->GetBorderRadius().has_value()) {
2562             BorderRadiusProperty borderRadius = menuLayoutProp->GetBorderRadiusValue();
2563             param.borderRadius = std::make_optional(borderRadius);
2564         }
2565     }
2566 }
2567 
IsSubMenu()2568 bool MenuItemPattern::IsSubMenu()
2569 {
2570     auto topLevelMenuPattern = GetMenuPattern(true);
2571     if (!topLevelMenuPattern) {
2572         return false;
2573     }
2574     return topLevelMenuPattern->IsSubMenu();
2575 }
2576 
IsEmbedded()2577 bool MenuItemPattern::IsEmbedded()
2578 {
2579     auto parentMenuPattern = GetMenuPattern();
2580     return parentMenuPattern ? parentMenuPattern->IsEmbedded() : false;
2581 }
2582 
ModifyDivider()2583 void MenuItemPattern::ModifyDivider()
2584 {
2585     auto menu = GetMenu();
2586     CHECK_NULL_VOID(menu);
2587     auto menuProperty = menu->GetLayoutProperty<MenuLayoutProperty>();
2588     CHECK_NULL_VOID(menuProperty);
2589     auto divider = menuProperty->GetItemDivider();
2590     auto host = GetHost();
2591     CHECK_NULL_VOID(host);
2592     auto context = host->GetContext();
2593     CHECK_NULL_VOID(context);
2594     auto selectTheme = context->GetTheme<SelectTheme>();
2595     CHECK_NULL_VOID(selectTheme);
2596     auto hasDivider = divider.has_value();
2597     auto isDefaultShowDivider = selectTheme->GetDefaultShowDivider();
2598     if (hasDivider || isDefaultShowDivider) {
2599         auto paintProperty = host->GetPaintProperty<MenuItemPaintProperty>();
2600         CHECK_NULL_VOID(paintProperty);
2601         auto strokeWidth = hasDivider ? divider->strokeWidth : selectTheme->GetDefaultDividerWidth();
2602         auto startMargin = hasDivider ? divider->startMargin : selectTheme->GetDefaultDividerStartMargin();
2603         auto endMargin = hasDivider ? divider->endMargin : selectTheme->GetDefaultDividerEndMargin();
2604         auto color = hasDivider ? divider->color : selectTheme->GetLineColor();
2605         paintProperty->UpdateStrokeWidth(strokeWidth);
2606         paintProperty->UpdateStartMargin(startMargin);
2607         paintProperty->UpdateEndMargin(endMargin);
2608         paintProperty->UpdateDividerColor(color);
2609         host->MarkModifyDone();
2610         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2611     }
2612 }
2613 
UpdateNeedDivider(bool need)2614 void MenuItemPattern::UpdateNeedDivider(bool need)
2615 {
2616     auto host = GetHost();
2617     CHECK_NULL_VOID(host);
2618     auto paintProperty = host->GetPaintProperty<MenuItemPaintProperty>();
2619     CHECK_NULL_VOID(paintProperty);
2620     paintProperty->UpdateNeedDivider(need);
2621     if (need) {
2622         ModifyDivider();
2623     }
2624 }
2625 
GetDividerStroke()2626 float MenuItemPattern::GetDividerStroke()
2627 {
2628     auto host = GetHost();
2629     CHECK_NULL_RETURN(host, 0.0f);
2630     auto props = host->GetPaintProperty<MenuItemPaintProperty>();
2631     CHECK_NULL_RETURN(props, 0.0f);
2632     return props->GetStrokeWidth().value_or(Dimension(0.0f, DimensionUnit::PX)).ConvertToPx();
2633 }
2634 
FindTouchedEmbeddedMenuItem(const PointF & position)2635 RefPtr<FrameNode> MenuItemPattern::FindTouchedEmbeddedMenuItem(const PointF& position)
2636 {
2637     auto host = GetHost();
2638     CHECK_NULL_RETURN(host, nullptr);
2639     if (expandingMode_ != SubMenuExpandingMode::EMBEDDED || !isExpanded_
2640         || embeddedMenu_ == nullptr || embeddedMenu_->GetTag() != V2::MENU_ETS_TAG) {
2641         return host;
2642     }
2643     CHECK_NULL_RETURN(clickableArea_, host);
2644     auto clickableAreaOffset = clickableArea_->GetPaintRectOffset(false, true);
2645     auto clickableAreaSize = clickableArea_->GetGeometryNode()->GetFrameSize();
2646     auto clickableAreaZone = RectF(clickableAreaOffset.GetX(), clickableAreaOffset.GetY(),
2647         clickableAreaSize.Width(), clickableAreaSize.Height());
2648     if (clickableAreaZone.IsInRegion(position)) {
2649         return host;
2650     }
2651     RefPtr<FrameNode> menuItem = nullptr;
2652     for (const auto& child : embeddedMenu_->GetChildren()) {
2653         if (child->GetTag() == V2::MENU_ITEM_ETS_TAG) {
2654             menuItem = AceType::DynamicCast<FrameNode>(child);
2655         }
2656         if (menuItem) {
2657             auto menuItemOffset = menuItem->GetPaintRectOffset(false, true);
2658             auto menuItemSize = menuItem->GetGeometryNode()->GetFrameSize();
2659             auto menuItemZone = RectF(menuItemOffset.GetX(), menuItemOffset.GetY(),
2660                 menuItemSize.Width(), menuItemSize.Height());
2661             if (menuItemZone.IsInRegion(position)) {
2662                 break;
2663             } else {
2664                 menuItem = nullptr;
2665             }
2666         }
2667     }
2668     return menuItem;
2669 }
2670 
SetBgColor(const Color & color)2671 void MenuItemPattern::SetBgColor(const Color& color)
2672 {
2673     auto renderContext = GetHost()->GetRenderContext();
2674     CHECK_NULL_VOID(renderContext);
2675     renderContext->UpdateBackgroundColor(color);
2676     bgColor_ = color;
2677 }
2678 
SetFontColor(const Color & color,bool isNeedRecord)2679 void MenuItemPattern::SetFontColor(const Color& color, bool isNeedRecord)
2680 {
2681     CHECK_NULL_VOID(text_);
2682     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
2683     CHECK_NULL_VOID(props);
2684     text_->MarkModifyDone();
2685     props->UpdateTextColor(color);
2686     if (isNeedRecord) {
2687         fontColor_ = color;
2688     }
2689     auto context = text_->GetRenderContext();
2690     CHECK_NULL_VOID(context);
2691     context->UpdateForegroundColor(color);
2692     context->UpdateForegroundColorFlag(false);
2693     context->ResetForegroundColorStrategy();
2694 }
2695 
SetFontSize(const Dimension & value)2696 void MenuItemPattern::SetFontSize(const Dimension& value)
2697 {
2698     CHECK_NULL_VOID(text_);
2699     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
2700     CHECK_NULL_VOID(props);
2701     text_->MarkModifyDone();
2702     CHECK_NULL_VOID(selectTheme_);
2703     props->UpdateFontSize(value.IsNegative() ? selectTheme_->GetMenuFontSize() : value);
2704 }
2705 
SetFontWeight(const FontWeight & value)2706 void MenuItemPattern::SetFontWeight(const FontWeight& value)
2707 {
2708     CHECK_NULL_VOID(text_);
2709     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
2710     CHECK_NULL_VOID(props);
2711     text_->MarkModifyDone();
2712     props->UpdateFontWeight(value);
2713 }
2714 
SetFontFamily(const std::vector<std::string> & value)2715 void MenuItemPattern::SetFontFamily(const std::vector<std::string>& value)
2716 {
2717     CHECK_NULL_VOID(text_);
2718     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
2719     CHECK_NULL_VOID(props);
2720     text_->MarkModifyDone();
2721     props->UpdateFontFamily(value);
2722 }
2723 
SetItalicFontStyle(const Ace::FontStyle & value)2724 void MenuItemPattern::SetItalicFontStyle(const Ace::FontStyle& value)
2725 {
2726     CHECK_NULL_VOID(text_);
2727     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
2728     CHECK_NULL_VOID(props);
2729     text_->MarkModifyDone();
2730     props->UpdateItalicFontStyle(value);
2731 }
2732 
SetSelected(int32_t selected)2733 void MenuItemPattern::SetSelected(int32_t selected)
2734 {
2735     rowSelected_ = selected;
2736 }
2737 
SetBorderColor(const Color & color)2738 void MenuItemPattern::SetBorderColor(const Color& color)
2739 {
2740     auto host = GetHost();
2741     CHECK_NULL_VOID(host);
2742     auto renderContext = host->GetRenderContext();
2743     CHECK_NULL_VOID(renderContext);
2744 
2745     BorderColorProperty borderColor;
2746     borderColor.SetColor(color);
2747     renderContext->UpdateBorderColor(borderColor);
2748 }
2749 
SetBorderWidth(const Dimension & value)2750 void MenuItemPattern::SetBorderWidth(const Dimension& value)
2751 {
2752     auto host = GetHost();
2753     CHECK_NULL_VOID(host);
2754     auto renderContext = host->GetRenderContext();
2755     CHECK_NULL_VOID(renderContext);
2756     BorderWidthProperty borderWidth;
2757     borderWidth.SetBorderWidth(value);
2758     auto layoutProp = host->GetLayoutProperty();
2759     CHECK_NULL_VOID(layoutProp);
2760     layoutProp->UpdateBorderWidth(borderWidth);
2761     renderContext->UpdateBorderWidth(borderWidth);
2762 }
2763 
GetBorderColor() const2764 Color MenuItemPattern::GetBorderColor() const
2765 {
2766     return Color::TRANSPARENT;
2767 }
2768 
GetBorderWidth() const2769 Dimension MenuItemPattern::GetBorderWidth() const
2770 {
2771     return BORDER_DEFAULT_WIDTH;
2772 }
2773 
UpdateIconSrc()2774 void MenuItemPattern::UpdateIconSrc()
2775 {
2776     if (icon_ == nullptr || iconSrc_.empty()) {
2777         return;
2778     }
2779     auto selectTheme = GetCurrentSelectTheme();
2780     CHECK_NULL_VOID(selectTheme);
2781     ImageSourceInfo imageSourceInfo(iconSrc_);
2782     bool useDefaultIcon = UseDefaultThemeIcon(imageSourceInfo);
2783     if (useDefaultIcon) {
2784         auto iconRenderProperty = icon_->GetPaintProperty<ImageRenderProperty>();
2785         CHECK_NULL_VOID(iconRenderProperty);
2786         iconRenderProperty->UpdateSvgFillColor(selectTheme->GetMenuIconColor());
2787     }
2788 }
2789 
UpdateNextNodeDivider(bool needDivider)2790 void MenuItemPattern::UpdateNextNodeDivider(bool needDivider)
2791 {
2792     auto host = GetHost();
2793     // find next option node from parent menuNode
2794     CHECK_NULL_VOID(host);
2795     auto parent = host->GetParent();
2796     CHECK_NULL_VOID(parent);
2797     auto nextNode = parent->GetChildAtIndex(index_ + 1);
2798     if (nextNode) {
2799         auto pattern = DynamicCast<FrameNode>(nextNode)->GetPattern<MenuItemPattern>();
2800         CHECK_NULL_VOID(pattern);
2801         if (pattern->IsSelected()) {
2802             return;
2803         }
2804         if (!InstanceOf<FrameNode>(nextNode)) {
2805             LOGW("next optionNode is not a frameNode! type = %{public}s", nextNode->GetTag().c_str());
2806             return;
2807         }
2808         auto props = DynamicCast<FrameNode>(nextNode)->GetPaintProperty<MenuItemPaintProperty>();
2809         CHECK_NULL_VOID(props);
2810         props->UpdateNeedDivider(needDivider);
2811         nextNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2812     }
2813 }
2814 
UpdateText(const std::string & content)2815 void MenuItemPattern::UpdateText(const std::string& content)
2816 {
2817     CHECK_NULL_VOID(text_);
2818     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
2819     CHECK_NULL_VOID(props);
2820     props->UpdateContent(content);
2821     text_->MarkModifyDone();
2822     text_->MarkDirtyNode();
2823 }
2824 
UpdateIcon(const std::string & src,const std::function<void (WeakPtr<NG::FrameNode>)> symbolIcon)2825 void MenuItemPattern::UpdateIcon(const std::string& src, const std::function<void(WeakPtr<NG::FrameNode>)> symbolIcon)
2826 {
2827     iconSrc_ = src;
2828     auto host = GetHost();
2829     CHECK_NULL_VOID(host);
2830     RefPtr<FrameNode> row =
2831         host->GetChildAtIndex(0) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(0)) : nullptr;
2832     CHECK_NULL_VOID(row);
2833     if (symbolIcon && (!icon_ || icon_->GetTag() != V2::SYMBOL_ETS_TAG)) {
2834         icon_ = MenuView::CreateSymbol(symbolIcon, row, icon_);
2835         row->MarkModifyDone();
2836         row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2837         return;
2838     } else if (symbolIcon == nullptr && !src.empty() && (!icon_ || icon_->GetTag() != V2::IMAGE_ETS_TAG)) {
2839         icon_ = MenuView::CreateIcon(src, row, icon_);
2840         row->MarkModifyDone();
2841         row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2842         return;
2843     } else if (icon_) {
2844         if (symbolIcon != nullptr) {
2845             symbolIcon(AccessibilityManager::WeakClaim(AccessibilityManager::RawPtr(icon_)));
2846             icon_->MarkModifyDone();
2847             icon_->MarkDirtyNode();
2848             return;
2849         } else if (!src.empty()) {
2850             auto props = icon_->GetLayoutProperty<ImageLayoutProperty>();
2851             CHECK_NULL_VOID(props);
2852             auto imageSrcInfo = props->GetImageSourceInfo();
2853             CHECK_NULL_VOID(imageSrcInfo);
2854             imageSrcInfo->SetSrc(src);
2855             props->UpdateImageSourceInfo(imageSrcInfo.value());
2856             icon_->MarkModifyDone();
2857             icon_->MarkDirtyNode();
2858             return;
2859         }
2860     }
2861 
2862     row->RemoveChild(icon_); // it's safe even if icon_ is nullptr
2863     row->MarkModifyDone();
2864     row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2865     icon_ = nullptr;
2866 }
2867 
GetItalicFontStyle()2868 Ace::FontStyle MenuItemPattern::GetItalicFontStyle()
2869 {
2870     CHECK_NULL_RETURN(text_, Ace::FontStyle());
2871     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
2872     CHECK_NULL_RETURN(props, Ace::FontStyle());
2873     auto defaultStyle = textTheme_->GetTextStyle().GetFontStyle();
2874     return props->GetItalicFontStyleValue(defaultStyle);
2875 }
2876 
GetBgColor()2877 Color MenuItemPattern::GetBgColor()
2878 {
2879     auto theme = GetCurrentSelectTheme();
2880     CHECK_NULL_RETURN(theme, Color());
2881     auto bgColor = Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) ? Color::TRANSPARENT
2882                                                                                       : theme->GetBackgroundColor();
2883     return bgColor_.value_or(bgColor);
2884 }
2885 
GetFontColor()2886 Color MenuItemPattern::GetFontColor()
2887 {
2888     CHECK_NULL_RETURN(text_, Color::TRANSPARENT);
2889     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
2890     CHECK_NULL_RETURN(props, Color::TRANSPARENT);
2891     auto defaultColor = selectTheme_->GetMenuFontColor();
2892     return props->GetTextColorValue(defaultColor);
2893 }
2894 
GetFontSize()2895 Dimension MenuItemPattern::GetFontSize()
2896 {
2897     CHECK_NULL_RETURN(text_, Dimension());
2898     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
2899     CHECK_NULL_RETURN(props, Dimension());
2900     CHECK_NULL_RETURN(selectTheme_, Dimension());
2901     auto defaultSize = selectTheme_->GetMenuFontSize();
2902     return props->GetFontSizeValue(defaultSize);
2903 }
2904 
GetSelectOptionWidth()2905 float MenuItemPattern::GetSelectOptionWidth()
2906 {
2907     RefPtr<GridColumnInfo> columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
2908     auto parent = columnInfo->GetParent();
2909     CHECK_NULL_RETURN(parent, MIN_OPTION_WIDTH.ConvertToPx());
2910     parent->BuildColumnWidth();
2911     auto defaultWidth = static_cast<float>(columnInfo->GetWidth(COLUMN_NUM)) - OPTION_MARGIN.ConvertToPx();
2912     auto optionNode = GetHost();
2913     CHECK_NULL_RETURN(optionNode, MIN_OPTION_WIDTH.ConvertToPx());
2914     float finalWidth = MIN_OPTION_WIDTH.ConvertToPx();
2915 
2916     if (IsWidthModifiedBySelect()) {
2917         auto optionPatintProperty = optionNode->GetPaintProperty<MenuItemPaintProperty>();
2918         CHECK_NULL_RETURN(optionPatintProperty, MIN_OPTION_WIDTH.ConvertToPx());
2919         auto selectmodifiedwidth = optionPatintProperty->GetSelectModifiedWidth();
2920         finalWidth = selectmodifiedwidth.value_or(0.0f);
2921     } else {
2922         finalWidth = defaultWidth;
2923     }
2924 
2925     if (finalWidth < MIN_OPTION_WIDTH.ConvertToPx()) {
2926         finalWidth = defaultWidth;
2927     }
2928 
2929     return finalWidth;
2930 }
2931 
GetFontWeight()2932 FontWeight MenuItemPattern::GetFontWeight()
2933 {
2934     CHECK_NULL_RETURN(text_, FontWeight());
2935     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
2936     CHECK_NULL_RETURN(props, FontWeight());
2937     auto defaultWeight = textTheme_->GetTextStyle().GetFontWeight();
2938     return props->GetFontWeightValue(defaultWeight);
2939 }
2940 
GetFontFamily()2941 std::vector<std::string> MenuItemPattern::GetFontFamily()
2942 {
2943     CHECK_NULL_RETURN(text_, std::vector<std::string>());
2944     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
2945     CHECK_NULL_RETURN(props, std::vector<std::string>());
2946     auto defaultFamily = textTheme_->GetTextStyle().GetFontFamilies();
2947     return props->GetFontFamilyValue(defaultFamily);
2948 }
2949 
GetText()2950 std::string MenuItemPattern::GetText()
2951 {
2952     CHECK_NULL_RETURN(text_, std::string());
2953     auto textProps = text_->GetLayoutProperty<TextLayoutProperty>();
2954     CHECK_NULL_RETURN(textProps, std::string());
2955     return UtfUtils::Str16ToStr8(textProps->GetContentValue(u""));
2956 }
2957 
InspectorGetFont()2958 std::string MenuItemPattern::InspectorGetFont()
2959 {
2960     CHECK_NULL_RETURN(text_, "");
2961     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
2962     CHECK_NULL_RETURN(props, "");
2963     return props->InspectorGetTextFont();
2964 }
2965 
OnPress(const UIState & state)2966 void MenuItemPattern::OnPress(const UIState& state)
2967 {
2968     auto host = GetHost();
2969     CHECK_NULL_VOID(host);
2970     const auto& renderContext = host->GetRenderContext();
2971     CHECK_NULL_VOID(renderContext);
2972     auto props = GetPaintProperty<MenuItemPaintProperty>();
2973     CHECK_NULL_VOID(props);
2974     auto theme = GetCurrentSelectTheme();
2975     // enter press status
2976     if (state == UI_STATE_PRESSED) {
2977         // change background color, update press status
2978         SetBgBlendColor(theme ? theme->GetClickedColor() : DEFAULT_CLICKED_COLOR);
2979         PlayBgColorAnimation(false);
2980 
2981         props->UpdatePress(true);
2982         UpdateDividerPressStatus(true);
2983         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2984         if (isOptionPattern_ && showDefaultSelectedIcon_) {
2985             return;
2986         }
2987         // disable next option node's divider
2988         UpdateNextNodeDivider(false);
2989     } else if (state == UI_STATE_NORMAL) {
2990         // leave press status
2991         if (IsHover()) {
2992             SetBgBlendColor(theme ? theme->GetHoverColor() : DEFAULT_HOVER_COLOR);
2993         } else {
2994             SetBgBlendColor(Color::TRANSPARENT);
2995         }
2996         PlayBgColorAnimation(false);
2997 
2998         props->UpdatePress(false);
2999         UpdateDividerPressStatus(false);
3000         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3001         if (!IsSelected()) {
3002             UpdateNextNodeDivider(true);
3003         }
3004     }
3005 }
3006 
OnSelectProcess()3007 bool MenuItemPattern::OnSelectProcess()
3008 {
3009     auto host = GetHost();
3010     CHECK_NULL_RETURN(host, false);
3011     auto hub = host->GetOrCreateEventHub<MenuItemEventHub>();
3012     CHECK_NULL_RETURN(hub, false);
3013     auto JsAction = hub->GetJsCallback();
3014     if (JsAction) {
3015         JsAction();
3016     }
3017     ClearFocusStyle();
3018     auto onSelect = hub->GetOnSelect();
3019     if (onSelect) {
3020         onSelect(index_);
3021     }
3022     host->OnAccessibilityEvent(AccessibilityEventType::SELECTED);
3023     // hide menu when option is clicked
3024     auto pipeline = host->GetContext();
3025     CHECK_NULL_RETURN(pipeline, false);
3026     auto overlayManager = pipeline->GetOverlayManager();
3027     CHECK_NULL_RETURN(overlayManager, false);
3028     auto menu = GetMenuWeak().Upgrade();
3029     CHECK_NULL_RETURN(menu, false);
3030     auto menuPattern = menu->GetPattern<MenuPattern>();
3031     CHECK_NULL_RETURN(menuPattern, false);
3032     if (!blockClick_) {
3033         menuPattern->HideMenu();
3034     }
3035     return true;
3036 }
3037 
UpdateOptionFocus(KeyCode code)3038 bool MenuItemPattern::UpdateOptionFocus(KeyCode code)
3039 {
3040     auto meunNode = GetMenuWeak().Upgrade();
3041     CHECK_NULL_RETURN(meunNode, false);
3042     auto menuPattern = meunNode->GetPattern<MenuPattern>();
3043     CHECK_NULL_RETURN(menuPattern, false);
3044     auto options = menuPattern->GetOptions();
3045     if (!options.empty()) {
3046         auto optionNode = (code == KeyCode::KEY_MOVE_HOME) ? options.front() : options.back();
3047         auto eventHub = optionNode->GetOrCreateFocusHub();
3048         eventHub->RequestFocusImmediately();
3049         return true;
3050     }
3051     return false;
3052 }
3053 
UpdatePasteFontColor(const Color & fontColor)3054 void MenuItemPattern::UpdatePasteFontColor(const Color& fontColor)
3055 {
3056     CHECK_NULL_VOID(pasteButton_);
3057     auto property = pasteButton_->GetPaintProperty<SecurityComponentPaintProperty>();
3058     CHECK_NULL_VOID(property);
3059     property->UpdateFontColor(fontColor);
3060     pasteButton_->MarkModifyDone();
3061 }
3062 
OptionOnModifyDone(const RefPtr<FrameNode> & host)3063 void MenuItemPattern::OptionOnModifyDone(const RefPtr<FrameNode>& host)
3064 {
3065     HandleOptionBackgroundColor();
3066     HandleOptionFontColor();
3067     auto context = host->GetContext();
3068     CHECK_NULL_VOID(context);
3069     textTheme_ = context->GetTheme<TextTheme>();
3070     CHECK_NULL_VOID(textTheme_);
3071     selectTheme_ = context->GetTheme<SelectTheme>();
3072     CHECK_NULL_VOID(selectTheme_);
3073 
3074     auto eventHub = host->GetOrCreateEventHub<MenuItemEventHub>();
3075     CHECK_NULL_VOID(eventHub);
3076     UpdateIconSrc();
3077     if (!eventHub->IsEnabled()) {
3078         UpdatePasteDisabledOpacity(selectTheme_->GetDisabledFontColorAlpha());
3079 
3080         CHECK_NULL_VOID(text_);
3081         auto textRenderContext = text_->GetRenderContext();
3082         CHECK_NULL_VOID(textRenderContext);
3083         textRenderContext->UpdateOpacity(selectTheme_->GetDisabledFontColorAlpha());
3084         text_->MarkModifyDone();
3085         if (icon_) {
3086             icon_->GetRenderContext()->UpdateOpacity(selectTheme_->GetDisabledFontColorAlpha());
3087             icon_->MarkModifyDone();
3088         }
3089     } else {
3090         UpdatePasteFontColor(selectTheme_->GetMenuFontColor());
3091     }
3092     SetAccessibilityAction();
3093     InitFocusEvent();
3094     auto textAlign = static_cast<TextAlign>(selectTheme_->GetOptionContentNormalAlign());
3095     if (textAlign != TextAlign::CENTER) {
3096         return;
3097     }
3098     CHECK_NULL_VOID(text_);
3099     auto textProperty = text_->GetLayoutProperty<TextLayoutProperty>();
3100     CHECK_NULL_VOID(textProperty);
3101     textProperty->UpdateTextAlign(textAlign);
3102     text_->MarkModifyDone();
3103 }
3104 
HandleOptionBackgroundColor()3105 void MenuItemPattern::HandleOptionBackgroundColor()
3106 {
3107     auto property = GetPaintProperty<MenuItemPaintProperty>();
3108     CHECK_NULL_VOID(property);
3109     auto hasOptionBgColor = property->HasOptionBgColor();
3110     auto hasSelectedOptionBgColor = property->HasSelectedOptionBgColor();
3111     if (!hasOptionBgColor && !hasSelectedOptionBgColor) {
3112         return;
3113     }
3114     if (isSelected_ && hasSelectedOptionBgColor) {
3115         SetBgColor(property->GetSelectedOptionBgColor().value());
3116         return;
3117     }
3118     if (!isSelected_ && hasOptionBgColor) {
3119         SetBgColor(property->GetOptionBgColor().value());
3120     }
3121 }
3122 
HandleOptionFontColor()3123 void MenuItemPattern::HandleOptionFontColor()
3124 {
3125     auto property = GetPaintProperty<MenuItemPaintProperty>();
3126     CHECK_NULL_VOID(property);
3127     auto hasOptionFontColor = property->HasOptionFontColor();
3128     auto hasSelectedOptionFontColor = property->HasSelectedOptionFontColor();
3129     if (!hasOptionFontColor && !hasSelectedOptionFontColor) {
3130         return;
3131     }
3132     if (isSelected_ && hasSelectedOptionFontColor) {
3133         SetFontColor(property->GetSelectedOptionFontColor().value());
3134         return;
3135     }
3136     if (!isSelected_ && hasOptionFontColor) {
3137         SetFontColor(property->GetOptionFontColor().value());
3138     }
3139 }
3140 
UpdatePasteDisabledOpacity(const double disabledColorAlpha)3141 void MenuItemPattern::UpdatePasteDisabledOpacity(const double disabledColorAlpha)
3142 {
3143     CHECK_NULL_VOID(pasteButton_);
3144     auto pasteButtonRenderContext = pasteButton_->GetRenderContext();
3145     CHECK_NULL_VOID(pasteButtonRenderContext);
3146     pasteButtonRenderContext->UpdateOpacity(disabledColorAlpha);
3147     pasteButton_->MarkModifyDone();
3148 }
3149 
UpdateDividerSelectedStatus(bool isSelected)3150 void MenuItemPattern::UpdateDividerSelectedStatus(bool isSelected)
3151 {
3152     if (bottomDivider_) {
3153         auto bottomPaintProperty = bottomDivider_->GetPaintProperty<MenuDividerPaintProperty>();
3154         CHECK_NULL_VOID(bottomPaintProperty);
3155         bottomPaintProperty->UpdateBottomSelected(isSelected);
3156         bottomDivider_->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3157     }
3158     auto topDivider = GetTopDivider();
3159     if (topDivider) {
3160         auto topPaintProperty = topDivider->GetPaintProperty<MenuDividerPaintProperty>();
3161         CHECK_NULL_VOID(topPaintProperty);
3162         topPaintProperty->UpdateTopSelected(isSelected);
3163         topDivider->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3164     }
3165 }
3166 
UpdateDividerHoverStatus(bool isHover)3167 void MenuItemPattern::UpdateDividerHoverStatus(bool isHover)
3168 {
3169     if (bottomDivider_) {
3170         auto bottomPaintProperty = bottomDivider_->GetPaintProperty<MenuDividerPaintProperty>();
3171         CHECK_NULL_VOID(bottomPaintProperty);
3172         bottomPaintProperty->UpdateBottomHover(isHover);
3173         bottomDivider_->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3174     }
3175     auto topDivider = GetTopDivider();
3176     if (topDivider) {
3177         auto topPaintProperty = topDivider->GetPaintProperty<MenuDividerPaintProperty>();
3178         CHECK_NULL_VOID(topPaintProperty);
3179         topPaintProperty->UpdateTopHover(isHover);
3180         topDivider->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3181     }
3182 }
3183 
UpdateDividerPressStatus(bool isPress)3184 void MenuItemPattern::UpdateDividerPressStatus(bool isPress)
3185 {
3186     if (bottomDivider_) {
3187         auto bottomPaintProperty = bottomDivider_->GetPaintProperty<MenuDividerPaintProperty>();
3188         CHECK_NULL_VOID(bottomPaintProperty);
3189         bottomPaintProperty->UpdateBottomPress(isPress);
3190         bottomDivider_->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3191     }
3192     auto topDivider = GetTopDivider();
3193     if (topDivider) {
3194         auto topPaintProperty = topDivider->GetPaintProperty<MenuDividerPaintProperty>();
3195         CHECK_NULL_VOID(topPaintProperty);
3196         topPaintProperty->UpdateTopPress(isPress);
3197         topDivider->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3198     }
3199 }
3200 
SetOptionTextModifier(const std::function<void (WeakPtr<NG::FrameNode>)> & optionApply)3201 void MenuItemPattern::SetOptionTextModifier(const std::function<void(WeakPtr<NG::FrameNode>)>& optionApply)
3202 {
3203     if (optionApply_ && !optionApply) {
3204         ResetSelectTextProps();
3205         ApplyOptionThemeStyles();
3206         return;
3207     }
3208     optionApply_ = optionApply;
3209     if (optionApply_) {
3210         ResetSelectTextProps();
3211         ApplyOptionThemeStyles();
3212         ApplyTextModifier(optionApply_);
3213     }
3214 }
3215 
CreateCheckMarkNode(const RefPtr<FrameNode> & parent,uint32_t index)3216 RefPtr<FrameNode> MenuItemPattern::CreateCheckMarkNode(const RefPtr<FrameNode>& parent, uint32_t index)
3217 {
3218     auto checkMarkNode = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG,
3219         ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<TextPattern>(); });
3220     CHECK_NULL_RETURN(checkMarkNode, nullptr);
3221     auto checkLayoutProperty = checkMarkNode->GetLayoutProperty<TextLayoutProperty>();
3222     CHECK_NULL_RETURN(checkLayoutProperty, nullptr);
3223     auto pipeline = GetContext();
3224     CHECK_NULL_RETURN(pipeline, nullptr);
3225     auto selectTheme = pipeline->GetTheme<SelectTheme>();
3226     CHECK_NULL_RETURN(selectTheme, nullptr);
3227     checkLayoutProperty->UpdateSymbolSourceInfo(SymbolSourceInfo { selectTheme->GetCheckMarkIconId() });
3228     checkLayoutProperty->UpdateSymbolColorList({ selectTheme->GetCheckMarkColor() });
3229     checkLayoutProperty->UpdateFontSize(selectTheme->GetCheckMarkFontSize());
3230     checkLayoutProperty->UpdateFontWeight(selectTheme->GetCheckMarkFontWeight());
3231 
3232     checkLayoutProperty->UpdateAlignment(Alignment::CENTER_RIGHT);
3233 
3234     checkMarkNode->MountToParent(parent, index);
3235     checkMarkNode->MarkModifyDone();
3236     return checkMarkNode;
3237 }
3238 
UpdateCheckMarkColor(const Color & color)3239 void MenuItemPattern::UpdateCheckMarkColor(const Color& color)
3240 {
3241     CHECK_NULL_VOID(checkMarkNode_);
3242     auto checkLayoutProperty = checkMarkNode_->GetLayoutProperty<TextLayoutProperty>();
3243     CHECK_NULL_VOID(checkLayoutProperty);
3244     auto pipeline = GetContext();
3245     CHECK_NULL_VOID(pipeline);
3246     auto selectTheme = pipeline->GetTheme<SelectTheme>();
3247     CHECK_NULL_VOID(selectTheme);
3248     checkLayoutProperty->UpdateSymbolColorList({ color });
3249     checkMarkNode_->MarkModifyDone();
3250 }
3251 
SetShowDefaultSelectedIcon(bool show)3252 void MenuItemPattern::SetShowDefaultSelectedIcon(bool show)
3253 {
3254     showDefaultSelectedIcon_ = show;
3255     auto host = GetHost();
3256     CHECK_NULL_VOID(host);
3257     if (isOptionPattern_ && showDefaultSelectedIcon_ && !endRowNode_) {
3258         auto endRow = FrameNode::CreateFrameNode(
3259             V2::ROW_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<MenuItemRowPattern>());
3260         CHECK_NULL_VOID(endRow);
3261         endRow->MountToParent(host);
3262         auto rightRowLayoutProps = endRow->GetLayoutProperty<LinearLayoutProperty>();
3263         CHECK_NULL_VOID(rightRowLayoutProps);
3264         rightRowLayoutProps->UpdateMainAxisAlign(FlexAlign::FLEX_START);
3265         rightRowLayoutProps->UpdateCrossAxisAlign(FlexAlign::FLEX_START);
3266         auto checkMarkNode = CreateCheckMarkNode(endRow, endRow->GetChildren().size());
3267         checkMarkNode_ = checkMarkNode;
3268         SetCheckMarkVisibleType(isSelected_ ? VisibleType::VISIBLE : VisibleType::INVISIBLE);
3269         endRowNode_ = endRow;
3270     } else if (isOptionPattern_ && !showDefaultSelectedIcon_) {
3271         host->RemoveChild(endRowNode_);
3272         checkMarkNode_ = nullptr;
3273         endRowNode_ = nullptr;
3274     }
3275 }
3276 
SetCheckMarkVisibleType(VisibleType type)3277 void MenuItemPattern::SetCheckMarkVisibleType(VisibleType type)
3278 {
3279     CHECK_NULL_VOID(checkMarkNode_);
3280     auto checkMarkLayoutProps = checkMarkNode_->GetLayoutProperty<TextLayoutProperty>();
3281     CHECK_NULL_VOID(checkMarkLayoutProps);
3282     checkMarkLayoutProps->UpdateVisibility(type);
3283 }
3284 
SetSelectedOptionTextModifier(const std::function<void (WeakPtr<NG::FrameNode>)> & optionSelectedApply)3285 void MenuItemPattern::SetSelectedOptionTextModifier(
3286     const std::function<void(WeakPtr<NG::FrameNode>)>& optionSelectedApply)
3287 {
3288     if (optionSelectedApply_ && !optionSelectedApply) {
3289         ResetSelectTextProps();
3290         ApplySelectedThemeStyles();
3291         return;
3292     }
3293     optionSelectedApply_ = optionSelectedApply;
3294     if (optionSelectedApply_) {
3295         ResetSelectTextProps();
3296         ApplySelectedThemeStyles();
3297         ApplyTextModifier(optionSelectedApply_);
3298     }
3299 }
3300 
ApplyTextModifier(const std::function<void (WeakPtr<NG::FrameNode>)> & optionApply)3301 void MenuItemPattern::ApplyTextModifier(const std::function<void(WeakPtr<NG::FrameNode>)>& optionApply)
3302 {
3303     if (!optionApply) {
3304         return;
3305     }
3306     auto textLayoutProperty = text_->GetLayoutProperty<TextLayoutProperty>();
3307     CHECK_NULL_VOID(textLayoutProperty);
3308     std::optional<Dimension> backupFontSize;
3309     std::optional<Dimension> backupMaxFontSize;
3310     std::optional<Dimension> backupMinFontSize;
3311     if (textLayoutProperty->HasFontSize()) {
3312         backupFontSize = textLayoutProperty->GetFontSizeValue(Dimension());
3313     }
3314     if (textLayoutProperty->HasAdaptMaxFontSize()) {
3315         backupMaxFontSize = textLayoutProperty->GetAdaptMaxFontSizeValue(Dimension());
3316     }
3317     if (textLayoutProperty->HasAdaptMinFontSize()) {
3318         backupMinFontSize = textLayoutProperty->GetAdaptMinFontSizeValue(Dimension());
3319     }
3320     textLayoutProperty->ResetFontSize();
3321     textLayoutProperty->ResetAdaptMaxFontSize();
3322     textLayoutProperty->ResetAdaptMinFontSize();
3323     optionApply(AceType::WeakClaim(AceType::RawPtr(text_)));
3324     if (!textLayoutProperty->HasFontSize() && !textLayoutProperty->HasAdaptMinFontSize() &&
3325         !textLayoutProperty->HasAdaptMaxFontSize()) {
3326         if (backupFontSize.has_value()) {
3327             textLayoutProperty->UpdateFontSize(backupFontSize.value());
3328         }
3329         if (backupMaxFontSize.has_value()) {
3330             textLayoutProperty->UpdateAdaptMaxFontSize(backupMaxFontSize.value());
3331         }
3332         if (backupMinFontSize.has_value()) {
3333             textLayoutProperty->UpdateAdaptMinFontSize(backupMinFontSize.value());
3334         }
3335     }
3336     text_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
3337     text_->MarkModifyDone();
3338 }
3339 
GetOptionTextModifier()3340 std::function<void(WeakPtr<NG::FrameNode>)>& MenuItemPattern::GetOptionTextModifier()
3341 {
3342     return optionApply_;
3343 }
3344 
GetSelectedOptionTextModifier()3345 std::function<void(WeakPtr<NG::FrameNode>)>& MenuItemPattern::GetSelectedOptionTextModifier()
3346 {
3347     return optionSelectedApply_;
3348 }
3349 
ResetSelectTextProps()3350 void MenuItemPattern::ResetSelectTextProps()
3351 {
3352     auto textLayoutProperty = text_->GetLayoutProperty<TextLayoutProperty>();
3353     CHECK_NULL_VOID(textLayoutProperty);
3354     auto row = AceType::DynamicCast<FrameNode>(text_->GetParent());
3355     CHECK_NULL_VOID(row);
3356     auto textContent = UtfUtils::Str16ToStr8(textLayoutProperty->GetContentValue(u""));
3357     row->RemoveChild(text_);
3358     text_ = MenuView::CreateText(textContent, row);
3359 }
3360 
ApplySelectedThemeStyles()3361 void MenuItemPattern::ApplySelectedThemeStyles()
3362 {
3363     auto host = GetHost();
3364     CHECK_NULL_VOID(host);
3365     auto pipeline = host->GetContextWithCheck();
3366     CHECK_NULL_VOID(pipeline);
3367     auto selectTheme = pipeline->GetTheme<SelectTheme>(host->GetThemeScopeId());
3368     CHECK_NULL_VOID(selectTheme);
3369     auto selectedColorText = selectTheme->GetSelectedColorText();
3370     auto selectedFontSizeText = selectTheme->GetSelectFontSizeText();
3371     auto selectedColor = selectTheme->GetSelectedColor();
3372     auto selectedBorderColor = selectTheme->GetOptionSelectedBorderColor();
3373     auto selectedBorderWidth = selectTheme->GetOptionSelectedBorderWidth();
3374     SetFontSize(selectedFontSizeText);
3375     if (!showDefaultSelectedIcon_) {
3376         SetFontColor(selectedColorText);
3377         SetBgColor(selectedColor);
3378     }
3379     SetBorderColor(selectedBorderColor);
3380     SetBorderWidth(selectedBorderWidth);
3381 }
3382 
ApplyOptionThemeStyles()3383 void MenuItemPattern::ApplyOptionThemeStyles()
3384 {
3385     auto host = GetHost();
3386     CHECK_NULL_VOID(host);
3387     auto pipeline = host->GetContextWithCheck();
3388     CHECK_NULL_VOID(pipeline);
3389     auto selectTheme = pipeline->GetTheme<SelectTheme>(host->GetThemeScopeId());
3390     auto textTheme = pipeline->GetTheme<TextTheme>();
3391     CHECK_NULL_VOID(selectTheme && textTheme);
3392     SetFontColor(selectTheme->GetMenuFontColor());
3393     SetFontFamily(textTheme->GetTextStyle().GetFontFamilies());
3394     SetFontSize(selectTheme->GetMenuFontSize());
3395     SetItalicFontStyle(textTheme->GetTextStyle().GetFontStyle());
3396     SetFontWeight(textTheme->GetTextStyle().GetFontWeight());
3397     SetBorderColor(GetBorderColor());
3398     SetBorderWidth(GetBorderWidth());
3399     if (IsSelectOption() && showDefaultSelectedIcon_ && !selectTheme->GetMenuBlendBgColor()) {
3400         SetBgColor(Color::TRANSPARENT);
3401         return;
3402     }
3403     SetBgColor(selectTheme->GetBackgroundColor());
3404 }
3405 
GetCurrentSelectTheme()3406 RefPtr<SelectTheme> MenuItemPattern::GetCurrentSelectTheme()
3407 {
3408     auto host = GetHost();
3409     CHECK_NULL_RETURN(host, nullptr);
3410     auto pipeline = host->GetContext();
3411     CHECK_NULL_RETURN(pipeline, nullptr);
3412     auto theme = pipeline->GetTheme<SelectTheme>();
3413     CHECK_NULL_RETURN(theme, nullptr);
3414     return theme;
3415 }
3416 
OnColorConfigurationUpdate()3417 void MenuItemPattern::OnColorConfigurationUpdate()
3418 {
3419     auto host = GetHost();
3420     CHECK_NULL_VOID(host);
3421     auto menuNode = GetMenu();
3422     CHECK_NULL_VOID(menuNode);
3423     auto menuProperty = menuNode->GetLayoutProperty<MenuLayoutProperty>();
3424     auto pipeline = menuNode->GetContextWithCheck();
3425     CHECK_NULL_VOID(pipeline);
3426     auto menuTheme = pipeline->GetTheme<SelectTheme>();
3427     CHECK_NULL_VOID(menuTheme);
3428     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
3429     CHECK_NULL_VOID(itemProperty);
3430 
3431     if (SystemProperties::ConfigChangePerform() && label_) {
3432         auto isSetByUser = itemProperty->GetLabelFontColorSetByUser().value_or(false);
3433         if (!isSetByUser) {
3434             itemProperty->UpdateLabelFontColor(menuTheme->GetSecondaryFontColor());
3435             host->MarkModifyDone();
3436             host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
3437         }
3438     }
3439     if (SystemProperties::ConfigChangePerform() && content_) {
3440         auto fontColor = itemProperty->GetFontColor();
3441         auto isSetbyUser = menuProperty->GetFontColorSetByUser().value_or(false);
3442         auto property = isSetbyUser ? menuProperty : AceType::MakeRefPtr<MenuLayoutProperty>();
3443         auto defaultFontColor = menuTheme->GetMenuFontColor();
3444         UpdateFontColor(content_, property, fontColor, defaultFontColor);
3445         content_->MarkModifyDone();
3446         content_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
3447     }
3448 }
3449 } // namespace OHOS::Ace::NG
3450