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