• 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 <memory>
19 
20 #include "base/geometry/ng/offset_t.h"
21 #include "base/log/log.h"
22 #include "base/memory/ace_type.h"
23 #include "base/utils/utils.h"
24 #include "core/common/recorder/event_recorder.h"
25 #include "core/common/recorder/node_data_cache.h"
26 #include "core/components/select/select_theme.h"
27 #include "core/components/theme/icon_theme.h"
28 #include "core/components_ng/base/frame_node.h"
29 #include "core/components_ng/base/view_stack_processor.h"
30 #include "core/components_ng/pattern/image/image_pattern.h"
31 #include "core/components_ng/pattern/menu/menu_item/menu_item_event_hub.h"
32 #include "core/components_ng/pattern/menu/menu_view.h"
33 #include "core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.h"
34 #include "core/components_ng/pattern/text/text_layout_property.h"
35 #include "core/components_ng/pattern/text/text_pattern.h"
36 #include "core/components_v2/inspector/inspector_constants.h"
37 #include "core/pipeline/pipeline_base.h"
38 
39 namespace OHOS::Ace::NG {
40 namespace {
41 const Color ITEM_FILL_COLOR = Color::TRANSPARENT;
UpdateFontSize(RefPtr<TextLayoutProperty> & textProperty,RefPtr<MenuLayoutProperty> & menuProperty,const std::optional<Dimension> & fontSize,const Dimension & defaultFontSize)42 void UpdateFontSize(RefPtr<TextLayoutProperty>& textProperty, RefPtr<MenuLayoutProperty>& menuProperty,
43     const std::optional<Dimension>& fontSize, const Dimension& defaultFontSize)
44 {
45     if (fontSize.has_value()) {
46         textProperty->UpdateFontSize(fontSize.value());
47     } else if (menuProperty && menuProperty->GetFontSize().has_value()) {
48         textProperty->UpdateFontSize(menuProperty->GetFontSize().value());
49     } else {
50         textProperty->UpdateFontSize(defaultFontSize);
51     }
52 }
53 
UpdateFontWeight(RefPtr<TextLayoutProperty> & textProperty,RefPtr<MenuLayoutProperty> & menuProperty,const std::optional<FontWeight> & fontWeight)54 void UpdateFontWeight(RefPtr<TextLayoutProperty>& textProperty, RefPtr<MenuLayoutProperty>& menuProperty,
55     const std::optional<FontWeight>& fontWeight)
56 {
57     if (fontWeight.has_value()) {
58         textProperty->UpdateFontWeight(fontWeight.value());
59     } else if (menuProperty && menuProperty->GetFontWeight().has_value()) {
60         textProperty->UpdateFontWeight(menuProperty->GetFontWeight().value());
61     } else {
62         textProperty->UpdateFontWeight(FontWeight::REGULAR);
63     }
64 }
65 
UpdateFontStyle(RefPtr<TextLayoutProperty> & textProperty,RefPtr<MenuLayoutProperty> & menuProperty,const std::optional<Ace::FontStyle> & fontStyle)66 void UpdateFontStyle(RefPtr<TextLayoutProperty>& textProperty, RefPtr<MenuLayoutProperty>& menuProperty,
67     const std::optional<Ace::FontStyle>& fontStyle)
68 {
69     if (fontStyle.has_value()) {
70         textProperty->UpdateItalicFontStyle(fontStyle.value());
71     } else if (menuProperty && menuProperty->GetItalicFontStyle().has_value()) {
72         textProperty->UpdateItalicFontStyle(menuProperty->GetItalicFontStyle().value());
73     } else {
74         textProperty->UpdateItalicFontStyle(Ace::FontStyle::NORMAL);
75     }
76 }
77 
UpdateFontColor(const RefPtr<FrameNode> & textNode,RefPtr<MenuLayoutProperty> & menuProperty,const std::optional<Color> & fontColor,const Color & defaultFontColor)78 void UpdateFontColor(const RefPtr<FrameNode>& textNode, RefPtr<MenuLayoutProperty>& menuProperty,
79     const std::optional<Color>& fontColor, const Color& defaultFontColor)
80 {
81     auto textProperty = textNode ? textNode->GetLayoutProperty<TextLayoutProperty>() : nullptr;
82     CHECK_NULL_VOID(textProperty);
83     auto renderContext = textNode->GetRenderContext();
84     CHECK_NULL_VOID(renderContext);
85     if (fontColor.has_value()) {
86         textProperty->UpdateTextColor(fontColor.value());
87     } else if (menuProperty && menuProperty->GetFontColor().has_value()) {
88         textProperty->UpdateTextColor(menuProperty->GetFontColor().value());
89     } else {
90         textProperty->UpdateTextColor(defaultFontColor);
91     }
92 }
93 
UpdateFontFamily(RefPtr<TextLayoutProperty> & textProperty,RefPtr<MenuLayoutProperty> & menuProperty,const std::optional<std::vector<std::string>> & fontFamilies)94 void UpdateFontFamily(RefPtr<TextLayoutProperty>& textProperty, RefPtr<MenuLayoutProperty>& menuProperty,
95     const std::optional<std::vector<std::string>>& fontFamilies)
96 {
97     std::vector<std::string> emptyFontfamily;
98     if (fontFamilies.has_value()) {
99         textProperty->UpdateFontFamily(fontFamilies.value());
100     } else if (menuProperty && menuProperty->GetFontFamily().has_value()) {
101         textProperty->UpdateFontFamily(menuProperty->GetFontFamily().value());
102     } else {
103         textProperty->UpdateFontFamily(emptyFontfamily);
104     }
105 }
106 
UpdateIconSrc(RefPtr<FrameNode> & node,const std::string & src,const Dimension & horizontalSize,const Dimension & verticalSize,const Color & color,const bool & useDefaultIcon)107 void UpdateIconSrc(RefPtr<FrameNode>& node, const std::string& src, const Dimension& horizontalSize,
108     const Dimension& verticalSize, const Color& color, const bool& useDefaultIcon)
109 {
110     ImageSourceInfo imageSourceInfo;
111     imageSourceInfo.SetSrc(src);
112     if (useDefaultIcon) {
113         imageSourceInfo.SetFillColor(color);
114     }
115     auto props = node->GetLayoutProperty<ImageLayoutProperty>();
116     CHECK_NULL_VOID(props);
117     props->UpdateImageSourceInfo(imageSourceInfo);
118     props->UpdateAlignment(Alignment::CENTER);
119     CalcSize idealSize = { CalcLength(horizontalSize), CalcLength(verticalSize) };
120     MeasureProperty layoutConstraint;
121     layoutConstraint.selfIdealSize = idealSize;
122     props->UpdateCalcLayoutProperty(layoutConstraint);
123     if (useDefaultIcon) {
124         auto iconRenderProperty = node->GetPaintProperty<ImageRenderProperty>();
125         CHECK_NULL_VOID(iconRenderProperty);
126         iconRenderProperty->UpdateSvgFillColor(color);
127     }
128 }
129 } // namespace
130 
OnMountToParentDone()131 void MenuItemPattern::OnMountToParentDone()
132 {
133     UpdateTextNodes();
134 }
135 
OnAttachToFrameNode()136 void MenuItemPattern::OnAttachToFrameNode()
137 {
138     RegisterOnKeyEvent();
139     RegisterOnClick();
140     RegisterOnTouch();
141     RegisterOnHover();
142 }
143 
OnAttachToFrameNode()144 void CustomMenuItemPattern::OnAttachToFrameNode()
145 {
146     RegisterOnKeyEvent();
147     RegisterOnTouch();
148 }
149 
OnModifyDone()150 void MenuItemPattern::OnModifyDone()
151 {
152     Pattern::OnModifyDone();
153     auto host = GetHost();
154     CHECK_NULL_VOID(host);
155     RefPtr<FrameNode> leftRow =
156         host->GetChildAtIndex(0) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(0)) : nullptr;
157     CHECK_NULL_VOID(leftRow);
158     AddSelectIcon(leftRow);
159     UpdateIcon(leftRow, true);
160     auto menuNode = GetMenu();
161     auto menuProperty = menuNode ? menuNode->GetLayoutProperty<MenuLayoutProperty>() : nullptr;
162     UpdateText(leftRow, menuProperty, false);
163 
164     RefPtr<FrameNode> rightRow =
165         host->GetChildAtIndex(1) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(1)) : nullptr;
166     CHECK_NULL_VOID(rightRow);
167     UpdateText(rightRow, menuProperty, true);
168     UpdateIcon(rightRow, false);
169     if (IsDisabled()) {
170         UpdateDisabledStyle();
171     }
172     SetAccessibilityAction();
173 
174     host->GetRenderContext()->SetClipToBounds(true);
175 }
176 
OnAfterModifyDone()177 void MenuItemPattern::OnAfterModifyDone()
178 {
179     auto host = GetHost();
180     CHECK_NULL_VOID(host);
181     auto inspectorId = host->GetInspectorId().value_or("");
182     if (inspectorId.empty()) {
183         return;
184     }
185     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
186     CHECK_NULL_VOID(itemProperty);
187     auto content = itemProperty->GetContent().value_or("");
188     Recorder::NodeDataCache::Get().PutMultiple(inspectorId, content, isSelected_);
189 }
190 
RecordChangeEvent() const191 void MenuItemPattern::RecordChangeEvent() const
192 {
193     if (!Recorder::EventRecorder::Get().IsComponentRecordEnable()) {
194         return;
195     }
196     auto host = GetHost();
197     CHECK_NULL_VOID(host);
198     auto inspectorId = host->GetInspectorId().value_or("");
199     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
200     CHECK_NULL_VOID(itemProperty);
201     auto content = itemProperty->GetContent().value_or("");
202     Recorder::EventParamsBuilder builder;
203     builder.SetId(inspectorId)
204         .SetType(host->GetTag())
205         .SetChecked(isSelected_)
206         .SetText(content)
207         .SetDescription(host->GetAutoEventParamValue(""));
208     Recorder::EventRecorder::Get().OnChange(std::move(builder));
209     Recorder::NodeDataCache::Get().PutMultiple(inspectorId, content, isSelected_);
210 }
211 
GetMenuWrapper()212 RefPtr<FrameNode> MenuItemPattern::GetMenuWrapper()
213 {
214     auto host = GetHost();
215     CHECK_NULL_RETURN(host, nullptr);
216     auto parent = host->GetParent();
217     while (parent) {
218         if (parent->GetTag() == V2::MENU_WRAPPER_ETS_TAG || parent->GetTag() == V2::SELECT_OVERLAY_ETS_TAG) {
219             return AceType::DynamicCast<FrameNode>(parent);
220         }
221         parent = parent->GetParent();
222     }
223     return nullptr;
224 }
225 
GetMenu(bool needTopMenu)226 RefPtr<FrameNode> MenuItemPattern::GetMenu(bool needTopMenu)
227 {
228     auto host = GetHost();
229     CHECK_NULL_RETURN(host, nullptr);
230     auto parent = host->GetParent();
231     RefPtr<FrameNode> menuNode = nullptr;
232     while (parent) {
233         if (parent->GetTag() == V2::MENU_ETS_TAG) {
234             menuNode = AceType::DynamicCast<FrameNode>(parent);
235             if (!needTopMenu) {
236                 return menuNode;
237             }
238         }
239         parent = parent->GetParent();
240     }
241     return menuNode;
242 }
243 
GetMenuPattern(bool needTopMenu)244 RefPtr<MenuPattern> MenuItemPattern::GetMenuPattern(bool needTopMenu)
245 {
246     auto menu = GetMenu(true);
247     if (!menu) {
248         return nullptr;
249     }
250     return menu->GetPattern<MenuPattern>();
251 }
252 
ShowSubMenu()253 void MenuItemPattern::ShowSubMenu()
254 {
255     auto host = GetHost();
256     CHECK_NULL_VOID(host);
257     auto buildFunc = GetSubBuilder();
258     if (!buildFunc || isSubMenuShowed_) {
259         return;
260     }
261 
262     // Hide SubMenu of parent Menu node
263     auto parentMenu = GetMenu();
264     CHECK_NULL_VOID(parentMenu);
265     // parentMenu no need focus
266     auto focusMenu = GetMenu(true);
267     CHECK_NULL_VOID(focusMenu);
268     auto focusHub = focusMenu->GetFocusHub();
269     CHECK_NULL_VOID(focusHub);
270     focusHub->SetParentFocusable(false);
271     auto parentMenuPattern = parentMenu->GetPattern<MenuPattern>();
272     CHECK_NULL_VOID(parentMenuPattern);
273     auto showedSubMenu = parentMenuPattern->GetShowedSubMenu();
274     if (showedSubMenu) {
275         auto showedSubMenuPattern = showedSubMenu->GetPattern<MenuPattern>();
276         CHECK_NULL_VOID(showedSubMenuPattern);
277         auto showedMenuItem = showedSubMenuPattern->GetParentMenuItem();
278         CHECK_NULL_VOID(showedMenuItem);
279         if (showedMenuItem->GetId() != host->GetId()) {
280             parentMenuPattern->HideSubMenu();
281         }
282     }
283 
284     isSubMenuShowed_ = true;
285 
286     NG::ScopedViewStackProcessor builderViewStackProcessor;
287     buildFunc();
288     auto customNode = NG::ViewStackProcessor::GetInstance()->Finish();
289     bool isSelectOverlayMenu = IsSelectOverlayMenu();
290     MenuParam param;
291     auto layoutProps = focusMenu->GetLayoutProperty<MenuLayoutProperty>();
292     CHECK_NULL_VOID(layoutProps);
293     param.isShowInSubWindow = layoutProps->GetShowInSubWindowValue(false);
294     param.type = isSelectOverlayMenu ? MenuType::SELECT_OVERLAY_SUB_MENU : MenuType::SUB_MENU;
295     auto subMenu = MenuView::Create(customNode, host->GetId(), host->GetTag(), param);
296     CHECK_NULL_VOID(subMenu);
297     auto menuPattern = subMenu->GetPattern<MenuPattern>();
298     CHECK_NULL_VOID(menuPattern);
299     menuPattern->SetParentMenuItem(host);
300     subMenuId_ = subMenu->GetId();
301     AddSelfHoverRegion(host);
302 
303     auto menuWrapper = GetMenuWrapper();
304     CHECK_NULL_VOID(menuWrapper);
305     subMenu->MountToParent(menuWrapper);
306 
307     OffsetF offset = GetSubMenuPostion(host);
308     auto menuProps = subMenu->GetLayoutProperty<MenuLayoutProperty>();
309     CHECK_NULL_VOID(menuProps);
310     menuProps->UpdateMenuOffset(offset);
311     menuWrapper->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
312     RegisterWrapperMouseEvent();
313 
314     // select overlay menu no need focus
315     if (!isSelectOverlayMenu) {
316         auto focusHub = subMenu->GetOrCreateFocusHub();
317         CHECK_NULL_VOID(focusHub);
318         focusHub->RequestFocusWithDefaultFocusFirstly();
319     }
320     parentMenuPattern->SetShowedSubMenu(subMenu);
321 }
322 
CloseMenu()323 void MenuItemPattern::CloseMenu()
324 {
325     // no need close for selection menu
326     if (IsSelectOverlayMenu()) {
327         return;
328     }
329     auto menuWrapper = GetMenuWrapper();
330     CHECK_NULL_VOID(menuWrapper);
331     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
332     CHECK_NULL_VOID(menuWrapperPattern);
333     menuWrapperPattern->HideMenu();
334 }
335 
RegisterOnClick()336 void MenuItemPattern::RegisterOnClick()
337 {
338     auto host = GetHost();
339     CHECK_NULL_VOID(host);
340     auto event = [weak = WeakClaim(this)](GestureEvent& /* info */) {
341         auto pattern = weak.Upgrade();
342         CHECK_NULL_VOID(pattern);
343         auto host = pattern->GetHost();
344         CHECK_NULL_VOID(host);
345         auto hub = host->GetEventHub<MenuItemEventHub>();
346         CHECK_NULL_VOID(hub);
347         auto onChange = hub->GetOnChange();
348         auto selectedChangeEvent = hub->GetSelectedChangeEvent();
349         pattern->SetChange();
350         if (selectedChangeEvent) {
351             LOGI("trigger onChangeEvent");
352             selectedChangeEvent(pattern->IsSelected());
353         }
354         if (onChange) {
355             LOGI("trigger onChange");
356             onChange(pattern->IsSelected());
357             pattern->RecordChangeEvent();
358         }
359         host->OnAccessibilityEvent(AccessibilityEventType::SELECTED);
360 
361         if (pattern->GetSubBuilder() != nullptr) {
362             pattern->ShowSubMenu();
363             return;
364         }
365 
366         // hide menu when menu item is clicked
367         pattern->CloseMenu();
368     };
369     auto clickEvent = MakeRefPtr<ClickEvent>(std::move(event));
370 
371     auto gestureHub = host->GetOrCreateGestureEventHub();
372     CHECK_NULL_VOID(gestureHub);
373     gestureHub->AddClickEvent(clickEvent);
374 }
375 
RegisterOnTouch()376 void MenuItemPattern::RegisterOnTouch()
377 {
378     auto host = GetHost();
379     CHECK_NULL_VOID(host);
380     auto gestureHub = host->GetOrCreateGestureEventHub();
381     CHECK_NULL_VOID(gestureHub);
382 
383     auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
384         auto pattern = weak.Upgrade();
385         CHECK_NULL_VOID(pattern);
386         pattern->OnTouch(info);
387     };
388     auto touchEvent = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
389     gestureHub->AddTouchEvent(touchEvent);
390 }
391 
RegisterOnHover()392 void MenuItemPattern::RegisterOnHover()
393 {
394     auto host = GetHost();
395     CHECK_NULL_VOID(host);
396     auto inputHub = host->GetOrCreateInputEventHub();
397     CHECK_NULL_VOID(inputHub);
398     auto mouseTask = [weak = WeakClaim(this)](bool isHover) {
399         auto pattern = weak.Upgrade();
400         CHECK_NULL_VOID(pattern);
401         pattern->OnHover(isHover);
402     };
403     auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
404     inputHub->AddOnHoverEvent(mouseEvent);
405 }
406 
RegisterOnKeyEvent()407 void MenuItemPattern::RegisterOnKeyEvent()
408 {
409     auto host = GetHost();
410     CHECK_NULL_VOID(host);
411     auto focusHub = host->GetOrCreateFocusHub();
412     CHECK_NULL_VOID(focusHub);
413     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
414         auto pattern = wp.Upgrade();
415         CHECK_NULL_RETURN(pattern, false);
416         return pattern->OnKeyEvent(event);
417     };
418     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
419 }
420 
OnTouch(const TouchEventInfo & info)421 void MenuItemPattern::OnTouch(const TouchEventInfo& info)
422 {
423     // change menu item paint props on press
424     auto touchType = info.GetTouches().front().GetTouchType();
425     auto pipeline = PipelineBase::GetCurrentContext();
426     CHECK_NULL_VOID(pipeline);
427     auto theme = pipeline->GetTheme<SelectTheme>();
428     CHECK_NULL_VOID(theme);
429 
430     if (touchType == TouchType::DOWN) {
431         // change background color, update press status
432         SetBgBlendColor(GetSubBuilder() ? theme->GetHoverColor() : theme->GetClickedColor());
433     } else if (touchType == TouchType::UP) {
434         SetBgBlendColor(isHovered_ ? theme->GetHoverColor() : Color::TRANSPARENT);
435     } else {
436         return;
437     }
438     PlayBgColorAnimation(false);
439     auto host = GetHost();
440     CHECK_NULL_VOID(host);
441     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
442 }
443 
OnTouch(const TouchEventInfo & info)444 void CustomMenuItemPattern::OnTouch(const TouchEventInfo& info)
445 {
446     auto touchType = info.GetTouches().front().GetTouchType();
447 
448     // close menu when touch up
449     // can't use onClick because that conflicts with interactions developers might set to the customNode
450     // recognize gesture as click if touch up position is close to last touch down position
451     if (touchType == TouchType::DOWN) {
452         lastTouchOffset_ = std::make_unique<Offset>(info.GetTouches().front().GetLocalLocation());
453     } else if (touchType == TouchType::UP) {
454         auto touchUpOffset = info.GetTouches().front().GetLocalLocation();
455         if (lastTouchOffset_ && (touchUpOffset - *lastTouchOffset_).GetDistance() <= DEFAULT_CLICK_DISTANCE) {
456             CloseMenu();
457         }
458         lastTouchOffset_.reset();
459     }
460 }
461 
OnHover(bool isHover)462 void MenuItemPattern::OnHover(bool isHover)
463 {
464     isHovered_ = isHover;
465     auto pipeline = PipelineBase::GetCurrentContext();
466     CHECK_NULL_VOID(pipeline);
467     auto theme = pipeline->GetTheme<SelectTheme>();
468     CHECK_NULL_VOID(theme);
469 
470     if (isHover || isSubMenuShowed_) {
471         // keep hover color when subMenu showed
472         SetBgBlendColor(theme->GetHoverColor());
473         ShowSubMenu();
474     } else {
475         SetBgBlendColor(Color::TRANSPARENT);
476     }
477     PlayBgColorAnimation();
478     auto host = GetHost();
479     CHECK_NULL_VOID(host);
480     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
481 }
482 
OnVisibleChange(bool isVisible)483 void MenuItemPattern::OnVisibleChange(bool isVisible)
484 {
485     auto host = GetHost();
486     CHECK_NULL_VOID(host);
487     auto parentNode = host->GetParent();
488     CHECK_NULL_VOID(parentNode);
489     if (parentNode->GetTag() == V2::MENU_ITEM_GROUP_ETS_TAG) {
490         parentNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
491     }
492 }
493 
OnKeyEvent(const KeyEvent & event)494 bool MenuItemPattern::OnKeyEvent(const KeyEvent& event)
495 {
496     if (event.action != KeyAction::DOWN) {
497         return false;
498     }
499     auto host = GetHost();
500     CHECK_NULL_RETURN(host, false);
501     auto focusHub = host->GetOrCreateFocusHub();
502     CHECK_NULL_RETURN(focusHub, false);
503     if (event.code == KeyCode::KEY_ENTER) {
504         focusHub->OnClick(event);
505         return true;
506     }
507     if (event.code == KeyCode::KEY_DPAD_RIGHT && GetSubBuilder() && !isSubMenuShowed_) {
508         auto pipeline = PipelineBase::GetCurrentContext();
509         CHECK_NULL_RETURN(pipeline, false);
510         auto theme = pipeline->GetTheme<SelectTheme>();
511         CHECK_NULL_RETURN(theme, false);
512         SetBgBlendColor(theme->GetHoverColor());
513         PlayBgColorAnimation();
514         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
515         ShowSubMenu();
516         return true;
517     }
518     return false;
519 }
520 
OnKeyEvent(const KeyEvent & event)521 bool CustomMenuItemPattern::OnKeyEvent(const KeyEvent& event)
522 {
523     if (event.action != KeyAction::DOWN) {
524         return false;
525     }
526     auto host = GetHost();
527     CHECK_NULL_RETURN(host, false);
528     auto focusHub = host->GetOrCreateFocusHub();
529     CHECK_NULL_RETURN(focusHub, false);
530     if (event.code == KeyCode::KEY_ENTER || event.code == KeyCode::KEY_SPACE) {
531         focusHub->OnClick(event);
532         CloseMenu();
533         return true;
534     }
535     return false;
536 }
537 
RegisterWrapperMouseEvent()538 void MenuItemPattern::RegisterWrapperMouseEvent()
539 {
540     auto menuWrapper = GetMenuWrapper();
541     if (menuWrapper && !wrapperMouseEvent_) {
542         auto inputHub = menuWrapper->GetOrCreateInputEventHub();
543         CHECK_NULL_VOID(inputHub);
544         auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
545         CHECK_NULL_VOID(menuWrapperPattern);
546 
547         auto mouseTask = [weak = WeakClaim(this), menuWrapperPattern](MouseInfo& info) {
548             auto pattern = weak.Upgrade();
549             CHECK_NULL_VOID(pattern);
550             if (menuWrapperPattern) {
551                 menuWrapperPattern->HandleMouseEvent(info, pattern);
552             }
553         };
554         wrapperMouseEvent_ = MakeRefPtr<InputEvent>(std::move(mouseTask));
555         inputHub->AddOnMouseEvent(wrapperMouseEvent_);
556     }
557 }
558 
AddSelfHoverRegion(const RefPtr<FrameNode> & targetNode)559 void MenuItemPattern::AddSelfHoverRegion(const RefPtr<FrameNode>& targetNode)
560 {
561     OffsetF topLeftPoint;
562     OffsetF bottomRightPoint;
563     auto frameSize = targetNode->GetGeometryNode()->GetMarginFrameSize();
564     topLeftPoint = targetNode->GetPaintRectOffset();
565     bottomRightPoint = targetNode->GetPaintRectOffset() + OffsetF(frameSize.Width(), frameSize.Height());
566     AddHoverRegions(topLeftPoint, bottomRightPoint);
567 }
568 
GetSubMenuPostion(const RefPtr<FrameNode> & targetNode)569 OffsetF MenuItemPattern::GetSubMenuPostion(const RefPtr<FrameNode>& targetNode)
570 {
571     // show menu at left top point of targetNode
572     OffsetF position;
573     auto frameSize = targetNode->GetGeometryNode()->GetMarginFrameSize();
574     position = targetNode->GetPaintRectOffset() + OffsetF(frameSize.Width(), 0.0);
575     return position;
576 }
577 
AddHoverRegions(const OffsetF & topLeftPoint,const OffsetF & bottomRightPoint)578 void MenuItemPattern::AddHoverRegions(const OffsetF& topLeftPoint, const OffsetF& bottomRightPoint)
579 {
580     TouchRegion hoverRegion = TouchRegion(
581         Offset(topLeftPoint.GetX(), topLeftPoint.GetY()), Offset(bottomRightPoint.GetX(), bottomRightPoint.GetY()));
582     hoverRegions_.emplace_back(hoverRegion);
583     LOGI("MenuItemPattern::AddHoverRegions hoverRegion is %{public}s to %{public}s", topLeftPoint.ToString().c_str(),
584         bottomRightPoint.ToString().c_str());
585 }
586 
IsInHoverRegions(double x,double y)587 bool MenuItemPattern::IsInHoverRegions(double x, double y)
588 {
589     for (auto hoverRegion : hoverRegions_) {
590         if (hoverRegion.ContainsInRegion(x, y)) {
591             return true;
592         }
593     }
594     return false;
595 }
596 
PlayBgColorAnimation(bool isHoverChange)597 void MenuItemPattern::PlayBgColorAnimation(bool isHoverChange)
598 {
599     auto pipeline = PipelineBase::GetCurrentContext();
600     CHECK_NULL_VOID(pipeline);
601     auto theme = pipeline->GetTheme<SelectTheme>();
602     CHECK_NULL_VOID(theme);
603     AnimationOption option;
604     if (isHoverChange) {
605         option.SetDuration(theme->GetHoverAnimationDuration());
606         option.SetCurve(Curves::FRICTION);
607     } else {
608         option.SetDuration(theme->GetPressAnimationDuration());
609         option.SetCurve(Curves::SHARP);
610     }
611 
612     AnimationUtils::Animate(option, [weak = WeakClaim(this)]() {
613         auto pattern = weak.Upgrade();
614         CHECK_NULL_VOID(pattern);
615         auto host = pattern->GetHost();
616         CHECK_NULL_VOID(host);
617         auto renderContext = host->GetRenderContext();
618         CHECK_NULL_VOID(renderContext);
619         renderContext->BlendBgColor(pattern->GetBgBlendColor());
620     });
621 }
622 
AddSelectIcon(RefPtr<FrameNode> & row)623 void MenuItemPattern::AddSelectIcon(RefPtr<FrameNode>& row)
624 {
625     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
626     CHECK_NULL_VOID(itemProperty);
627     if (!itemProperty->GetSelectIcon().value_or(false)) {
628         if (selectIcon_) {
629             row->RemoveChildAtIndex(0);
630             selectIcon_ = nullptr;
631             row->MarkModifyDone();
632             row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
633         }
634         return;
635     }
636     if (!selectIcon_) {
637         selectIcon_ = FrameNode::CreateFrameNode(
638             V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
639         CHECK_NULL_VOID(selectIcon_);
640     }
641     auto pipeline = PipelineBase::GetCurrentContext();
642     CHECK_NULL_VOID(pipeline);
643     auto iconTheme = pipeline->GetTheme<IconTheme>();
644     CHECK_NULL_VOID(iconTheme);
645     auto userIcon = itemProperty->GetSelectIconSrc().value_or("");
646     auto iconPath = userIcon.empty() ? iconTheme->GetIconPath(InternalResource::ResourceId::MENU_OK_SVG) : userIcon;
647     auto selectTheme = pipeline->GetTheme<SelectTheme>();
648     CHECK_NULL_VOID(selectTheme);
649     UpdateIconSrc(selectIcon_, iconPath, selectTheme->GetIconSideLength(), selectTheme->GetIconSideLength(),
650         selectTheme->GetMenuIconColor(), userIcon.empty());
651 
652     auto renderContext = selectIcon_->GetRenderContext();
653     CHECK_NULL_VOID(renderContext);
654     renderContext->SetVisible(isSelected_);
655 
656     selectIcon_->MountToParent(row, 0);
657     selectIcon_->MarkModifyDone();
658     selectIcon_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
659 }
660 
UpdateIcon(RefPtr<FrameNode> & row,bool isStart)661 void MenuItemPattern::UpdateIcon(RefPtr<FrameNode>& row, bool isStart)
662 {
663     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
664     CHECK_NULL_VOID(itemProperty);
665     auto iconSrc = isStart ? itemProperty->GetStartIcon().value_or("") : itemProperty->GetEndIcon().value_or("");
666     auto& iconNode = isStart ? startIcon_ : endIcon_;
667     if (iconSrc.empty()) {
668         row->RemoveChild(iconNode); // it's safe even if iconNode is nullptr
669         iconNode = nullptr;
670         row->MarkModifyDone();
671         row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
672         return;
673     }
674 
675     if (!iconNode) {
676         iconNode = FrameNode::CreateFrameNode(
677             V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
678         CHECK_NULL_VOID(iconNode);
679     }
680     auto pipeline = PipelineBase::GetCurrentContext();
681     CHECK_NULL_VOID(pipeline);
682     auto selectTheme = pipeline->GetTheme<SelectTheme>();
683     CHECK_NULL_VOID(selectTheme);
684     auto iconWidth = isStart ? selectTheme->GetIconSideLength() : selectTheme->GetEndIconWidth();
685     auto iconHeight = isStart ? selectTheme->GetIconSideLength() : selectTheme->GetEndIconHeight();
686     UpdateIconSrc(iconNode, iconSrc, iconWidth, iconHeight, selectTheme->GetMenuIconColor(), false);
687 
688     iconNode->MountToParent(row, ((isStart && selectIcon_) || (!isStart && label_)) ? 1 : 0);
689     iconNode->MarkModifyDone();
690     iconNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
691 }
692 
UpdateText(RefPtr<FrameNode> & row,RefPtr<MenuLayoutProperty> & menuProperty,bool isLabel)693 void MenuItemPattern::UpdateText(RefPtr<FrameNode>& row, RefPtr<MenuLayoutProperty>& menuProperty, bool isLabel)
694 {
695     auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
696     CHECK_NULL_VOID(itemProperty);
697     auto content = isLabel ? itemProperty->GetLabel().value_or("") : itemProperty->GetContent().value_or("");
698     auto& node = isLabel ? label_ : content_;
699     if (content.empty()) {
700         (void)row->RemoveChild(node); // it's safe even if node is nullptr
701         node = nullptr;
702         row->MarkModifyDone();
703         row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
704         return;
705     }
706 
707     if (!node) {
708         node = FrameNode::CreateFrameNode(
709             V2::TEXT_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<TextPattern>());
710     }
711     auto textProperty = node ? node->GetLayoutProperty<TextLayoutProperty>() : nullptr;
712     CHECK_NULL_VOID(textProperty);
713     auto renderContext = node->GetRenderContext();
714     CHECK_NULL_VOID(renderContext);
715     renderContext->UpdateClipEdge(false);
716     auto context = PipelineBase::GetCurrentContext();
717     auto theme = context ? context->GetTheme<SelectTheme>() : nullptr;
718     CHECK_NULL_VOID(theme);
719     auto fontSize = isLabel ? itemProperty->GetLabelFontSize() : itemProperty->GetFontSize();
720     UpdateFontSize(textProperty, menuProperty, fontSize, theme->GetMenuFontSize());
721     auto fontWeight = isLabel ? itemProperty->GetLabelFontWeight() : itemProperty->GetFontWeight();
722     UpdateFontWeight(textProperty, menuProperty, fontWeight);
723     auto fontStyle = isLabel ? itemProperty->GetLabelItalicFontStyle() : itemProperty->GetItalicFontStyle();
724     UpdateFontStyle(textProperty, menuProperty, fontStyle);
725     auto fontColor = isLabel ? itemProperty->GetLabelFontColor() : itemProperty->GetFontColor();
726     auto menuItemNode = GetHost();
727     UpdateFontColor(
728         node, menuProperty, fontColor, isLabel ? theme->GetSecondaryFontColor() : theme->GetMenuFontColor());
729     if (!isLabel) {
730         auto menuItemRenderContext = menuItemNode->GetRenderContext();
731         CHECK_NULL_VOID(menuItemRenderContext);
732         auto renderContext = node->GetRenderContext();
733         CHECK_NULL_VOID(renderContext);
734         if (menuItemRenderContext->HasForegroundColor()) {
735             renderContext->UpdateForegroundColor(menuItemRenderContext->GetForegroundColorValue());
736         }
737     }
738     auto fontFamily = isLabel ? itemProperty->GetLabelFontFamily() : itemProperty->GetFontFamily();
739     UpdateFontFamily(textProperty, menuProperty, fontFamily);
740     textProperty->UpdateContent(content);
741     textProperty->UpdateMaxLines(1);
742     textProperty->UpdateTextOverflow(TextOverflow::ELLIPSIS);
743     node->MountToParent(row, isLabel ? 0 : DEFAULT_NODE_SLOT);
744     node->MarkModifyDone();
745     node->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
746 }
747 
UpdateTextNodes()748 void MenuItemPattern::UpdateTextNodes()
749 {
750     auto host = GetHost();
751     CHECK_NULL_VOID(host);
752     auto menuNode = GetMenu();
753     CHECK_NULL_VOID(menuNode);
754     auto menuProperty = menuNode->GetLayoutProperty<MenuLayoutProperty>();
755     RefPtr<FrameNode> leftRow =
756         host->GetChildAtIndex(0) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(0)) : nullptr;
757     CHECK_NULL_VOID(leftRow);
758     UpdateText(leftRow, menuProperty, false);
759     RefPtr<FrameNode> rightRow =
760         host->GetChildAtIndex(1) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(1)) : nullptr;
761     CHECK_NULL_VOID(rightRow);
762     UpdateText(rightRow, menuProperty, true);
763     if (IsDisabled()) {
764         UpdateDisabledStyle();
765     }
766 }
767 
IsDisabled()768 bool MenuItemPattern::IsDisabled()
769 {
770     auto eventHub = GetHost()->GetEventHub<MenuItemEventHub>();
771     CHECK_NULL_RETURN(eventHub, true);
772     return !eventHub->IsEnabled();
773 }
774 
UpdateDisabledStyle()775 void MenuItemPattern::UpdateDisabledStyle()
776 {
777     auto context = PipelineBase::GetCurrentContext();
778     CHECK_NULL_VOID(context);
779     auto theme = context->GetTheme<SelectTheme>();
780     CHECK_NULL_VOID(theme);
781     if (content_) {
782         content_->GetRenderContext()->UpdateForegroundColor(theme->GetDisabledMenuFontColor());
783         auto textLayoutProperty = content_->GetLayoutProperty<TextLayoutProperty>();
784         CHECK_NULL_VOID(textLayoutProperty);
785         textLayoutProperty->UpdateTextColor(theme->GetDisabledMenuFontColor());
786         content_->MarkModifyDone();
787     }
788     if (label_) {
789         label_->GetRenderContext()->UpdateForegroundColor(theme->GetDisabledMenuFontColor());
790         auto labelTextLayoutProperty = label_->GetLayoutProperty<TextLayoutProperty>();
791         CHECK_NULL_VOID(labelTextLayoutProperty);
792         labelTextLayoutProperty->UpdateTextColor(theme->GetDisabledMenuFontColor());
793         label_->MarkModifyDone();
794     }
795     if (startIcon_) {
796         startIcon_->GetRenderContext()->UpdateOpacity(theme->GetDisabledFontColorAlpha());
797         startIcon_->MarkModifyDone();
798     }
799     if (endIcon_) {
800         endIcon_->GetRenderContext()->UpdateOpacity(theme->GetDisabledFontColorAlpha());
801         endIcon_->MarkModifyDone();
802     }
803 }
804 
SetAccessibilityAction()805 void MenuItemPattern::SetAccessibilityAction()
806 {
807     auto host = GetHost();
808     CHECK_NULL_VOID(host);
809     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
810     CHECK_NULL_VOID(accessibilityProperty);
811     accessibilityProperty->SetActionSelect([weakPtr = WeakClaim(this)]() {
812         const auto& pattern = weakPtr.Upgrade();
813         CHECK_NULL_VOID(pattern);
814         auto host = pattern->GetHost();
815         CHECK_NULL_VOID(host);
816         auto hub = host->GetEventHub<MenuItemEventHub>();
817         CHECK_NULL_VOID(hub);
818         auto onChange = hub->GetOnChange();
819         auto selectedChangeEvent = hub->GetSelectedChangeEvent();
820         pattern->SetChange();
821         if (selectedChangeEvent) {
822             selectedChangeEvent(pattern->IsSelected());
823         }
824         if (onChange) {
825             onChange(pattern->IsSelected());
826             pattern->RecordChangeEvent();
827         }
828         auto context = host->GetRenderContext();
829         CHECK_NULL_VOID(context);
830         pattern->MarkIsSelected(pattern->IsSelected());
831         context->OnMouseSelectUpdate(pattern->IsSelected(), ITEM_FILL_COLOR, ITEM_FILL_COLOR);
832         if (pattern->GetSubBuilder() != nullptr) {
833             pattern->ShowSubMenu();
834             return;
835         }
836 
837         pattern->CloseMenu();
838     });
839 }
840 
MarkIsSelected(bool isSelected)841 void MenuItemPattern::MarkIsSelected(bool isSelected)
842 {
843     if (isSelected_ == isSelected) {
844         return;
845     }
846     isSelected_ = isSelected;
847     auto eventHub = GetEventHub<MenuItemEventHub>();
848     CHECK_NULL_VOID(eventHub);
849     auto onChange = eventHub->GetOnChange();
850     auto selectedChangeEvent = eventHub->GetSelectedChangeEvent();
851     if (selectedChangeEvent) {
852         selectedChangeEvent(isSelected);
853     }
854     if (onChange) {
855         onChange(isSelected);
856     }
857     auto host = GetHost();
858     CHECK_NULL_VOID(host);
859     if (isSelected) {
860         eventHub->SetCurrentUIState(UI_STATE_SELECTED, isSelected);
861         host->OnAccessibilityEvent(AccessibilityEventType::SELECTED);
862     } else {
863         eventHub->SetCurrentUIState(UI_STATE_SELECTED, isSelected);
864         host->OnAccessibilityEvent(AccessibilityEventType::CHANGE);
865     }
866 }
867 
IsSelectOverlayMenu()868 bool MenuItemPattern::IsSelectOverlayMenu()
869 {
870     auto topLevelMenuPattern = GetMenuPattern(true);
871     if (!topLevelMenuPattern) {
872         return false;
873     }
874     return topLevelMenuPattern->IsSelectOverlayExtensionMenu() || topLevelMenuPattern->IsSelectOverlayCustomMenu() ||
875            topLevelMenuPattern->IsSelectOverlaySubMenu();
876 }
877 } // namespace OHOS::Ace::NG
878