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 "frameworks/bridge/common/dom/dom_menu.h"
17 
18 #include "frameworks/bridge/common/utils/utils.h"
19 
20 namespace OHOS::Ace::Framework {
21 
DOMMenu(NodeId nodeId,const std::string & nodeName)22 DOMMenu::DOMMenu(NodeId nodeId, const std::string& nodeName) : DOMNode(nodeId, nodeName)
23 {
24     menuChild_ = AceType::MakeRefPtr<MenuComponent>(std::to_string(nodeId), nodeName);
25 }
26 
~DOMMenu()27 DOMMenu::~DOMMenu()
28 {
29     if (!clickMarkerId_.IsEmpty()) {
30         BackEndEventManager<void()>::GetInstance().RemoveBackEndEvent(clickMarkerId_);
31     }
32     if (!focusMarkerId_.IsEmpty()) {
33         BackEndEventManager<void()>::GetInstance().RemoveBackEndEvent(focusMarkerId_);
34     }
35     if (!longPressMarkerId_.IsEmpty()) {
36         BackEndEventManager<void()>::GetInstance().RemoveBackEndEvent(longPressMarkerId_);
37     }
38 }
39 
InitializeStyle()40 void DOMMenu::InitializeStyle()
41 {
42     ResetInitializedStyle();
43 }
44 
ResetInitializedStyle()45 void DOMMenu::ResetInitializedStyle()
46 {
47     menuChild_->InitTheme(GetThemeManager());
48     titleStyle_ = menuChild_->GetTitleStyle();
49 }
50 
OnChildNodeRemoved(const RefPtr<DOMNode> & child)51 void DOMMenu::OnChildNodeRemoved(const RefPtr<DOMNode>& child)
52 {
53     if (!child) {
54         return;
55     }
56 
57     auto option = AceType::DynamicCast<OptionComponent>(child->GetSpecializedComponent());
58     if (!option) {
59         return;
60     }
61 
62     menuChild_->RemoveOption(option);
63 }
64 
PrepareSpecializedComponent()65 void DOMMenu::PrepareSpecializedComponent()
66 {
67     menuChild_->SetTextDirection((IsRightToLeft() ? TextDirection::RTL : TextDirection::LTR));
68     menuChild_->SetTitleStyle(titleStyle_);
69 }
70 
SetSpecializedAttr(const std::pair<std::string,std::string> & attr)71 bool DOMMenu::SetSpecializedAttr(const std::pair<std::string, std::string>& attr)
72 {
73     if (attr.first == DOM_TYPE) {
74         isClickType_ = (attr.second == DOM_CLICK);
75         return true;
76     }
77 
78     if (attr.first == DOM_TITLE && !attr.second.empty()) {
79         menuChild_->SetTitle(attr.second);
80     }
81 
82     return true;
83 }
84 
SetSpecializedStyle(const std::pair<std::string,std::string> & style)85 bool DOMMenu::SetSpecializedStyle(const std::pair<std::string, std::string>& style)
86 {
87     static const LinearMapNode<void (*)(TextStyle&, const std::string&, const DOMMenu&)> menuStyleOperators[] = {
88         { DOM_TEXT_ALLOW_SCALE, [](TextStyle& textStyle, const std::string& val,
89                                 const DOMMenu&) { textStyle.SetAllowScale(StringToBool(val)); } },
90         { DOM_TEXT_FONT_FAMILY, [](TextStyle& textStyle, const std::string& val,
91                                 const DOMMenu& node) { textStyle.SetFontFamilies(node.ParseFontFamilies(val)); } },
92         { DOM_TEXT_FONT_SIZE, [](TextStyle& textStyle, const std::string& val,
93                               const DOMMenu& node) { textStyle.SetFontSize(node.ParseDimension(val)); } },
94         { DOM_TEXT_FONT_STYLE, [](TextStyle& textStyle, const std::string& val,
95                                const DOMMenu&) { textStyle.SetFontStyle(ConvertStrToFontStyle(val)); } },
96         { DOM_TEXT_FONT_WEIGHT, [](TextStyle& textStyle, const std::string& val,
97                                 const DOMMenu&) { textStyle.SetFontWeight(ConvertStrToFontWeight(val)); } },
98         { DOM_TEXT_LETTER_SPACING, [](TextStyle& textStyle, const std::string& val,
99                                    const DOMMenu& node) { textStyle.SetLetterSpacing(node.ParseDimension(val)); } },
100         { DOM_PICKER_TEXT_COLOR, // use define of picker which is the same "text-color"
101             [](TextStyle& textStyle, const std::string& val, const DOMMenu& node) {
102                 textStyle.SetTextColor(node.ParseColor(val));
103             } },
104         { DOM_TEXT_DECORATION, [](TextStyle& textStyle, const std::string& val,
105                                const DOMMenu&) { textStyle.SetTextDecoration(ConvertStrToTextDecoration(val)); } },
106     };
107     auto operatorIter = BinarySearchFindIndex(menuStyleOperators, ArraySize(menuStyleOperators), style.first.c_str());
108     if (operatorIter != -1) {
109         menuStyleOperators[operatorIter].value(titleStyle_, style.second, *this);
110         return true;
111     }
112     return false;
113 }
114 
AddSpecializedEvent(int32_t pageId,const std::string & event)115 bool DOMMenu::AddSpecializedEvent(int32_t pageId, const std::string& event)
116 {
117     if (event == DOM_SELECTED) {
118         menuChild_->SetOnSuccess(EventMarker(GetNodeIdForEvent(), event, pageId));
119     } else if (event == DOM_CANCEL) {
120         menuChild_->SetOnCancel(EventMarker(GetNodeIdForEvent(), event, pageId));
121     } else {
122         return false;
123     }
124     return true;
125 }
126 
GetSpecializedComponent()127 RefPtr<Component> DOMMenu::GetSpecializedComponent()
128 {
129     return menuChild_;
130 }
131 
BindIdNode(const RefPtr<DOMNode> & idNode)132 void DOMMenu::BindIdNode(const RefPtr<DOMNode>& idNode)
133 {
134     if (SystemProperties::GetDeviceType() == DeviceType::TV) {
135         focusMarkerId_ = BackEndEventManager<void()>::GetInstance().GetAvailableMarker();
136         BackEndEventManager<void()>::GetInstance().BindBackendEvent(focusMarkerId_, [weak = WeakClaim(this), idNode]() {
137             if (!idNode || idNode->IsNodeDisabled()) {
138                 return;
139             }
140             auto domMenu = weak.Upgrade();
141             if (!domMenu) {
142                 return;
143             }
144             const auto& targetCallback = domMenu->menuChild_->GetTargetCallback();
145             const auto& targetId = idNode->GetRootComponent()->GetId();
146             if (targetCallback) {
147                 targetCallback(targetId, Offset(0, 0));
148             }
149         });
150         idNode->SetOnFocusClick(focusMarkerId_);
151     }
152     if (isClickType_) {
153         clickMarkerId_ = BackEndEventManager<void(const ClickInfo&)>::GetInstance().GetAvailableMarker();
154         BackEndEventManager<void(const ClickInfo&)>::GetInstance().BindBackendEvent(
155             clickMarkerId_, [weak = WeakClaim(this), idNode](const ClickInfo& clickInfo) {
156                 auto domMenu = weak.Upgrade();
157                 if (!domMenu || !idNode) {
158                     return;
159                 }
160                 const auto& targetCallback = domMenu->menuChild_->GetTargetCallback();
161                 const auto& targetId = idNode->GetRootComponent()->GetId();
162                 if (targetCallback) {
163                     targetCallback(targetId, clickInfo.GetGlobalLocation());
164                 }
165             });
166         idNode->SetOnClick(clickMarkerId_);
167     } else {
168         longPressMarkerId_ =
169             BackEndEventManager<void(const LongPressInfo&)>::GetInstance().GetAvailableMarker();
170         BackEndEventManager<void(const LongPressInfo&)>::GetInstance().BindBackendEvent(
171             longPressMarkerId_, [weak = WeakClaim(this), idNode](const LongPressInfo& longPressInfo) {
172                 auto domMenu = weak.Upgrade();
173                 if (!domMenu || !idNode) {
174                     return;
175                 }
176                 const auto& targetCallback = domMenu->menuChild_->GetTargetCallback();
177                 const auto& targetId = idNode->GetRootComponent()->GetId();
178                 if (targetCallback) {
179                     targetCallback(targetId, longPressInfo.GetGlobalLocation());
180                 }
181             });
182         idNode->SetOnLongPress(longPressMarkerId_);
183     }
184 }
185 
GetJsonDouble(const std::unique_ptr<JsonValue> & json,const std::string & name,double defaultValue) const186 double DOMMenu::GetJsonDouble(
187     const std::unique_ptr<JsonValue>& json, const std::string& name, double defaultValue) const
188 {
189     if (json && json->IsArray() && json->GetArraySize() > 0) {
190         std::unique_ptr<JsonValue> itemValue = json->GetArrayItem(0)->GetValue(name);
191         if (itemValue && itemValue->IsNumber()) {
192             return itemValue->GetDouble();
193         }
194     }
195 
196     return defaultValue;
197 }
198 
CallSpecializedMethod(const std::string & method,const std::string & args)199 void DOMMenu::CallSpecializedMethod(const std::string& method, const std::string& args)
200 {
201     if (method == "show") {
202         std::unique_ptr<JsonValue> argsValue = JsonUtil::ParseJsonString(args);
203         double x = GetJsonDouble(argsValue, "x", 0);
204         double y = GetJsonDouble(argsValue, "y", 0);
205         const auto& targetCallback = menuChild_->GetTargetCallback();
206         if (targetCallback) {
207             targetCallback("", Offset(x, y));
208         }
209     }
210 }
211 
OnChildNodeAdded(const RefPtr<DOMNode> & child,int32_t slot)212 void DOMMenu::OnChildNodeAdded(const RefPtr<DOMNode>& child, int32_t slot)
213 {
214     if (!child) {
215         return;
216     }
217 
218     auto option = AceType::DynamicCast<OptionComponent>(child->GetSpecializedComponent());
219     if (!option) {
220         return;
221     }
222 
223     if (slot < 0) {
224         menuChild_->AppendOption(option);
225     } else {
226         menuChild_->InsertOption(option, static_cast<uint32_t>(slot));
227     }
228 }
229 
230 } // namespace OHOS::Ace::Framework
231