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
352 subMenu->OnAccessibilityEvent(AccessibilityEventType::PAGE_OPEN);
353 TAG_LOGI(AceLogTag::ACE_OVERLAY, "Send event to %{public}d",
354 static_cast<int32_t>(AccessibilityEventType::PAGE_OPEN));
355 }
356
GetSubMenu(RefPtr<UINode> & customNode)357 RefPtr<FrameNode> GetSubMenu(RefPtr<UINode>& customNode)
358 {
359 CHECK_NULL_RETURN(customNode, nullptr);
360 if (customNode->GetTag() == V2::MENU_ETS_TAG) {
361 auto frameNode = AceType::DynamicCast<FrameNode>(customNode);
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 bool isSelectOverlayMenu = IsSelectOverlayMenu();
411 auto menuPattern = subMenu->GetPattern<MenuPattern>();
412 CHECK_NULL_VOID(menuPattern);
413 menuPattern->SetParentMenuItem(host);
414 subMenuId_ = subMenu->GetId();
415 AddSelfHoverRegion(host);
416 auto menuWrapper = GetMenuWrapper();
417 CHECK_NULL_VOID(menuWrapper);
418 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) &&
419 expandingMode_ == SubMenuExpandingMode::STACK) {
420 SetClickMenuItemId(host->GetId());
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 auto menuNode = GetMenu();
698 CHECK_NULL_VOID(menuNode);
699 auto menuPattern = menuNode->GetPattern<MenuPattern>();
700 CHECK_NULL_VOID(menuPattern);
701 auto lastSelectedItem = menuPattern->GetLastSelectedItem();
702 if (lastSelectedItem && lastSelectedItem != host) {
703 auto pattern = lastSelectedItem->GetPattern<MenuItemPattern>();
704 CHECK_NULL_VOID(pattern);
705 pattern->SetChange();
706 }
707 menuPattern->SetLastSelectedItem(host);
708 if (GetSubBuilder() != nullptr && (expandingMode_ == SubMenuExpandingMode::SIDE ||
709 (expandingMode_ == SubMenuExpandingMode::STACK && !IsSubMenu() && !hasSubMenu) ||
710 (expandingMode_ == SubMenuExpandingMode::EMBEDDED && !IsEmbedded()))) {
711 ShowSubMenu();
712 return;
713 }
714 // hide menu when menu item is clicked
715 CloseMenu();
716 }
717
OnTouch(const TouchEventInfo & info)718 void MenuItemPattern::OnTouch(const TouchEventInfo& info)
719 {
720 auto menuWrapper = GetMenuWrapper();
721 // When menu wrapper exists, the pressed state is handed over to the menu wrapper
722 if (menuWrapper && menuWrapper->GetTag() == V2::MENU_WRAPPER_ETS_TAG) {
723 return;
724 }
725 // change menu item paint props on press
726 auto touchType = info.GetTouches().front().GetTouchType();
727 if (touchType == TouchType::DOWN) {
728 // change background color, update press status
729 NotifyPressStatus(true);
730 } else if (touchType == TouchType::UP) {
731 NotifyPressStatus(false);
732 }
733 }
734
NotifyPressStatus(bool isPress)735 void MenuItemPattern::NotifyPressStatus(bool isPress)
736 {
737 auto host = GetHost();
738 CHECK_NULL_VOID(host);
739 auto pipeline = host->GetContext();
740 CHECK_NULL_VOID(pipeline);
741 auto theme = pipeline->GetTheme<SelectTheme>();
742 CHECK_NULL_VOID(theme);
743 auto props = GetPaintProperty<MenuItemPaintProperty>();
744 CHECK_NULL_VOID(props);
745 auto menu = GetMenu();
746 CHECK_NULL_VOID(menu);
747 auto menuPattern = menu->GetPattern<MenuPattern>();
748 CHECK_NULL_VOID(menuPattern);
749 auto parent = AceType::DynamicCast<UINode>(host->GetParent());
750 auto menuWrapper = GetMenuWrapper();
751 auto menuWrapperPattern = menuWrapper ? menuWrapper->GetPattern<MenuWrapperPattern>() : nullptr;
752
753 // do not change color for stacked level-1 menu items if level-2 is shown
754 auto canChangeColor = !(expandingMode_ == SubMenuExpandingMode::STACK
755 && menuWrapperPattern && menuWrapperPattern->HasStackSubMenu() && !IsSubMenu());
756 if (!canChangeColor) return;
757 if (isPress) {
758 // change background color, update press status
759 SetBgBlendColor(GetSubBuilder() ? theme->GetHoverColor() : theme->GetClickedColor());
760 if (menuWrapperPattern) {
761 menuWrapperPattern->SetLastTouchItem(host);
762 }
763 props->UpdatePress(true);
764 menuPattern->OnItemPressed(parent, index_, true);
765 } else {
766 SetBgBlendColor(isHovered_ ? theme->GetHoverColor() : Color::TRANSPARENT);
767 props->UpdatePress(false);
768 menuPattern->OnItemPressed(parent, index_, false);
769 }
770 PlayBgColorAnimation(false);
771 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
772 }
773
OnTouch(const TouchEventInfo & info)774 void CustomMenuItemPattern::OnTouch(const TouchEventInfo& info)
775 {
776 auto touchType = info.GetTouches().front().GetTouchType();
777
778 // close menu when touch up
779 // can't use onClick because that conflicts with interactions developers might set to the customNode
780 // recognize gesture as click if touch up position is close to last touch down position
781 if (touchType == TouchType::DOWN) {
782 lastTouchOffset_ = std::make_unique<Offset>(info.GetTouches().front().GetLocalLocation());
783 } else if (touchType == TouchType::UP) {
784 auto touchUpOffset = info.GetTouches().front().GetLocalLocation();
785 if (lastTouchOffset_ && (touchUpOffset - *lastTouchOffset_).GetDistance() <= DEFAULT_CLICK_DISTANCE) {
786 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
787 HandleOnChange();
788 }
789 CloseMenu();
790 }
791 lastTouchOffset_.reset();
792 }
793 }
794
HandleOnChange()795 void CustomMenuItemPattern::HandleOnChange()
796 {
797 auto host = GetHost();
798 CHECK_NULL_VOID(host);
799 auto hub = host->GetEventHub<MenuItemEventHub>();
800 CHECK_NULL_VOID(hub);
801 auto onChange = hub->GetOnChange();
802 auto selectedChangeEvent = hub->GetSelectedChangeEvent();
803 SetChange();
804 if (selectedChangeEvent) {
805 TAG_LOGI(AceLogTag::ACE_MENU, "trigger selectedChangeEvent");
806 selectedChangeEvent(IsSelected());
807 }
808 if (onChange) {
809 TAG_LOGI(AceLogTag::ACE_MENU, "trigger onChange");
810 onChange(IsSelected());
811 }
812 }
813
OnHover(bool isHover)814 void MenuItemPattern::OnHover(bool isHover)
815 {
816 isHovered_ = isHover;
817 auto pipeline = PipelineBase::GetCurrentContext();
818 CHECK_NULL_VOID(pipeline);
819 auto theme = pipeline->GetTheme<SelectTheme>();
820 CHECK_NULL_VOID(theme);
821 auto props = GetPaintProperty<MenuItemPaintProperty>();
822 CHECK_NULL_VOID(props);
823 auto menu = GetMenu(false);
824 CHECK_NULL_VOID(menu);
825 auto menuPattern = menu->GetPattern<MenuPattern>();
826 CHECK_NULL_VOID(menuPattern);
827 auto host = GetHost();
828 CHECK_NULL_VOID(host);
829 auto parent = AceType::DynamicCast<UINode>(host->GetParent());
830
831 if (isHover || isSubMenuShowed_) {
832 // keep hover color when subMenu showed
833 SetBgBlendColor(theme->GetHoverColor());
834 ShowSubMenu();
835 props->UpdateHover(true);
836 menuPattern->OnItemPressed(parent, index_, true, true);
837 } else {
838 SetBgBlendColor(Color::TRANSPARENT);
839 props->UpdateHover(false);
840 menuPattern->OnItemPressed(parent, index_, false, true);
841 }
842 PlayBgColorAnimation();
843 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
844 }
845
OnVisibleChange(bool isVisible)846 void MenuItemPattern::OnVisibleChange(bool isVisible)
847 {
848 auto host = GetHost();
849 CHECK_NULL_VOID(host);
850 auto parentNode = host->GetParent();
851 CHECK_NULL_VOID(parentNode);
852 if (parentNode->GetTag() == V2::MENU_ITEM_GROUP_ETS_TAG) {
853 parentNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
854 }
855 }
856
OnKeyEvent(const KeyEvent & event)857 bool MenuItemPattern::OnKeyEvent(const KeyEvent& event)
858 {
859 if (event.action != KeyAction::DOWN) {
860 return false;
861 }
862 auto host = GetHost();
863 CHECK_NULL_RETURN(host, false);
864 auto focusHub = host->GetOrCreateFocusHub();
865 CHECK_NULL_RETURN(focusHub, false);
866 if (event.code == KeyCode::KEY_ENTER) {
867 focusHub->OnClick(event);
868 return true;
869 }
870 if (event.code == KeyCode::KEY_DPAD_RIGHT && GetSubBuilder() && !isSubMenuShowed_) {
871 auto pipeline = PipelineBase::GetCurrentContext();
872 CHECK_NULL_RETURN(pipeline, false);
873 auto theme = pipeline->GetTheme<SelectTheme>();
874 CHECK_NULL_RETURN(theme, false);
875 SetBgBlendColor(theme->GetHoverColor());
876 PlayBgColorAnimation();
877 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
878 ShowSubMenu();
879 return true;
880 }
881 return false;
882 }
883
OnKeyEvent(const KeyEvent & event)884 bool CustomMenuItemPattern::OnKeyEvent(const KeyEvent& event)
885 {
886 if (event.action != KeyAction::DOWN) {
887 return false;
888 }
889 auto host = GetHost();
890 CHECK_NULL_RETURN(host, false);
891 auto focusHub = host->GetOrCreateFocusHub();
892 CHECK_NULL_RETURN(focusHub, false);
893 if (event.code == KeyCode::KEY_ENTER || event.code == KeyCode::KEY_SPACE) {
894 focusHub->OnClick(event);
895 CloseMenu();
896 return true;
897 }
898 return false;
899 }
900
InitLongPressEvent()901 void MenuItemPattern::InitLongPressEvent()
902 {
903 auto gesture = GetHost()->GetOrCreateGestureEventHub();
904 CHECK_NULL_VOID(gesture);
905 auto longPressCallback = [weak = WeakClaim(this)](GestureEvent& info) {
906 auto itemPattern = weak.Upgrade();
907 auto menuWrapper = itemPattern->GetMenuWrapper();
908 CHECK_NULL_VOID(menuWrapper);
909 auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
910 CHECK_NULL_VOID(menuWrapperPattern);
911 auto topLevelMenuPattern = itemPattern->GetMenuPattern(true);
912 CHECK_NULL_VOID(topLevelMenuPattern);
913 if (itemPattern && itemPattern->GetSubBuilder() != nullptr &&
914 menuWrapperPattern->GetPreviewMode() == MenuPreviewMode::NONE &&
915 !(topLevelMenuPattern->IsSelectOverlayCustomMenu())) {
916 itemPattern->ShowSubMenu();
917 }
918 };
919 longPressEvent_ = MakeRefPtr<LongPressEvent>(std::move(longPressCallback));
920 gesture->SetLongPressEvent(longPressEvent_);
921 }
922
RegisterWrapperMouseEvent()923 void MenuItemPattern::RegisterWrapperMouseEvent()
924 {
925 auto menuWrapper = GetMenuWrapper();
926 if (menuWrapper && !wrapperMouseEvent_) {
927 auto inputHub = menuWrapper->GetOrCreateInputEventHub();
928 CHECK_NULL_VOID(inputHub);
929 auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
930 CHECK_NULL_VOID(menuWrapperPattern);
931
932 auto mouseTask = [weak = WeakClaim(this), menuWrapperPattern](MouseInfo& info) {
933 auto pattern = weak.Upgrade();
934 CHECK_NULL_VOID(pattern);
935 if (menuWrapperPattern) {
936 menuWrapperPattern->HandleMouseEvent(info, pattern);
937 }
938 };
939 wrapperMouseEvent_ = MakeRefPtr<InputEvent>(std::move(mouseTask));
940 inputHub->AddOnMouseEvent(wrapperMouseEvent_);
941 }
942 }
943
AddSelfHoverRegion(const RefPtr<FrameNode> & targetNode)944 void MenuItemPattern::AddSelfHoverRegion(const RefPtr<FrameNode>& targetNode)
945 {
946 OffsetF topLeftPoint;
947 OffsetF bottomRightPoint;
948 auto frameSize = targetNode->GetGeometryNode()->GetMarginFrameSize();
949 topLeftPoint = targetNode->GetPaintRectOffset();
950 bottomRightPoint = targetNode->GetPaintRectOffset() + OffsetF(frameSize.Width(), frameSize.Height());
951 AddHoverRegions(topLeftPoint, bottomRightPoint);
952 }
953
GetSubMenuPosition(const RefPtr<FrameNode> & targetNode)954 OffsetF MenuItemPattern::GetSubMenuPosition(const RefPtr<FrameNode>& targetNode)
955 { // show menu at left top point of targetNode
956 auto frameSize = targetNode->GetGeometryNode()->GetMarginFrameSize();
957 OffsetF position = targetNode->GetPaintRectOffset() + OffsetF(frameSize.Width(), 0.0);
958 return position;
959 }
960
961
AddHoverRegions(const OffsetF & topLeftPoint,const OffsetF & bottomRightPoint)962 void MenuItemPattern::AddHoverRegions(const OffsetF& topLeftPoint, const OffsetF& bottomRightPoint)
963 {
964 TouchRegion hoverRegion = TouchRegion(
965 Offset(topLeftPoint.GetX(), topLeftPoint.GetY()), Offset(bottomRightPoint.GetX(), bottomRightPoint.GetY()));
966 hoverRegions_.emplace_back(hoverRegion);
967 }
968
IsInHoverRegions(double x,double y)969 bool MenuItemPattern::IsInHoverRegions(double x, double y)
970 {
971 for (auto hoverRegion : hoverRegions_) {
972 if (hoverRegion.ContainsInRegion(x, y)) {
973 return true;
974 }
975 }
976 return false;
977 }
978
PlayBgColorAnimation(bool isHoverChange)979 void MenuItemPattern::PlayBgColorAnimation(bool isHoverChange)
980 {
981 auto pipeline = PipelineBase::GetCurrentContext();
982 CHECK_NULL_VOID(pipeline);
983 auto theme = pipeline->GetTheme<SelectTheme>();
984 CHECK_NULL_VOID(theme);
985 AnimationOption option;
986 if (isHoverChange) {
987 option.SetDuration(theme->GetHoverAnimationDuration());
988 option.SetCurve(Curves::FRICTION);
989 } else {
990 option.SetDuration(theme->GetPressAnimationDuration());
991 option.SetCurve(Curves::SHARP);
992 }
993
994 AnimationUtils::Animate(option, [weak = WeakClaim(this)]() {
995 auto pattern = weak.Upgrade();
996 CHECK_NULL_VOID(pattern);
997 auto clickableArea = pattern->GetClickableArea();
998 CHECK_NULL_VOID(clickableArea);
999 auto renderContext = clickableArea->GetRenderContext();
1000 CHECK_NULL_VOID(renderContext);
1001 renderContext->BlendBgColor(pattern->GetBgBlendColor());
1002 });
1003 }
1004
UpdateImageNode(RefPtr<FrameNode> & row,RefPtr<FrameNode> & selectIcon)1005 void MenuItemPattern::UpdateImageNode(RefPtr<FrameNode>& row, RefPtr<FrameNode>& selectIcon)
1006 {
1007 auto pipeline = PipelineBase::GetCurrentContext();
1008 CHECK_NULL_VOID(pipeline);
1009 auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1010 CHECK_NULL_VOID(itemProperty);
1011 auto symbol = itemProperty->GetSelectSymbol();
1012 if (itemProperty->GetSelectIconSrc().value_or("").empty() &&
1013 Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1014 // iamge -> symbol
1015 row->RemoveChild(selectIcon);
1016 selectIcon = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG,
1017 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<TextPattern>(); });
1018 auto selectTheme = pipeline->GetTheme<SelectTheme>();
1019 CHECK_NULL_VOID(selectTheme);
1020 auto props = selectIcon->GetLayoutProperty<TextLayoutProperty>();
1021 CHECK_NULL_VOID(props);
1022 props->UpdateFontSize(selectTheme->GetEndIconWidth());
1023 props->UpdateSymbolColorList({ selectTheme->GetMenuIconColor() });
1024 symbol(AccessibilityManager::WeakClaim(AccessibilityManager::RawPtr(selectIcon)));
1025 } else {
1026 // image -> image
1027 auto iconTheme = pipeline->GetTheme<IconTheme>();
1028 CHECK_NULL_VOID(iconTheme);
1029 auto userIcon = itemProperty->GetSelectIconSrc().value_or("");
1030 auto iconPath = userIcon.empty() ? iconTheme->GetIconPath(InternalResource::ResourceId::MENU_OK_SVG) : userIcon;
1031 auto selectTheme = pipeline->GetTheme<SelectTheme>();
1032 CHECK_NULL_VOID(selectTheme);
1033 ImageSourceInfo imageSourceInfo;
1034 imageSourceInfo.SetSrc(iconPath);
1035 auto props = selectIcon->GetLayoutProperty<ImageLayoutProperty>();
1036 CHECK_NULL_VOID(props);
1037 props->UpdateImageSourceInfo(imageSourceInfo);
1038 UpdateIconSrc(selectIcon, selectTheme->GetIconSideLength(), selectTheme->GetIconSideLength(),
1039 selectTheme->GetMenuIconColor(), userIcon.empty());
1040 }
1041 }
1042
UpdateSymbolNode(RefPtr<FrameNode> & row,RefPtr<FrameNode> & selectIcon)1043 void MenuItemPattern::UpdateSymbolNode(RefPtr<FrameNode>& row, RefPtr<FrameNode>& selectIcon)
1044 {
1045 auto pipeline = PipelineBase::GetCurrentContext();
1046 CHECK_NULL_VOID(pipeline);
1047 auto props = selectIcon->GetLayoutProperty<TextLayoutProperty>();
1048 CHECK_NULL_VOID(props);
1049 auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1050 CHECK_NULL_VOID(itemProperty);
1051 auto selectTheme = pipeline->GetTheme<SelectTheme>();
1052 CHECK_NULL_VOID(selectTheme);
1053 auto symbol = itemProperty->GetSelectSymbol();
1054 if (itemProperty->GetSelectIconSrc().value_or("").empty()) {
1055 // symbol -> symbol
1056 props->UpdateFontSize(selectTheme->GetEndIconWidth());
1057 props->UpdateSymbolColorList({ selectTheme->GetMenuIconColor() });
1058 if (symbol) {
1059 symbol(AccessibilityManager::WeakClaim(AccessibilityManager::RawPtr(selectIcon)));
1060 } else {
1061 auto menuTheme = pipeline->GetTheme<MenuTheme>();
1062 CHECK_NULL_VOID(menuTheme);
1063 uint32_t symbolId = menuTheme->GetSymbolId();
1064 props->UpdateSymbolSourceInfo(SymbolSourceInfo(symbolId));
1065 }
1066 } else {
1067 // symbol -> image
1068 row->RemoveChild(selectIcon);
1069 selectIcon = FrameNode::CreateFrameNode(
1070 V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
1071 ImageSourceInfo imageSourceInfo;
1072 auto userIcon = itemProperty->GetSelectIconSrc().value_or("");
1073 imageSourceInfo.SetSrc(userIcon);
1074 auto props = selectIcon->GetLayoutProperty<ImageLayoutProperty>();
1075 CHECK_NULL_VOID(props);
1076 props->UpdateImageSourceInfo(imageSourceInfo);
1077 UpdateIconSrc(selectIcon, selectTheme->GetIconSideLength(), selectTheme->GetIconSideLength(),
1078 selectTheme->GetMenuIconColor(), userIcon.empty());
1079 }
1080 }
1081
1082
AddSelectIcon(RefPtr<FrameNode> & row)1083 void MenuItemPattern::AddSelectIcon(RefPtr<FrameNode>& row)
1084 {
1085 auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1086 CHECK_NULL_VOID(itemProperty);
1087 if (!itemProperty->GetSelectIcon().value_or(false)) {
1088 if (selectIcon_) {
1089 row->RemoveChildAtIndex(0);
1090 selectIcon_ = nullptr;
1091 itemProperty->SetSelectSymbol(nullptr);
1092 row->MarkModifyDone();
1093 row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1094 }
1095 return;
1096 }
1097 if (!selectIcon_) {
1098 if (!itemProperty->GetSelectIconSrc().value_or("").empty() ||
1099 Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1100 selectIcon_ = FrameNode::CreateFrameNode(
1101 V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
1102 } else {
1103 selectIcon_ = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG,
1104 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<TextPattern>(); });
1105 }
1106 }
1107 if (selectIcon_->GetTag() == V2::IMAGE_ETS_TAG) {
1108 UpdateImageNode(row, selectIcon_);
1109 } else {
1110 UpdateSymbolNode(row, selectIcon_);
1111 }
1112 auto renderContext = selectIcon_->GetRenderContext();
1113 CHECK_NULL_VOID(renderContext);
1114 renderContext->SetVisible(isSelected_);
1115
1116 selectIcon_->MountToParent(row, 0);
1117 selectIcon_->MarkModifyDone();
1118 selectIcon_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1119 }
1120
AddExpandIcon(RefPtr<FrameNode> & row)1121 void MenuItemPattern::AddExpandIcon(RefPtr<FrameNode>& row)
1122 {
1123 auto host = GetHost();
1124 CHECK_NULL_VOID(host);
1125 auto menuNode = GetMenu();
1126 auto menuPattern = menuNode ? menuNode->GetPattern<MenuPattern>() : nullptr;
1127 auto menuProperty = menuNode ? menuNode->GetLayoutProperty<MenuLayoutProperty>() : nullptr;
1128 CHECK_NULL_VOID(menuProperty);
1129 auto canExpand = GetSubBuilder() != nullptr && menuPattern
1130 && !menuPattern->IsEmbedded() && !menuPattern->IsStackSubmenu()
1131 && (expandingMode_ == SubMenuExpandingMode::EMBEDDED || expandingMode_ == SubMenuExpandingMode::STACK);
1132 if (!canExpand) {
1133 if (expandIcon_) {
1134 row->RemoveChild(expandIcon_);
1135 expandIcon_ = nullptr;
1136 row->MarkModifyDone();
1137 row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1138 }
1139 return;
1140 }
1141 if (!expandIcon_) {
1142 expandIcon_ = FrameNode::CreateFrameNode(
1143 V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
1144 CHECK_NULL_VOID(expandIcon_);
1145 }
1146 auto pipeline = PipelineBase::GetCurrentContext();
1147 CHECK_NULL_VOID(pipeline);
1148 auto iconTheme = pipeline->GetTheme<IconTheme>();
1149 CHECK_NULL_VOID(iconTheme);
1150 auto iconPath = iconTheme->GetIconPath(
1151 expandingMode_ == SubMenuExpandingMode::STACK
1152 ? InternalResource::ResourceId::IC_PUBLIC_ARROW_RIGHT_SVG
1153 : InternalResource::ResourceId::IC_PUBLIC_ARROW_DOWN_SVG);
1154 auto selectTheme = pipeline->GetTheme<SelectTheme>();
1155 CHECK_NULL_VOID(selectTheme);
1156 ImageSourceInfo imageSourceInfo(iconPath);
1157 auto props = expandIcon_->GetLayoutProperty<ImageLayoutProperty>();
1158 CHECK_NULL_VOID(props);
1159 props->UpdateImageSourceInfo(imageSourceInfo);
1160 UpdateIconSrc(expandIcon_, selectTheme->GetIconSideLength(), selectTheme->GetIconSideLength(),
1161 selectTheme->GetMenuIconColor(), true);
1162
1163 auto expandIconIndex = row->GetChildren().size();
1164 expandIcon_->MountToParent(row, expandIconIndex);
1165 expandIcon_->MarkModifyDone();
1166 expandIcon_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1167 }
1168
AddClickableArea()1169 void MenuItemPattern::AddClickableArea()
1170 {
1171 if (expandingMode_ == SubMenuExpandingMode::EMBEDDED && GetSubBuilder() != nullptr && !IsEmbedded() &&
1172 !clickableArea_) {
1173 auto host = GetHost();
1174 CHECK_NULL_VOID(host);
1175 auto hostAccessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
1176 CHECK_NULL_VOID(hostAccessibilityProperty);
1177 hostAccessibilityProperty->SetAccessibilityLevel(AccessibilityProperty::Level::NO_STR);
1178 auto clickableArea = FrameNode::CreateFrameNode(V2::ROW_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
1179 AceType::MakeRefPtr<LinearLayoutPattern>(false));
1180 CHECK_NULL_VOID(clickableArea);
1181 auto pipeline = PipelineBase::GetCurrentContext();
1182 CHECK_NULL_VOID(pipeline);
1183 auto theme = pipeline->GetTheme<SelectTheme>();
1184 CHECK_NULL_VOID(theme);
1185 BorderRadiusProperty border;
1186 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1187 border.SetRadius(theme->GetMenuDefaultInnerRadius());
1188 } else {
1189 border.SetRadius(theme->GetInnerBorderRadius());
1190 }
1191 auto clickableContext = clickableArea->GetRenderContext();
1192 CHECK_NULL_VOID(clickableContext);
1193 clickableContext->UpdateBorderRadius(border);
1194 auto menuProperty = host->GetLayoutProperty<MenuItemLayoutProperty>();
1195 CHECK_NULL_VOID(menuProperty);
1196 std::string content = menuProperty->GetContent().value_or("");
1197 std::string label = menuProperty->GetLabel().value_or("");
1198 auto accessibilityProperty = clickableArea->GetAccessibilityProperty<AccessibilityProperty>();
1199 CHECK_NULL_VOID(accessibilityProperty);
1200 accessibilityProperty->SetAccessibilityText(content + "," + label);
1201 clickableArea_ = clickableArea;
1202 clickableArea_->MountToParent(host, CLICKABLE_AREA_VIEW_INDEX);
1203 }
1204 }
1205
AddStackSubMenuHeader(RefPtr<FrameNode> & menuNode)1206 void MenuItemPattern::AddStackSubMenuHeader(RefPtr<FrameNode>& menuNode)
1207 {
1208 auto host = GetHost();
1209 CHECK_NULL_VOID(host);
1210 auto layoutProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1211 CHECK_NULL_VOID(layoutProperty);
1212 auto pipeline = PipelineBase::GetCurrentContext();
1213 CHECK_NULL_VOID(pipeline);
1214 auto iconTheme = pipeline->GetTheme<IconTheme>();
1215 CHECK_NULL_VOID(iconTheme);
1216 auto selectTheme = pipeline->GetTheme<SelectTheme>();
1217 CHECK_NULL_VOID(selectTheme);
1218 auto iconPath = iconTheme->GetIconPath(InternalResource::ResourceId::IC_PUBLIC_ARROW_RIGHT_SVG);
1219 ImageSourceInfo imageSourceInfo;
1220 imageSourceInfo.SetSrc(iconPath);
1221 imageSourceInfo.SetFillColor(selectTheme->GetMenuIconColor());
1222 auto content = layoutProperty->GetContent().value_or(layoutProperty->GetLabel().value_or(""));
1223
1224 MenuItemProperties menuItemProps;
1225 menuItemProps.content = content;
1226 menuItemProps.endIcon = imageSourceInfo;
1227 MenuItemModelNG menuItemModel;
1228 menuItemModel.Create(menuItemProps);
1229 auto stack = ViewStackProcessor::GetInstance();
1230
1231 auto titleItem = AceType::DynamicCast<FrameNode>(stack->Finish());
1232 auto pattern = titleItem->GetPattern<MenuItemPattern>();
1233 CHECK_NULL_VOID(pattern);
1234 pattern->SetIsStackSubmenuHeader();
1235 titleItem->MountToParent(menuNode, 0);
1236 }
1237
GetClickableArea()1238 RefPtr<FrameNode> MenuItemPattern::GetClickableArea()
1239 {
1240 auto host = GetHost();
1241 CHECK_NULL_RETURN(host, nullptr);
1242 auto clickableArea = host->GetChildAtIndex(CLICKABLE_AREA_VIEW_INDEX);
1243 CHECK_NULL_RETURN(clickableArea, host);
1244 auto clickableAreaNode = AceType::DynamicCast<FrameNode>(clickableArea);
1245 CHECK_NULL_RETURN(clickableAreaNode, host);
1246 return clickableAreaNode;
1247 }
1248
UpdateIcon(RefPtr<FrameNode> & row,bool isStart)1249 void MenuItemPattern::UpdateIcon(RefPtr<FrameNode>& row, bool isStart)
1250 {
1251 auto pipeline = PipelineBase::GetCurrentContext();
1252 CHECK_NULL_VOID(pipeline);
1253 auto selectTheme = pipeline->GetTheme<SelectTheme>();
1254 CHECK_NULL_VOID(selectTheme);
1255 auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1256 CHECK_NULL_VOID(itemProperty);
1257 ImageSourceInfo defaultImageSourceInfo;
1258 auto iconSrc = isStart ? itemProperty->GetStartIcon().value_or(defaultImageSourceInfo)
1259 : itemProperty->GetEndIcon().value_or(defaultImageSourceInfo);
1260 auto symbol = isStart ? itemProperty->GetStartSymbol() : itemProperty->GetEndSymbol();
1261 auto& iconNode = isStart ? startIcon_ : endIcon_;
1262 if (iconSrc.GetSrc().empty() && symbol == nullptr) {
1263 row->RemoveChild(iconNode); // it's safe even if iconNode is nullptr
1264 iconNode = nullptr;
1265 if (isStart) {
1266 itemProperty->SetStartSymbol(nullptr);
1267 } else {
1268 itemProperty->SetEndSymbol(nullptr);
1269 }
1270 row->MarkModifyDone();
1271 row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1272 return;
1273 }
1274 if (!iconNode) {
1275 if (symbol) {
1276 iconNode = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG,
1277 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<TextPattern>(); });
1278 } else {
1279 iconNode = FrameNode::CreateFrameNode(
1280 V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
1281 }
1282 CHECK_NULL_VOID(iconNode);
1283 }
1284 if (iconNode->GetTag() == V2::IMAGE_ETS_TAG) {
1285 UpdateImageIcon(row, iconNode, iconSrc, symbol, isStart);
1286 } else {
1287 UpdateSymbolIcon(row, iconNode, iconSrc, symbol, isStart);
1288 }
1289 iconNode->MountToParent(row, ((isStart && selectIcon_) || (!isStart && label_)) ? 1 : 0);
1290 iconNode->MarkModifyDone();
1291 iconNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1292 }
1293
UpdateImageIcon(RefPtr<FrameNode> & row,RefPtr<FrameNode> & iconNode,ImageSourceInfo & iconSrc,std::function<void (WeakPtr<NG::FrameNode>)> & symbol,bool isStart)1294 void MenuItemPattern::UpdateImageIcon(RefPtr<FrameNode>& row, RefPtr<FrameNode>& iconNode, ImageSourceInfo& iconSrc,
1295 std::function<void(WeakPtr<NG::FrameNode>)>& symbol, bool isStart)
1296 {
1297 auto pipeline = PipelineBase::GetCurrentContext();
1298 CHECK_NULL_VOID(pipeline);
1299 auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1300 CHECK_NULL_VOID(itemProperty);
1301 auto selectTheme = pipeline->GetTheme<SelectTheme>();
1302 CHECK_NULL_VOID(selectTheme);
1303 if (symbol) {
1304 // iamge -> symbol
1305 row->RemoveChild(iconNode);
1306 iconNode = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
1307 []() { return AceType::MakeRefPtr<TextPattern>(); });
1308
1309 auto props = iconNode->GetLayoutProperty<TextLayoutProperty>();
1310 CHECK_NULL_VOID(props);
1311 props->UpdateFontSize(selectTheme->GetEndIconWidth());
1312 props->UpdateSymbolColorList({ selectTheme->GetMenuIconColor() });
1313 symbol(AccessibilityManager::WeakClaim(AccessibilityManager::RawPtr(iconNode)));
1314 } else {
1315 // image -> image
1316 auto iconWidth = isStart ? selectTheme->GetIconSideLength() : selectTheme->GetEndIconWidth();
1317 auto iconHeight = isStart ? selectTheme->GetIconSideLength() : selectTheme->GetEndIconHeight();
1318 ImageSourceInfo imageSourceInfo(iconSrc);
1319 auto props = iconNode->GetLayoutProperty<ImageLayoutProperty>();
1320 CHECK_NULL_VOID(props);
1321 props->UpdateImageSourceInfo(imageSourceInfo);
1322 bool useDefaultThemeIcon = UseDefaultThemeIcon(imageSourceInfo);
1323 UpdateIconSrc(iconNode, iconWidth, iconHeight, selectTheme->GetMenuIconColor(), useDefaultThemeIcon);
1324 }
1325 }
1326
UseDefaultThemeIcon(const ImageSourceInfo & imageSourceInfo)1327 bool MenuItemPattern::UseDefaultThemeIcon(const ImageSourceInfo& imageSourceInfo)
1328 {
1329 if (imageSourceInfo.IsSvg()) {
1330 auto src = imageSourceInfo.GetSrc();
1331 auto srcId = src.substr(SYSTEM_RESOURCE_PREFIX.size(),
1332 src.substr(0, src.rfind(".svg")).size() - SYSTEM_RESOURCE_PREFIX.size());
1333 if (srcId.find("ohos_") != std::string::npos) {
1334 return true;
1335 }
1336 uint64_t parsedSrcId = StringUtils::StringToLongUint(srcId);
1337 return (parsedSrcId != 0
1338 && (parsedSrcId >= MIN_SYSTEM_RESOURCE_ID)
1339 && (parsedSrcId <= MAX_SYSTEM_RESOURCE_ID));
1340 }
1341 return false;
1342 }
1343
UpdateSymbolIcon(RefPtr<FrameNode> & row,RefPtr<FrameNode> & iconNode,ImageSourceInfo & iconSrc,std::function<void (WeakPtr<NG::FrameNode>)> & symbol,bool isStart)1344 void MenuItemPattern::UpdateSymbolIcon(RefPtr<FrameNode>& row, RefPtr<FrameNode>& iconNode, ImageSourceInfo& iconSrc,
1345 std::function<void(WeakPtr<NG::FrameNode>)>& symbol, bool isStart)
1346 {
1347 auto pipeline = PipelineBase::GetCurrentContext();
1348 CHECK_NULL_VOID(pipeline);
1349 auto props = iconNode->GetLayoutProperty<TextLayoutProperty>();
1350 CHECK_NULL_VOID(props);
1351 auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1352 CHECK_NULL_VOID(itemProperty);
1353 auto selectTheme = pipeline->GetTheme<SelectTheme>();
1354 CHECK_NULL_VOID(selectTheme);
1355 if (symbol) {
1356 // symbol -> symbol
1357 props->UpdateFontSize(selectTheme->GetEndIconWidth());
1358 props->UpdateSymbolColorList({ selectTheme->GetMenuIconColor() });
1359 symbol(AccessibilityManager::WeakClaim(AccessibilityManager::RawPtr(iconNode)));
1360 } else {
1361 // symbol -> image
1362 row->RemoveChild(iconNode);
1363 iconNode = FrameNode::CreateFrameNode(
1364 V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
1365 auto iconWidth = isStart ? selectTheme->GetIconSideLength() : selectTheme->GetEndIconWidth();
1366 auto iconHeight = isStart ? selectTheme->GetIconSideLength() : selectTheme->GetEndIconHeight();
1367 ImageSourceInfo imageSourceInfo(iconSrc);
1368 auto props = iconNode->GetLayoutProperty<ImageLayoutProperty>();
1369 CHECK_NULL_VOID(props);
1370 props->UpdateImageSourceInfo(imageSourceInfo);
1371 UpdateIconSrc(iconNode, iconWidth, iconHeight, selectTheme->GetMenuIconColor(), false);
1372 }
1373 }
1374
UpdateText(RefPtr<FrameNode> & row,RefPtr<MenuLayoutProperty> & menuProperty,bool isLabel)1375 void MenuItemPattern::UpdateText(RefPtr<FrameNode>& row, RefPtr<MenuLayoutProperty>& menuProperty, bool isLabel)
1376 {
1377 auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1378 CHECK_NULL_VOID(itemProperty);
1379 auto content = isLabel ? itemProperty->GetLabel().value_or("") : itemProperty->GetContent().value_or("");
1380 auto& node = isLabel ? label_ : content_;
1381 if (content.empty()) {
1382 (void)row->RemoveChild(node); // it's safe even if node is nullptr
1383 node = nullptr;
1384 row->MarkModifyDone();
1385 row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1386 return;
1387 }
1388
1389 if (!node) {
1390 node = FrameNode::CreateFrameNode(
1391 V2::TEXT_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<TextPattern>());
1392 }
1393 auto textProperty = node ? node->GetLayoutProperty<TextLayoutProperty>() : nullptr;
1394 CHECK_NULL_VOID(textProperty);
1395 auto renderContext = node->GetRenderContext();
1396 CHECK_NULL_VOID(renderContext);
1397 renderContext->UpdateClipEdge(false);
1398 auto context = PipelineBase::GetCurrentContext();
1399 auto theme = context ? context->GetTheme<SelectTheme>() : nullptr;
1400 CHECK_NULL_VOID(theme);
1401 auto layoutDirection = itemProperty->GetNonAutoLayoutDirection();
1402 TextAlign textAlign = static_cast<TextAlign>(theme->GetMenuItemContentAlign());
1403 if (layoutDirection == TextDirection::RTL) {
1404 if (textAlign == TextAlign::LEFT) {
1405 textAlign = TextAlign::RIGHT;
1406 } else if (textAlign ==TextAlign::RIGHT) {
1407 textAlign = TextAlign::LEFT;
1408 } else if (textAlign == TextAlign::START) {
1409 textAlign = TextAlign::END;
1410 } else if (textAlign == TextAlign::END) {
1411 textAlign = TextAlign::START;
1412 }
1413 textProperty->UpdateTextAlign(textAlign);
1414 }
1415 UpdateFont(menuProperty, theme, isLabel);
1416 textProperty->UpdateContent(content);
1417 UpdateTextOverflow(textProperty, theme);
1418 node->MountToParent(row, isLabel ? 0 : DEFAULT_NODE_SLOT);
1419 node->MarkModifyDone();
1420 node->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1421 }
1422
UpdateTextOverflow(RefPtr<TextLayoutProperty> & textProperty,RefPtr<SelectTheme> & theme)1423 void MenuItemPattern::UpdateTextOverflow(RefPtr<TextLayoutProperty>& textProperty,
1424 RefPtr<SelectTheme>& theme)
1425 {
1426 if (theme && Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_THIRTEEN)) {
1427 if (theme->GetExpandDisplay()) {
1428 textProperty->UpdateTextOverflow(TextOverflow::ELLIPSIS);
1429 textProperty->UpdateMaxLines(1);
1430 } else {
1431 textProperty->UpdateMaxLines(std::numeric_limits<int32_t>::max());
1432 }
1433 } else {
1434 textProperty->UpdateTextOverflow(TextOverflow::ELLIPSIS);
1435 textProperty->UpdateMaxLines(1);
1436 }
1437 UpdateMaxLinesFromTheme(textProperty);
1438 }
1439
UpdateFont(RefPtr<MenuLayoutProperty> & menuProperty,RefPtr<SelectTheme> & theme,bool isLabel)1440 void MenuItemPattern::UpdateFont(RefPtr<MenuLayoutProperty>& menuProperty, RefPtr<SelectTheme>& theme, bool isLabel)
1441 {
1442 auto itemProperty = GetLayoutProperty<MenuItemLayoutProperty>();
1443 CHECK_NULL_VOID(itemProperty);
1444 auto& node = isLabel ? label_ : content_;
1445 auto textProperty = node ? node->GetLayoutProperty<TextLayoutProperty>() : nullptr;
1446 CHECK_NULL_VOID(textProperty);
1447
1448 auto fontSize = isLabel ? itemProperty->GetLabelFontSize() : itemProperty->GetFontSize();
1449 UpdateFontSize(textProperty, menuProperty, fontSize, theme->GetMenuFontSize());
1450 auto fontWeight = isLabel ? itemProperty->GetLabelFontWeight() : itemProperty->GetFontWeight();
1451 UpdateFontWeight(textProperty, menuProperty, fontWeight);
1452 auto fontStyle = isLabel ? itemProperty->GetLabelItalicFontStyle() : itemProperty->GetItalicFontStyle();
1453 UpdateFontStyle(textProperty, menuProperty, fontStyle);
1454 auto fontColor = isLabel ? itemProperty->GetLabelFontColor() : itemProperty->GetFontColor();
1455 auto menuItemNode = GetHost();
1456 UpdateFontColor(
1457 node, menuProperty, fontColor, isLabel ? theme->GetSecondaryFontColor() : theme->GetMenuFontColor());
1458 if (!isLabel) {
1459 auto menuItemRenderContext = menuItemNode->GetRenderContext();
1460 CHECK_NULL_VOID(menuItemRenderContext);
1461 auto renderContext = node->GetRenderContext();
1462 CHECK_NULL_VOID(renderContext);
1463 if (menuItemRenderContext->HasForegroundColor()) {
1464 textProperty->UpdateTextColor(menuItemRenderContext->GetForegroundColorValue());
1465 renderContext->UpdateForegroundColor(menuItemRenderContext->GetForegroundColorValue());
1466 }
1467 }
1468 auto fontFamily = isLabel ? itemProperty->GetLabelFontFamily() : itemProperty->GetFontFamily();
1469 UpdateFontFamily(textProperty, menuProperty, fontFamily);
1470 }
1471
UpdateMaxLinesFromTheme(RefPtr<TextLayoutProperty> & textProperty)1472 void MenuItemPattern::UpdateMaxLinesFromTheme(RefPtr<TextLayoutProperty>& textProperty)
1473 {
1474 auto host = GetHost();
1475 CHECK_NULL_VOID(host);
1476 auto pipeline = host->GetContext();
1477 CHECK_NULL_VOID(pipeline);
1478 auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
1479 CHECK_NULL_VOID(menuTheme);
1480 auto fontScale = pipeline->GetFontScale();
1481 if (NearEqual(fontScale, menuTheme->GetBigFontSizeScale()) ||
1482 NearEqual(fontScale, menuTheme->GetLargeFontSizeScale()) ||
1483 NearEqual(fontScale, menuTheme->GetMaxFontSizeScale())) {
1484 textProperty->UpdateMaxLines(menuTheme->GetTextMaxLines());
1485 }
1486 }
1487
UpdateTextNodes()1488 void MenuItemPattern::UpdateTextNodes()
1489 {
1490 auto host = GetHost();
1491 CHECK_NULL_VOID(host);
1492 auto menuNode = GetMenu();
1493 CHECK_NULL_VOID(menuNode);
1494 auto menuProperty = menuNode->GetLayoutProperty<MenuLayoutProperty>();
1495 RefPtr<FrameNode> leftRow =
1496 host->GetChildAtIndex(0) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(0)) : nullptr;
1497 CHECK_NULL_VOID(leftRow);
1498 UpdateText(leftRow, menuProperty, false);
1499 RefPtr<FrameNode> rightRow =
1500 host->GetChildAtIndex(1) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(1)) : nullptr;
1501 CHECK_NULL_VOID(rightRow);
1502 UpdateText(rightRow, menuProperty, true);
1503 if (IsDisabled()) {
1504 UpdateDisabledStyle();
1505 }
1506 host->MarkModifyDone();
1507 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1508 }
1509
IsDisabled()1510 bool MenuItemPattern::IsDisabled()
1511 {
1512 auto eventHub = GetHost()->GetEventHub<MenuItemEventHub>();
1513 CHECK_NULL_RETURN(eventHub, true);
1514 return !eventHub->IsEnabled();
1515 }
1516
UpdateDisabledStyle()1517 void MenuItemPattern::UpdateDisabledStyle()
1518 {
1519 auto context = PipelineBase::GetCurrentContext();
1520 CHECK_NULL_VOID(context);
1521 auto theme = context->GetTheme<SelectTheme>();
1522 CHECK_NULL_VOID(theme);
1523 if (content_) {
1524 content_->GetRenderContext()->UpdateOpacity(theme->GetDisabledFontColorAlpha());
1525 content_->MarkModifyDone();
1526 }
1527 if (label_) {
1528 label_->GetRenderContext()->UpdateOpacity(theme->GetDisabledFontColorAlpha());
1529 label_->MarkModifyDone();
1530 }
1531 if (startIcon_) {
1532 startIcon_->GetRenderContext()->UpdateOpacity(theme->GetDisabledFontColorAlpha());
1533 startIcon_->MarkModifyDone();
1534 }
1535 if (endIcon_) {
1536 endIcon_->GetRenderContext()->UpdateOpacity(theme->GetDisabledFontColorAlpha());
1537 endIcon_->MarkModifyDone();
1538 }
1539 }
1540
SetAccessibilityAction()1541 void MenuItemPattern::SetAccessibilityAction()
1542 {
1543 auto host = GetHost();
1544 CHECK_NULL_VOID(host);
1545 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
1546 CHECK_NULL_VOID(accessibilityProperty);
1547 accessibilityProperty->SetActionSelect([weakPtr = WeakClaim(this)]() {
1548 const auto& pattern = weakPtr.Upgrade();
1549 CHECK_NULL_VOID(pattern);
1550 auto host = pattern->GetHost();
1551 CHECK_NULL_VOID(host);
1552 auto hub = host->GetEventHub<MenuItemEventHub>();
1553 CHECK_NULL_VOID(hub);
1554 auto onChange = hub->GetOnChange();
1555 auto selectedChangeEvent = hub->GetSelectedChangeEvent();
1556 pattern->SetChange();
1557 if (selectedChangeEvent) {
1558 selectedChangeEvent(pattern->IsSelected());
1559 }
1560 if (onChange) {
1561 onChange(pattern->IsSelected());
1562 pattern->RecordChangeEvent();
1563 }
1564 auto context = host->GetRenderContext();
1565 CHECK_NULL_VOID(context);
1566 pattern->MarkIsSelected(pattern->IsSelected());
1567 context->OnMouseSelectUpdate(pattern->IsSelected(), ITEM_FILL_COLOR, ITEM_FILL_COLOR);
1568 if (pattern->GetSubBuilder() != nullptr) {
1569 pattern->ShowSubMenu();
1570 return;
1571 }
1572
1573 pattern->CloseMenu();
1574 });
1575 }
1576
MarkIsSelected(bool isSelected)1577 void MenuItemPattern::MarkIsSelected(bool isSelected)
1578 {
1579 if (isSelected_ == isSelected) {
1580 return;
1581 }
1582 isSelected_ = isSelected;
1583 auto eventHub = GetEventHub<MenuItemEventHub>();
1584 CHECK_NULL_VOID(eventHub);
1585 auto onChange = eventHub->GetOnChange();
1586 auto selectedChangeEvent = eventHub->GetSelectedChangeEvent();
1587 if (selectedChangeEvent) {
1588 selectedChangeEvent(isSelected);
1589 }
1590 if (onChange) {
1591 onChange(isSelected);
1592 }
1593 auto host = GetHost();
1594 CHECK_NULL_VOID(host);
1595 if (isSelected) {
1596 eventHub->SetCurrentUIState(UI_STATE_SELECTED, isSelected);
1597 } else {
1598 eventHub->SetCurrentUIState(UI_STATE_SELECTED, isSelected);
1599 }
1600 }
1601
IsSelectOverlayMenu()1602 bool MenuItemPattern::IsSelectOverlayMenu()
1603 {
1604 auto topLevelMenuPattern = GetMenuPattern(true);
1605 if (!topLevelMenuPattern) {
1606 return false;
1607 }
1608 return topLevelMenuPattern->IsSelectOverlayExtensionMenu() || topLevelMenuPattern->IsSelectOverlayCustomMenu() ||
1609 topLevelMenuPattern->IsSelectOverlaySubMenu();
1610 }
1611
ParseMenuRadius(MenuParam & param)1612 void MenuItemPattern::ParseMenuRadius(MenuParam& param)
1613 {
1614 auto menuWrapperNode = GetMenuWrapper();
1615 CHECK_NULL_VOID(menuWrapperNode);
1616 auto menuWrapperPattern = menuWrapperNode->GetPattern<MenuWrapperPattern>();
1617 CHECK_NULL_VOID(menuWrapperPattern);
1618
1619 if (menuWrapperPattern->GetHasCustomRadius()) {
1620 auto outterMenuNode = GetMenu(true);
1621 CHECK_NULL_VOID(outterMenuNode);
1622 auto menuLayoutProp = outterMenuNode->GetLayoutProperty<MenuLayoutProperty>();
1623 CHECK_NULL_VOID(menuLayoutProp);
1624 if (menuLayoutProp->GetBorderRadius().has_value()) {
1625 BorderRadiusProperty borderRadius = menuLayoutProp->GetBorderRadiusValue();
1626 param.borderRadius = std::make_optional(borderRadius);
1627 }
1628 }
1629 }
1630
IsSubMenu()1631 bool MenuItemPattern::IsSubMenu()
1632 {
1633 auto topLevelMenuPattern = GetMenuPattern(true);
1634 if (!topLevelMenuPattern) {
1635 return false;
1636 }
1637 return topLevelMenuPattern->IsSubMenu();
1638 }
1639
IsEmbedded()1640 bool MenuItemPattern::IsEmbedded()
1641 {
1642 auto parentMenuPattern = GetMenuPattern();
1643 return parentMenuPattern ? parentMenuPattern->IsEmbedded() : false;
1644 }
1645
ModifyDivider()1646 void MenuItemPattern::ModifyDivider()
1647 {
1648 auto menu = GetMenu();
1649 CHECK_NULL_VOID(menu);
1650 auto menuProperty = menu->GetLayoutProperty<MenuLayoutProperty>();
1651 CHECK_NULL_VOID(menuProperty);
1652 auto divider = menuProperty->GetItemDivider();
1653 if (divider.has_value()) {
1654 auto host = GetHost();
1655 CHECK_NULL_VOID(host);
1656 auto paintProperty = host->GetPaintProperty<MenuItemPaintProperty>();
1657 CHECK_NULL_VOID(paintProperty);
1658 paintProperty->UpdateStrokeWidth(divider->strokeWidth);
1659 paintProperty->UpdateStartMargin(divider->startMargin);
1660 paintProperty->UpdateEndMargin(divider->endMargin);
1661 paintProperty->UpdateDividerColor(divider->color);
1662 host->MarkModifyDone();
1663 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1664 }
1665 }
1666
UpdateNeedDivider(bool need)1667 void MenuItemPattern::UpdateNeedDivider(bool need)
1668 {
1669 auto host = GetHost();
1670 CHECK_NULL_VOID(host);
1671 auto paintProperty = host->GetPaintProperty<MenuItemPaintProperty>();
1672 CHECK_NULL_VOID(paintProperty);
1673 paintProperty->UpdateNeedDivider(need);
1674 if (need) {
1675 ModifyDivider();
1676 }
1677 }
1678
GetDividerStroke()1679 float MenuItemPattern::GetDividerStroke()
1680 {
1681 auto host = GetHost();
1682 CHECK_NULL_RETURN(host, 0.0f);
1683 auto props = host->GetPaintProperty<MenuItemPaintProperty>();
1684 CHECK_NULL_RETURN(props, 0.0f);
1685 return props->GetStrokeWidth().value_or(Dimension(0.0f, DimensionUnit::PX)).ConvertToPx();
1686 }
1687
FindTouchedEmbeddedMenuItem(const OffsetF & position)1688 RefPtr<FrameNode> MenuItemPattern::FindTouchedEmbeddedMenuItem(const OffsetF& position)
1689 {
1690 auto host = GetHost();
1691 CHECK_NULL_RETURN(host, nullptr);
1692 if (expandingMode_ != SubMenuExpandingMode::EMBEDDED || !isExpanded_
1693 || embeddedMenu_ == nullptr || embeddedMenu_->GetTag() != V2::MENU_ETS_TAG) {
1694 return host;
1695 }
1696 CHECK_NULL_RETURN(clickableArea_, host);
1697 auto clickableAreaOffset = clickableArea_->GetPaintRectOffset();
1698 auto clickableAreaSize = clickableArea_->GetGeometryNode()->GetFrameSize();
1699 auto clickableAreaZone = RectF(clickableAreaOffset.GetX(), clickableAreaOffset.GetY(),
1700 clickableAreaSize.Width(), clickableAreaSize.Height());
1701 if (clickableAreaZone.IsInRegion(PointF(position.GetX(), position.GetY()))) {
1702 return host;
1703 }
1704 RefPtr<FrameNode> menuItem = nullptr;
1705 for (const auto& child : embeddedMenu_->GetChildren()) {
1706 if (child->GetTag() == V2::MENU_ITEM_ETS_TAG) {
1707 menuItem = AceType::DynamicCast<FrameNode>(child);
1708 }
1709 if (menuItem) {
1710 auto menuItemOffset = menuItem->GetPaintRectOffset();
1711 auto menuItemSize = menuItem->GetGeometryNode()->GetFrameSize();
1712 auto menuItemZone = RectF(menuItemOffset.GetX(), menuItemOffset.GetY(),
1713 menuItemSize.Width(), menuItemSize.Height());
1714 if (menuItemZone.IsInRegion(PointF(position.GetX(), position.GetY()))) {
1715 break;
1716 } else {
1717 menuItem = nullptr;
1718 }
1719 }
1720 }
1721 return menuItem;
1722 }
1723 } // namespace OHOS::Ace::NG
1724