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/input/dom_button_util.h"
17
18 #include "frameworks/bridge/common/utils/utils.h"
19
20 namespace OHOS::Ace::Framework {
21 namespace {
22
23 constexpr uint32_t WATCH_BACKGROUND_COLOR = 0xff007dff;
24 constexpr uint32_t WATCH_TEXT_COLOR = 0xffffffff;
25 constexpr Dimension BOX_HOVER_RADIUS = 8.0_vp;
26
27 } // namespace
28
InitDefaultValue(const RefPtr<ButtonComponent> & component,const RefPtr<TextComponent> & textChild,const RefPtr<ButtonTheme> & theme)29 void DOMButtonUtil::InitDefaultValue(
30 const RefPtr<ButtonComponent>& component, const RefPtr<TextComponent>& textChild, const RefPtr<ButtonTheme>& theme)
31 {
32 // set default properties
33 component->SetLayoutFlag(LAYOUT_FLAG_EXTEND_TO_PARENT);
34 component->SetBackgroundColor(theme->GetBgColor());
35 component->SetFocusColor(theme->GetBgFocusColor());
36 component->SetFocusAnimationColor(theme->GetBgFocusColor());
37 component->SetHoverColor(theme->GetHoverColor());
38 component->SetCatchMode(false);
39 textChild->SetFocusColor(theme->GetTextFocusColor());
40 // set text styles
41 auto textStyle = theme->GetTextStyle();
42 textStyle.SetAdaptTextSize(textStyle.GetFontSize(), theme->GetMinFontSize());
43 textStyle.SetTextAlign(TextAlign::CENTER);
44 textStyle.SetMaxLines(theme->GetTextMaxLines());
45 textStyle.SetTextOverflow(TextOverflow::ELLIPSIS);
46 // set default background color and text color in watch
47 if (SystemProperties::GetDeviceType() == DeviceType::WATCH) {
48 component->SetBackgroundColor(Color(WATCH_BACKGROUND_COLOR));
49 textStyle.SetTextColor(Color(WATCH_TEXT_COLOR));
50 textChild->SetFocusColor(Color(WATCH_TEXT_COLOR));
51 }
52 textChild->SetTextStyle(textStyle);
53 }
54
CreateComponentAndSetChildAttr(const std::map<std::string,std::string> & attrs,DOMInput & node)55 RefPtr<ButtonComponent> DOMButtonUtil::CreateComponentAndSetChildAttr(
56 const std::map<std::string, std::string>& attrs, DOMInput& node)
57 {
58 std::list<RefPtr<Component>> buttonChildren;
59 RefPtr<ButtonComponent> component = AceType::MakeRefPtr<ButtonComponent>(buttonChildren);
60 RefPtr<ButtonTheme> theme = node.GetTheme<ButtonTheme>();
61 if (!theme) {
62 return component;
63 }
64 RefPtr<TextComponent> textChild = AceType::MakeRefPtr<TextComponent>("");
65 RefPtr<PaddingComponent> padding = AceType::MakeRefPtr<PaddingComponent>();
66 padding->SetPadding(theme->GetPadding());
67 padding->SetChild(textChild);
68 component->AppendChild(padding);
69 InitDefaultValue(component, textChild, theme);
70
71 auto boxComponent = node.GetBoxComponent();
72 if (boxComponent) {
73 if (!boxComponent->GetBackDecoration()) {
74 RefPtr<Decoration> backDecoration = AceType::MakeRefPtr<Decoration>();
75 backDecoration->SetBorderRadius(Radius(BOX_HOVER_RADIUS));
76 boxComponent->SetBackDecoration(backDecoration);
77 }
78 } else {
79 LOGE("boxComponent is null");
80 return component;
81 }
82 if (LessOrEqual(node.GetHeight().Value(), 0.0)) {
83 node.SetHeight(theme->GetHeight());
84 component->SetHeight(theme->GetHeight());
85 boxComponent->SetHeight(theme->GetHeight());
86 } else {
87 component->SetHeight(boxComponent->GetHeightDimension());
88 }
89 if (GreatNotEqual(boxComponent->GetWidthDimension().Value(), 0.0)) {
90 padding->SetPadding(Edge());
91 component->SetWidth(boxComponent->GetWidthDimension());
92 }
93 if (component->GetHeight().Unit() == DimensionUnit::PERCENT) {
94 component->SetInputButton(true);
95 } else {
96 component->SetRectRadius(component->GetHeight() / 2);
97 }
98 component->SetMouseAnimationType(HoverAnimationType::SCALE);
99 SetChildAttr(component, attrs, theme);
100 return component;
101 }
102
SetChildAttr(const RefPtr<ButtonComponent> & component,const std::map<std::string,std::string> & attrs,const RefPtr<ButtonTheme> & theme)103 void DOMButtonUtil::SetChildAttr(const RefPtr<ButtonComponent>& component,
104 const std::map<std::string, std::string>& attrs, const RefPtr<ButtonTheme>& theme)
105 {
106 if (!component) {
107 LOGE("fail to set child attr due to button component is null");
108 return;
109 }
110 component->SetType(ButtonType::NORMAL);
111 for (const auto& attr : attrs) {
112 if (attr.first == DOM_DISABLED) {
113 component->SetDisabledState(StringToBool(attr.second));
114 } else if (attr.first == DOM_INPUT_AUTO_FOCUS) {
115 component->SetAutoFocusState(StringToBool(attr.second));
116 }
117 }
118 // set text data to Text child
119 std::list<RefPtr<Component>> children = component->GetChildren();
120 RefPtr<PaddingComponent> padding = AceType::DynamicCast<PaddingComponent>(children.front());
121 if (!padding) {
122 return;
123 }
124 RefPtr<TextComponent> textChild = AceType::DynamicCast<TextComponent>(padding->GetChild());
125 auto inputValue = attrs.find(DOM_INPUT_VALUE);
126 if (inputValue != attrs.end()) {
127 textChild->SetData(inputValue->second);
128 }
129 if (component->GetDisabledState()) {
130 auto textStyle = textChild->GetTextStyle();
131 textStyle.SetTextColor(theme->GetTextDisabledColor());
132 textChild->SetTextStyle(textStyle);
133 }
134 }
135
SetChildStyle(const RefPtr<BoxComponent> & boxComponent,const RefPtr<ButtonComponent> & component,const std::map<std::string,std::string> & styles,DOMInput & node)136 void DOMButtonUtil::SetChildStyle(const RefPtr<BoxComponent>& boxComponent, const RefPtr<ButtonComponent>& component,
137 const std::map<std::string, std::string>& styles, DOMInput& node)
138 {
139 if (!boxComponent || !component) {
140 LOGE("fail to set child style due to box and button component is null");
141 return;
142 }
143 // get style which is set by theme
144 std::list<RefPtr<Component>> children = component->GetChildren();
145 RefPtr<PaddingComponent> padding = AceType::DynamicCast<PaddingComponent>(children.front());
146 if (!padding) {
147 return;
148 }
149 RefPtr<TextComponent> textChild = AceType::DynamicCast<TextComponent>(padding->GetChild());
150 TextStyle parentStyle = textChild->GetTextStyle();
151 static const LinearMapNode<void (*)(
152 const std::string&, const DOMInput&, const RefPtr<ButtonComponent>&, TextStyle&)>
153 styleOperators[] = {
154 { DOM_INPUT_BACKGROUND_COLOR,
155 [](const std::string& value, const DOMInput& node, const RefPtr<ButtonComponent>& component,
156 TextStyle& style) { component->SetBackgroundColor(node.ParseColor(value)); } },
157 { DOM_INPUT_CLICKED_COLOR,
158 [](const std::string& value, const DOMInput& node, const RefPtr<ButtonComponent>& component,
159 TextStyle& style) { component->SetClickedColor(node.ParseColor(value)); } },
160 { DOM_INPUT_COLOR,
161 [](const std::string& value, const DOMInput& node, const RefPtr<ButtonComponent>& component,
162 TextStyle& style) { style.SetTextColor(node.ParseColor(value)); } },
163 { DOM_INPUT_DISABLE_COLOR,
164 [](const std::string& value, const DOMInput& node, const RefPtr<ButtonComponent>& component,
165 TextStyle& style) { component->SetDisabledColor(node.ParseColor(value)); } },
166 { DOM_INPUT_FOCUS_COLOR,
167 [](const std::string& value, const DOMInput& node, const RefPtr<ButtonComponent>& component,
168 TextStyle& style) { component->SetFocusColor(node.ParseColor(value)); } },
169 { DOM_INPUT_FONT_FAMILY,
170 [](const std::string& value, const DOMInput& node, const RefPtr<ButtonComponent>& component,
171 TextStyle& style) {
172 std::vector<std::string> fontFamilies;
173 std::stringstream sstr(value);
174 std::string fontFamily;
175 while (getline(sstr, fontFamily, ',')) {
176 fontFamilies.emplace_back(fontFamily);
177 }
178 style.SetFontFamilies(fontFamilies);
179 } },
180 { DOM_INPUT_FONT_SIZE,
181 [](const std::string& value, const DOMInput& node, const RefPtr<ButtonComponent>& component,
182 TextStyle& style) { style.SetFontSize(node.ParseDimension(value)); } },
183 { DOM_INPUT_FONT_WEIGHT,
184 [](const std::string& value, const DOMInput& node, const RefPtr<ButtonComponent>& component,
185 TextStyle& style) { style.SetFontWeight(ConvertStrToFontWeight(value)); } },
186 { DOM_INPUT_RECT_RADIUS,
187 [](const std::string& value, const DOMInput& node, const RefPtr<ButtonComponent>& component,
188 TextStyle& style) { component->SetRectRadius(node.ParseDimension(value)); } },
189 };
190 static const LinearMapNode<void (*)(const std::string&, const DOMInput&, const RefPtr<PaddingComponent>&)>
191 paddingStyleOperators[] = {
192 { DOM_PADDING_BOTTOM,
193 [](const std::string& value, const DOMInput& node, const RefPtr<PaddingComponent>& component) {
194 component->SetPaddingBottom(node.ParseDimension(value));
195 } },
196 { DOM_PADDING_LEFT,
197 [](const std::string& value, const DOMInput& node, const RefPtr<PaddingComponent>& component) {
198 component->SetPaddingLeft(node.ParseDimension(value));
199 } },
200 { DOM_PADDING_RIGHT,
201 [](const std::string& value, const DOMInput& node, const RefPtr<PaddingComponent>& component) {
202 component->SetPaddingRight(node.ParseDimension(value));
203 } },
204 { DOM_PADDING_TOP,
205 [](const std::string& value, const DOMInput& node, const RefPtr<PaddingComponent>& component) {
206 component->SetPaddingTop(node.ParseDimension(value));
207 } },
208 };
209 // set text style properties
210 for (const auto& [key, value] : styles) {
211 auto operatorIter = BinarySearchFindIndex(styleOperators, ArraySize(styleOperators), key.c_str());
212 if (operatorIter != -1) {
213 styleOperators[operatorIter].value(value, node, component, parentStyle);
214 continue;
215 }
216 auto paddingOperator =
217 BinarySearchFindIndex(paddingStyleOperators, ArraySize(paddingStyleOperators), key.c_str());
218 if (paddingOperator != -1) {
219 paddingStyleOperators[paddingOperator].value(value, node, padding);
220 }
221 }
222 auto theme = node.GetTheme<ButtonTheme>();
223 if (theme) {
224 component->SetDisabledColor(component->GetBackgroundColor().BlendOpacity(theme->GetBgDisabledAlpha()));
225 component->SetClickedColor(component->GetBackgroundColor().BlendColor(theme->GetClickedColor()));
226 if (parentStyle.GetFontSize() != theme->GetTextStyle().GetFontSize()) {
227 parentStyle.SetAdaptTextSize(parentStyle.GetFontSize(), parentStyle.GetFontSize());
228 }
229 }
230 // set text style to Text child
231 if (SystemProperties::GetDeviceType() != DeviceType::TV) {
232 textChild->SetFocusColor(parentStyle.GetTextColor());
233 }
234 textChild->SetTextStyle(parentStyle);
235
236 auto backDecoration = boxComponent->GetBackDecoration();
237 if (backDecoration) {
238 const auto& border = backDecoration->GetBorder();
239 if (node.HasBorderRadiusStyle()) {
240 component->SetRectRadius(border.TopLeftRadius().GetX() - border.Top().GetWidth());
241 component->SetRadiusState(true);
242 } else {
243 backDecoration->SetBorderRadius(Radius(component->GetRectRadius()));
244 }
245 if (node.CheckPseduo(backDecoration)) {
246 component->SetType(ButtonType::ICON);
247 }
248 }
249 boxComponent->SetPadding(Edge());
250 }
251
AddChildEvent(const RefPtr<ButtonComponent> & component,int32_t pageId,const std::string & nodeId,const std::vector<std::string> & events)252 void DOMButtonUtil::AddChildEvent(const RefPtr<ButtonComponent>& component, int32_t pageId, const std::string& nodeId,
253 const std::vector<std::string>& events)
254 {
255 if (!component) {
256 LOGE("fail to add child event due to button component is null");
257 return;
258 }
259 static const LinearMapNode<void (*)(const RefPtr<ButtonComponent>&, EventMarker&)> eventOperators[] = {
260 { DOM_CATCH_BUBBLE_CLICK,
261 [](const RefPtr<ButtonComponent>& component, EventMarker& event) {
262 event.SetCatchMode(true);
263 component->SetClickedEventId(event);
264 } },
265 { DOM_CLICK,
266 [](const RefPtr<ButtonComponent>& component, EventMarker& event) {
267 event.SetCatchMode(false);
268 component->SetClickedEventId(event);
269 } },
270 };
271 for (const auto& event : events) {
272 auto operatorIter = BinarySearchFindIndex(eventOperators, ArraySize(eventOperators), event.c_str());
273 if (operatorIter != -1) {
274 EventMarker eventMarker(nodeId, event, pageId);
275 eventOperators[operatorIter].value(component, eventMarker);
276 }
277 }
278 }
279
280 } // namespace OHOS::Ace::Framework
281