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         return component;
80     }
81     if (LessOrEqual(node.GetHeight().Value(), 0.0)) {
82         node.SetHeight(theme->GetHeight());
83         component->SetHeight(theme->GetHeight());
84         boxComponent->SetHeight(theme->GetHeight());
85     } else {
86         component->SetHeight(boxComponent->GetHeightDimension());
87     }
88     if (GreatNotEqual(boxComponent->GetWidthDimension().Value(), 0.0)) {
89         padding->SetPadding(Edge());
90         component->SetWidth(boxComponent->GetWidthDimension());
91     }
92     if (component->GetHeight().Unit() == DimensionUnit::PERCENT) {
93         component->SetInputButton(true);
94     } else {
95         component->SetRectRadius(component->GetHeight() / 2);
96     }
97     component->SetMouseAnimationType(HoverAnimationType::SCALE);
98     SetChildAttr(component, attrs, theme);
99     return component;
100 }
101 
SetChildAttr(const RefPtr<ButtonComponent> & component,const std::map<std::string,std::string> & attrs,const RefPtr<ButtonTheme> & theme)102 void DOMButtonUtil::SetChildAttr(const RefPtr<ButtonComponent>& component,
103     const std::map<std::string, std::string>& attrs, const RefPtr<ButtonTheme>& theme)
104 {
105     if (!component) {
106         return;
107     }
108     component->SetType(ButtonType::NORMAL);
109     for (const auto& attr : attrs) {
110         if (attr.first == DOM_DISABLED) {
111             component->SetDisabledState(StringToBool(attr.second));
112         } else if (attr.first == DOM_INPUT_AUTO_FOCUS) {
113             component->SetAutoFocusState(StringToBool(attr.second));
114         }
115     }
116     // set text data to Text child
117     std::list<RefPtr<Component>> children = component->GetChildren();
118     RefPtr<PaddingComponent> padding = AceType::DynamicCast<PaddingComponent>(children.front());
119     if (!padding) {
120         return;
121     }
122     RefPtr<TextComponent> textChild = AceType::DynamicCast<TextComponent>(padding->GetChild());
123     auto inputValue = attrs.find(DOM_INPUT_VALUE);
124     if (inputValue != attrs.end()) {
125         textChild->SetData(inputValue->second);
126     }
127     if (component->GetDisabledState()) {
128         auto textStyle = textChild->GetTextStyle();
129         textStyle.SetTextColor(theme->GetTextDisabledColor());
130         textChild->SetTextStyle(textStyle);
131     }
132 }
133 
SetChildStyle(const RefPtr<BoxComponent> & boxComponent,const RefPtr<ButtonComponent> & component,const std::map<std::string,std::string> & styles,DOMInput & node)134 void DOMButtonUtil::SetChildStyle(const RefPtr<BoxComponent>& boxComponent, const RefPtr<ButtonComponent>& component,
135     const std::map<std::string, std::string>& styles, DOMInput& node)
136 {
137     if (!boxComponent || !component) {
138         return;
139     }
140     // get style which is set by theme
141     std::list<RefPtr<Component>> children = component->GetChildren();
142     RefPtr<PaddingComponent> padding = AceType::DynamicCast<PaddingComponent>(children.front());
143     if (!padding) {
144         return;
145     }
146     RefPtr<TextComponent> textChild = AceType::DynamicCast<TextComponent>(padding->GetChild());
147     TextStyle parentStyle = textChild->GetTextStyle();
148     static const LinearMapNode<void (*)(
149         const std::string&, const DOMInput&, const RefPtr<ButtonComponent>&, TextStyle&)>
150         styleOperators[] = {
151             { DOM_INPUT_BACKGROUND_COLOR,
152                 [](const std::string& value, const DOMInput& node, const RefPtr<ButtonComponent>& component,
153                     TextStyle& style) { component->SetBackgroundColor(node.ParseColor(value)); } },
154             { DOM_INPUT_CLICKED_COLOR,
155                 [](const std::string& value, const DOMInput& node, const RefPtr<ButtonComponent>& component,
156                     TextStyle& style) { component->SetClickedColor(node.ParseColor(value)); } },
157             { DOM_INPUT_COLOR,
158                 [](const std::string& value, const DOMInput& node, const RefPtr<ButtonComponent>& component,
159                     TextStyle& style) { style.SetTextColor(node.ParseColor(value)); } },
160             { DOM_INPUT_DISABLE_COLOR,
161                 [](const std::string& value, const DOMInput& node, const RefPtr<ButtonComponent>& component,
162                     TextStyle& style) { component->SetDisabledColor(node.ParseColor(value)); } },
163             { DOM_INPUT_FOCUS_COLOR,
164                 [](const std::string& value, const DOMInput& node, const RefPtr<ButtonComponent>& component,
165                     TextStyle& style) { component->SetFocusColor(node.ParseColor(value)); } },
166             { DOM_INPUT_FONT_FAMILY,
167                 [](const std::string& value, const DOMInput& node, const RefPtr<ButtonComponent>& component,
168                     TextStyle& style) {
169                     std::vector<std::string> fontFamilies;
170                     std::stringstream sstr(value);
171                     std::string fontFamily;
172                     while (getline(sstr, fontFamily, ',')) {
173                         fontFamilies.emplace_back(fontFamily);
174                     }
175                     style.SetFontFamilies(fontFamilies);
176                 } },
177             { DOM_INPUT_FONT_SIZE,
178                 [](const std::string& value, const DOMInput& node, const RefPtr<ButtonComponent>& component,
179                     TextStyle& style) { style.SetFontSize(node.ParseDimension(value)); } },
180             { DOM_INPUT_FONT_WEIGHT,
181                 [](const std::string& value, const DOMInput& node, const RefPtr<ButtonComponent>& component,
182                     TextStyle& style) { style.SetFontWeight(ConvertStrToFontWeight(value)); } },
183             { DOM_INPUT_RECT_RADIUS,
184                 [](const std::string& value, const DOMInput& node, const RefPtr<ButtonComponent>& component,
185                     TextStyle& style) { component->SetRectRadius(node.ParseDimension(value)); } },
186         };
187     static const LinearMapNode<void (*)(const std::string&, const DOMInput&, const RefPtr<PaddingComponent>&)>
188         paddingStyleOperators[] = {
189             { DOM_PADDING_BOTTOM,
190                 [](const std::string& value, const DOMInput& node, const RefPtr<PaddingComponent>& component) {
191                     component->SetPaddingBottom(node.ParseDimension(value));
192                 } },
193             { DOM_PADDING_LEFT,
194                 [](const std::string& value, const DOMInput& node, const RefPtr<PaddingComponent>& component) {
195                     component->SetPaddingLeft(node.ParseDimension(value));
196                 } },
197             { DOM_PADDING_RIGHT,
198                 [](const std::string& value, const DOMInput& node, const RefPtr<PaddingComponent>& component) {
199                     component->SetPaddingRight(node.ParseDimension(value));
200                 } },
201             { DOM_PADDING_TOP,
202                 [](const std::string& value, const DOMInput& node, const RefPtr<PaddingComponent>& component) {
203                     component->SetPaddingTop(node.ParseDimension(value));
204                 } },
205         };
206     // set text style properties
207     for (const auto& [key, value] : styles) {
208         auto operatorIter = BinarySearchFindIndex(styleOperators, ArraySize(styleOperators), key.c_str());
209         if (operatorIter != -1) {
210             styleOperators[operatorIter].value(value, node, component, parentStyle);
211             continue;
212         }
213         auto paddingOperator =
214             BinarySearchFindIndex(paddingStyleOperators, ArraySize(paddingStyleOperators), key.c_str());
215         if (paddingOperator != -1) {
216             paddingStyleOperators[paddingOperator].value(value, node, padding);
217         }
218     }
219     auto theme = node.GetTheme<ButtonTheme>();
220     if (theme) {
221         component->SetDisabledColor(component->GetBackgroundColor().BlendOpacity(theme->GetBgDisabledAlpha()));
222         component->SetClickedColor(component->GetBackgroundColor().BlendColor(theme->GetClickedColor()));
223         if (parentStyle.GetFontSize() != theme->GetTextStyle().GetFontSize()) {
224             parentStyle.SetAdaptTextSize(parentStyle.GetFontSize(), parentStyle.GetFontSize());
225         }
226     }
227     // set text style to Text child
228     if (SystemProperties::GetDeviceType() != DeviceType::TV) {
229         textChild->SetFocusColor(parentStyle.GetTextColor());
230     }
231     textChild->SetTextStyle(parentStyle);
232 
233     auto backDecoration = boxComponent->GetBackDecoration();
234     if (backDecoration) {
235         const auto& border = backDecoration->GetBorder();
236         if (node.HasBorderRadiusStyle()) {
237             component->SetRectRadius(border.TopLeftRadius().GetX() - border.Top().GetWidth());
238             component->SetRadiusState(true);
239         } else {
240             backDecoration->SetBorderRadius(Radius(component->GetRectRadius()));
241         }
242         if (node.CheckPseduo(backDecoration)) {
243             component->SetType(ButtonType::ICON);
244         }
245     }
246     boxComponent->SetPadding(Edge());
247 }
248 
AddChildEvent(const RefPtr<ButtonComponent> & component,int32_t pageId,const std::string & nodeId,const std::vector<std::string> & events)249 void DOMButtonUtil::AddChildEvent(const RefPtr<ButtonComponent>& component, int32_t pageId, const std::string& nodeId,
250     const std::vector<std::string>& events)
251 {
252     if (!component) {
253         return;
254     }
255     static const LinearMapNode<void (*)(const RefPtr<ButtonComponent>&, EventMarker&)> eventOperators[] = {
256         { DOM_CATCH_BUBBLE_CLICK,
257             [](const RefPtr<ButtonComponent>& component, EventMarker& event) {
258                 event.SetCatchMode(true);
259                 component->SetClickedEventId(event);
260             } },
261         { DOM_CLICK,
262             [](const RefPtr<ButtonComponent>& component, EventMarker& event) {
263                 event.SetCatchMode(false);
264                 component->SetClickedEventId(event);
265             } },
266     };
267     for (const auto& event : events) {
268         auto operatorIter = BinarySearchFindIndex(eventOperators, ArraySize(eventOperators), event.c_str());
269         if (operatorIter != -1) {
270             EventMarker eventMarker(nodeId, event, pageId);
271             eventOperators[operatorIter].value(component, eventMarker);
272         }
273     }
274 }
275 
276 } // namespace OHOS::Ace::Framework
277