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