1 /*
2 * Copyright (c) 2021-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/menu/menu_element.h"
17
18 #include "core/components/box/box_element.h"
19
20 namespace OHOS::Ace {
21
PerformBuild()22 void MenuElement::PerformBuild()
23 {
24 data_ = AceType::DynamicCast<MenuComponent>(component_);
25 if (!data_) {
26 LOGE("component is null.");
27 return;
28 }
29
30 auto popup = data_->GetPopup();
31 if (!popup) {
32 LOGE("popup of menu is null.");
33 return;
34 }
35
36 popup->SetOptionClickedCallback([weak = WeakClaim(this)](std::size_t index) {
37 auto refPtr = weak.Upgrade();
38 if (!refPtr) {
39 return;
40 }
41 refPtr->OnOptionCallback(index);
42 });
43 popup->SetPopupCanceledCallback([weak = WeakClaim(this)]() {
44 auto refPtr = weak.Upgrade();
45 if (!refPtr) {
46 return;
47 }
48 refPtr->OnCanceledCallback();
49 });
50 if (!data_->GetOnCancel().IsEmpty()) {
51 jsCancelCallback_ = AceAsyncEvent<void(const std::string&)>::Create(data_->GetOnCancel(), context_);
52 }
53 if (!data_->GetOnSuccess().IsEmpty()) {
54 jsSuccessCallback_ = AceAsyncEvent<void(const std::string&)>::Create(data_->GetOnSuccess(), context_);
55 }
56
57 data_->SetTargetCallback(
58 [weak = WeakClaim(this), isContextMenu = data_->IsContextMenu()](const ComposeId& id, const Offset& point) {
59 auto refPtr = weak.Upgrade();
60 if (!refPtr) {
61 return;
62 }
63 if (isContextMenu) {
64 LOGI("The menu is contextMenu, set the callback.");
65 refPtr->OnTargetContextCallback(id, point);
66 } else {
67 refPtr->OnTargetCallback(id, point);
68 }
69 });
70 }
71
OnTargetCallback(const ComposeId & id,const Offset & point)72 void MenuElement::OnTargetCallback(const ComposeId& id, const Offset& point)
73 {
74 auto context = context_.Upgrade();
75 if (!context) {
76 LOGE("context is null.");
77 return;
78 }
79
80 auto stack = context->GetLastStack();
81 if (!stack) {
82 LOGE("can not get last stack.");
83 return;
84 }
85
86 if (!data_ || !data_->GetPopup()) {
87 LOGE("can not get popup component.");
88 return;
89 }
90
91 auto popup = data_->GetPopup();
92 if (id == "BindMenu" || id.empty()) {
93 popup->ShowDialog(stack, point, point, true);
94 context->SetContextMenu(popup);
95 return;
96 }
97
98 auto targetRender = GetBoxRenderChild(context->GetComposedElementById(id));
99 if (!targetRender) {
100 LOGE("OnTargetCallback: targetRender is null.");
101 return;
102 }
103
104 Size targetSize = targetRender->GetPaintSize();
105 Offset targetGlobalOffset = targetRender->GetOffsetToStage() + targetRender->GetPaintPosition();
106
107 Offset targetRightBottom(targetGlobalOffset.GetX() + targetSize.Width(),
108 targetGlobalOffset.GetY() + targetSize.Height());
109
110 popup->ShowDialog(stack, targetGlobalOffset, targetRightBottom, true);
111 }
112
OnTargetContextCallback(const ComposeId & id,const Offset & point)113 void MenuElement::OnTargetContextCallback(const ComposeId& id, const Offset& point)
114 {
115 LOGI("Execute the callback");
116 auto context = context_.Upgrade();
117 if (!context) {
118 LOGE("Get Context failed, context is null.");
119 return;
120 }
121
122 if (!data_ || !data_->GetPopup()) {
123 LOGE("Get component failed, component is null.");
124 return;
125 }
126
127 auto popup = data_->GetPopup();
128 if (popup) {
129 LOGI("Window is ready, prepare to show the menu.");
130 popup->ShowContextMenu(point);
131 }
132 }
133
GetBoxRenderChild(const RefPtr<Element> & element)134 RefPtr<RenderBox> MenuElement::GetBoxRenderChild(const RefPtr<Element>& element)
135 {
136 auto targetElement = element;
137 RefPtr<RenderNode> targetRender;
138 while (targetElement) {
139 auto boxElement = AceType::DynamicCast<BoxElement>(targetElement);
140 if (boxElement) {
141 targetRender = boxElement->GetRenderNode();
142 break;
143 }
144 targetElement = targetElement->GetChildren().front();
145 }
146 return AceType::DynamicCast<RenderBox>(targetRender);
147 }
148
OnOptionCallback(std::size_t index)149 void MenuElement::OnOptionCallback(std::size_t index)
150 {
151 if (!jsSuccessCallback_) {
152 return;
153 }
154
155 if (!data_ || !data_->GetPopup()) {
156 LOGE("data component or popup is null.");
157 return;
158 }
159
160 auto popup = data_->GetPopup();
161 auto option = popup->GetSelectOption(index);
162 if (!option) {
163 LOGE("option of the index is null.");
164 return;
165 }
166
167 std::string param = std::string("\"selected\",{\"value\":\"").append(option->GetValue().append("\"},null"));
168 jsSuccessCallback_(param);
169 }
170
OnCanceledCallback()171 void MenuElement::OnCanceledCallback()
172 {
173 if (!jsCancelCallback_) {
174 return;
175 }
176
177 jsCancelCallback_("\"cancel\",{},null");
178 }
179
180 } // namespace OHOS::Ace