• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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_pattern.h"
17 
18 #include "base/utils/utils.h"
19 #include "core/components/common/properties/shadow_config.h"
20 #include "core/components/select/select_theme.h"
21 #include "core/components_ng/event/click_event.h"
22 #include "core/components_ng/pattern/menu/menu_item/menu_item_pattern.h"
23 #include "core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.h"
24 #include "core/components_ng/pattern/option/option_pattern.h"
25 #include "core/components_v2/inspector/inspector_constants.h"
26 #include "core/event/touch_event.h"
27 #include "core/pipeline/pipeline_base.h"
28 #include "core/pipeline_ng/pipeline_context.h"
29 
30 namespace OHOS::Ace::NG {
OnModifyDone()31 void MenuPattern::OnModifyDone()
32 {
33     RegisterOnTouch();
34     auto host = GetHost();
35     CHECK_NULL_VOID(host);
36     auto focusHub = host->GetOrCreateFocusHub();
37     CHECK_NULL_VOID(focusHub);
38     RegisterOnKeyEvent(focusHub);
39     DisableTabInMenu();
40 
41     if (IsMultiMenu()) {
42         return;
43     }
44 
45     auto renderContext = host->GetRenderContext();
46     CHECK_NULL_VOID(renderContext);
47 
48     // get theme from SelectThemeManager
49     auto pipeline = PipelineBase::GetCurrentContext();
50     CHECK_NULL_VOID(pipeline);
51     auto theme = pipeline->GetTheme<SelectTheme>();
52 
53     // set background color
54     auto bgColor = theme->GetBackgroundColor();
55     renderContext->UpdateBackgroundColor(bgColor);
56 
57     // make menu round rect
58     BorderRadiusProperty borderRadius;
59     borderRadius.SetRadius(theme->GetMenuBorderRadius());
60     renderContext->UpdateBorderRadius(borderRadius);
61     renderContext->UpdateBackShadow(ShadowConfig::DefaultShadowM);
62     renderContext->SetClipToBounds(true);
63 }
64 
65 // close menu on touch up
RegisterOnTouch()66 void MenuPattern::RegisterOnTouch()
67 {
68     CHECK_NULL_VOID_NOLOG(!onTouch_);
69     auto host = GetHost();
70     CHECK_NULL_VOID(host);
71     auto gesture = host->GetOrCreateGestureEventHub();
72     CHECK_NULL_VOID(gesture);
73     auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
74         auto pattern = weak.Upgrade();
75         CHECK_NULL_VOID_NOLOG(pattern);
76         pattern->OnTouchEvent(info);
77     };
78     onTouch_ = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
79     gesture->AddTouchEvent(onTouch_);
80 }
81 
OnTouchEvent(const TouchEventInfo & info)82 void MenuPattern::OnTouchEvent(const TouchEventInfo& info)
83 {
84     if (IsMultiMenuOutside() || IsMultiMenu()) {
85         // not click hide menu for multi menu
86         return;
87     }
88     if (!options_.empty()) {
89         // not click hide menu for select and bindMenu default option
90         return;
91     }
92     auto touchType = info.GetTouches().front().GetTouchType();
93     if (touchType == TouchType::DOWN) {
94         lastTouchOffset_ = info.GetTouches().front().GetLocalLocation();
95     } else if (touchType == TouchType::UP) {
96         if (lastTouchOffset_.has_value() && info.GetTouches().front().GetLocalLocation() == lastTouchOffset_) {
97             HideMenu();
98         }
99         lastTouchOffset_.reset();
100     }
101 }
102 
RegisterOnKeyEvent(const RefPtr<FocusHub> & focusHub)103 void MenuPattern::RegisterOnKeyEvent(const RefPtr<FocusHub>& focusHub)
104 {
105     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
106         auto pattern = wp.Upgrade();
107         CHECK_NULL_RETURN_NOLOG(pattern, false);
108         return pattern->OnKeyEvent(event);
109     };
110     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
111 }
112 
OnKeyEvent(const KeyEvent & event) const113 bool MenuPattern::OnKeyEvent(const KeyEvent& event) const
114 {
115     if (event.action != KeyAction::DOWN || IsMultiMenu()) {
116         return false;
117     }
118     if (event.code == KeyCode::KEY_ESCAPE) {
119         HideMenu();
120         return true;
121     }
122     if (event.code == KeyCode::KEY_DPAD_LEFT && IsSubMenu()) {
123         auto menuWrapper = GetMenuWrapper();
124         CHECK_NULL_RETURN(menuWrapper, true);
125         auto wrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
126         CHECK_NULL_RETURN(wrapperPattern, true);
127         wrapperPattern->HideSubMenu();
128         return true;
129     }
130     return false;
131 }
132 
RemoveParentHoverStyle()133 void MenuPattern::RemoveParentHoverStyle()
134 {
135     if (!IsSubMenu()) {
136         return;
137     }
138     auto menuItemParent = GetParentMenuItem();
139     CHECK_NULL_VOID(menuItemParent);
140     auto menuItemPattern = menuItemParent->GetPattern<MenuItemPattern>();
141     CHECK_NULL_VOID(menuItemPattern);
142     menuItemPattern->SetIsSubMenuShowed(false);
143 
144     auto pipeline = PipelineBase::GetCurrentContext();
145     CHECK_NULL_VOID(pipeline);
146     auto theme = pipeline->GetTheme<SelectTheme>();
147     CHECK_NULL_VOID(theme);
148     menuItemPattern->UpdateBackgroundColor(theme->GetBackgroundColor());
149 }
150 
HideMenu() const151 void MenuPattern::HideMenu() const
152 {
153     if (IsContextMenu()) {
154         SubwindowManager::GetInstance()->HideMenuNG(targetId_);
155         return;
156     }
157     auto pipeline = PipelineContext::GetCurrentContext();
158     CHECK_NULL_VOID(pipeline);
159     auto overlayManager = pipeline->GetOverlayManager();
160     CHECK_NULL_VOID(overlayManager);
161     overlayManager->HideMenu(targetId_);
162     LOGI("MenuPattern closing menu %{public}d", targetId_);
163 }
164 
GetMenuWrapper() const165 RefPtr<FrameNode> MenuPattern::GetMenuWrapper() const
166 {
167     auto host = GetHost();
168     CHECK_NULL_RETURN(host, nullptr);
169     auto parent = host->GetParent();
170     while (parent) {
171         if (parent->GetTag() == V2::MENU_WRAPPER_ETS_TAG) {
172             return AceType::DynamicCast<FrameNode>(parent);
173         }
174         parent = parent->GetParent();
175     }
176     return nullptr;
177 }
178 
179 // judge children has component menu
IsMultiMenuOutside() const180 bool MenuPattern::IsMultiMenuOutside() const
181 {
182     auto host = GetHost();
183     CHECK_NULL_RETURN(host, false);
184     auto child = host->GetChildAtIndex(0);
185     while (child) {
186         // is component menu
187         if (child->GetTag() == V2::MENU_ETS_TAG) {
188             return AceType::DynamicCast<FrameNode>(child);
189         }
190         child = child->GetChildAtIndex(0);
191     }
192     return false;
193 }
194 
195 // mount option on menu
MountOption(const RefPtr<FrameNode> & option)196 void MenuPattern::MountOption(const RefPtr<FrameNode>& option)
197 {
198     auto column = GetMenuColumn();
199     CHECK_NULL_VOID(column);
200     auto pattern = option->GetPattern<OptionPattern>();
201     CHECK_NULL_VOID(pattern);
202     pattern->SetMenu(WeakClaim(RawPtr(GetHost())));
203     AddOptionNode(option);
204     option->MountToParent(column);
205 }
206 
207 // remove option from menu
RemoveOption()208 void MenuPattern::RemoveOption()
209 {
210     auto column = GetMenuColumn();
211     CHECK_NULL_VOID(column);
212     auto endOption = column->GetChildren().back();
213     CHECK_NULL_VOID(endOption);
214     column->RemoveChild(endOption);
215     PopOptionNode();
216 }
217 
GetMenuColumn() const218 RefPtr<FrameNode> MenuPattern::GetMenuColumn() const
219 {
220     auto menu = GetHost();
221     CHECK_NULL_RETURN(menu, nullptr);
222     auto scroll = menu->GetChildren().front();
223     CHECK_NULL_RETURN(scroll, nullptr);
224     auto column = scroll->GetChildren().front();
225     return DynamicCast<FrameNode>(column);
226 }
227 
DisableTabInMenu()228 void MenuPattern::DisableTabInMenu()
229 {
230     if (IsMultiMenu()) {
231         // multi menu not has scroll and column
232         return;
233     }
234     // disable tab in menu
235     auto column = GetMenuColumn();
236     CHECK_NULL_VOID(column);
237     auto columnFocusHub = column->GetOrCreateFocusHub();
238     CHECK_NULL_VOID(columnFocusHub);
239 
240     auto onKeyEvent = [](const KeyEvent& event) -> bool {
241         if (event.action != KeyAction::DOWN) {
242             return false;
243         }
244         return event.code == KeyCode::KEY_TAB;
245     };
246     columnFocusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
247 }
248 } // namespace OHOS::Ace::NG
249