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