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 #include "core/components_ng/pattern/menu/menu_item/menu_item_model_ng.h"
18
19 #include <memory>
20 #include <optional>
21 #include "menu_item_model.h"
22
23 #include "base/geometry/ng/offset_t.h"
24 #include "base/log/log.h"
25 #include "base/memory/ace_type.h"
26 #include "base/utils/utils.h"
27 #include "core/common/recorder/event_recorder.h"
28 #include "core/common/recorder/node_data_cache.h"
29 #include "core/components/common/properties/shadow_config.h"
30 #include "core/components/select/select_theme.h"
31 #include "core/components/text/text_theme.h"
32 #include "core/components/theme/icon_theme.h"
33 #include "core/components_ng/base/frame_node.h"
34 #include "core/components_ng/base/view_stack_processor.h"
35 #include "core/components_ng/pattern/image/image_pattern.h"
36 #include "core/components_ng/pattern/menu/menu_item/menu_item_event_hub.h"
37 #include "core/components_ng/pattern/menu/menu_item/menu_item_model_ng.h"
38 #include "core/components_ng/pattern/menu/menu_layout_property.h"
39 #include "core/components_ng/pattern/menu/menu_pattern.h"
40 #include "core/components_ng/pattern/menu/menu_theme.h"
41 #include "core/components_ng/pattern/menu/menu_view.h"
42 #include "core/components_ng/pattern/menu/menu_theme.h"
43 #include "core/components_ng/pattern/menu/menu_pattern.h"
44 #include "core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.h"
45 #include "core/components_ng/pattern/text/text_layout_property.h"
46 #include "core/components_ng/pattern/text/text_pattern.h"
47 #include "core/components_ng/property/border_property.h"
48 #include "core/components_v2/inspector/inspector_constants.h"
49 #include "core/pipeline/pipeline_base.h"
50
51 namespace OHOS::Ace::NG {
52 namespace {
53 const Color ITEM_FILL_COLOR = Color::TRANSPARENT;
54 constexpr double VELOCITY = 0.0f;
55 constexpr double MASS = 1.0f;
56 constexpr double STIFFNESS = 328.0f;
57 constexpr double DAMPING = 33.0f;
58 constexpr double SEMI_CIRCLE_ANGEL = 180.0f;
59 constexpr float OPACITY_EFFECT = 0.99;
60 const std::string SYSTEM_RESOURCE_PREFIX = std::string("resource:///");
61 // id of system resource start from 0x07000000
62 constexpr uint64_t MIN_SYSTEM_RESOURCE_ID = 0x07000000;
63 // id of system resource end to 0x07FFFFFF
64 constexpr uint64_t MAX_SYSTEM_RESOURCE_ID = 0x07FFFFFF;
65
UpdateFontSize(RefPtr<TextLayoutProperty> & textProperty,RefPtr<MenuLayoutProperty> & menuProperty,const std::optional<Dimension> & fontSize,const Dimension & defaultFontSize)66 void UpdateFontSize(RefPtr<TextLayoutProperty>& textProperty, RefPtr<MenuLayoutProperty>& menuProperty,
67 const std::optional<Dimension>& fontSize, const Dimension& defaultFontSize)
68 {
69 if (fontSize.has_value()) {
70 textProperty->UpdateFontSize(fontSize.value());
71 } else if (menuProperty && menuProperty->GetFontSize().has_value()) {
72 textProperty->UpdateFontSize(menuProperty->GetFontSize().value());
73 } else {
74 textProperty->UpdateFontSize(defaultFontSize);
75 }
76 }
77
UpdateFontWeight(RefPtr<TextLayoutProperty> & textProperty,RefPtr<MenuLayoutProperty> & menuProperty,const std::optional<FontWeight> & fontWeight)78 void UpdateFontWeight(RefPtr<TextLayoutProperty>& textProperty, RefPtr<MenuLayoutProperty>& menuProperty,
79 const std::optional<FontWeight>& fontWeight)
80 {
81 if (fontWeight.has_value()) {
82 textProperty->UpdateFontWeight(fontWeight.value());
83 } else if (menuProperty && menuProperty->GetFontWeight().has_value()) {
84 textProperty->UpdateFontWeight(menuProperty->GetFontWeight().value());
85 } else {
86 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
87 textProperty->UpdateFontWeight(FontWeight::MEDIUM);
88 } else {
89 textProperty->UpdateFontWeight(FontWeight::REGULAR);
90 }
91 }
92 }
93
UpdateFontStyle(RefPtr<TextLayoutProperty> & textProperty,RefPtr<MenuLayoutProperty> & menuProperty,const std::optional<Ace::FontStyle> & fontStyle)94 void UpdateFontStyle(RefPtr<TextLayoutProperty>& textProperty, RefPtr<MenuLayoutProperty>& menuProperty,
95 const std::optional<Ace::FontStyle>& fontStyle)
96 {
97 if (fontStyle.has_value()) {
98 textProperty->UpdateItalicFontStyle(fontStyle.value());
99 } else if (menuProperty && menuProperty->GetItalicFontStyle().has_value()) {
100 textProperty->UpdateItalicFontStyle(menuProperty->GetItalicFontStyle().value());
101 } else {
102 textProperty->UpdateItalicFontStyle(Ace::FontStyle::NORMAL);
103 }
104 }
105
UpdateFontColor(const RefPtr<FrameNode> & textNode,RefPtr<MenuLayoutProperty> & menuProperty,const std::optional<Color> & fontColor,const Color & defaultFontColor)106 void UpdateFontColor(const RefPtr<FrameNode>& textNode, RefPtr<MenuLayoutProperty>& menuProperty,
107 const std::optional<Color>& fontColor, const Color& defaultFontColor)
108 {
109 auto textProperty = textNode ? textNode->GetLayoutProperty<TextLayoutProperty>() : nullptr;
110 CHECK_NULL_VOID(textProperty);
111 auto renderContext = textNode->GetRenderContext();
112 CHECK_NULL_VOID(renderContext);
113 if (fontColor.has_value()) {
114 textProperty->UpdateTextColor(fontColor.value());
115 } else if (menuProperty && menuProperty->GetFontColor().has_value()) {
116 textProperty->UpdateTextColor(menuProperty->GetFontColor().value());
117 } else {
118 textProperty->UpdateTextColor(defaultFontColor);
119 }
120 }
121
UpdateFontFamily(RefPtr<TextLayoutProperty> & textProperty,RefPtr<MenuLayoutProperty> & menuProperty,const std::optional<std::vector<std::string>> & fontFamilies)122 void UpdateFontFamily(RefPtr<TextLayoutProperty>& textProperty, RefPtr<MenuLayoutProperty>& menuProperty,
123 const std::optional<std::vector<std::string>>& fontFamilies)
124 {
125 if (fontFamilies.has_value()) {
126 textProperty->UpdateFontFamily(fontFamilies.value());
127 } else if (menuProperty && menuProperty->GetFontFamily().has_value()) {
128 textProperty->UpdateFontFamily(menuProperty->GetFontFamily().value());
129 }
130 }
131
UpdateIconSrc(RefPtr<FrameNode> & node,const Dimension & horizontalSize,const Dimension & verticalSize,const Color & color,const bool & useDefaultIcon)132 void UpdateIconSrc(RefPtr<FrameNode>& node, const Dimension& horizontalSize,
133 const Dimension& verticalSize, const Color& color, const bool& useDefaultIcon)
134 {
135 auto props = node->GetLayoutProperty<ImageLayoutProperty>();
136 CHECK_NULL_VOID(props);
137 props->UpdateAlignment(Alignment::CENTER);
138 CalcSize idealSize = { CalcLength(horizontalSize), CalcLength(verticalSize) };
139 MeasureProperty layoutConstraint;
140 layoutConstraint.selfIdealSize = idealSize;
141 props->UpdateCalcLayoutProperty(layoutConstraint);
142 if (useDefaultIcon) {
143 auto iconRenderProperty = node->GetPaintProperty<ImageRenderProperty>();
144 CHECK_NULL_VOID(iconRenderProperty);
145 iconRenderProperty->UpdateSvgFillColor(color);
146 }
147 }
148 } // namespace
149
OnMountToParentDone()150 void MenuItemPattern::OnMountToParentDone()
151 {
152 UpdateTextNodes();
153 }
154
OnAttachToFrameNode()155 void MenuItemPattern::OnAttachToFrameNode()
156 {
157 RegisterOnKeyEvent();
158 RegisterOnClick();
159 RegisterOnTouch();
160 RegisterOnHover();
161 }
162
OnAttachToFrameNode()163 void CustomMenuItemPattern::OnAttachToFrameNode()
164 {
165 RegisterOnKeyEvent();
166 RegisterOnTouch();
167 }
168
OnModifyDone()169 void MenuItemPattern::OnModifyDone()
170 {
171 Pattern::OnModifyDone();
172 auto host = GetHost();
173 CHECK_NULL_VOID(host);
174
175 RefPtr<FrameNode> leftRow =
176 host->GetChildAtIndex(0) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(0)) : nullptr;
177 CHECK_NULL_VOID(leftRow);
178 AddSelectIcon(leftRow);
179 UpdateIcon(leftRow, true);
180 auto menuNode = GetMenu();
181 auto menuProperty = menuNode ? menuNode->GetLayoutProperty<MenuLayoutProperty>() : nullptr;
182 UpdateText(leftRow, menuProperty, false);
183
184 if (menuProperty) {
185 expandingMode_ = menuProperty->GetExpandingMode().value_or(SubMenuExpandingMode::SIDE);
186 expandingModeSet_ = true;
187 }
188
189 RefPtr<FrameNode> rightRow =
190 host->GetChildAtIndex(1) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(1)) : nullptr;
191 CHECK_NULL_VOID(rightRow);
192 UpdateText(rightRow, menuProperty, true);
193 UpdateIcon(rightRow, false);
194 AddExpandIcon(rightRow);
195 AddClickableArea();
196 if (IsDisabled()) {
197 UpdateDisabledStyle();
198 }
199 SetAccessibilityAction();
200
201 host->GetRenderContext()->SetClipToBounds(true);
202 if (!longPressEvent_ && Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
203 InitLongPressEvent();
204 }
205 if (expandingModeSet_) {
206 RegisterOnKeyEvent();
207 RegisterOnTouch();
208 RegisterOnHover();
209 RegisterOnClick();
210 }
211 }
212
OnAfterModifyDone()213 void MenuItemPattern::OnAfterModifyDone()
214 {
215 auto host = GetHost();
216 CHECK_NULL_VOID(host);
217 auto inspectorId = host->GetInspectorId().value_or("");
218 if (inspectorId.empty()) {
219 return;
220 }
221 auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
222 CHECK_NULL_VOID(itemProperty);
223 auto content = itemProperty->GetContent().value_or("");
224 Recorder::NodeDataCache::Get().PutMultiple(host, inspectorId, content, isSelected_);
225 }
226
RecordChangeEvent() const227 void MenuItemPattern::RecordChangeEvent() const
228 {
229 if (!Recorder::EventRecorder::Get().IsComponentRecordEnable()) {
230 return;
231 }
232 auto host = GetHost();
233 CHECK_NULL_VOID(host);
234 auto inspectorId = host->GetInspectorId().value_or("");
235 auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
236 CHECK_NULL_VOID(itemProperty);
237 auto content = itemProperty->GetContent().value_or("");
238 Recorder::EventParamsBuilder builder;
239 builder.SetId(inspectorId)
240 .SetType(host->GetTag())
241 .SetChecked(isSelected_)
242 .SetText(content)
243 .SetDescription(host->GetAutoEventParamValue(""));
244 Recorder::EventRecorder::Get().OnChange(std::move(builder));
245 Recorder::NodeDataCache::Get().PutMultiple(host, inspectorId, content, isSelected_);
246 }
247
GetMenuWrapper()248 RefPtr<FrameNode> MenuItemPattern::GetMenuWrapper()
249 {
250 auto host = GetHost();
251 CHECK_NULL_RETURN(host, nullptr);
252 auto parent = host->GetParent();
253 while (parent) {
254 if (parent->GetTag() == V2::MENU_WRAPPER_ETS_TAG || parent->GetTag() == V2::SELECT_OVERLAY_ETS_TAG) {
255 return AceType::DynamicCast<FrameNode>(parent);
256 }
257 parent = parent->GetParent();
258 }
259 return nullptr;
260 }
261
GetMenu(bool needTopMenu)262 RefPtr<FrameNode> MenuItemPattern::GetMenu(bool needTopMenu)
263 {
264 auto host = GetHost();
265 CHECK_NULL_RETURN(host, nullptr);
266 auto parent = host->GetParent();
267 RefPtr<FrameNode> menuNode = nullptr;
268 while (parent) {
269 if (parent->GetTag() == V2::MENU_ETS_TAG) {
270 menuNode = AceType::DynamicCast<FrameNode>(parent);
271 if (!needTopMenu) {
272 // innner menu
273 return menuNode;
274 }
275 }
276 parent = parent->GetParent();
277 }
278 // outter menu
279 return menuNode;
280 }
281
GetMenuPattern(bool needTopMenu)282 RefPtr<MenuPattern> MenuItemPattern::GetMenuPattern(bool needTopMenu)
283 {
284 auto menu = GetMenu(needTopMenu);
285 if (!menu) {
286 return nullptr;
287 }
288 return menu->GetPattern<MenuPattern>();
289 }
290
ShowSubMenu()291 void MenuItemPattern::ShowSubMenu()
292 {
293 auto host = GetHost();
294 CHECK_NULL_VOID(host);
295 auto menuWrapper = GetMenuWrapper();
296 CHECK_NULL_VOID(menuWrapper);
297 auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
298 CHECK_NULL_VOID(menuWrapperPattern);
299 auto hasSubMenu = menuWrapperPattern->HasStackSubMenu();
300 auto buildFunc = GetSubBuilder();
301 if (!buildFunc || isSubMenuShowed_ || IsEmbedded() ||
302 (expandingMode_ == SubMenuExpandingMode::STACK && hasSubMenu)) {
303 return;
304 }
305 // Hide SubMenu of parent Menu node
306 auto parentMenu = GetMenu();
307 CHECK_NULL_VOID(parentMenu);
308 // parentMenu no need focus
309 auto menuNode = GetMenu(true);
310 CHECK_NULL_VOID(menuNode);
311 auto layoutProps = parentMenu->GetLayoutProperty<MenuLayoutProperty>();
312 CHECK_NULL_VOID(layoutProps);
313 NG::ScopedViewStackProcessor builderViewStackProcessor;
314 buildFunc();
315 auto customNode = NG::ViewStackProcessor::GetInstance()->Finish();
316 CHECK_NULL_VOID(customNode);
317 UpdateSubmenuExpandingMode(customNode);
318 if (expandingMode_ == SubMenuExpandingMode::EMBEDDED) {
319 auto frameNode = AceType::DynamicCast<FrameNode>(customNode);
320 OnExpandChanged(frameNode);
321 return;
322 }
323
324 auto menuPattern = menuNode->GetPattern<MenuPattern>();
325 CHECK_NULL_VOID(menuPattern);
326 menuPattern->FocusViewHide();
327 auto parentMenuPattern = parentMenu->GetPattern<MenuPattern>();
328 CHECK_NULL_VOID(parentMenuPattern);
329 HideSubMenu();
330 isSubMenuShowed_ = true;
331 bool isSelectOverlayMenu = IsSelectOverlayMenu();
332 MenuParam param;
333 auto outterMenuLayoutProps = menuNode->GetLayoutProperty<MenuLayoutProperty>();
334 CHECK_NULL_VOID(outterMenuLayoutProps);
335 param.isShowInSubWindow = outterMenuLayoutProps->GetShowInSubWindowValue(false);
336 auto focusMenuRenderContext = menuNode->GetRenderContext();
337 CHECK_NULL_VOID(focusMenuRenderContext);
338 if (focusMenuRenderContext->GetBackBlurStyle().has_value()) {
339 auto focusMenuBlurStyle = focusMenuRenderContext->GetBackBlurStyle();
340 param.backgroundBlurStyle = static_cast<int>(focusMenuBlurStyle->blurStyle);
341 }
342 param.type = isSelectOverlayMenu ? MenuType::SELECT_OVERLAY_SUB_MENU : MenuType::SUB_MENU;
343 ParseMenuRadius(param);
344 auto subMenu = MenuView::Create(customNode, host->GetId(), host->GetTag(), param);
345 CHECK_NULL_VOID(subMenu);
346 ShowSubMenuHelper(subMenu);
347 menuPattern->SetShowedSubMenu(subMenu);
348 auto accessibilityProperty = subMenu->GetAccessibilityProperty<MenuAccessibilityProperty>();
349 CHECK_NULL_VOID(accessibilityProperty);
350 accessibilityProperty->SetAccessibilityIsShow(true);
351 subMenu->OnAccessibilityEvent(AccessibilityEventType::PAGE_OPEN);
352 TAG_LOGI(AceLogTag::ACE_OVERLAY, "Send event to %{public}d",
353 static_cast<int32_t>(AccessibilityEventType::PAGE_OPEN));
354 }
355
GetSubMenu(RefPtr<UINode> & customNode)356 RefPtr<FrameNode> GetSubMenu(RefPtr<UINode>& customNode)
357 {
358 CHECK_NULL_RETURN(customNode, nullptr);
359 if (customNode->GetTag() == V2::MENU_ETS_TAG) {
360 auto frameNode = AceType::DynamicCast<FrameNode>(customNode);
361 CHECK_NULL_RETURN(frameNode, nullptr);
362 return frameNode;
363 }
364 uint32_t depth = 0;
365 auto child = customNode->GetFrameChildByIndex(0, false);
366 while (child && depth < MAX_SEARCH_DEPTH) {
367 if (child->GetTag() == V2::JS_VIEW_ETS_TAG) {
368 child = child->GetFrameChildByIndex(0, false);
369 if (child && child->GetTag() == V2::JS_VIEW_ETS_TAG) {
370 child = child->GetChildAtIndex(0);
371 ++depth;
372 }
373 continue;
374 }
375 if (child->GetTag() == V2::MENU_ETS_TAG) {
376 return AceType::DynamicCast<FrameNode>(child);
377 }
378 child = child->GetChildAtIndex(0);
379 ++depth;
380 }
381 return nullptr;
382 }
383
UpdateSubmenuExpandingMode(RefPtr<UINode> & customNode)384 void MenuItemPattern::UpdateSubmenuExpandingMode(RefPtr<UINode>& customNode)
385 {
386 auto frameNode = GetSubMenu(customNode);
387 CHECK_NULL_VOID(frameNode);
388 if (frameNode->GetTag() == V2::MENU_ETS_TAG) {
389 auto props = frameNode->GetLayoutProperty<MenuLayoutProperty>();
390 CHECK_NULL_VOID(props);
391 auto pattern = frameNode->GetPattern<MenuPattern>();
392 CHECK_NULL_VOID(pattern);
393 props->UpdateExpandingMode(expandingMode_);
394 if (expandingMode_ == SubMenuExpandingMode::STACK) {
395 AddStackSubMenuHeader(frameNode);
396 pattern->SetIsStackSubmenu();
397 } else if (expandingMode_ == SubMenuExpandingMode::EMBEDDED) {
398 pattern->SetIsEmbedded();
399 return;
400 }
401 frameNode->MarkModifyDone();
402 frameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
403 }
404 }
405
ShowSubMenuHelper(const RefPtr<FrameNode> & subMenu)406 void MenuItemPattern::ShowSubMenuHelper(const RefPtr<FrameNode>& subMenu)
407 {
408 auto host = GetHost();
409 CHECK_NULL_VOID(host);
410 SetClickMenuItemId(host->GetId());
411 bool isSelectOverlayMenu = IsSelectOverlayMenu();
412 auto menuPattern = subMenu->GetPattern<MenuPattern>();
413 CHECK_NULL_VOID(menuPattern);
414 menuPattern->SetParentMenuItem(host);
415 subMenuId_ = subMenu->GetId();
416 AddSelfHoverRegion(host);
417 auto menuWrapper = GetMenuWrapper();
418 CHECK_NULL_VOID(menuWrapper);
419 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) &&
420 expandingMode_ == SubMenuExpandingMode::STACK) {
421 subMenu->MountToParent(menuWrapper);
422 menuWrapper->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
423 menuPattern->SetSubMenuShow();
424 RegisterWrapperMouseEvent();
425 } else {
426 subMenu->MountToParent(menuWrapper);
427 OffsetF offset = GetSubMenuPosition(host);
428 auto menuProps = subMenu->GetLayoutProperty<MenuLayoutProperty>();
429 CHECK_NULL_VOID(menuProps);
430 menuProps->UpdateMenuOffset(offset);
431 menuWrapper->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
432 RegisterWrapperMouseEvent();
433 }
434 // select overlay menu no need focus
435 if (!isSelectOverlayMenu) {
436 menuPattern->FocusViewShow();
437 }
438 }
439
HideSubMenu()440 void MenuItemPattern::HideSubMenu()
441 {
442 auto host = GetHost();
443 CHECK_NULL_VOID(host);
444 auto menuWrapper = GetMenuWrapper();
445 CHECK_NULL_VOID(menuWrapper);
446 auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
447 CHECK_NULL_VOID(menuWrapperPattern);
448 auto showedSubMenu = menuWrapperPattern->GetShowedSubMenu();
449 if (showedSubMenu) {
450 auto showedSubMenuPattern = showedSubMenu->GetPattern<MenuPattern>();
451 CHECK_NULL_VOID(showedSubMenuPattern);
452 auto showedMenuItem = showedSubMenuPattern->GetParentMenuItem();
453 CHECK_NULL_VOID(showedMenuItem);
454 if (showedMenuItem->GetId() != host->GetId()) {
455 auto outterMenu = GetMenu(true);
456 CHECK_NULL_VOID(outterMenu);
457 auto outterMenuPattern = outterMenu->GetPattern<MenuPattern>();
458 CHECK_NULL_VOID(outterMenuPattern);
459 outterMenuPattern->HideSubMenu();
460 }
461 }
462 }
463
OnExpandChanged(const RefPtr<FrameNode> & expandableNode)464 void MenuItemPattern::OnExpandChanged(const RefPtr<FrameNode>& expandableNode)
465 {
466 CHECK_NULL_VOID(expandableNode);
467 isExpanded_ = !isExpanded_;
468 if (isExpanded_) {
469 embeddedMenu_ = expandableNode;
470 ShowEmbeddedExpandMenu(embeddedMenu_);
471 } else {
472 HideEmbeddedExpandMenu(embeddedMenu_);
473 embeddedMenu_ = nullptr;
474 }
475 }
476
ShowEmbeddedExpandMenu(const RefPtr<FrameNode> & expandableNode)477 void MenuItemPattern::ShowEmbeddedExpandMenu(const RefPtr<FrameNode>& expandableNode)
478 {
479 CHECK_NULL_VOID(expandableNode);
480 auto host = GetHost();
481 CHECK_NULL_VOID(host);
482 auto menuWrapper = GetMenuWrapper();
483 CHECK_NULL_VOID(menuWrapper);
484 auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
485 CHECK_NULL_VOID(menuWrapperPattern);
486 menuWrapperPattern->IncreaseEmbeddedSubMenuCount();
487 auto rightRow = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(1));
488 CHECK_NULL_VOID(rightRow);
489 auto imageNode = AceType::DynamicCast<FrameNode>(rightRow->GetChildren().back());
490 CHECK_NULL_VOID(imageNode);
491 auto imageContext = imageNode->GetRenderContext();
492 CHECK_NULL_VOID(imageContext);
493 imageContext->UpdateTransformRotate(Vector5F(0.0f, 0.0f, 1.0f, 0.0f, 0.0f));
494
495 auto expandableAreaContext = expandableNode->GetRenderContext();
496 CHECK_NULL_VOID(expandableAreaContext);
497 expandableAreaContext->UpdateBackShadow(ShadowConfig::NoneShadow);
498 auto itemSize = host->GetGeometryNode()->GetFrameSize();
499 expandableAreaContext->ClipWithRRect(RectF(0.0f, 0.0f, itemSize.Width(), 0.0f),
500 RadiusF(EdgeF(0.0f, 0.0f)));
501
502 AnimationOption option = AnimationOption();
503 auto rotateOption = AceType::MakeRefPtr<InterpolatingSpring>(VELOCITY, MASS, STIFFNESS, DAMPING);
504 option.SetCurve(rotateOption);
505 AnimationUtils::Animate(option, [host, rightRow, expandableNode, expandableAreaContext, imageContext]() {
506 expandableNode->MountToParent(host, EXPANDABLE_AREA_VIEW_INDEX);
507 imageContext->UpdateTransformRotate(Vector5F(0.0f, 0.0f, 1.0f, SEMI_CIRCLE_ANGEL, 0.0f));
508 expandableNode->MarkModifyDone();
509 expandableNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
510
511 auto pipeline = PipelineContext::GetCurrentContext();
512 CHECK_NULL_VOID(pipeline);
513 pipeline->FlushUITasks();
514 auto expandableAreaFrameSize = expandableNode->GetGeometryNode()->GetFrameSize();
515 expandableAreaContext->ClipWithRRect(RectF(0.0f, 0.0f, expandableAreaFrameSize.Width(),
516 expandableAreaFrameSize.Height()), RadiusF(EdgeF(0.0f, 0.0f)));
517 });
518 }
519
HideEmbeddedExpandMenu(const RefPtr<FrameNode> & expandableNode)520 void MenuItemPattern::HideEmbeddedExpandMenu(const RefPtr<FrameNode>& expandableNode)
521 {
522 CHECK_NULL_VOID(expandableNode);
523 auto host = GetHost();
524 CHECK_NULL_VOID(host);
525 auto menuWrapper = GetMenuWrapper();
526 CHECK_NULL_VOID(menuWrapper);
527 auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
528 CHECK_NULL_VOID(menuWrapperPattern);
529 menuWrapperPattern->DecreaseEmbeddedSubMenuCount();
530 auto expandableAreaContext = expandableNode->GetRenderContext();
531 CHECK_NULL_VOID(expandableAreaContext);
532
533 AnimationOption option = AnimationOption();
534 auto rotateOption = AceType::MakeRefPtr<InterpolatingSpring>(VELOCITY, MASS, STIFFNESS, DAMPING);
535 option.SetCurve(rotateOption);
536 RefPtr<ChainedTransitionEffect> opacity = AceType::MakeRefPtr<ChainedOpacityEffect>(OPACITY_EFFECT);
537 expandableAreaContext->UpdateChainedTransition(opacity);
538
539 AnimationUtils::Animate(option, [this, host, expandableNode]() {
540 host->RemoveChild(expandableNode, true);
541 host->MarkModifyDone();
542 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
543 auto rightRow = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(1));
544 CHECK_NULL_VOID(rightRow);
545 auto imageNode = AceType::DynamicCast<FrameNode>(rightRow->GetChildren().back());
546 CHECK_NULL_VOID(imageNode);
547 auto imageContext = imageNode->GetRenderContext();
548 CHECK_NULL_VOID(imageContext);
549 imageContext->UpdateTransformRotate(Vector5F(0.0f, 0.0f, 1.0f, 0.0f, 0.0f));
550 auto pipeline = PipelineContext::GetCurrentContext();
551 CHECK_NULL_VOID(pipeline);
552 pipeline->FlushUITasks();
553 });
554 }
555
CloseMenu()556 void MenuItemPattern::CloseMenu()
557 {
558 // no need close for selection menu
559 if (IsSelectOverlayMenu()) {
560 return;
561 }
562 auto menuWrapper = GetMenuWrapper();
563 CHECK_NULL_VOID(menuWrapper);
564 auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
565 CHECK_NULL_VOID(menuWrapperPattern);
566 menuWrapperPattern->UpdateMenuAnimation(menuWrapper);
567 menuWrapperPattern->HideMenu();
568 }
569
RegisterOnClick()570 void MenuItemPattern::RegisterOnClick()
571 {
572 auto host = GetHost();
573 CHECK_NULL_VOID(host);
574 if (!onClickEvent_) {
575 auto event = [weak = WeakClaim(this)](GestureEvent& /* info */) {
576 auto pattern = weak.Upgrade();
577 CHECK_NULL_VOID(pattern);
578 pattern->OnClick();
579 };
580 onClickEvent_ = MakeRefPtr<ClickEvent>(std::move(event));
581 }
582 auto gestureHub = host->GetOrCreateGestureEventHub();
583 CHECK_NULL_VOID(gestureHub);
584 if (expandingMode_ == SubMenuExpandingMode::EMBEDDED && clickableArea_) {
585 auto clickableAreaGestureHub = clickableArea_->GetOrCreateGestureEventHub();
586 CHECK_NULL_VOID(clickableAreaGestureHub);
587 gestureHub->RemoveClickEvent(onClickEvent_);
588 clickableAreaGestureHub->AddClickEvent(onClickEvent_);
589 } else if (!onClickEventSet_) {
590 gestureHub->AddClickEvent(onClickEvent_);
591 onClickEventSet_ = true;
592 }
593 }
594
RegisterOnTouch()595 void MenuItemPattern::RegisterOnTouch()
596 {
597 if (!onTouchEvent_) {
598 auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
599 auto pattern = weak.Upgrade();
600 CHECK_NULL_VOID(pattern);
601 pattern->OnTouch(info);
602 };
603 onTouchEvent_ = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
604 }
605 auto host = GetHost();
606 CHECK_NULL_VOID(host);
607 auto gestureHub = host->GetOrCreateGestureEventHub();
608 CHECK_NULL_VOID(gestureHub);
609 if (expandingMode_ == SubMenuExpandingMode::EMBEDDED && clickableArea_) {
610 auto clickableAreaGestureHub = clickableArea_->GetOrCreateGestureEventHub();
611 CHECK_NULL_VOID(clickableAreaGestureHub);
612 gestureHub->RemoveTouchEvent(onTouchEvent_);
613 clickableAreaGestureHub->AddTouchEvent(onTouchEvent_);
614 } else if (!onTouchEventSet_) {
615 gestureHub->AddTouchEvent(onTouchEvent_);
616 onTouchEventSet_ = true;
617 }
618 }
619
RegisterOnHover()620 void MenuItemPattern::RegisterOnHover()
621 {
622 if (!onHoverEvent_) {
623 auto mouseTask = [weak = WeakClaim(this)](bool isHover) {
624 auto pattern = weak.Upgrade();
625 CHECK_NULL_VOID(pattern);
626 pattern->OnHover(isHover);
627 };
628 onHoverEvent_ = MakeRefPtr<InputEvent>(std::move(mouseTask));
629 }
630 auto host = GetHost();
631 CHECK_NULL_VOID(host);
632 auto inputHub = host->GetOrCreateInputEventHub();
633 CHECK_NULL_VOID(inputHub);
634 if (expandingMode_ == SubMenuExpandingMode::EMBEDDED && clickableArea_) {
635 auto clickableAreaInputHub = clickableArea_->GetOrCreateInputEventHub();
636 CHECK_NULL_VOID(clickableAreaInputHub);
637 inputHub->RemoveOnHoverEvent(onHoverEvent_);
638 clickableAreaInputHub->AddOnHoverEvent(onHoverEvent_);
639 } else if (!onHoverEventSet_) {
640 inputHub->AddOnHoverEvent(onHoverEvent_);
641 onHoverEventSet_ = true;
642 }
643 }
644
RegisterOnKeyEvent()645 void MenuItemPattern::RegisterOnKeyEvent()
646 {
647 auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
648 auto pattern = wp.Upgrade();
649 CHECK_NULL_RETURN(pattern, false);
650 return pattern->OnKeyEvent(event);
651 };
652 auto event = std::move(onKeyEvent);
653 auto host = GetHost();
654 CHECK_NULL_VOID(host);
655 auto focusHub = host->GetOrCreateFocusHub();
656 CHECK_NULL_VOID(focusHub);
657 if (expandingMode_ == SubMenuExpandingMode::EMBEDDED && clickableArea_) {
658 auto clickableAreaFocusHub = clickableArea_->GetOrCreateFocusHub();
659 CHECK_NULL_VOID(clickableAreaFocusHub);
660 focusHub->SetOnKeyEventInternal(nullptr);
661 clickableAreaFocusHub->SetOnKeyEventInternal(event);
662 } else if (!onKeyEventSet_) {
663 focusHub->SetOnKeyEventInternal(event);
664 onKeyEventSet_ = true;
665 }
666 }
667
OnClick()668 void MenuItemPattern::OnClick()
669 {
670 auto host = GetHost();
671 CHECK_NULL_VOID(host);
672 if (onClickAIMenuItem_) {
673 onClickAIMenuItem_();
674 }
675 auto menuWrapper = GetMenuWrapper();
676 auto menuWrapperPattern = menuWrapper ? menuWrapper->GetPattern<MenuWrapperPattern>() : nullptr;
677 auto hasSubMenu = menuWrapperPattern ? menuWrapperPattern->HasStackSubMenu() : false;
678 if (expandingMode_ == SubMenuExpandingMode::STACK && !IsSubMenu() && hasSubMenu) {
679 return;
680 }
681 if (expandingMode_ == SubMenuExpandingMode::STACK && IsStackSubmenuHeader()) {
682 menuWrapperPattern->HideSubMenu();
683 return;
684 }
685 auto hub = host->GetEventHub<MenuItemEventHub>();
686 CHECK_NULL_VOID(hub);
687 auto onChange = hub->GetOnChange();
688 auto selectedChangeEvent = hub->GetSelectedChangeEvent();
689 SetChange();
690 if (selectedChangeEvent) {
691 selectedChangeEvent(IsSelected());
692 }
693 if (onChange) {
694 onChange(IsSelected());
695 RecordChangeEvent();
696 }
697 host->OnAccessibilityEvent(AccessibilityEventType::SELECTED);
698 auto menuNode = GetMenu();
699 CHECK_NULL_VOID(menuNode);
700 auto menuPattern = menuNode->GetPattern<MenuPattern>();
701 CHECK_NULL_VOID(menuPattern);
702 auto lastSelectedItem = menuPattern->GetLastSelectedItem();
703 if (lastSelectedItem && lastSelectedItem != host) {
704 auto pattern = lastSelectedItem->GetPattern<MenuItemPattern>();
705 CHECK_NULL_VOID(pattern);
706 pattern->SetChange();
707 }
708 menuPattern->SetLastSelectedItem(host);
709 if (GetSubBuilder() != nullptr && (expandingMode_ == SubMenuExpandingMode::SIDE ||
710 (expandingMode_ == SubMenuExpandingMode::STACK && !IsSubMenu() && !hasSubMenu) ||
711 (expandingMode_ == SubMenuExpandingMode::EMBEDDED && !IsEmbedded()))) {
712 ShowSubMenu();
713 return;
714 }
715 // hide menu when menu item is clicked
716 CloseMenu();
717 }
718
OnTouch(const TouchEventInfo & info)719 void MenuItemPattern::OnTouch(const TouchEventInfo& info)
720 {
721 auto menuWrapper = GetMenuWrapper();
722 // When menu wrapper exists, the pressed state is handed over to the menu wrapper
723 if (menuWrapper && menuWrapper->GetTag() == V2::MENU_WRAPPER_ETS_TAG) {
724 return;
725 }
726 // change menu item paint props on press
727 auto touchType = info.GetTouches().front().GetTouchType();
728 if (touchType == TouchType::DOWN) {
729 // change background color, update press status
730 NotifyPressStatus(true);
731 } else if (touchType == TouchType::UP) {
732 NotifyPressStatus(false);
733 }
734 }
735
NotifyPressStatus(bool isPress)736 void MenuItemPattern::NotifyPressStatus(bool isPress)
737 {
738 auto host = GetHost();
739 CHECK_NULL_VOID(host);
740 auto pipeline = host->GetContext();
741 CHECK_NULL_VOID(pipeline);
742 auto theme = pipeline->GetTheme<SelectTheme>();
743 CHECK_NULL_VOID(theme);
744 auto props = GetPaintProperty<MenuItemPaintProperty>();
745 CHECK_NULL_VOID(props);
746 auto menu = GetMenu();
747 CHECK_NULL_VOID(menu);
748 auto menuPattern = menu->GetPattern<MenuPattern>();
749 CHECK_NULL_VOID(menuPattern);
750 auto parent = AceType::DynamicCast<UINode>(host->GetParent());
751 auto menuWrapper = GetMenuWrapper();
752 auto menuWrapperPattern = menuWrapper ? menuWrapper->GetPattern<MenuWrapperPattern>() : nullptr;
753
754 // do not change color for stacked level-1 menu items if level-2 is shown
755 auto canChangeColor = !(expandingMode_ == SubMenuExpandingMode::STACK
756 && menuWrapperPattern && menuWrapperPattern->HasStackSubMenu() && !IsSubMenu());
757 if (!canChangeColor) return;
758 if (isPress) {
759 // change background color, update press status
760 SetBgBlendColor(GetSubBuilder() ? theme->GetHoverColor() : theme->GetClickedColor());
761 if (menuWrapperPattern) {
762 menuWrapperPattern->SetLastTouchItem(host);
763 }
764 props->UpdatePress(true);
765 menuPattern->OnItemPressed(parent, index_, true);
766 } else {
767 SetBgBlendColor(isHovered_ ? theme->GetHoverColor() : Color::TRANSPARENT);
768 props->UpdatePress(false);
769 menuPattern->OnItemPressed(parent, index_, false);
770 }
771 PlayBgColorAnimation(false);
772 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
773 }
774
OnTouch(const TouchEventInfo & info)775 void CustomMenuItemPattern::OnTouch(const TouchEventInfo& info)
776 {
777 auto touchType = info.GetTouches().front().GetTouchType();
778
779 // close menu when touch up
780 // can't use onClick because that conflicts with interactions developers might set to the customNode
781 // recognize gesture as click if touch up position is close to last touch down position
782 if (touchType == TouchType::DOWN) {
783 lastTouchOffset_ = std::make_unique<Offset>(info.GetTouches().front().GetLocalLocation());
784 } else if (touchType == TouchType::UP) {
785 auto touchUpOffset = info.GetTouches().front().GetLocalLocation();
786 if (lastTouchOffset_ && (touchUpOffset - *lastTouchOffset_).GetDistance() <= DEFAULT_CLICK_DISTANCE) {
787 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
788 HandleOnChange();
789 }
790 CloseMenu();
791 }
792 lastTouchOffset_.reset();
793 }
794 }
795
HandleOnChange()796 void CustomMenuItemPattern::HandleOnChange()
797 {
798 auto host = GetHost();
799 CHECK_NULL_VOID(host);
800 auto hub = host->GetEventHub<MenuItemEventHub>();
801 CHECK_NULL_VOID(hub);
802 auto onChange = hub->GetOnChange();
803 auto selectedChangeEvent = hub->GetSelectedChangeEvent();
804 SetChange();
805 if (selectedChangeEvent) {
806 TAG_LOGI(AceLogTag::ACE_MENU, "trigger selectedChangeEvent");
807 selectedChangeEvent(IsSelected());
808 }
809 if (onChange) {
810 TAG_LOGI(AceLogTag::ACE_MENU, "trigger onChange");
811 onChange(IsSelected());
812 }
813 }
814
OnHover(bool isHover)815 void MenuItemPattern::OnHover(bool isHover)
816 {
817 isHovered_ = isHover;
818 auto pipeline = PipelineBase::GetCurrentContext();
819 CHECK_NULL_VOID(pipeline);
820 auto theme = pipeline->GetTheme<SelectTheme>();
821 CHECK_NULL_VOID(theme);
822 auto props = GetPaintProperty<MenuItemPaintProperty>();
823 CHECK_NULL_VOID(props);
824 auto menu = GetMenu(false);
825 CHECK_NULL_VOID(menu);
826 auto menuPattern = menu->GetPattern<MenuPattern>();
827 CHECK_NULL_VOID(menuPattern);
828 auto host = GetHost();
829 CHECK_NULL_VOID(host);
830 auto parent = AceType::DynamicCast<UINode>(host->GetParent());
831
832 if (isHover || isSubMenuShowed_) {
833 // keep hover color when subMenu showed
834 SetBgBlendColor(theme->GetHoverColor());
835 ShowSubMenu();
836 props->UpdateHover(true);
837 menuPattern->OnItemPressed(parent, index_, true, true);
838 } else {
839 SetBgBlendColor(Color::TRANSPARENT);
840 props->UpdateHover(false);
841 menuPattern->OnItemPressed(parent, index_, false, true);
842 }
843 PlayBgColorAnimation();
844 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
845 }
846
OnVisibleChange(bool isVisible)847 void MenuItemPattern::OnVisibleChange(bool isVisible)
848 {
849 auto host = GetHost();
850 CHECK_NULL_VOID(host);
851 auto parentNode = host->GetParent();
852 CHECK_NULL_VOID(parentNode);
853 if (parentNode->GetTag() == V2::MENU_ITEM_GROUP_ETS_TAG) {
854 parentNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
855 }
856 }
857
OnKeyEvent(const KeyEvent & event)858 bool MenuItemPattern::OnKeyEvent(const KeyEvent& event)
859 {
860 if (event.action != KeyAction::DOWN) {
861 return false;
862 }
863 auto host = GetHost();
864 CHECK_NULL_RETURN(host, false);
865 auto focusHub = host->GetOrCreateFocusHub();
866 CHECK_NULL_RETURN(focusHub, false);
867 if (event.code == KeyCode::KEY_ENTER) {
868 focusHub->OnClick(event);
869 return true;
870 }
871 if (event.code == KeyCode::KEY_DPAD_RIGHT && GetSubBuilder() && !isSubMenuShowed_) {
872 auto pipeline = PipelineBase::GetCurrentContext();
873 CHECK_NULL_RETURN(pipeline, false);
874 auto theme = pipeline->GetTheme<SelectTheme>();
875 CHECK_NULL_RETURN(theme, false);
876 SetBgBlendColor(theme->GetHoverColor());
877 PlayBgColorAnimation();
878 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
879 ShowSubMenu();
880 return true;
881 }
882 return false;
883 }
884
OnKeyEvent(const KeyEvent & event)885 bool CustomMenuItemPattern::OnKeyEvent(const KeyEvent& event)
886 {
887 if (event.action != KeyAction::DOWN) {
888 return false;
889 }
890 auto host = GetHost();
891 CHECK_NULL_RETURN(host, false);
892 auto focusHub = host->GetOrCreateFocusHub();
893 CHECK_NULL_RETURN(focusHub, false);
894 if (event.code == KeyCode::KEY_ENTER || event.code == KeyCode::KEY_SPACE) {
895 focusHub->OnClick(event);
896 CloseMenu();
897 return true;
898 }
899 return false;
900 }
901
InitLongPressEvent()902 void MenuItemPattern::InitLongPressEvent()
903 {
904 auto gesture = GetHost()->GetOrCreateGestureEventHub();
905 CHECK_NULL_VOID(gesture);
906 auto longPressCallback = [weak = WeakClaim(this)](GestureEvent& info) {
907 auto itemPattern = weak.Upgrade();
908 auto menuWrapper = itemPattern->GetMenuWrapper();
909 CHECK_NULL_VOID(menuWrapper);
910 auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
911 CHECK_NULL_VOID(menuWrapperPattern);
912 auto topLevelMenuPattern = itemPattern->GetMenuPattern(true);
913 CHECK_NULL_VOID(topLevelMenuPattern);
914 if (itemPattern && itemPattern->GetSubBuilder() != nullptr &&
915 menuWrapperPattern->GetPreviewMode() == MenuPreviewMode::NONE &&
916 !(topLevelMenuPattern->IsSelectOverlayCustomMenu())) {
917 itemPattern->ShowSubMenu();
918 }
919 };
920 longPressEvent_ = MakeRefPtr<LongPressEvent>(std::move(longPressCallback));
921 gesture->SetLongPressEvent(longPressEvent_);
922 }
923
RegisterWrapperMouseEvent()924 void MenuItemPattern::RegisterWrapperMouseEvent()
925 {
926 auto menuWrapper = GetMenuWrapper();
927 if (menuWrapper && !wrapperMouseEvent_) {
928 auto inputHub = menuWrapper->GetOrCreateInputEventHub();
929 CHECK_NULL_VOID(inputHub);
930 auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
931 CHECK_NULL_VOID(menuWrapperPattern);
932
933 auto mouseTask = [weak = WeakClaim(this), menuWrapperPattern](MouseInfo& info) {
934 auto pattern = weak.Upgrade();
935 CHECK_NULL_VOID(pattern);
936 if (menuWrapperPattern) {
937 menuWrapperPattern->HandleMouseEvent(info, pattern);
938 }
939 };
940 wrapperMouseEvent_ = MakeRefPtr<InputEvent>(std::move(mouseTask));
941 inputHub->AddOnMouseEvent(wrapperMouseEvent_);
942 }
943 }
944
AddSelfHoverRegion(const RefPtr<FrameNode> & targetNode)945 void MenuItemPattern::AddSelfHoverRegion(const RefPtr<FrameNode>& targetNode)
946 {
947 OffsetF topLeftPoint;
948 OffsetF bottomRightPoint;
949 auto frameSize = targetNode->GetGeometryNode()->GetMarginFrameSize();
950 topLeftPoint = targetNode->GetPaintRectOffset();
951 bottomRightPoint = targetNode->GetPaintRectOffset() + OffsetF(frameSize.Width(), frameSize.Height());
952 AddHoverRegions(topLeftPoint, bottomRightPoint);
953 }
954
GetSubMenuPosition(const RefPtr<FrameNode> & targetNode)955 OffsetF MenuItemPattern::GetSubMenuPosition(const RefPtr<FrameNode>& targetNode)
956 { // show menu at left top point of targetNode
957 auto frameSize = targetNode->GetGeometryNode()->GetMarginFrameSize();
958 OffsetF position = targetNode->GetPaintRectOffset() + OffsetF(frameSize.Width(), 0.0);
959 return position;
960 }
961
962
AddHoverRegions(const OffsetF & topLeftPoint,const OffsetF & bottomRightPoint)963 void MenuItemPattern::AddHoverRegions(const OffsetF& topLeftPoint, const OffsetF& bottomRightPoint)
964 {
965 TouchRegion hoverRegion = TouchRegion(
966 Offset(topLeftPoint.GetX(), topLeftPoint.GetY()), Offset(bottomRightPoint.GetX(), bottomRightPoint.GetY()));
967 hoverRegions_.emplace_back(hoverRegion);
968 }
969
IsInHoverRegions(double x,double y)970 bool MenuItemPattern::IsInHoverRegions(double x, double y)
971 {
972 for (auto hoverRegion : hoverRegions_) {
973 if (hoverRegion.ContainsInRegion(x, y)) {
974 return true;
975 }
976 }
977 return false;
978 }
979
PlayBgColorAnimation(bool isHoverChange)980 void MenuItemPattern::PlayBgColorAnimation(bool isHoverChange)
981 {
982 auto pipeline = PipelineBase::GetCurrentContext();
983 CHECK_NULL_VOID(pipeline);
984 auto theme = pipeline->GetTheme<SelectTheme>();
985 CHECK_NULL_VOID(theme);
986 AnimationOption option;
987 if (isHoverChange) {
988 option.SetDuration(theme->GetHoverAnimationDuration());
989 option.SetCurve(Curves::FRICTION);
990 } else {
991 option.SetDuration(theme->GetPressAnimationDuration());
992 option.SetCurve(Curves::SHARP);
993 }
994
995 AnimationUtils::Animate(option, [weak = WeakClaim(this)]() {
996 auto pattern = weak.Upgrade();
997 CHECK_NULL_VOID(pattern);
998 auto clickableArea = pattern->GetClickableArea();
999 CHECK_NULL_VOID(clickableArea);
1000 auto renderContext = clickableArea->GetRenderContext();
1001 CHECK_NULL_VOID(renderContext);
1002 renderContext->BlendBgColor(pattern->GetBgBlendColor());
1003 });
1004 }
1005
UpdateImageNode(RefPtr<FrameNode> & row,RefPtr<FrameNode> & selectIcon)1006 void MenuItemPattern::UpdateImageNode(RefPtr<FrameNode>& row, RefPtr<FrameNode>& selectIcon)
1007 {
1008 auto pipeline = PipelineBase::GetCurrentContext();
1009 CHECK_NULL_VOID(pipeline);
1010 auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1011 CHECK_NULL_VOID(itemProperty);
1012 auto symbol = itemProperty->GetSelectSymbol();
1013 if (itemProperty->GetSelectIconSrc().value_or("").empty() &&
1014 Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1015 // iamge -> symbol
1016 row->RemoveChild(selectIcon);
1017 selectIcon = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG,
1018 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<TextPattern>(); });
1019 auto selectTheme = pipeline->GetTheme<SelectTheme>();
1020 CHECK_NULL_VOID(selectTheme);
1021 auto props = selectIcon->GetLayoutProperty<TextLayoutProperty>();
1022 CHECK_NULL_VOID(props);
1023 props->UpdateFontSize(selectTheme->GetEndIconWidth());
1024 props->UpdateSymbolColorList({ selectTheme->GetMenuIconColor() });
1025 symbol(AccessibilityManager::WeakClaim(AccessibilityManager::RawPtr(selectIcon)));
1026 } else {
1027 // image -> image
1028 auto iconTheme = pipeline->GetTheme<IconTheme>();
1029 CHECK_NULL_VOID(iconTheme);
1030 auto userIcon = itemProperty->GetSelectIconSrc().value_or("");
1031 auto iconPath = userIcon.empty() ? iconTheme->GetIconPath(InternalResource::ResourceId::MENU_OK_SVG) : userIcon;
1032 auto selectTheme = pipeline->GetTheme<SelectTheme>();
1033 CHECK_NULL_VOID(selectTheme);
1034 ImageSourceInfo imageSourceInfo;
1035 imageSourceInfo.SetSrc(iconPath);
1036 auto props = selectIcon->GetLayoutProperty<ImageLayoutProperty>();
1037 CHECK_NULL_VOID(props);
1038 props->UpdateImageSourceInfo(imageSourceInfo);
1039 UpdateIconSrc(selectIcon, selectTheme->GetIconSideLength(), selectTheme->GetIconSideLength(),
1040 selectTheme->GetMenuIconColor(), userIcon.empty());
1041 }
1042 }
1043
UpdateSymbolNode(RefPtr<FrameNode> & row,RefPtr<FrameNode> & selectIcon)1044 void MenuItemPattern::UpdateSymbolNode(RefPtr<FrameNode>& row, RefPtr<FrameNode>& selectIcon)
1045 {
1046 auto pipeline = PipelineBase::GetCurrentContext();
1047 CHECK_NULL_VOID(pipeline);
1048 auto props = selectIcon->GetLayoutProperty<TextLayoutProperty>();
1049 CHECK_NULL_VOID(props);
1050 auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1051 CHECK_NULL_VOID(itemProperty);
1052 auto selectTheme = pipeline->GetTheme<SelectTheme>();
1053 CHECK_NULL_VOID(selectTheme);
1054 auto symbol = itemProperty->GetSelectSymbol();
1055 if (itemProperty->GetSelectIconSrc().value_or("").empty()) {
1056 // symbol -> symbol
1057 props->UpdateFontSize(selectTheme->GetEndIconWidth());
1058 props->UpdateSymbolColorList({ selectTheme->GetMenuIconColor() });
1059 if (symbol) {
1060 symbol(AccessibilityManager::WeakClaim(AccessibilityManager::RawPtr(selectIcon)));
1061 } else {
1062 auto menuTheme = pipeline->GetTheme<MenuTheme>();
1063 CHECK_NULL_VOID(menuTheme);
1064 uint32_t symbolId = menuTheme->GetSymbolId();
1065 props->UpdateSymbolSourceInfo(SymbolSourceInfo(symbolId));
1066 }
1067 } else {
1068 // symbol -> image
1069 row->RemoveChild(selectIcon);
1070 selectIcon = FrameNode::CreateFrameNode(
1071 V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
1072 ImageSourceInfo imageSourceInfo;
1073 auto userIcon = itemProperty->GetSelectIconSrc().value_or("");
1074 imageSourceInfo.SetSrc(userIcon);
1075 auto props = selectIcon->GetLayoutProperty<ImageLayoutProperty>();
1076 CHECK_NULL_VOID(props);
1077 props->UpdateImageSourceInfo(imageSourceInfo);
1078 UpdateIconSrc(selectIcon, selectTheme->GetIconSideLength(), selectTheme->GetIconSideLength(),
1079 selectTheme->GetMenuIconColor(), userIcon.empty());
1080 }
1081 }
1082
1083
AddSelectIcon(RefPtr<FrameNode> & row)1084 void MenuItemPattern::AddSelectIcon(RefPtr<FrameNode>& row)
1085 {
1086 auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1087 CHECK_NULL_VOID(itemProperty);
1088 if (!itemProperty->GetSelectIcon().value_or(false)) {
1089 if (selectIcon_) {
1090 row->RemoveChildAtIndex(0);
1091 selectIcon_ = nullptr;
1092 itemProperty->SetSelectSymbol(nullptr);
1093 row->MarkModifyDone();
1094 row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1095 }
1096 return;
1097 }
1098 if (!selectIcon_) {
1099 if (!itemProperty->GetSelectIconSrc().value_or("").empty() ||
1100 Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1101 selectIcon_ = FrameNode::CreateFrameNode(
1102 V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
1103 } else {
1104 selectIcon_ = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG,
1105 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<TextPattern>(); });
1106 }
1107 }
1108 if (selectIcon_->GetTag() == V2::IMAGE_ETS_TAG) {
1109 UpdateImageNode(row, selectIcon_);
1110 } else {
1111 UpdateSymbolNode(row, selectIcon_);
1112 }
1113 auto renderContext = selectIcon_->GetRenderContext();
1114 CHECK_NULL_VOID(renderContext);
1115 renderContext->SetVisible(isSelected_);
1116
1117 selectIcon_->MountToParent(row, 0);
1118 selectIcon_->MarkModifyDone();
1119 selectIcon_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1120 }
1121
AddExpandIcon(RefPtr<FrameNode> & row)1122 void MenuItemPattern::AddExpandIcon(RefPtr<FrameNode>& row)
1123 {
1124 auto host = GetHost();
1125 CHECK_NULL_VOID(host);
1126 auto menuNode = GetMenu();
1127 auto menuPattern = menuNode ? menuNode->GetPattern<MenuPattern>() : nullptr;
1128 auto menuProperty = menuNode ? menuNode->GetLayoutProperty<MenuLayoutProperty>() : nullptr;
1129 CHECK_NULL_VOID(menuProperty);
1130 auto canExpand = GetSubBuilder() != nullptr && menuPattern
1131 && !menuPattern->IsEmbedded() && !menuPattern->IsStackSubmenu()
1132 && (expandingMode_ == SubMenuExpandingMode::EMBEDDED || expandingMode_ == SubMenuExpandingMode::STACK);
1133 if (!canExpand) {
1134 if (expandIcon_) {
1135 row->RemoveChild(expandIcon_);
1136 expandIcon_ = nullptr;
1137 row->MarkModifyDone();
1138 row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1139 }
1140 return;
1141 }
1142 if (!expandIcon_) {
1143 expandIcon_ = FrameNode::CreateFrameNode(
1144 V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
1145 CHECK_NULL_VOID(expandIcon_);
1146 }
1147 auto pipeline = PipelineBase::GetCurrentContext();
1148 CHECK_NULL_VOID(pipeline);
1149 auto iconTheme = pipeline->GetTheme<IconTheme>();
1150 CHECK_NULL_VOID(iconTheme);
1151 auto iconPath = iconTheme->GetIconPath(
1152 expandingMode_ == SubMenuExpandingMode::STACK
1153 ? InternalResource::ResourceId::IC_PUBLIC_ARROW_RIGHT_SVG
1154 : InternalResource::ResourceId::IC_PUBLIC_ARROW_DOWN_SVG);
1155 auto selectTheme = pipeline->GetTheme<SelectTheme>();
1156 CHECK_NULL_VOID(selectTheme);
1157 ImageSourceInfo imageSourceInfo(iconPath);
1158 auto props = expandIcon_->GetLayoutProperty<ImageLayoutProperty>();
1159 CHECK_NULL_VOID(props);
1160 props->UpdateImageSourceInfo(imageSourceInfo);
1161 UpdateIconSrc(expandIcon_, selectTheme->GetIconSideLength(), selectTheme->GetIconSideLength(),
1162 selectTheme->GetMenuIconColor(), true);
1163
1164 auto expandIconIndex = row->GetChildren().size();
1165 expandIcon_->MountToParent(row, expandIconIndex);
1166 expandIcon_->MarkModifyDone();
1167 expandIcon_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1168 }
1169
AddClickableArea()1170 void MenuItemPattern::AddClickableArea()
1171 {
1172 if (expandingMode_ == SubMenuExpandingMode::EMBEDDED && GetSubBuilder() != nullptr && !IsEmbedded() &&
1173 !clickableArea_) {
1174 auto host = GetHost();
1175 CHECK_NULL_VOID(host);
1176 auto hostAccessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
1177 CHECK_NULL_VOID(hostAccessibilityProperty);
1178 hostAccessibilityProperty->SetAccessibilityLevel(AccessibilityProperty::Level::NO_STR);
1179 auto clickableArea = FrameNode::CreateFrameNode(V2::ROW_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
1180 AceType::MakeRefPtr<LinearLayoutPattern>(false));
1181 CHECK_NULL_VOID(clickableArea);
1182 auto pipeline = PipelineBase::GetCurrentContext();
1183 CHECK_NULL_VOID(pipeline);
1184 auto theme = pipeline->GetTheme<SelectTheme>();
1185 CHECK_NULL_VOID(theme);
1186 BorderRadiusProperty border;
1187 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1188 border.SetRadius(theme->GetMenuDefaultInnerRadius());
1189 } else {
1190 border.SetRadius(theme->GetInnerBorderRadius());
1191 }
1192 auto clickableContext = clickableArea->GetRenderContext();
1193 CHECK_NULL_VOID(clickableContext);
1194 clickableContext->UpdateBorderRadius(border);
1195 auto menuProperty = host->GetLayoutProperty<MenuItemLayoutProperty>();
1196 CHECK_NULL_VOID(menuProperty);
1197 std::string content = menuProperty->GetContent().value_or("");
1198 std::string label = menuProperty->GetLabel().value_or("");
1199 auto accessibilityProperty = clickableArea->GetAccessibilityProperty<AccessibilityProperty>();
1200 CHECK_NULL_VOID(accessibilityProperty);
1201 accessibilityProperty->SetAccessibilityText(content + "," + label);
1202 clickableArea_ = clickableArea;
1203 clickableArea_->MountToParent(host, CLICKABLE_AREA_VIEW_INDEX);
1204 }
1205 }
1206
AddStackSubMenuHeader(RefPtr<FrameNode> & menuNode)1207 void MenuItemPattern::AddStackSubMenuHeader(RefPtr<FrameNode>& menuNode)
1208 {
1209 auto host = GetHost();
1210 CHECK_NULL_VOID(host);
1211 auto layoutProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1212 CHECK_NULL_VOID(layoutProperty);
1213 auto pipeline = PipelineBase::GetCurrentContext();
1214 CHECK_NULL_VOID(pipeline);
1215 auto iconTheme = pipeline->GetTheme<IconTheme>();
1216 CHECK_NULL_VOID(iconTheme);
1217 auto selectTheme = pipeline->GetTheme<SelectTheme>();
1218 CHECK_NULL_VOID(selectTheme);
1219 auto iconPath = iconTheme->GetIconPath(InternalResource::ResourceId::IC_PUBLIC_ARROW_RIGHT_SVG);
1220 ImageSourceInfo imageSourceInfo;
1221 imageSourceInfo.SetSrc(iconPath);
1222 imageSourceInfo.SetFillColor(selectTheme->GetMenuIconColor());
1223 auto content = layoutProperty->GetContent().value_or(layoutProperty->GetLabel().value_or(""));
1224
1225 MenuItemProperties menuItemProps;
1226 menuItemProps.content = content;
1227 menuItemProps.endIcon = imageSourceInfo;
1228 MenuItemModelNG menuItemModel;
1229 menuItemModel.Create(menuItemProps);
1230 auto stack = ViewStackProcessor::GetInstance();
1231
1232 auto titleItem = AceType::DynamicCast<FrameNode>(stack->Finish());
1233 auto pattern = titleItem->GetPattern<MenuItemPattern>();
1234 CHECK_NULL_VOID(pattern);
1235 pattern->SetIsStackSubmenuHeader();
1236 titleItem->MountToParent(menuNode, 0);
1237 }
1238
GetClickableArea()1239 RefPtr<FrameNode> MenuItemPattern::GetClickableArea()
1240 {
1241 auto host = GetHost();
1242 CHECK_NULL_RETURN(host, nullptr);
1243 auto clickableArea = host->GetChildAtIndex(CLICKABLE_AREA_VIEW_INDEX);
1244 CHECK_NULL_RETURN(clickableArea, host);
1245 auto clickableAreaNode = AceType::DynamicCast<FrameNode>(clickableArea);
1246 CHECK_NULL_RETURN(clickableAreaNode, host);
1247 return clickableAreaNode;
1248 }
1249
UpdateIcon(RefPtr<FrameNode> & row,bool isStart)1250 void MenuItemPattern::UpdateIcon(RefPtr<FrameNode>& row, bool isStart)
1251 {
1252 auto pipeline = PipelineBase::GetCurrentContext();
1253 CHECK_NULL_VOID(pipeline);
1254 auto selectTheme = pipeline->GetTheme<SelectTheme>();
1255 CHECK_NULL_VOID(selectTheme);
1256 auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1257 CHECK_NULL_VOID(itemProperty);
1258 ImageSourceInfo defaultImageSourceInfo;
1259 auto iconSrc = isStart ? itemProperty->GetStartIcon().value_or(defaultImageSourceInfo)
1260 : itemProperty->GetEndIcon().value_or(defaultImageSourceInfo);
1261 auto symbol = isStart ? itemProperty->GetStartSymbol() : itemProperty->GetEndSymbol();
1262 auto& iconNode = isStart ? startIcon_ : endIcon_;
1263 if (iconSrc.GetSrc().empty() && symbol == nullptr) {
1264 row->RemoveChild(iconNode); // it's safe even if iconNode is nullptr
1265 iconNode = nullptr;
1266 if (isStart) {
1267 itemProperty->SetStartSymbol(nullptr);
1268 } else {
1269 itemProperty->SetEndSymbol(nullptr);
1270 }
1271 row->MarkModifyDone();
1272 row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1273 return;
1274 }
1275 if (!iconNode) {
1276 if (symbol) {
1277 iconNode = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG,
1278 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<TextPattern>(); });
1279 } else {
1280 iconNode = FrameNode::CreateFrameNode(
1281 V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
1282 }
1283 CHECK_NULL_VOID(iconNode);
1284 }
1285 if (iconNode->GetTag() == V2::IMAGE_ETS_TAG) {
1286 UpdateImageIcon(row, iconNode, iconSrc, symbol, isStart);
1287 } else {
1288 UpdateSymbolIcon(row, iconNode, iconSrc, symbol, isStart);
1289 }
1290 iconNode->MountToParent(row, ((isStart && selectIcon_) || (!isStart && label_)) ? 1 : 0);
1291 iconNode->MarkModifyDone();
1292 iconNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1293 }
1294
UpdateImageIcon(RefPtr<FrameNode> & row,RefPtr<FrameNode> & iconNode,ImageSourceInfo & iconSrc,std::function<void (WeakPtr<NG::FrameNode>)> & symbol,bool isStart)1295 void MenuItemPattern::UpdateImageIcon(RefPtr<FrameNode>& row, RefPtr<FrameNode>& iconNode, ImageSourceInfo& iconSrc,
1296 std::function<void(WeakPtr<NG::FrameNode>)>& symbol, bool isStart)
1297 {
1298 auto pipeline = PipelineBase::GetCurrentContext();
1299 CHECK_NULL_VOID(pipeline);
1300 auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1301 CHECK_NULL_VOID(itemProperty);
1302 auto selectTheme = pipeline->GetTheme<SelectTheme>();
1303 CHECK_NULL_VOID(selectTheme);
1304 if (symbol) {
1305 // iamge -> symbol
1306 row->RemoveChild(iconNode);
1307 iconNode = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
1308 []() { return AceType::MakeRefPtr<TextPattern>(); });
1309
1310 auto props = iconNode->GetLayoutProperty<TextLayoutProperty>();
1311 CHECK_NULL_VOID(props);
1312 props->UpdateFontSize(selectTheme->GetEndIconWidth());
1313 props->UpdateSymbolColorList({ selectTheme->GetMenuIconColor() });
1314 symbol(AccessibilityManager::WeakClaim(AccessibilityManager::RawPtr(iconNode)));
1315 } else {
1316 // image -> image
1317 auto iconWidth = isStart ? selectTheme->GetIconSideLength() : selectTheme->GetEndIconWidth();
1318 auto iconHeight = isStart ? selectTheme->GetIconSideLength() : selectTheme->GetEndIconHeight();
1319 ImageSourceInfo imageSourceInfo(iconSrc);
1320 auto props = iconNode->GetLayoutProperty<ImageLayoutProperty>();
1321 CHECK_NULL_VOID(props);
1322 props->UpdateImageSourceInfo(imageSourceInfo);
1323 bool useDefaultThemeIcon = UseDefaultThemeIcon(imageSourceInfo);
1324 UpdateIconSrc(iconNode, iconWidth, iconHeight, selectTheme->GetMenuIconColor(), useDefaultThemeIcon);
1325 }
1326 }
1327
UseDefaultThemeIcon(const ImageSourceInfo & imageSourceInfo)1328 bool MenuItemPattern::UseDefaultThemeIcon(const ImageSourceInfo& imageSourceInfo)
1329 {
1330 if (imageSourceInfo.IsSvg()) {
1331 auto src = imageSourceInfo.GetSrc();
1332 auto srcId = src.substr(SYSTEM_RESOURCE_PREFIX.size(),
1333 src.substr(0, src.rfind(".svg")).size() - SYSTEM_RESOURCE_PREFIX.size());
1334 if (srcId.find("ohos_") != std::string::npos) {
1335 return true;
1336 }
1337 uint64_t parsedSrcId = StringUtils::StringToLongUint(srcId);
1338 return (parsedSrcId != 0
1339 && (parsedSrcId >= MIN_SYSTEM_RESOURCE_ID)
1340 && (parsedSrcId <= MAX_SYSTEM_RESOURCE_ID));
1341 }
1342 return false;
1343 }
1344
UpdateSymbolIcon(RefPtr<FrameNode> & row,RefPtr<FrameNode> & iconNode,ImageSourceInfo & iconSrc,std::function<void (WeakPtr<NG::FrameNode>)> & symbol,bool isStart)1345 void MenuItemPattern::UpdateSymbolIcon(RefPtr<FrameNode>& row, RefPtr<FrameNode>& iconNode, ImageSourceInfo& iconSrc,
1346 std::function<void(WeakPtr<NG::FrameNode>)>& symbol, bool isStart)
1347 {
1348 auto pipeline = PipelineBase::GetCurrentContext();
1349 CHECK_NULL_VOID(pipeline);
1350 auto props = iconNode->GetLayoutProperty<TextLayoutProperty>();
1351 CHECK_NULL_VOID(props);
1352 auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1353 CHECK_NULL_VOID(itemProperty);
1354 auto selectTheme = pipeline->GetTheme<SelectTheme>();
1355 CHECK_NULL_VOID(selectTheme);
1356 if (symbol) {
1357 // symbol -> symbol
1358 props->UpdateFontSize(selectTheme->GetEndIconWidth());
1359 props->UpdateSymbolColorList({ selectTheme->GetMenuIconColor() });
1360 symbol(AccessibilityManager::WeakClaim(AccessibilityManager::RawPtr(iconNode)));
1361 } else {
1362 // symbol -> image
1363 row->RemoveChild(iconNode);
1364 iconNode = FrameNode::CreateFrameNode(
1365 V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
1366 auto iconWidth = isStart ? selectTheme->GetIconSideLength() : selectTheme->GetEndIconWidth();
1367 auto iconHeight = isStart ? selectTheme->GetIconSideLength() : selectTheme->GetEndIconHeight();
1368 ImageSourceInfo imageSourceInfo(iconSrc);
1369 auto props = iconNode->GetLayoutProperty<ImageLayoutProperty>();
1370 CHECK_NULL_VOID(props);
1371 props->UpdateImageSourceInfo(imageSourceInfo);
1372 UpdateIconSrc(iconNode, iconWidth, iconHeight, selectTheme->GetMenuIconColor(), false);
1373 }
1374 }
1375
UpdateText(RefPtr<FrameNode> & row,RefPtr<MenuLayoutProperty> & menuProperty,bool isLabel)1376 void MenuItemPattern::UpdateText(RefPtr<FrameNode>& row, RefPtr<MenuLayoutProperty>& menuProperty, bool isLabel)
1377 {
1378 auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1379 CHECK_NULL_VOID(itemProperty);
1380 auto content = isLabel ? itemProperty->GetLabel().value_or("") : itemProperty->GetContent().value_or("");
1381 auto& node = isLabel ? label_ : content_;
1382 if (content.empty()) {
1383 (void)row->RemoveChild(node); // it's safe even if node is nullptr
1384 node = nullptr;
1385 row->MarkModifyDone();
1386 row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1387 return;
1388 }
1389
1390 if (!node) {
1391 node = FrameNode::CreateFrameNode(
1392 V2::TEXT_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<TextPattern>());
1393 }
1394 auto textProperty = node ? node->GetLayoutProperty<TextLayoutProperty>() : nullptr;
1395 CHECK_NULL_VOID(textProperty);
1396 auto renderContext = node->GetRenderContext();
1397 CHECK_NULL_VOID(renderContext);
1398 renderContext->UpdateClipEdge(false);
1399 auto context = PipelineBase::GetCurrentContext();
1400 auto theme = context ? context->GetTheme<SelectTheme>() : nullptr;
1401 CHECK_NULL_VOID(theme);
1402 auto layoutDirection = itemProperty->GetNonAutoLayoutDirection();
1403 TextAlign textAlign = static_cast<TextAlign>(theme->GetMenuItemContentAlign());
1404 if (layoutDirection == TextDirection::RTL) {
1405 if (textAlign == TextAlign::LEFT) {
1406 textAlign = TextAlign::RIGHT;
1407 } else if (textAlign ==TextAlign::RIGHT) {
1408 textAlign = TextAlign::LEFT;
1409 } else if (textAlign == TextAlign::START) {
1410 textAlign = TextAlign::END;
1411 } else if (textAlign == TextAlign::END) {
1412 textAlign = TextAlign::START;
1413 }
1414 }
1415
1416 UpdateFont(menuProperty, theme, isLabel);
1417 textProperty->UpdateContent(content);
1418 UpdateTextOverflow(textProperty, theme);
1419 node->MountToParent(row, isLabel ? 0 : DEFAULT_NODE_SLOT);
1420 node->MarkModifyDone();
1421 node->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1422 }
1423
UpdateTextOverflow(RefPtr<TextLayoutProperty> & textProperty,RefPtr<SelectTheme> & theme)1424 void MenuItemPattern::UpdateTextOverflow(RefPtr<TextLayoutProperty>& textProperty,
1425 RefPtr<SelectTheme>& theme)
1426 {
1427 if (theme && Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_THIRTEEN)) {
1428 if (theme->GetExpandDisplay()) {
1429 textProperty->UpdateTextOverflow(TextOverflow::ELLIPSIS);
1430 textProperty->UpdateMaxLines(1);
1431 } else {
1432 textProperty->UpdateMaxLines(std::numeric_limits<int32_t>::max());
1433 }
1434 } else {
1435 textProperty->UpdateTextOverflow(TextOverflow::ELLIPSIS);
1436 textProperty->UpdateMaxLines(1);
1437 }
1438 UpdateMaxLinesFromTheme(textProperty);
1439 }
1440
UpdateFont(RefPtr<MenuLayoutProperty> & menuProperty,RefPtr<SelectTheme> & theme,bool isLabel)1441 void MenuItemPattern::UpdateFont(RefPtr<MenuLayoutProperty>& menuProperty, RefPtr<SelectTheme>& theme, bool isLabel)
1442 {
1443 auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1444 CHECK_NULL_VOID(itemProperty);
1445 auto& node = isLabel ? label_ : content_;
1446 auto textProperty = node ? node->GetLayoutProperty<TextLayoutProperty>() : nullptr;
1447 CHECK_NULL_VOID(textProperty);
1448
1449 auto fontSize = isLabel ? itemProperty->GetLabelFontSize() : itemProperty->GetFontSize();
1450 UpdateFontSize(textProperty, menuProperty, fontSize, theme->GetMenuFontSize());
1451 auto fontWeight = isLabel ? itemProperty->GetLabelFontWeight() : itemProperty->GetFontWeight();
1452 UpdateFontWeight(textProperty, menuProperty, fontWeight);
1453 auto fontStyle = isLabel ? itemProperty->GetLabelItalicFontStyle() : itemProperty->GetItalicFontStyle();
1454 UpdateFontStyle(textProperty, menuProperty, fontStyle);
1455 auto fontColor = isLabel ? itemProperty->GetLabelFontColor() : itemProperty->GetFontColor();
1456 auto menuItemNode = GetHost();
1457 UpdateFontColor(
1458 node, menuProperty, fontColor, isLabel ? theme->GetSecondaryFontColor() : theme->GetMenuFontColor());
1459 if (!isLabel) {
1460 auto menuItemRenderContext = menuItemNode->GetRenderContext();
1461 CHECK_NULL_VOID(menuItemRenderContext);
1462 auto renderContext = node->GetRenderContext();
1463 CHECK_NULL_VOID(renderContext);
1464 if (menuItemRenderContext->HasForegroundColor()) {
1465 textProperty->UpdateTextColor(menuItemRenderContext->GetForegroundColorValue());
1466 renderContext->UpdateForegroundColor(menuItemRenderContext->GetForegroundColorValue());
1467 }
1468 }
1469 auto fontFamily = isLabel ? itemProperty->GetLabelFontFamily() : itemProperty->GetFontFamily();
1470 UpdateFontFamily(textProperty, menuProperty, fontFamily);
1471 }
1472
UpdateMaxLinesFromTheme(RefPtr<TextLayoutProperty> & textProperty)1473 void MenuItemPattern::UpdateMaxLinesFromTheme(RefPtr<TextLayoutProperty>& textProperty)
1474 {
1475 auto host = GetHost();
1476 CHECK_NULL_VOID(host);
1477 auto pipeline = host->GetContext();
1478 CHECK_NULL_VOID(pipeline);
1479 auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
1480 CHECK_NULL_VOID(menuTheme);
1481 auto fontScale = pipeline->GetFontScale();
1482 if (NearEqual(fontScale, menuTheme->GetBigFontSizeScale()) ||
1483 NearEqual(fontScale, menuTheme->GetLargeFontSizeScale()) ||
1484 NearEqual(fontScale, menuTheme->GetMaxFontSizeScale())) {
1485 textProperty->UpdateMaxLines(menuTheme->GetTextMaxLines());
1486 }
1487 }
1488
UpdateTextNodes()1489 void MenuItemPattern::UpdateTextNodes()
1490 {
1491 auto host = GetHost();
1492 CHECK_NULL_VOID(host);
1493 auto menuNode = GetMenu();
1494 CHECK_NULL_VOID(menuNode);
1495 auto menuProperty = menuNode->GetLayoutProperty<MenuLayoutProperty>();
1496 RefPtr<FrameNode> leftRow =
1497 host->GetChildAtIndex(0) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(0)) : nullptr;
1498 CHECK_NULL_VOID(leftRow);
1499 UpdateText(leftRow, menuProperty, false);
1500 RefPtr<FrameNode> rightRow =
1501 host->GetChildAtIndex(1) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(1)) : nullptr;
1502 CHECK_NULL_VOID(rightRow);
1503 UpdateText(rightRow, menuProperty, true);
1504 if (IsDisabled()) {
1505 UpdateDisabledStyle();
1506 }
1507 host->MarkModifyDone();
1508 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1509 }
1510
IsDisabled()1511 bool MenuItemPattern::IsDisabled()
1512 {
1513 auto eventHub = GetHost()->GetEventHub<MenuItemEventHub>();
1514 CHECK_NULL_RETURN(eventHub, true);
1515 return !eventHub->IsEnabled();
1516 }
1517
UpdateDisabledStyle()1518 void MenuItemPattern::UpdateDisabledStyle()
1519 {
1520 auto context = PipelineBase::GetCurrentContext();
1521 CHECK_NULL_VOID(context);
1522 auto theme = context->GetTheme<SelectTheme>();
1523 CHECK_NULL_VOID(theme);
1524 if (content_) {
1525 content_->GetRenderContext()->UpdateOpacity(theme->GetDisabledFontColorAlpha());
1526 content_->MarkModifyDone();
1527 }
1528 if (label_) {
1529 label_->GetRenderContext()->UpdateOpacity(theme->GetDisabledFontColorAlpha());
1530 label_->MarkModifyDone();
1531 }
1532 if (startIcon_) {
1533 startIcon_->GetRenderContext()->UpdateOpacity(theme->GetDisabledFontColorAlpha());
1534 startIcon_->MarkModifyDone();
1535 }
1536 if (endIcon_) {
1537 endIcon_->GetRenderContext()->UpdateOpacity(theme->GetDisabledFontColorAlpha());
1538 endIcon_->MarkModifyDone();
1539 }
1540 }
1541
SetAccessibilityAction()1542 void MenuItemPattern::SetAccessibilityAction()
1543 {
1544 auto host = GetHost();
1545 CHECK_NULL_VOID(host);
1546 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
1547 CHECK_NULL_VOID(accessibilityProperty);
1548 accessibilityProperty->SetActionSelect([weakPtr = WeakClaim(this)]() {
1549 const auto& pattern = weakPtr.Upgrade();
1550 CHECK_NULL_VOID(pattern);
1551 auto host = pattern->GetHost();
1552 CHECK_NULL_VOID(host);
1553 auto hub = host->GetEventHub<MenuItemEventHub>();
1554 CHECK_NULL_VOID(hub);
1555 auto onChange = hub->GetOnChange();
1556 auto selectedChangeEvent = hub->GetSelectedChangeEvent();
1557 pattern->SetChange();
1558 if (selectedChangeEvent) {
1559 selectedChangeEvent(pattern->IsSelected());
1560 }
1561 if (onChange) {
1562 onChange(pattern->IsSelected());
1563 pattern->RecordChangeEvent();
1564 }
1565 auto context = host->GetRenderContext();
1566 CHECK_NULL_VOID(context);
1567 pattern->MarkIsSelected(pattern->IsSelected());
1568 context->OnMouseSelectUpdate(pattern->IsSelected(), ITEM_FILL_COLOR, ITEM_FILL_COLOR);
1569 if (pattern->GetSubBuilder() != nullptr) {
1570 pattern->ShowSubMenu();
1571 return;
1572 }
1573
1574 pattern->CloseMenu();
1575 });
1576 }
1577
MarkIsSelected(bool isSelected)1578 void MenuItemPattern::MarkIsSelected(bool isSelected)
1579 {
1580 if (isSelected_ == isSelected) {
1581 return;
1582 }
1583 isSelected_ = isSelected;
1584 auto eventHub = GetEventHub<MenuItemEventHub>();
1585 CHECK_NULL_VOID(eventHub);
1586 auto onChange = eventHub->GetOnChange();
1587 auto selectedChangeEvent = eventHub->GetSelectedChangeEvent();
1588 if (selectedChangeEvent) {
1589 selectedChangeEvent(isSelected);
1590 }
1591 if (onChange) {
1592 onChange(isSelected);
1593 }
1594 auto host = GetHost();
1595 CHECK_NULL_VOID(host);
1596 if (isSelected) {
1597 eventHub->SetCurrentUIState(UI_STATE_SELECTED, isSelected);
1598 host->OnAccessibilityEvent(AccessibilityEventType::SELECTED);
1599 } else {
1600 eventHub->SetCurrentUIState(UI_STATE_SELECTED, isSelected);
1601 host->OnAccessibilityEvent(AccessibilityEventType::CHANGE);
1602 }
1603 }
1604
IsSelectOverlayMenu()1605 bool MenuItemPattern::IsSelectOverlayMenu()
1606 {
1607 auto topLevelMenuPattern = GetMenuPattern(true);
1608 if (!topLevelMenuPattern) {
1609 return false;
1610 }
1611 return topLevelMenuPattern->IsSelectOverlayExtensionMenu() || topLevelMenuPattern->IsSelectOverlayCustomMenu() ||
1612 topLevelMenuPattern->IsSelectOverlaySubMenu();
1613 }
1614
ParseMenuRadius(MenuParam & param)1615 void MenuItemPattern::ParseMenuRadius(MenuParam& param)
1616 {
1617 auto menuWrapperNode = GetMenuWrapper();
1618 CHECK_NULL_VOID(menuWrapperNode);
1619 auto menuWrapperPattern = menuWrapperNode->GetPattern<MenuWrapperPattern>();
1620 CHECK_NULL_VOID(menuWrapperPattern);
1621
1622 if (menuWrapperPattern->GetHasCustomRadius()) {
1623 auto outterMenuNode = GetMenu(true);
1624 CHECK_NULL_VOID(outterMenuNode);
1625 auto menuLayoutProp = outterMenuNode->GetLayoutProperty<MenuLayoutProperty>();
1626 CHECK_NULL_VOID(menuLayoutProp);
1627 if (menuLayoutProp->GetBorderRadius().has_value()) {
1628 BorderRadiusProperty borderRadius = menuLayoutProp->GetBorderRadiusValue();
1629 param.borderRadius = std::make_optional(borderRadius);
1630 }
1631 }
1632 }
1633
IsSubMenu()1634 bool MenuItemPattern::IsSubMenu()
1635 {
1636 auto topLevelMenuPattern = GetMenuPattern(true);
1637 if (!topLevelMenuPattern) {
1638 return false;
1639 }
1640 return topLevelMenuPattern->IsSubMenu();
1641 }
1642
IsEmbedded()1643 bool MenuItemPattern::IsEmbedded()
1644 {
1645 auto parentMenuPattern = GetMenuPattern();
1646 return parentMenuPattern ? parentMenuPattern->IsEmbedded() : false;
1647 }
1648
ModifyDivider()1649 void MenuItemPattern::ModifyDivider()
1650 {
1651 auto menu = GetMenu();
1652 CHECK_NULL_VOID(menu);
1653 auto menuProperty = menu->GetLayoutProperty<MenuLayoutProperty>();
1654 CHECK_NULL_VOID(menuProperty);
1655 auto divider = menuProperty->GetItemDivider();
1656 if (divider.has_value()) {
1657 auto host = GetHost();
1658 CHECK_NULL_VOID(host);
1659 auto paintProperty = host->GetPaintProperty<MenuItemPaintProperty>();
1660 CHECK_NULL_VOID(paintProperty);
1661 paintProperty->UpdateStrokeWidth(divider->strokeWidth);
1662 paintProperty->UpdateStartMargin(divider->startMargin);
1663 paintProperty->UpdateEndMargin(divider->endMargin);
1664 paintProperty->UpdateDividerColor(divider->color);
1665 host->MarkModifyDone();
1666 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1667 }
1668 }
1669
UpdateNeedDivider(bool need)1670 void MenuItemPattern::UpdateNeedDivider(bool need)
1671 {
1672 auto host = GetHost();
1673 CHECK_NULL_VOID(host);
1674 auto paintProperty = host->GetPaintProperty<MenuItemPaintProperty>();
1675 CHECK_NULL_VOID(paintProperty);
1676 paintProperty->UpdateNeedDivider(need);
1677 if (need) {
1678 ModifyDivider();
1679 }
1680 }
1681
GetDividerStroke()1682 float MenuItemPattern::GetDividerStroke()
1683 {
1684 auto host = GetHost();
1685 CHECK_NULL_RETURN(host, 0.0f);
1686 auto props = host->GetPaintProperty<MenuItemPaintProperty>();
1687 CHECK_NULL_RETURN(props, 0.0f);
1688 return props->GetStrokeWidth().value_or(Dimension(0.0f, DimensionUnit::PX)).ConvertToPx();
1689 }
1690
FindTouchedEmbeddedMenuItem(const OffsetF & position)1691 RefPtr<FrameNode> MenuItemPattern::FindTouchedEmbeddedMenuItem(const OffsetF& position)
1692 {
1693 auto host = GetHost();
1694 CHECK_NULL_RETURN(host, nullptr);
1695 if (expandingMode_ != SubMenuExpandingMode::EMBEDDED || !isExpanded_
1696 || embeddedMenu_ == nullptr || embeddedMenu_->GetTag() != V2::MENU_ETS_TAG) {
1697 return host;
1698 }
1699 CHECK_NULL_RETURN(clickableArea_, host);
1700 auto clickableAreaOffset = clickableArea_->GetPaintRectOffset();
1701 auto clickableAreaSize = clickableArea_->GetGeometryNode()->GetFrameSize();
1702 auto clickableAreaZone = RectF(clickableAreaOffset.GetX(), clickableAreaOffset.GetY(),
1703 clickableAreaSize.Width(), clickableAreaSize.Height());
1704 if (clickableAreaZone.IsInRegion(PointF(position.GetX(), position.GetY()))) {
1705 return host;
1706 }
1707 RefPtr<FrameNode> menuItem = nullptr;
1708 for (const auto& child : embeddedMenu_->GetChildren()) {
1709 if (child->GetTag() == V2::MENU_ITEM_ETS_TAG) {
1710 menuItem = AceType::DynamicCast<FrameNode>(child);
1711 }
1712 if (menuItem) {
1713 auto menuItemOffset = menuItem->GetPaintRectOffset();
1714 auto menuItemSize = menuItem->GetGeometryNode()->GetFrameSize();
1715 auto menuItemZone = RectF(menuItemOffset.GetX(), menuItemOffset.GetY(),
1716 menuItemSize.Width(), menuItemSize.Height());
1717 if (menuItemZone.IsInRegion(PointF(position.GetX(), position.GetY()))) {
1718 break;
1719 } else {
1720 menuItem = nullptr;
1721 }
1722 }
1723 }
1724 return menuItem;
1725 }
1726 } // namespace OHOS::Ace::NG
1727