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