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