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