• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "core/components_ng/pattern/button/button_pattern.h"
17 
18 #include "base/utils/utils.h"
19 #include "core/components/button/button_theme.h"
20 #include "core/components/common/layout/constants.h"
21 #include "core/components/common/properties/color.h"
22 #include "core/components_ng/pattern/button/button_event_hub.h"
23 #include "core/components_ng/pattern/button/toggle_button_pattern.h"
24 #include "core/components_ng/pattern/text/text_layout_property.h"
25 #include "core/components_ng/property/property.h"
26 #include "core/event/mouse_event.h"
27 #include "core/pipeline/pipeline_base.h"
28 
29 namespace OHOS::Ace::NG {
30 namespace {
31 constexpr float HOVER_OPACITY = 0.05f;
32 constexpr float TOUCH_OPACITY = 0.1f;
33 constexpr int32_t HOVER_TO_TOUCH_DURATION = 100;
34 constexpr int32_t TOUCH_DURATION = 250;
35 } // namespace
36 
OnAttachToFrameNode()37 void ButtonPattern::OnAttachToFrameNode()
38 {
39     auto pipeline = PipelineBase::GetCurrentContext();
40     CHECK_NULL_VOID(pipeline);
41     auto buttonTheme = pipeline->GetTheme<ButtonTheme>();
42     CHECK_NULL_VOID(buttonTheme);
43     clickedColor_ = buttonTheme->GetClickedColor();
44 }
45 
UpdateTextLayoutProperty(RefPtr<ButtonLayoutProperty> & layoutProperty,RefPtr<TextLayoutProperty> & textLayoutProperty)46 void ButtonPattern::UpdateTextLayoutProperty(
47     RefPtr<ButtonLayoutProperty>& layoutProperty, RefPtr<TextLayoutProperty>& textLayoutProperty)
48 {
49     CHECK_NULL_VOID(layoutProperty);
50     CHECK_NULL_VOID(textLayoutProperty);
51 
52     auto label = layoutProperty->GetLabelValue("");
53     textLayoutProperty->UpdateContent(label);
54     if (layoutProperty->GetFontSize().has_value()) {
55         textLayoutProperty->UpdateFontSize(layoutProperty->GetFontSize().value());
56     }
57     if (layoutProperty->GetFontWeight().has_value()) {
58         textLayoutProperty->UpdateFontWeight(layoutProperty->GetFontWeight().value());
59     }
60     if (layoutProperty->GetFontColor().has_value()) {
61         textLayoutProperty->UpdateTextColor(layoutProperty->GetFontColor().value());
62     }
63     if (layoutProperty->GetFontStyle().has_value()) {
64         textLayoutProperty->UpdateItalicFontStyle(layoutProperty->GetFontStyle().value());
65     }
66     if (layoutProperty->GetFontFamily().has_value()) {
67         textLayoutProperty->UpdateFontFamily(layoutProperty->GetFontFamily().value());
68     }
69     if (layoutProperty->GetTextOverflow().has_value()) {
70         textLayoutProperty->UpdateTextOverflow(layoutProperty->GetTextOverflow().value());
71     }
72     if (layoutProperty->GetMaxLines().has_value()) {
73         textLayoutProperty->UpdateMaxLines(layoutProperty->GetMaxLines().value());
74     }
75     if (layoutProperty->GetMinFontSize().has_value()) {
76         textLayoutProperty->UpdateAdaptMinFontSize(layoutProperty->GetMinFontSize().value());
77     }
78     if (layoutProperty->GetMaxFontSize().has_value()) {
79         textLayoutProperty->UpdateAdaptMaxFontSize(layoutProperty->GetMaxFontSize().value());
80     }
81     if (layoutProperty->GetHeightAdaptivePolicy().has_value()) {
82         textLayoutProperty->UpdateHeightAdaptivePolicy(layoutProperty->GetHeightAdaptivePolicy().value());
83     }
84 }
85 
IsNeedToHandleHoverOpacity()86 bool ButtonPattern::IsNeedToHandleHoverOpacity()
87 {
88     auto host = GetHost();
89     CHECK_NULL_RETURN(host, false);
90     auto inputEventHub = host->GetOrCreateInputEventHub();
91     auto hoverEffect = inputEventHub->GetHoverEffect();
92     return isHover_ && hoverEffect != HoverEffectType::BOARD && hoverEffect != HoverEffectType::SCALE &&
93            hoverEffect != HoverEffectType::NONE;
94 }
95 
InitButtonLabel()96 void ButtonPattern::InitButtonLabel()
97 {
98     auto host = GetHost();
99     CHECK_NULL_VOID(host);
100     auto focusHub = host->GetFocusHub();
101     CHECK_NULL_VOID(focusHub);
102     auto layoutProperty = GetLayoutProperty<ButtonLayoutProperty>();
103     CHECK_NULL_VOID(layoutProperty);
104     if (!layoutProperty->GetLabel().has_value()) {
105         LOGI("No label, no need to initialize label.");
106         focusHub->SetFocusType(FocusType::SCOPE);
107         return;
108     }
109     focusHub->SetFocusType(FocusType::NODE);
110     auto textNode = DynamicCast<FrameNode>(host->GetFirstChild());
111     CHECK_NULL_VOID(textNode);
112     auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
113     CHECK_NULL_VOID(textLayoutProperty);
114     UpdateTextLayoutProperty(layoutProperty, textLayoutProperty);
115     auto buttonRenderContext = host->GetRenderContext();
116     CHECK_NULL_VOID(buttonRenderContext);
117     auto textRenderContext = textNode->GetRenderContext();
118     CHECK_NULL_VOID(textRenderContext);
119     textRenderContext->UpdateClipEdge(buttonRenderContext->GetClipEdgeValue(false));
120     textNode->MarkModifyDone();
121     textNode->MarkDirtyNode();
122 }
123 
OnModifyDone()124 void ButtonPattern::OnModifyDone()
125 {
126     Pattern::OnModifyDone();
127     InitButtonLabel();
128     HandleBackgroundColor();
129     HandleEnabled();
130     InitHoverEvent();
131     InitTouchEvent();
132 }
133 
InitTouchEvent()134 void ButtonPattern::InitTouchEvent()
135 {
136     if (touchListener_) {
137         return;
138     }
139     auto host = GetHost();
140     CHECK_NULL_VOID(host);
141     auto gesture = host->GetOrCreateGestureEventHub();
142     CHECK_NULL_VOID(gesture);
143     auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
144         auto buttonPattern = weak.Upgrade();
145         CHECK_NULL_VOID(buttonPattern);
146         if (info.GetTouches().front().GetTouchType() == TouchType::DOWN) {
147             buttonPattern->OnTouchDown();
148         }
149         if (info.GetTouches().front().GetTouchType() == TouchType::UP ||
150             info.GetTouches().front().GetTouchType() == TouchType::CANCEL) {
151             buttonPattern->OnTouchUp();
152         }
153     };
154     touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
155     gesture->AddTouchEvent(touchListener_);
156 }
157 
InitHoverEvent()158 void ButtonPattern::InitHoverEvent()
159 {
160     auto host = GetHost();
161     CHECK_NULL_VOID(host);
162     auto eventHub = host->GetEventHub<ButtonEventHub>();
163     auto inputHub = eventHub->GetOrCreateInputEventHub();
164     auto hoverEffect = inputHub->GetHoverEffect();
165     inputHub->SetHoverEffect(hoverEffect == HoverEffectType::BOARD ? HoverEffectType::AUTO : hoverEffect);
166     if (hoverListener_) {
167         return;
168     }
169     auto hoverTask = [weak = WeakClaim(this)](bool isHover) {
170         auto pattern = weak.Upgrade();
171         if (pattern) {
172             pattern->HandleHoverEvent(isHover);
173         }
174     };
175 
176     hoverListener_ = MakeRefPtr<InputEvent>(std::move(hoverTask));
177     inputHub->AddOnHoverEvent(hoverListener_);
178 }
179 
OnTouchDown()180 void ButtonPattern::OnTouchDown()
181 {
182     auto host = GetHost();
183     CHECK_NULL_VOID(host);
184     auto buttonEventHub = GetEventHub<ButtonEventHub>();
185     CHECK_NULL_VOID(buttonEventHub);
186     if (buttonEventHub->GetStateEffect()) {
187         auto renderContext = host->GetRenderContext();
188         CHECK_NULL_VOID(renderContext);
189         backgroundColor_ = renderContext->GetBackgroundColor().value_or(Color::TRANSPARENT);
190         if (isSetClickedColor_) {
191             // for user self-defined
192             renderContext->UpdateBackgroundColor(clickedColor_);
193             return;
194         }
195         // for system default
196         auto isNeedToHandleHoverOpacity = IsNeedToHandleHoverOpacity();
197         AnimateTouchAndHover(renderContext, isNeedToHandleHoverOpacity ? HOVER_OPACITY : 0.0f, TOUCH_OPACITY,
198             isNeedToHandleHoverOpacity ? HOVER_TO_TOUCH_DURATION : TOUCH_DURATION,
199             isNeedToHandleHoverOpacity ? Curves::SHARP : Curves::FRICTION);
200     }
201 }
202 
OnTouchUp()203 void ButtonPattern::OnTouchUp()
204 {
205     auto host = GetHost();
206     CHECK_NULL_VOID(host);
207     auto buttonEventHub = GetEventHub<ButtonEventHub>();
208     CHECK_NULL_VOID(buttonEventHub);
209     auto toggleButtonPattern = host->GetPattern<ToggleButtonPattern>();
210     if (toggleButtonPattern) {
211         toggleButtonPattern->OnClick();
212     }
213     if (buttonEventHub->GetStateEffect() && buttonEventHub->IsEnabled()) {
214         auto renderContext = host->GetRenderContext();
215         if (isSetClickedColor_) {
216             renderContext->UpdateBackgroundColor(backgroundColor_);
217             return;
218         }
219         auto isNeedToHandleHoverOpacity = IsNeedToHandleHoverOpacity();
220         AnimateTouchAndHover(renderContext, TOUCH_OPACITY, isNeedToHandleHoverOpacity ? HOVER_OPACITY : 0.0,
221             isNeedToHandleHoverOpacity ? HOVER_TO_TOUCH_DURATION : TOUCH_DURATION,
222             isNeedToHandleHoverOpacity ? Curves::SHARP : Curves::FRICTION);
223     }
224 }
225 
HandleHoverEvent(bool isHover)226 void ButtonPattern::HandleHoverEvent(bool isHover)
227 {
228     isHover_ = isHover;
229     auto host = GetHost();
230     CHECK_NULL_VOID(host);
231     auto eventHub = host->GetEventHub<EventHub>();
232     CHECK_NULL_VOID(eventHub);
233     auto enabled = eventHub->IsEnabled();
234     auto inputEventHub = host->GetOrCreateInputEventHub();
235     auto hoverEffect = inputEventHub->GetHoverEffect();
236     if (enabled && hoverEffect != HoverEffectType::NONE && hoverEffect != HoverEffectType::SCALE) {
237         auto renderContext = host->GetRenderContext();
238         CHECK_NULL_VOID(renderContext);
239         AnimateTouchAndHover(renderContext, isHover ? 0.0f : HOVER_OPACITY, isHover ? HOVER_OPACITY : 0.0f,
240             TOUCH_DURATION, Curves::FRICTION);
241     }
242 }
243 
HandleBackgroundColor()244 void ButtonPattern::HandleBackgroundColor()
245 {
246     auto host = GetHost();
247     CHECK_NULL_VOID(host);
248     auto pipeline = PipelineBase::GetCurrentContext();
249     CHECK_NULL_VOID(pipeline);
250     auto renderContext = host->GetRenderContext();
251     CHECK_NULL_VOID(renderContext);
252     auto buttonTheme = pipeline->GetTheme<ButtonTheme>();
253     CHECK_NULL_VOID(buttonTheme);
254     if (!renderContext->HasBackgroundColor()) {
255         renderContext->UpdateBackgroundColor(buttonTheme->GetBgColor());
256     }
257     themeBgColor_ = buttonTheme->GetBgColor();
258     themeTextColor_ = buttonTheme->GetTextStyle().GetTextColor();
259 }
260 
HandleEnabled()261 void ButtonPattern::HandleEnabled()
262 {
263     auto host = GetHost();
264     CHECK_NULL_VOID(host);
265     auto eventHub = host->GetEventHub<EventHub>();
266     CHECK_NULL_VOID(eventHub);
267     auto enabled = eventHub->IsEnabled();
268     auto renderContext = host->GetRenderContext();
269     CHECK_NULL_VOID(renderContext);
270     auto pipeline = PipelineBase::GetCurrentContext();
271     CHECK_NULL_VOID(pipeline);
272     auto theme = pipeline->GetTheme<ButtonTheme>();
273     CHECK_NULL_VOID(theme);
274     auto alpha = theme->GetBgDisabledAlpha();
275     auto backgroundColor = renderContext->GetBackgroundColor().value_or(theme->GetBgColor());
276     if (!enabled) {
277         renderContext->OnBackgroundColorUpdate(backgroundColor.BlendOpacity(alpha));
278     } else {
279         renderContext->OnBackgroundColorUpdate(backgroundColor);
280     }
281 }
282 
AnimateTouchAndHover(RefPtr<RenderContext> & renderContext,float startOpacity,float endOpacity,int32_t duration,const RefPtr<Curve> & curve)283 void ButtonPattern::AnimateTouchAndHover(RefPtr<RenderContext>& renderContext, float startOpacity, float endOpacity,
284     int32_t duration, const RefPtr<Curve>& curve)
285 {
286     Color touchColorFrom = Color::FromRGBO(0, 0, 0, startOpacity);
287     Color touchColorTo = Color::FromRGBO(0, 0, 0, endOpacity);
288     renderContext->BlendBgColor(touchColorFrom);
289     AnimationOption option = AnimationOption();
290     option.SetDuration(duration);
291     option.SetCurve(curve);
292     AnimationUtils::Animate(option, [renderContext, touchColorTo]() { renderContext->BlendBgColor(touchColorTo); });
293 }
294 
OnColorConfigurationUpdate()295 void ButtonPattern::OnColorConfigurationUpdate()
296 {
297     auto node = GetHost();
298     if (isColorUpdateFlag_) {
299         node->SetNeedCallChildrenUpdate(false);
300         return;
301     }
302     node->SetNeedCallChildrenUpdate(false);
303     auto pipeline = PipelineBase::GetCurrentContext();
304     auto buttonTheme = pipeline->GetTheme<ButtonTheme>();
305     auto renderContext = node->GetRenderContext();
306     CHECK_NULL_VOID(renderContext);
307     auto buttonLayoutProperty = node->GetLayoutProperty<ButtonLayoutProperty>();
308     CHECK_NULL_VOID(buttonLayoutProperty);
309     if (renderContext->GetBackgroundColor().value_or(themeBgColor_) == themeBgColor_) {
310         auto color = buttonTheme->GetBgColor();
311         renderContext->UpdateBackgroundColor(color);
312     }
313     auto textNode = DynamicCast<FrameNode>(node->GetFirstChild());
314     CHECK_NULL_VOID(textNode);
315     auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
316     CHECK_NULL_VOID(textLayoutProperty);
317     if (textLayoutProperty->GetTextColor().value_or(themeTextColor_) == themeTextColor_) {
318         textLayoutProperty->UpdateTextColor(buttonTheme->GetTextStyle().GetTextColor());
319         textNode->MarkDirtyNode();
320     }
321 }
322 } // namespace OHOS::Ace::NG
323