1 /*
2  * Copyright (c) 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 #ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_BUTTON_BUTTON_PATTERN_H
17 #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_BUTTON_BUTTON_PATTERN_H
18 
19 #include <optional>
20 
21 #include "base/memory/referenced.h"
22 #include "base/utils/utils.h"
23 #include "core/common/container.h"
24 #include "core/components/button/button_theme.h"
25 #include "core/components/common/layout/constants.h"
26 #include "core/components_ng/base/inspector_filter.h"
27 #include "core/components_ng/event/event_hub.h"
28 #include "core/components_ng/event/focus_hub.h"
29 #include "core/components_ng/pattern/button/button_event_hub.h"
30 #include "core/components_ng/pattern/button/button_layout_algorithm.h"
31 #include "core/components_ng/pattern/button/button_layout_property.h"
32 #include "core/components_ng/pattern/button/button_model_ng.h"
33 #include "core/components_ng/pattern/pattern.h"
34 #include "core/components_ng/pattern/text/text_layout_property.h"
35 namespace OHOS::Ace::NG {
36 enum class ComponentButtonType { POPUP, BUTTON, STEPPER, NAVIGATION };
37 class ButtonPattern : public Pattern {
38     DECLARE_ACE_TYPE(ButtonPattern, Pattern);
39 
40 public:
41     ButtonPattern() = default;
42 
43     ~ButtonPattern() override = default;
44 
IsAtomicNode()45     bool IsAtomicNode() const override
46     {
47         return false;
48     }
49 
CreateEventHub()50     RefPtr<EventHub> CreateEventHub() override
51     {
52         return MakeRefPtr<ButtonEventHub>();
53     }
54 
CreateLayoutAlgorithm()55     RefPtr<LayoutAlgorithm> CreateLayoutAlgorithm() override
56     {
57         return MakeRefPtr<ButtonLayoutAlgorithm>();
58     }
59 
CreateLayoutProperty()60     RefPtr<LayoutProperty> CreateLayoutProperty() override
61     {
62         return MakeRefPtr<ButtonLayoutProperty>();
63     }
64 
GetFocusPattern()65     FocusPattern GetFocusPattern() const override
66     {
67         if (buttonType_ == ComponentButtonType::POPUP || buttonType_ == ComponentButtonType::STEPPER) {
68             FocusPaintParam focusPaintParam;
69             focusPaintParam.SetPaintColor(focusBorderColor_);
70             return { FocusType::NODE, true, FocusStyleType::INNER_BORDER, focusPaintParam };
71         }
72         if (buttonType_ == ComponentButtonType::NAVIGATION) {
73             FocusPaintParam focusPaintParam;
74             focusPaintParam.SetPaintColor(focusBorderColor_);
75             focusPaintParam.SetPaintWidth(focusBorderWidth_);
76             return { FocusType::NODE, true, FocusStyleType::INNER_BORDER, focusPaintParam };
77         }
78         return { FocusType::NODE, true, FocusStyleType::OUTER_BORDER };
79     }
80 
IsNeedAdjustByAspectRatio()81     bool IsNeedAdjustByAspectRatio() override
82     {
83         auto host = GetHost();
84         CHECK_NULL_RETURN(host, false);
85         auto layoutProperty = host->GetLayoutProperty<ButtonLayoutProperty>();
86         CHECK_NULL_RETURN(host, false);
87         return layoutProperty->HasAspectRatio() &&
88                layoutProperty->GetType().value_or(ButtonType::CAPSULE) != ButtonType::CIRCLE;
89     }
90 
SetClickedColor(const Color & color)91     void SetClickedColor(const Color& color)
92     {
93         clickedColor_ = color;
94         isSetClickedColor_ = true;
95     }
96 
SetBlendColor(const std::optional<Color> & blendClickColor,const std::optional<Color> & blendHoverColor)97     void SetBlendColor(const std::optional<Color>& blendClickColor, const std::optional<Color>& blendHoverColor)
98     {
99         blendClickColor_ = blendClickColor;
100         blendHoverColor_ = blendHoverColor;
101     }
102 
SetFocusBorderColor(const Color & color)103     void SetFocusBorderColor(const Color& color)
104     {
105         focusBorderColor_ = color;
106     }
107 
SetFocusBorderWidth(const Dimension & width)108     void SetFocusBorderWidth(const Dimension& width)
109     {
110         focusBorderWidth_ = width;
111     }
112 
setComponentButtonType(const ComponentButtonType & buttonType)113     void setComponentButtonType(const ComponentButtonType& buttonType)
114     {
115         buttonType_ = buttonType;
116     }
117 
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter)118     void ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const override
119     {
120         Pattern::ToJsonValue(json, filter);
121         /* no fixed attr below, just return */
122         if (filter.IsFastFilter()) {
123             return;
124         }
125         auto host = GetHost();
126         CHECK_NULL_VOID(host);
127         auto layoutProperty = host->GetLayoutProperty<ButtonLayoutProperty>();
128         CHECK_NULL_VOID(layoutProperty);
129         auto context = PipelineBase::GetCurrentContext();
130         CHECK_NULL_VOID(context);
131         auto buttonTheme = context->GetTheme<ButtonTheme>();
132         CHECK_NULL_VOID(buttonTheme);
133         auto textStyle = buttonTheme->GetTextStyle();
134         json->PutExtAttr(
135             "type", host->GetTag() == "Toggle" ? "ToggleType.Button"
136             :ConvertButtonTypeToString(layoutProperty->GetType().value_or(ButtonType::CAPSULE)).c_str(), filter);
137         json->PutExtAttr("fontSize",
138             layoutProperty->GetFontSizeValue(layoutProperty->HasLabel() ? textStyle.GetFontSize() : Dimension(0))
139                 .ToString()
140                 .c_str(), filter);
141         json->PutExtAttr("fontWeight",
142         V2::ConvertWrapFontWeightToStirng(layoutProperty->GetFontWeight().value_or(FontWeight::MEDIUM)).c_str(), filter);
143         json->PutExtAttr("fontColor", layoutProperty->GetFontColor()
144                                    .value_or(layoutProperty->HasLabel() ? textStyle.GetTextColor() : Color::BLACK)
145                                    .ColorToString()
146                                    .c_str(), filter);
147         json->PutExtAttr("fontStyle", layoutProperty->GetFontStyle().value_or(Ace::FontStyle::NORMAL) == Ace::FontStyle::NORMAL
148                 ? "FontStyle.Normal"
149                 : "FontStyle.Italic", filter);
150         json->PutExtAttr("label", layoutProperty->GetLabelValue("").c_str(), filter);
151         auto eventHub = host->GetEventHub<ButtonEventHub>();
152         CHECK_NULL_VOID(eventHub);
153         json->PutExtAttr("stateEffect", eventHub->GetStateEffect() ? "true" : "false", filter);
154 
155         auto optionJson = JsonUtil::Create(true);
156         optionJson->Put(
157             "type", ConvertButtonTypeToString(layoutProperty->GetType().value_or(ButtonType::CAPSULE)).c_str());
158         optionJson->Put("stateEffect", eventHub->GetStateEffect() ? "true" : "false");
159         json->PutExtAttr("options", optionJson->ToString().c_str(), filter);
160         ToJsonValueAttribute(json, filter);
161     }
162 
ToJsonValueAttribute(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter)163     void ToJsonValueAttribute(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
164     {
165         auto host = GetHost();
166         CHECK_NULL_VOID(host);
167         auto layoutProperty = host->GetLayoutProperty<ButtonLayoutProperty>();
168         CHECK_NULL_VOID(layoutProperty);
169         auto fontFamilyVector =
170             layoutProperty->GetFontFamily().value_or<std::vector<std::string>>({ "HarmonyOS Sans" });
171         std::string fontFamily;
172         if (!fontFamilyVector.empty()) {
173             fontFamily = fontFamilyVector.at(0);
174             for (uint32_t i = 1; i < fontFamilyVector.size(); ++i) {
175                 fontFamily += ',' + fontFamilyVector.at(i);
176             }
177         }
178         json->PutExtAttr("fontFamily", fontFamily.c_str(), filter);
179         auto fontJsValue = JsonUtil::Create(true);
180         fontJsValue->Put("size", layoutProperty->GetFontSizeValue(Dimension(0)).ToString().c_str());
181         fontJsValue->Put("weight",
182             V2::ConvertWrapFontWeightToStirng(layoutProperty->GetFontWeight().value_or(FontWeight::MEDIUM)).c_str());
183         fontJsValue->Put("family", fontFamily.c_str());
184         fontJsValue->Put(
185             "style", layoutProperty->GetFontStyle().value_or(Ace::FontStyle::NORMAL) == Ace::FontStyle::NORMAL
186                          ? "FontStyle.Normal"
187                          : "FontStyle.Italic");
188         auto labelJsValue = JsonUtil::Create(true);
189         labelJsValue->Put("overflow",
190             V2::ConvertWrapTextOverflowToString(layoutProperty->GetTextOverflow().value_or(TextOverflow::CLIP))
191                 .c_str());
192         labelJsValue->Put("maxLines", std::to_string(layoutProperty->GetMaxLines().value_or(DEFAULT_MAXLINES)).c_str());
193         labelJsValue->Put("minFontSize", layoutProperty->GetMinFontSizeValue(Dimension(0)).ToString().c_str());
194         labelJsValue->Put("maxFontSize", layoutProperty->GetMaxFontSizeValue(Dimension(0)).ToString().c_str());
195         labelJsValue->Put("heightAdaptivePolicy",
196             V2::ConvertWrapTextHeightAdaptivePolicyToString(
197                 layoutProperty->GetHeightAdaptivePolicy().value_or(TextHeightAdaptivePolicy::MAX_LINES_FIRST))
198                 .c_str());
199         labelJsValue->Put("font", fontJsValue->ToString().c_str());
200         json->PutExtAttr("labelStyle", labelJsValue->ToString().c_str(), filter);
201 
202         json->PutExtAttr("buttonStyle",
203             ConvertButtonStyleToString(layoutProperty->GetButtonStyle().value_or(ButtonStyleMode::EMPHASIZE))
204             .c_str(), filter);
205         json->PutExtAttr("controlSize",
206             ConvertControlSizeToString(layoutProperty->GetControlSize().value_or(ControlSize::NORMAL))
207             .c_str(), filter);
208         json->PutExtAttr(
209             "role", ConvertButtonRoleToString(layoutProperty->GetButtonRole().value_or(ButtonRole::NORMAL))
210             .c_str(), filter);
211     }
212 
ConvertButtonRoleToString(ButtonRole buttonRole)213     static std::string ConvertButtonRoleToString(ButtonRole buttonRole)
214     {
215         std::string result;
216         switch (buttonRole) {
217             case ButtonRole::NORMAL:
218                 result = "ButtonRole.NORMAL";
219                 break;
220             case ButtonRole::ERROR:
221                 result = "ButtonRole.ERROR";
222                 break;
223             default:
224                 break;
225         }
226         return result;
227     }
228 
ConvertButtonTypeToString(ButtonType buttonType)229     static std::string ConvertButtonTypeToString(ButtonType buttonType)
230     {
231         std::string result;
232         switch (buttonType) {
233             case ButtonType::NORMAL:
234                 result = "ButtonType.Normal";
235                 break;
236             case ButtonType::CAPSULE:
237                 result = "ButtonType.Capsule";
238                 break;
239             case ButtonType::CIRCLE:
240                 result = "ButtonType.Circle";
241                 break;
242             default:
243                 break;
244         }
245         return result;
246     }
247 
ConvertButtonStyleToString(ButtonStyleMode buttonStyle)248     static std::string ConvertButtonStyleToString(ButtonStyleMode buttonStyle)
249     {
250         std::string result;
251         switch (buttonStyle) {
252             case ButtonStyleMode::NORMAL:
253                 result = "ButtonStyleMode.NORMAL";
254                 break;
255             case ButtonStyleMode::EMPHASIZE:
256                 result = "ButtonStyleMode.EMPHASIZED";
257                 break;
258             case ButtonStyleMode::TEXT:
259                 result = "ButtonStyleMode.TEXTUAL";
260                 break;
261             default:
262                 break;
263         }
264         return result;
265     }
266 
ConvertControlSizeToString(ControlSize controlSize)267     static std::string ConvertControlSizeToString(ControlSize controlSize)
268     {
269         std::string result;
270         switch (controlSize) {
271             case ControlSize::SMALL:
272                 result = "ControlSize.SMALL";
273                 break;
274             case ControlSize::NORMAL:
275                 result = "ControlSize.NORMAL";
276                 break;
277             default:
278                 break;
279         }
280         return result;
281     }
282 
SetLocalLocation(const Offset & localLocation)283     void SetLocalLocation(const Offset& localLocation)
284     {
285         localLocation_ = localLocation;
286     }
287 
GetLocalLocation()288     const Offset& GetLocalLocation() const
289     {
290         return localLocation_;
291     }
292 
SetInHover(bool inHover)293     void SetInHover(bool inHover)
294     {
295         isInHover_ = inHover;
296     }
297 
GetIsInHover()298     bool GetIsInHover() const
299     {
300         return isInHover_;
301     }
302 
GetHoverListener()303     RefPtr<InputEvent>& GetHoverListener()
304     {
305         return hoverListener_;
306     }
307 
GetTouchListener()308     RefPtr<TouchEventImpl>& GetTouchListener()
309     {
310         return touchListener_;
311     }
312 
313     void SetBuilderFunc(ButtonMakeCallback&& makeFunc);
314 
GetBuilderId()315     virtual int32_t GetBuilderId() const
316     {
317         return nodeId_;
318     }
319 
320     void SetButtonPress(double xPos, double yPos);
321 
UseContentModifier()322     virtual bool UseContentModifier() const
323     {
324         return contentModifierNode_ != nullptr;
325     }
326 
327     void OnColorConfigurationUpdate() override;
328 
SetSkipColorConfigurationUpdate()329     void SetSkipColorConfigurationUpdate()
330     {
331         isColorUpdateFlag_ = true;
332     }
333 
SetPreFrameSize(const SizeF & frameSize)334     void SetPreFrameSize(const SizeF& frameSize)
335     {
336         preFrameSize_.SetSizeT(frameSize);
337     }
338 
GetPreFrameSize()339     const SizeF& GetPreFrameSize() const
340     {
341         return preFrameSize_;
342     }
343 
SetHasCustomPadding(bool hasCustomPadding)344     void SetHasCustomPadding(bool hasCustomPadding)
345     {
346         hasCustomPadding_ = hasCustomPadding;
347     }
348 
GetHasCustomPadding()349     bool GetHasCustomPadding()
350     {
351         return hasCustomPadding_;
352     }
353 
354 protected:
IsNeedInitClickEventRecorder()355     bool IsNeedInitClickEventRecorder() const override
356     {
357         return true;
358     }
359 
360     void OnModifyDone() override;
361     void OnAfterModifyDone() override;
362     void OnAttachToFrameNode() override;
363     void InitTouchEvent();
364     void InitHoverEvent();
365     void OnTouchDown();
366     void OnTouchUp();
367     void HandleHoverEvent(bool isHover);
368     void HandleBackgroundColor();
369     void HandleEnabled();
370     void InitButtonLabel();
371     Color GetColorFromType(const RefPtr<ButtonTheme>& theme, const int32_t& type);
372     void AnimateTouchAndHover(RefPtr<RenderContext>& renderContext, int32_t typeFrom, int32_t typeTo, int32_t duration,
373         const RefPtr<Curve>& curve);
374     Color clickedColor_;
375 
376 private:
377     static void UpdateTextLayoutProperty(
378         RefPtr<ButtonLayoutProperty>& layoutProperty, RefPtr<TextLayoutProperty>& textLayoutProperty);
379     static void UpdateTextStyle(
380         RefPtr<ButtonLayoutProperty>& layoutProperty, RefPtr<TextLayoutProperty>& textLayoutProperty);
381     static bool NeedAgingUpdateText(RefPtr<ButtonLayoutProperty>& layoutProperty);
382     bool IsNeedToHandleHoverOpacity();
383     static void UpdateTextFontScale(
384         RefPtr<ButtonLayoutProperty>& layoutProperty, RefPtr<TextLayoutProperty>& textLayoutProperty);
385     Color backgroundColor_;
386     Color focusBorderColor_;
387     Color themeBgColor_;
388     Color themeTextColor_;
389     bool isSetClickedColor_ = false;
390     ComponentButtonType buttonType_ = ComponentButtonType::BUTTON;
391     void FireBuilder();
392     RefPtr<FrameNode> BuildContentModifierNode();
393     GestureEventFunc tapEventFunc_;
394     std::optional<ButtonMakeCallback> makeFunc_;
395     RefPtr<FrameNode> contentModifierNode_;
396     std::optional<GestureEventFunc> clickEventFunc_;
397     int32_t nodeId_ = -1;
398     RefPtr<TouchEventImpl> touchListener_;
399     RefPtr<InputEvent> hoverListener_;
400     bool isHover_ = false;
401     bool isPress_ = false;
402 
403     bool isInHover_ = false;
404     Offset localLocation_;
405     Dimension focusBorderWidth_;
406 
407     std::optional<Color> blendClickColor_ = std::nullopt;
408     std::optional<Color> blendHoverColor_ = std::nullopt;
409 
410     bool isColorUpdateFlag_ = false;
411     SizeF preFrameSize_;
412     bool hasCustomPadding_ = false;
413     ACE_DISALLOW_COPY_AND_MOVE(ButtonPattern);
414 };
415 } // namespace OHOS::Ace::NG
416 
417 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_BUTTON_BUTTON_PATTERN_H
418