1 /*
2 * Copyright (c) 2021 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/input/dom_radio_util.h"
17
18 #include "core/components/common/layout/constants.h"
19 #include "frameworks/bridge/common/utils/utils.h"
20 #include "frameworks/bridge/js_frontend/js_ace_page.h"
21 #include "frameworks/bridge/js_frontend/js_frontend.h"
22
23 namespace OHOS::Ace::Framework {
24 namespace {
25
GetRadioGroups(const DOMNode & node)26 std::shared_ptr<JsPageRadioGroups> GetRadioGroups(const DOMNode& node)
27 {
28 auto context = node.GetPipelineContext().Upgrade();
29 if (!context) {
30 LOGE("pipeline context is null");
31 return nullptr;
32 }
33 auto frontend = context->GetFrontend();
34 if (!frontend) {
35 LOGE("frontend is null");
36 return nullptr;
37 }
38 auto page = frontend->GetPage(node.GetPageId());
39 if (!page) {
40 auto jsFrontend = AceType::DynamicCast<JsFrontend>(frontend);
41 if (!jsFrontend) {
42 LOGE("not js frontend");
43 return nullptr;
44 }
45 page = jsFrontend->GetCurrentReadyPage().Upgrade();
46 }
47 auto jsPage = AceType::DynamicCast<JsAcePage>(page);
48 if (!jsPage) {
49 LOGE("js page is null");
50 return nullptr;
51 }
52 return jsPage->GetRadioGroups();
53 }
54
55 } // namespace
56
InitDefaultValue(const RefPtr<RadioComponent<std::string>> & component)57 void DOMRadioUtil::InitDefaultValue(const RefPtr<RadioComponent<std::string>>& component) {}
58
CreateComponentAndSetChildAttr(const std::map<std::string,std::string> & attrs,DOMInput & node)59 RefPtr<RadioComponent<std::string>> DOMRadioUtil::CreateComponentAndSetChildAttr(
60 const std::map<std::string, std::string>& attrs, DOMInput& node)
61 {
62 // get theme to create component
63 RefPtr<RadioTheme> theme = node.GetTheme<RadioTheme>();
64 RefPtr<RadioComponent<std::string>> component = AceType::MakeRefPtr<RadioComponent<std::string>>(theme);
65 // set default value
66 InitDefaultValue(component);
67 // set the default hot zone
68 auto pipelineContext = node.GetPipelineContext().Upgrade();
69 if (pipelineContext->UseLiteStyle()) {
70 component->SetHorizontalPadding(Dimension(0));
71 component->SetHotZoneVerticalPadding(Dimension(0));
72 }
73 auto boxComponent = node.GetBoxComponent();
74 if (LessOrEqual(node.GetWidth().Value(), 0.0)) {
75 node.SetWidth(theme->GetWidth());
76 boxComponent->SetWidth(theme->GetWidth().Value(), theme->GetWidth().Unit());
77 }
78 if (LessOrEqual(node.GetHeight().Value(), 0.0)) {
79 node.SetHeight(theme->GetHeight());
80 boxComponent->SetHeight(theme->GetHeight().Value(), theme->GetHeight().Unit());
81 }
82 auto diameter = boxComponent->GetHeightDimension() > boxComponent->GetWidthDimension()
83 ? boxComponent->GetWidthDimension()
84 : boxComponent->GetHeightDimension();
85 if (!boxComponent->GetBackDecoration()) {
86 RefPtr<Decoration> backDecoration = AceType::MakeRefPtr<Decoration>();
87 backDecoration->SetBorderRadius(Radius(diameter / 2.0));
88 boxComponent->SetBackDecoration(backDecoration);
89 }
90
91 SetChildAttr(node, component, attrs);
92
93 return component;
94 }
95
SetChildAttr(const DOMInput & node,const RefPtr<RadioComponent<std::string>> & component,const std::map<std::string,std::string> & attrs)96 void DOMRadioUtil::SetChildAttr(const DOMInput& node, const RefPtr<RadioComponent<std::string>>& component,
97 const std::map<std::string, std::string>& attrs)
98 {
99 if (!component) {
100 LOGE("fail to set child attr due to radio component is null");
101 return;
102 }
103 bool checked = false;
104 for (const auto& attr : attrs) {
105 if (attr.first == DOM_INPUT_CHECKED) {
106 checked = StringToBool(attr.second);
107 } else if (attr.first == DOM_INPUT_NAME) {
108 CheckRadioGroup(node, component, attr.second);
109 } else if (attr.first == DOM_DISABLED) {
110 component->SetDisabled(StringToBool(attr.second));
111 } else if (attr.first == DOM_INPUT_VALUE) {
112 component->SetValue(attr.second);
113 }
114 }
115 if (checked) {
116 component->SetGroupValue(component->GetValue());
117 component->SetOriginChecked(checked);
118 } else {
119 component->SetGroupValue("");
120 }
121 }
122
SetChildStyle(const RefPtr<BoxComponent> & boxComponent,const RefPtr<RadioComponent<std::string>> & component,const std::map<std::string,std::string> & styles)123 void DOMRadioUtil::SetChildStyle(const RefPtr<BoxComponent>& boxComponent,
124 const RefPtr<RadioComponent<std::string>>& component, const std::map<std::string, std::string>& styles)
125 {
126 if (!component) {
127 LOGE("fail to set child attr due to radio component is null");
128 return;
129 }
130 // Padding is set in radio specially so that it can respond click event.
131 boxComponent->SetPadding(Edge());
132 for (const auto& style : styles) {
133 if (style.first == DOM_PADDING) {
134 Edge padding;
135 if (Edge::FromString(style.second, padding)) {
136 component->SetHorizontalPadding(padding.Left());
137 component->SetHotZoneVerticalPadding(padding.Top());
138 }
139 }
140 }
141 }
142
AddChildEvent(const RefPtr<RadioComponent<std::string>> & component,int32_t pageId,const std::string & nodeId,const std::vector<std::string> & events)143 void DOMRadioUtil::AddChildEvent(const RefPtr<RadioComponent<std::string>>& component, int32_t pageId,
144 const std::string& nodeId, const std::vector<std::string>& events)
145 {
146 if (!component) {
147 LOGE("fail to add child event due to radio component is null");
148 return;
149 }
150 for (const auto& event : events) {
151 if (event == DOM_CHANGE) {
152 component->SetChangeEvent(EventMarker(nodeId, event, pageId));
153 } else if (event == DOM_CLICK) {
154 EventMarker eventMarker(nodeId, event, pageId);
155 eventMarker.SetCatchMode(false);
156 component->SetClickEvent(eventMarker);
157 } else if (event == DOM_CATCH_BUBBLE_CLICK) {
158 EventMarker eventMarker(nodeId, event, pageId);
159 eventMarker.SetCatchMode(true);
160 component->SetClickEvent(eventMarker);
161 }
162 }
163 }
164
RemoveRadioComponent(const DOMInput & node,const RefPtr<RadioComponent<std::string>> & component)165 void DOMRadioUtil::RemoveRadioComponent(const DOMInput& node, const RefPtr<RadioComponent<std::string>>& component)
166 {
167 if (!component) {
168 return;
169 }
170 auto radioGroups = GetRadioGroups(node);
171 if (radioGroups == nullptr) {
172 return;
173 }
174
175 auto radioGroupIter = (*radioGroups).find(component->GetGroupName());
176 if (radioGroupIter != (*radioGroups).end()) {
177 radioGroupIter->second.RemoveRadio(component);
178 if (radioGroupIter->second.IsEmpty()) {
179 (*radioGroups).erase(radioGroupIter);
180 }
181 }
182 }
183
CheckRadioGroup(const DOMInput & node,const RefPtr<RadioComponent<std::string>> & component,const std::string & name)184 void DOMRadioUtil::CheckRadioGroup(
185 const DOMInput& node, const RefPtr<RadioComponent<std::string>>& component, const std::string& name)
186 {
187 auto radioGroups = GetRadioGroups(node);
188 if (radioGroups == nullptr) {
189 return;
190 }
191 const auto& groupName = component->GetGroupName();
192 if (!groupName.empty()) {
193 if (groupName == name) {
194 return;
195 }
196 auto& oldRadioGroup = (*radioGroups)[groupName];
197 oldRadioGroup.RemoveRadio(component);
198 if (oldRadioGroup.IsEmpty()) {
199 (*radioGroups).erase(groupName);
200 }
201 }
202 component->SetGroupName(name);
203 auto& radioGroup = (*radioGroups)[name];
204 radioGroup.AddRadio(component);
205 }
206
207 } // namespace OHOS::Ace::Framework
208